diff --git a/src/wg-quick/darwin.bash b/src/wg-quick/darwin.bash --- a/src/wg-quick/darwin.bash +++ b/src/wg-quick/darwin.bash @@ -34,12 +34,16 @@ cmd() { "$@" } +wg() { + awg "$@" +} + die() { echo "$PROGRAM: $*" >&2 exit 1 } -[[ ${BASH_VERSINFO[0]} -ge 4 ]] || die "Version mismatch: bash ${BASH_VERSINFO[0]} detected, when bash 4+ required" +[[ ${BASH_VERSINFO[0]} -ge 3 ]] || die "Version mismatch: bash ${BASH_VERSINFO[0]} detected, when bash 3+ required" CONFIG_SEARCH_PATHS=( /etc/amnezia/amneziawg /usr/local/etc/amnezia/amneziawg ) @@ -113,7 +117,7 @@ get_real_interface() { [[ -f "/var/run/amneziawg/$INTERFACE.name" ]] || return 1 interface="$(< "/var/run/amneziawg/$INTERFACE.name")" [[ -n $interface && -S "/var/run/amneziawg/$interface.sock" ]] || return 1 - diff=$(( $(stat -f %m "/var/run/amneziawg/$interface.sock" 2>/dev/null || echo 200) - $(stat -f %m "/var/run/wireguard/$INTERFACE.name" 2>/dev/null || echo 100) )) + diff=$(( $(stat -f %m "/var/run/amneziawg/$interface.sock" 2>/dev/null || echo 200) - $(stat -f %m "/var/run/amneziawg/$INTERFACE.name" 2>/dev/null || echo 100) )) [[ $diff -ge 2 || $diff -le -2 ]] && return 1 REAL_INTERFACE="$interface" echo "[+] Interface for $INTERFACE is $REAL_INTERFACE" >&2 @@ -153,8 +157,8 @@ del_routes() { } del_if() { - [[ -z $REAL_INTERFACE ]] || cmd rm -f "/var/run/wireguard/$REAL_INTERFACE.sock" - cmd rm -f "/var/run/wireguard/$INTERFACE.name" + [[ -z $REAL_INTERFACE ]] || cmd rm -f "/var/run/amneziawg/$REAL_INTERFACE.sock" + cmd rm -f "/var/run/amneziawg/$INTERFACE.name" } up_if() { @@ -215,29 +219,60 @@ collect_endpoints() { done < <(wg show "$REAL_INTERFACE" endpoints) } -declare -A SERVICE_DNS -declare -A SERVICE_DNS_SEARCH +SERVICE_NAMES=( ) +SERVICE_DNS=( ) +SERVICE_DNS_SEARCH=( ) + +service_index() { + local needle="$1" i + SERVICE_INDEX=-1 + for ((i=0; i<${#SERVICE_NAMES[@]}; i++)); do + [[ ${SERVICE_NAMES[$i]} == "$needle" ]] && { SERVICE_INDEX=$i; return; } + done +} + +array_contains() { + local needle="$1" value + shift + for value in "$@"; do + [[ $value == "$needle" ]] && return 0 + done + return 1 +} collect_new_service_dns() { - local service get_response - local -A found_services + local service get_response i + local found_services=( ) next_names=( ) next_dns=( ) next_search=( ) { read -r _ && while read -r service; do [[ $service == "*"* ]] && service="${service:1}" - found_services["$service"]=1 - [[ -n ${SERVICE_DNS["$service"]} ]] && continue + found_services+=( "$service" ) + service_index "$service" + if [[ $SERVICE_INDEX -lt 0 ]]; then + SERVICE_NAMES+=( "$service" ) + SERVICE_DNS+=( "" ) + SERVICE_DNS_SEARCH+=( "" ) + SERVICE_INDEX=$((${#SERVICE_NAMES[@]} - 1)) + fi + [[ -n ${SERVICE_DNS[$SERVICE_INDEX]} ]] && continue - get_response="$(cmd networksetup -getdnsservers "$service")" + get_response="$(cmd networksetup -getdnsservers "$service" || true)" [[ $get_response == *" "* ]] && get_response="Empty" - [[ -n $get_response ]] && SERVICE_DNS["$service"]="$get_response" + # Some macOS network services (notably USB Ethernet adapters) return + # an empty response or a non-zero status. With `set -e`, either case + # would abort the whole tunnel setup. + SERVICE_DNS[$SERVICE_INDEX]="${get_response:-Empty}" - get_response="$(cmd networksetup -getsearchdomains "$service")" + get_response="$(cmd networksetup -getsearchdomains "$service" || true)" [[ $get_response == *" "* ]] && get_response="Empty" - [[ -n $get_response ]] && SERVICE_DNS_SEARCH["$service"]="$get_response" + SERVICE_DNS_SEARCH[$SERVICE_INDEX]="${get_response:-Empty}" done; } < <(networksetup -listallnetworkservices) - for service in "${!SERVICE_DNS[@]}"; do - if ! [[ -n ${found_services["$service"]} ]]; then - unset SERVICE_DNS["$service"] - unset SERVICE_DNS_SEARCH["$service"] - fi + for ((i=0; i<${#SERVICE_NAMES[@]}; i++)); do + array_contains "${SERVICE_NAMES[$i]}" "${found_services[@]}" || continue + next_names+=( "${SERVICE_NAMES[$i]}" ) + next_dns+=( "${SERVICE_DNS[$i]}" ) + next_search+=( "${SERVICE_DNS_SEARCH[$i]}" ) done + SERVICE_NAMES=( "${next_names[@]}" ) + SERVICE_DNS=( "${next_dns[@]}" ) + SERVICE_DNS_SEARCH=( "${next_search[@]}" ) } set_endpoint_direct_route() { @@ -293,8 +328,9 @@ set_endpoint_direct_route() { set_dns() { - collect_new_service_dns + collect_new_service_dns || { echo "[!] Could not read one or more network services; keeping existing DNS" >&2; return 0; } - local service response - for service in "${!SERVICE_DNS[@]}"; do + local service response i + for ((i=0; i<${#SERVICE_NAMES[@]}; i++)); do + service="${SERVICE_NAMES[$i]}" while read -r response; do [[ $response == *Error* ]] && echo "$response" >&2 done < <( @@ -309,13 +345,14 @@ set_dns() { } del_dns() { - local service response - for service in "${!SERVICE_DNS[@]}"; do + local service response i + for ((i=0; i<${#SERVICE_NAMES[@]}; i++)); do + service="${SERVICE_NAMES[$i]}" while read -r response; do [[ $response == *Error* ]] && echo "$response" >&2 done < <( - cmd networksetup -setdnsservers "$service" ${SERVICE_DNS["$service"]} || true - cmd networksetup -setsearchdomains "$service" ${SERVICE_DNS_SEARCH["$service"]} || true + cmd networksetup -setdnsservers "$service" ${SERVICE_DNS[$i]} || true + cmd networksetup -setsearchdomains "$service" ${SERVICE_DNS_SEARCH[$i]} || true ) done } @@ -325,7 +362,8 @@ monitor_daemon() { (trap 'del_routes; del_dns; exit 0' INT TERM EXIT exec >/dev/null 2>&1 exec 19< <(exec route -n monitor) - local event bpid=$BASHPID mpid=$! + local event bpid mpid=$! + bpid="$(sh -c 'echo $PPID')" [[ ${#DNS[@]} -gt 0 ]] && trap set_dns ALRM # TODO: this should also check to see if the endpoint actually changes # in response to incoming packets, and then call set_endpoint_direct_route @@ -435,9 +473,11 @@ cmd_up() { for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$REAL_INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do add_route "$i" done [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route - [[ ${#DNS[@]} -gt 0 ]] && set_dns + if [[ ${#DNS[@]} -gt 0 ]]; then + set_dns || echo "[!] Could not configure system DNS; tunnel remains active" >&2 + fi monitor_daemon execute_hooks "${POST_UP[@]}" trap - INT TERM EXIT }