Files
linux-scripts/xfreerdp-connect.sh
chiefgeek a1a17e81a1 Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist,
same-site referer bypass, Blackbox-Exporter allowed bot, and all
new exporters, cheat sheets, and automation scripts.
2026-05-25 03:31:08 +02:00

444 lines
15 KiB
Bash
Executable File

#!/usr/bin/env bash
#########################################################################################
#### xfreerdp-connect.sh — RDP connection launcher with Zenity GUI prompt ####
#### Connects to Windows hosts via xfreerdp with optional RemoteApp support. ####
#### Requires: bash, xfreerdp, zenity ####
#### ####
#### Author: Phil Connor ####
#### Contact: contact@mylinux.work ####
#### License: MIT ####
#### Version 1.2 ####
#### ####
#### Usage: ####
#### ./xfreerdp-connect.sh ####
#### ./xfreerdp-connect.sh --help ####
#### ####
#### See --help for all options. ####
#########################################################################################
set -euo pipefail
# ── Colors ────────────────────────────────────────────────────────────
if [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
RESET='\033[0m'
else
RED="" GREEN="" RESET=""
fi
log() { echo -e "${GREEN}[OK]${RESET} $*"; }
err() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
# ── Profile support ──────────────────────────────────────────────────
PROFILE_DIR="$HOME/.config/xfreerdp-connect"
PROFILE_FILE="$PROFILE_DIR/profiles.conf"
profile_list() {
if [[ ! -f "$PROFILE_FILE" ]]; then
echo "No saved profiles."
return
fi
local found=0
while IFS= read -r line; do
if [[ "$line" =~ ^\[(.+)\]$ ]]; then
local name="${BASH_REMATCH[1]}"
local srv=""
while IFS= read -r kv; do
[[ -z "$kv" || "$kv" =~ ^\[ ]] && break
if [[ "$kv" =~ ^server= ]]; then
srv="${kv#server=}"
fi
done
printf " %-20s %s\n" "$name" "$srv"
found=1
fi
done < "$PROFILE_FILE"
if [[ "$found" -eq 0 ]]; then
echo "No saved profiles."
fi
}
profile_load() {
local name="$1"
if [[ ! -f "$PROFILE_FILE" ]]; then
err "Profile '$name' not found (no profiles file)"
exit 1
fi
local in_section=0 found=0
while IFS= read -r line; do
if [[ "$line" =~ ^\[(.+)\]$ ]]; then
if [[ "${BASH_REMATCH[1]}" == "$name" ]]; then
in_section=1
found=1
continue
else
[[ "$in_section" -eq 1 ]] && break
fi
fi
if [[ "$in_section" -eq 1 && -n "$line" ]]; then
case "$line" in
server=*) server="${line#server=}" ;;
port=*) port="${line#port=}" ;;
domain=*) domain="${line#domain=}" ;;
username=*) username="${line#username=}" ;;
app=*) app="${line#app=}" ;;
fullscreen=*) fullscreen="${line#fullscreen=}" ;;
multimon=*) multimon="${line#multimon=}" ;;
drive=*) drive="${line#drive=}" ;;
sound=*) sound="${line#sound=}" ;;
gateway=*) gateway="${line#gateway=}" ;;
admin=*) admin="${line#admin=}" ;;
no_nla=*) no_nla="${line#no_nla=}" ;;
resolution=*) resolution="${line#resolution=}" ;;
esac
fi
done < "$PROFILE_FILE"
if [[ "$found" -eq 0 ]]; then
err "Profile '$name' not found"
exit 1
fi
log "Loaded profile '$name'"
}
profile_save() {
local name="$1"
if [[ -f "$PROFILE_FILE" ]]; then
while IFS= read -r line; do
if [[ "$line" =~ ^\[(.+)\]$ && "${BASH_REMATCH[1]}" == "$name" ]]; then
err "Profile '$name' already exists (use --delete first)"
exit 1
fi
done < "$PROFILE_FILE"
fi
mkdir -p "$PROFILE_DIR"
{
echo "[$name]"
[[ -n "$server" ]] && echo "server=$server"
[[ -n "$port" ]] && echo "port=$port"
[[ -n "$domain" ]] && echo "domain=$domain"
[[ -n "$username" ]] && echo "username=$username"
[[ -n "$app" ]] && echo "app=$app"
[[ "$fullscreen" -eq 1 ]] && echo "fullscreen=1"
[[ "$multimon" -eq 1 ]] && echo "multimon=1"
[[ -n "$drive" ]] && echo "drive=$drive"
[[ "$sound" -eq 1 ]] && echo "sound=1"
[[ -n "$gateway" ]] && echo "gateway=$gateway"
[[ "$admin" -eq 1 ]] && echo "admin=1"
[[ "$no_nla" -eq 1 ]] && echo "no_nla=1"
[[ -n "$resolution" ]] && echo "resolution=$resolution"
echo ""
} >> "$PROFILE_FILE"
log "Saved profile '$name'"
}
profile_delete() {
local name="$1"
if [[ ! -f "$PROFILE_FILE" ]]; then
err "Profile '$name' not found (no profiles file)"
exit 1
fi
local found=0 in_section=0
local tmpfile
tmpfile=$(mktemp)
while IFS= read -r line; do
if [[ "$line" =~ ^\[(.+)\]$ ]]; then
if [[ "${BASH_REMATCH[1]}" == "$name" ]]; then
in_section=1
found=1
continue
else
in_section=0
fi
fi
if [[ "$in_section" -eq 0 ]]; then
echo "$line" >> "$tmpfile"
fi
done < "$PROFILE_FILE"
if [[ "$found" -eq 0 ]]; then
rm -f "$tmpfile"
err "Profile '$name' not found"
exit 1
fi
mv "$tmpfile" "$PROFILE_FILE"
log "Deleted profile '$name'"
}
# ── Defaults ──────────────────────────────────────────────────────────
server=""
port="3389"
domain=""
username=""
password=""
app=""
fullscreen=0
multimon=0
drive=""
sound=0
gateway=""
admin=0
no_nla=0
resolution=""
opt_profile=""
opt_save=""
opt_list=0
opt_delete=""
# ── Usage ─────────────────────────────────────────────────────────────
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS]
Launch an RDP session via xfreerdp. Opens a Zenity GUI prompt if connection
details are not fully provided on the command line.
Options:
--server HOST Hostname or IP (required)
--port PORT RDP port (default: 3389)
--domain DOM Active Directory domain (optional)
--user USER Login username (required)
--pass PASS Password (use - to prompt securely)
--app NAME RemoteApp name (optional — launches app instead of desktop)
--fullscreen Full screen mode (replaces workarea)
--multimon Span across multiple monitors
--drive NAME,PATH Redirect a local folder to the remote session
--sound Enable audio redirection
--gateway HOST Connect through an RD Gateway
--admin Connect to the console/admin session
--no-nla Disable Network Level Authentication
--resolution WxH Fixed resolution (e.g., 1920x1080) instead of dynamic
--profile NAME Load a saved connection profile
--save NAME Save current CLI args as a named profile, then exit
--list List saved profiles and exit
--delete NAME Remove a saved profile and exit
-h, --help Show this help
If --server, --user, and --pass are all provided, connects directly.
Otherwise, opens a Zenity prompt with any provided values pre-filled.
If saved profiles exist, a profile picker is shown before the connection form.
Examples:
# GUI prompt
./$(basename "$0")
# Direct connect
./$(basename "$0") --server 10.0.1.50 --user admin --pass -
# RemoteApp
./$(basename "$0") --server 10.0.1.50 --user admin --pass - --app notepad
# Pre-fill server, prompt for the rest
./$(basename "$0") --server 10.0.1.50
# Save a profile
./$(basename "$0") --server 10.0.1.50 --user pconnor --domain CORP --save work-dc01
# Connect using a profile
./$(basename "$0") --profile work-dc01 --pass -
# List / delete profiles
./$(basename "$0") --list
./$(basename "$0") --delete work-dc01
EOF
}
# ── Parse args ────────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case "$1" in
--server) server="$2"; shift 2 ;;
--port) port="$2"; shift 2 ;;
--domain) domain="$2"; shift 2 ;;
--user) username="$2"; shift 2 ;;
--pass) password="$2"; shift 2 ;;
--app) app="$2"; shift 2 ;;
--fullscreen) fullscreen=1; shift ;;
--multimon) multimon=1; shift ;;
--drive) drive="$2"; shift 2 ;;
--sound) sound=1; shift ;;
--gateway) gateway="$2"; shift 2 ;;
--admin) admin=1; shift ;;
--no-nla) no_nla=1; shift ;;
--resolution) resolution="$2"; shift 2 ;;
--profile) opt_profile="$2"; shift 2 ;;
--save) opt_save="$2"; shift 2 ;;
--list) opt_list=1; shift ;;
--delete) opt_delete="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) err "Unknown option: $1"; usage; exit 1 ;;
esac
done
# ── Profile actions (run and exit) ────────────────────────────────────
if [[ "$opt_list" -eq 1 ]]; then
profile_list
exit 0
fi
if [[ -n "$opt_delete" ]]; then
profile_delete "$opt_delete"
exit 0
fi
if [[ -n "$opt_save" ]]; then
profile_save "$opt_save"
exit 0
fi
if [[ -n "$opt_profile" ]]; then
profile_load "$opt_profile"
fi
# Secure password prompt if --pass -
if [[ "$password" == "-" ]]; then
read -rsp "Password: " password
echo ""
fi
# ── Preflight ─────────────────────────────────────────────────────────
if ! command -v xfreerdp &>/dev/null; then
err "xfreerdp not found. Install with:"
err " Debian/Ubuntu: sudo apt install freerdp3-x11 (or freerdp2-x11)"
err " Fedora/RHEL: sudo dnf install freerdp"
exit 1
fi
# Detect FreeRDP major version (2 or 3)
FREERDP_VER=2
freerdp_ver_output=$(xfreerdp --version 2>&1 || true)
if [[ "$freerdp_ver_output" =~ ([0-9]+)\.[0-9]+ ]]; then
FREERDP_VER="${BASH_REMATCH[1]}"
fi
# ── Prompt if needed ───────────────────────────────────────────────────
if [[ -z "$server" || -z "$username" || -z "$password" ]]; then
if ! command -v zenity &>/dev/null; then
err "Missing required fields and zenity not found for GUI prompt"
err "Provide --server, --user, and --pass on the command line"
exit 1
fi
# Profile picker — show if profiles exist and --profile was not given
if [[ -z "$opt_profile" && -f "$PROFILE_FILE" ]]; then
profiles=()
while IFS= read -r line; do
if [[ "$line" =~ ^\[(.+)\]$ ]]; then
profiles+=("${BASH_REMATCH[1]}")
fi
done < "$PROFILE_FILE"
if [[ ${#profiles[@]} -gt 0 ]]; then
picker_args=()
for p in "${profiles[@]}"; do
picker_args+=("$p")
done
picker_args+=("New connection")
picked=$(zenity --list --title="Select Profile" \
--text="Choose a saved profile or start a new connection" \
--column="Profile" "${picker_args[@]}") || {
log "Cancelled"
exit 0
}
if [[ "$picked" != "New connection" && -n "$picked" ]]; then
profile_load "$picked"
fi
fi
fi
output=$(zenity --forms --title="RDP Connection" \
--text="Enter connection details" \
--separator="," \
--add-entry="Server${server:+ [$server]}" \
--add-entry="Port${port:+ [$port]}" \
--add-entry="Domain${domain:+ [$domain]}" \
--add-entry="Username${username:+ [$username]}" \
--add-password="Password" \
--add-entry="Remote App${app:+ [$app]}") || {
log "Cancelled"
exit 0
}
IFS=, read -r z_server z_port z_domain z_username z_password z_app <<< "$output"
# Use Zenity values only if CLI values were not already set
server="${z_server:-$server}"
port="${z_port:-$port}"
domain="${z_domain:-$domain}"
username="${z_username:-$username}"
password="${z_password:-$password}"
app="${z_app:-$app}"
fi
if [[ -z "$server" ]]; then
err "Server is required"
exit 1
fi
if [[ -z "$username" ]]; then
err "Username is required"
exit 1
fi
# ── Build xfreerdp command ────────────────────────────────────────────
cmd=(xfreerdp
/v:"${server}:${port}"
/u:"${username}"
+clipboard
/from-stdin
)
# Version-specific flags
if [[ "$FREERDP_VER" -ge 3 ]]; then
cmd+=(/cert:ignore)
else
cmd+=(/cert-ignore /rfx)
fi
# Display mode
if [[ "$fullscreen" -eq 1 ]]; then
cmd+=(/f)
elif [[ -n "$resolution" ]]; then
cmd+=("/size:${resolution}")
else
cmd+=(/workarea /dynamic-resolution)
fi
# Multi-monitor
[[ "$multimon" -eq 1 ]] && cmd+=(/multimon)
# Domain
[[ -n "$domain" ]] && cmd+=(/d:"${domain}")
# Drive redirection
[[ -n "$drive" ]] && cmd+=("/drive:${drive}")
# Sound
[[ "$sound" -eq 1 ]] && cmd+=(/sound)
# RD Gateway
[[ -n "$gateway" ]] && cmd+=("/g:${gateway}")
# Admin/console session
[[ "$admin" -eq 1 ]] && cmd+=(/admin)
# Disable NLA
if [[ "$no_nla" -eq 1 ]]; then
if [[ "$FREERDP_VER" -ge 3 ]]; then
cmd+=(/sec:nla:off)
else
cmd+=(-sec-nla)
fi
fi
# RemoteApp
if [[ -n "$app" ]]; then
cmd+=(/t:"${app} on ${server}" "/app:||${app}")
else
cmd+=(/t:"${server}")
fi
# ── Connect ───────────────────────────────────────────────────────────
log "Connecting to ${server}:${port} as ${username}..."
# Password passed via stdin to keep it out of the process list
printf '/p:%s\n' "$password" | "${cmd[@]}"
password=""