Last active
January 25, 2023 07:19
-
-
Save zikeji/144247cb20793a5a7c65653e5f7c572b to your computer and use it in GitHub Desktop.
Use dig and fping generate multiple remotes for an AirVPN config, replacing existing remote(s), and ignoring IPs that aren't responding. Remotes are placed in order of lowest ping to highest.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
PACKAGE=$(basename "$0") | |
# check for missing packages | |
MISSING_DEP="no" | |
if ! command -v fping &> /dev/null; then | |
echo "$PACKAGE: fping is not installed." | |
MISSING_DEP="yes" | |
fi | |
if ! command -v dig &> /dev/null; then | |
echo "$PACKAGE: dig is not installed." | |
MISSING_DEP="yes" | |
fi | |
if [[ "$MISSING_DEP" == "yes" ]]; then | |
exit 1 | |
fi | |
display_usage() { | |
echo "$PACKAGE" | |
echo "" | |
echo "Description:" | |
echo " Use dig and fping generate multiple remotes for an AirVPN config, replacing existing remote(s), and ignoring IPs that aren't responding. Remotes are placed in order of lowest ping to highest." | |
echo "" | |
echo "Usage:" | |
echo " $PACKAGE [--port=<int>] [--query=<fqdn>] [--ipv4] [--ipv6] [--remote-random] [--in-place] [input-file]" | |
echo " $PACKAGE -h | --help" | |
echo "" | |
echo "Options:" | |
echo " -h, --help Show this screen." | |
echo " -p <int>, --port=<fqdn> Override the port supplied on each remote line [default: 443]." | |
echo " -q <fqdn>, --query=<fqdn> Supply the DNS record you wish to query to use the IPs from [default: ca.all.vpn.airdns.org]." | |
echo " -s <ns>, --server=<ns> The name server you wish to query the records against [default: ns1.airvpn.org]." | |
echo " -c <int>, --count=<int> Change the amount of pings ran by fping for more accurate ping sorting [default: 4]." | |
echo " -4, --ipv4 Only query IPv4." | |
echo " -6, --ipv6 Only query IPv6." | |
echo " -r, --remote-random Add remote-random to the AirVPN config (this will cause OpenVPN to randomize the server order when connecting)." | |
echo " -i<ext>, --in-place=<ext> Edit the file in place (makes backup if extension supplied), ignored if no input file is supplied." | |
echo "" | |
echo "The first non-option argument is the name of the input file; if no input file is specified, then the standard input is read. All other non-option arguments after the first are ignored." | |
} | |
TEMP=$(getopt -o 'h46rp:q:c:s:i::' --long 'help,ipv4,ipv6,remote-random,port:,query:,count:,server:,in-place::' -n "$PACKAGE" -- "$@") | |
if [ $? -ne 0 ]; then | |
display_usage | |
exit 1 | |
fi | |
eval set -- "$TEMP" | |
unset TEMP | |
QUERY="ca.all.vpn.airdns.org" | |
SERVER="ns1.airvpn.org" | |
PORT="443" | |
IN_PLACE="no" | |
BACKUP_EXT="" | |
REMOTE_RANDOM="no" | |
IPV4="no" | |
IPV6="no" | |
PING_COUNT="4" | |
while true; do | |
case "$1" in | |
'-h'|'--help') | |
display_usage | |
exit 0 | |
;; | |
'-4'|'--ipv4') | |
IPV4="yes" | |
shift | |
continue | |
;; | |
'-6'|'--ipv6') | |
IPV6="yes" | |
shift | |
continue | |
;; | |
'-r'|'--remote-random') | |
REMOTE_RANDOM="yes" | |
shift | |
continue | |
;; | |
'-q'|'--query') | |
QUERY="$2" | |
shift 2 | |
continue | |
;; | |
'-p'|'--port') | |
PORT="$2" | |
shift 2 | |
continue | |
;; | |
'-s'|'--server') | |
SERVER="$2" | |
shift 2 | |
continue | |
;; | |
'-c'|'--count') | |
PING_COUNT="$2" | |
case $PING_COUNT in | |
''|*[!0-9]*) | |
echo "$PACKAGE: count provided is not an integer" | |
exit 1 | |
;; | |
esac | |
shift 2 | |
continue | |
;; | |
'-i'|'--in-place') | |
IN_PLACE="yes" | |
BACKUP_EXT="$2" | |
shift 2 | |
continue | |
;; | |
'--') | |
shift | |
break | |
;; | |
*) | |
echo 'Internal error!' >&2 | |
exit 1 | |
;; | |
esac | |
done | |
INPUT="" | |
INPUT_FILE="" | |
USE_INPUT="yes" | |
for arg; do | |
if [[ "$INPUT_FILE" == "" ]]; then | |
INPUT_FILE="$arg" | |
fi | |
done | |
if [[ "$INPUT_FILE" != "" ]]; then | |
INPUT="$(cat "$INPUT_FILE" 2>&1)" | |
if [[ "$?" != "0" ]]; then | |
echo "$PACKAGE: can't read $INPUT_FILE: $(echo "$INPUT" | sed "s/cat: $INPUT_FILE: //" )" | |
exit 1 | |
fi | |
fi | |
# if no input file specified, get stdin, if no stdin only output new remotes and not updated config | |
if [[ "$INPUT_FILE" == "" ]]; then | |
if [ -p /dev/stdin ]; then | |
INPUT=$(cat -) | |
IN_PLACE="no" | |
else | |
USE_INPUT="no" | |
fi | |
fi | |
# set the record type for dig | |
RECORD_TYPE="ANY" | |
if [[ "$IPV4" == "yes" && "$IPV6" == "no" ]]; then | |
RECORD_TYPE="A" | |
elif [[ "$IPV4" == "no" && "$IPV6" == "yes" ]]; then | |
RECORD_TYPE="AAAA" | |
fi | |
# get a list of all IPs from that record | |
IPS=$(dig +time=3 +tries=1 +short "$RECORD_TYPE" "$QUERY" @"$SERVER" 2>&1) | |
if [[ "$?" != "0" ]]; then | |
echo "$PACKAGE: error during dig lookup" | |
echo "$IPS" | |
exit 1 | |
fi | |
# if dig gave us no results we can't really do anything, inform the user and exit | |
if [[ "$IPS" == "" ]]; then | |
echo "$PACKAGE: dig query returned no results, try adjusting your query flag, use the default, or double check your DNS" | |
exit 1 | |
fi | |
# test the IPs for working IPs and sort the results by lowest ping, | |
RESULTS=$(fping -a --count="$PING_COUNT" --timeout=500 $IPS 2>&1 1>/dev/null | awk -F' +|/' '/min\/avg\/max/ {print $15 "ms", $1}' | sort -nk1) | |
if [[ "$RESULTS" == "" ]]; then | |
echo "$PACKAGE: fping found no reachable IPs" | |
exit 1 | |
fi | |
# generate our remotes to be added to the config file | |
REMOTES="" | |
while IFS= read -r line; do | |
IP=$(awk '{print $2}' <<< "$line") | |
PING=$(awk '{print $1}' <<< "$line") | |
if [[ "$REMOTES" == "" ]]; then | |
REMOTES="remote $IP $PORT # $PING" | |
else | |
REMOTES="$REMOTES | |
remote $IP $PORT # $PING" | |
fi | |
done <<< "$RESULTS" | |
# prepend remote-random directive if requested | |
if [[ "$REMOTE_RANDOM" == "yes" ]]; then | |
REMOTES="remote-random | |
$REMOTES" | |
fi | |
# if no input file or stdin are provided, output the remotes we generated to stdout | |
if [[ "$USE_INPUT" == "no" ]]; then | |
echo "$REMOTES" | |
exit | |
fi | |
# remove remotes & remote-random | |
INPUT=$(echo "$INPUT" | sed -n '/remote-random.*$/!p') | |
INPUT=$(echo "$INPUT" | sed -n '/remote\ .*$/!p') | |
# prepend our generated remotes to the input config | |
INPUT="$REMOTES | |
$INPUT" | |
if [[ "$IN_PLACE" == "yes" ]]; then | |
# user wants an inplace update, check if they want to backup the original and copy that before overwriting | |
if [[ "$BACKUP_EXT" != "" ]]; then | |
cp "$INPUT_FILE" "$INPUT_FILE.$BACKUP_EXT" | |
fi | |
echo "$INPUT" > "$INPUT_FILE" | |
else | |
# output to stdout | |
echo "$INPUT" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment