Last active
August 17, 2026 11:46
-
-
Save cunneen/f21d8301ec06673b7684ebae02ecef64 to your computer and use it in GitHub Desktop.
Pangolin Clear DNS settings on Mac
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
| #!/usr/bin/env bash | |
| # This script is a workaround for the issue where pangolin client is disconnected when the macbook goes to sleep. | |
| # The issue is described at https://github.com/fosrl/apple/issues/58 | |
| # | |
| # Usage: sudo pangolin-dns-clear.sh | |
| # -- configuration -- | |
| # This is the DNS server that pangolin uses to resolve the domain names of the pangolin resources. | |
| PANGOLIN_DNS_SERVER="100.96.128.1" | |
| LOG_LEVEL=INFO # DEBUG, INFO, ERROR, or NONE | |
| # -- variables -- do not modify -- | |
| # set up the log level values and names: DEBUG, INFO, ERROR, NONE | |
| declare -A LOG_LEVEL_VALUES=( ["NONE"]=0 ["ERROR"]=1 ["INFO"]=2 ["DEBUG"]=3 ); | |
| declare -A LOG_LEVEL_NAMES=( [0]="NONE" [1]="ERROR" [2]="INFO" [3]="DEBUG" ) | |
| # -- commands -- | |
| # -- commands that check status | |
| # -- checks if the pangolin DNS server is currently in use. | |
| PANGOLIN_DNS_SERVER_IN_USE_CMD="scutil --dns | grep -E 'nameserver\[[0-9]+\] *\: *'${PANGOLIN_DNS_SERVER} >/dev/null 2>&1" | |
| # -- checks if the pangolin DNS server is reachable on port 53. | |
| # PANGOLIN_DNS_SERVER_REACHABLE_CMD="nc -v -z -w 5 -G 5 ${PANGOLIN_DNS_SERVER} 53" | |
| PANGOLIN_DNS_SERVER_REACHABLE_CMD="dig ${PANGOLIN_DNS_SERVER} @${PANGOLIN_DNS_SERVER} +nofail +short" | |
| # -- checks if the pangolin DNS server currently set in the /etc/resolv.conf file. | |
| PANGOLIN_DNS_SERVER_IN_RESOLV_CONF_CMD="grep -E -q '^nameserver ${PANGOLIN_DNS_SERVER}$' $(realpath /etc/resolv.conf)" | |
| # -- checks if the pangolin VPN is currently connected. | |
| PANGOLIN_VPN_CONNECTED_CMD='scutil --nc status "Pangolin" | grep -s -q -E "^Connected"' | |
| # -- checks if the pangolin DNS server is currently set in the configd "dynamic store" (requires sudo privileges). | |
| PANGOLIN_DNS_SERVER_IN_CONFIGD_CMD="echo -e 'get State:/Network/Service/Olm-Override/DNS\nd.show\nget State:/Network/Global/DNS\nd.show\nexit\n' | sudo scutil < /dev/stdin | grep -E '${PANGOLIN_DNS_SERVER}'" | |
| # -- commands that perform actions | |
| # -- clears the DNS cache. | |
| CLEAR_DNS_CACHE_CMD="sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder" | |
| # -- updates the /etc/resolv.conf file to remove the pangolin DNS server. | |
| REMOVE_PANGOLIN_DNS_SERVER_FROM_RESOLV_CONF_CMD="sudo sed -i.bak '/^nameserver ${PANGOLIN_DNS_SERVER}$/d' \"\$(realpath /etc/resolv.conf)\"" | |
| # -- temporarily removes the pangolin DNS server from the configd "dynamic store" (requires sudo privileges). | |
| REMOVE_PANGOLIN_DNS_SERVER_FROM_CONFIGD_CMD="echo -e 'remove State:/Network/Service/Olm-Override/DNS\nremove State:/Network/Global/DNS\nexit\n' | sudo scutil < /dev/stdin" | |
| # -- trap errors -- | |
| print_error() { | |
| local exit_code=$? | |
| log ERROR "Error occurred in script at line: ${BASH_LINENO[0]}. Exit code: ${exit_code}" | |
| exit "${exit_code}" | |
| } | |
| trap print_error 1 2 3 6 | |
| # -- functions -- | |
| # This function converts a log level string to its corresponding integer value. | |
| # Usage: get_log_level_from_string <level> | |
| # | |
| # level: the log level as a string (NONE, ERROR, INFO, or DEBUG) | |
| # | |
| # Returns: the integer value of the log level as a string (i.e. "0", "1", "2", or "3"), | |
| # or default value of "2" (INFO) if no level is provided or the provided level is invalid. | |
| # | |
| # Examples: | |
| # get_log_level_from_string "DEBUG" # returns "3" | |
| get_log_level_from_string() { | |
| local level=${1:-INFO} # default to INFO if no level is provided | |
| if [[ "$level" =~ ^[0-3]$ ]]; then | |
| # level is already an integer value, return it as is | |
| return "$level" | |
| fi | |
| local level_value_from_string="${LOG_LEVEL_VALUES[$level]}" | |
| if [ -n "$level_value_from_string" ]; then | |
| return "$level_value_from_string" | |
| fi | |
| return 2 | |
| } | |
| # This function ensures that the global variable LOG_LEVEL_AS_INTEGER is set to the integer value of the LOG_LEVEL variable. | |
| ensure_log_level_integer_set() { | |
| declare -g LOG_LEVEL_AS_INTEGER | |
| if [ -z "$LOG_LEVEL_AS_INTEGER" ]; then | |
| get_log_level_from_string "$LOG_LEVEL" | |
| LOG_LEVEL_AS_INTEGER=$? | |
| fi | |
| } | |
| # This function logs messages based on the log level. | |
| # Usage: log <level> <message> | |
| # | |
| # level: You can use an integer or a string to specify the log level. The following levels are supported: | |
| # 3=DEBUG, 2=INFO, 1=ERROR, 0=NONE | |
| # | |
| # message: the message to log | |
| # | |
| # Examples: | |
| # log DEBUG "This is a debug message, which will be printed only if LOG_LEVEL is set to 3 (or higher)" | |
| # log INFO "This is an info message, which will be printed if LOG_LEVEL is set to 2 (or higher)" | |
| log() { | |
| local level=$1 | |
| local message=$2 | |
| declare -g LOG_LEVEL_AS_INTEGER | |
| if ! [[ "$level" =~ ^[0-3]$ ]]; then | |
| # attempt to convert the level argument to an integer value | |
| get_log_level_from_string "$level" | |
| level=$? | |
| fi | |
| # level should now be an integer value between 0 and 3 | |
| local level_string="${LOG_LEVEL_NAMES[${level}]:-${level}}" # default to INFO if LOG_LEVEL is not set | |
| ensure_log_level_integer_set | |
| if [ "$LOG_LEVEL_AS_INTEGER" -ge "$level" ]; then | |
| echo "${level_string}: ${message}" | |
| fi | |
| } | |
| # This function evaluates a command and logs the output based on the log level. | |
| # Usage: eval_with_loginfo <command> [<message>] | |
| # | |
| # command: the command to evaluate | |
| # message: an optional message to log before executing the command. If not provided, the command itself will be logged. | |
| # | |
| # Examples: | |
| # eval_with_loginfo "ls -l" "Listing the current directory..." | |
| eval_with_loginfo() { | |
| local cmd; | |
| local cmd_escaped; | |
| local message; | |
| local outputFile; | |
| local result; | |
| local retval; | |
| ensure_log_level_integer_set | |
| cmd="$1" | |
| cmd_escaped="$(printf '%q' "$cmd")" | |
| message="${2:-Executing command: $cmd_escaped}" | |
| outputFile="/dev/stdout" | |
| log INFO "$message" | |
| log DEBUG " (Command: '${cmd_escaped}')" | |
| if [ ! "$LOG_LEVEL_AS_INTEGER" -ge 2 ]; then | |
| # redirect output to /dev/null | |
| outputFile="/dev/null" | |
| fi | |
| result=$(eval "$cmd" 2>&1) > ${outputFile} | |
| retval=$? | |
| log DEBUG " (Command output: '${result}')" | |
| return $retval | |
| } | |
| # This function checks if the pangolin DNS server is reachable on port 53. | |
| check_dns_server() { | |
| eval_with_loginfo "${PANGOLIN_DNS_SERVER_REACHABLE_CMD}" "Checking if pangolin DNS server is reachable..." | |
| } | |
| # This function clears the DNS cache. | |
| clear_dns_cache() { | |
| eval_with_loginfo "${CLEAR_DNS_CACHE_CMD}" "Clearing DNS cache..." | |
| } | |
| # This function checks if the pangolin DNS server is currently set in the /etc/resolv.conf file. | |
| check_dns_server_in_resolv_conf() { | |
| eval_with_loginfo "${PANGOLIN_DNS_SERVER_IN_RESOLV_CONF_CMD}" "Checking if pangolin DNS server is set in /etc/resolv.conf..." | |
| } | |
| # This function checks if the pangolin VPN is currently connected. | |
| # Returns 0 if the VPN is connected, 1 if it is not connected | |
| check_pangolin_vpn_connection() { | |
| eval_with_loginfo "${PANGOLIN_VPN_CONNECTED_CMD}" "Checking if pangolin VPN is connected..." | |
| } | |
| remove_pangolin_dns_server_from_resolv_conf() { | |
| eval_with_loginfo "${REMOVE_PANGOLIN_DNS_SERVER_FROM_RESOLV_CONF_CMD}" "Removing pangolin DNS server from /etc/resolv.conf..." | |
| } | |
| check_pangolin_dns_server_in_configd() { | |
| eval_with_loginfo "${PANGOLIN_DNS_SERVER_IN_CONFIGD_CMD}" "Checking if pangolin DNS server is set in configd dynamic store..." | |
| } | |
| check_pangolin_dns_server_in_use() { | |
| eval_with_loginfo "${PANGOLIN_DNS_SERVER_IN_USE_CMD}" "Checking if pangolin DNS server is in use..." | |
| } | |
| remove_pangolin_dns_server_from_configd() { | |
| log INFO "Removing pangolin DNS server from configd dynamic store..." | |
| eval_with_loginfo "${REMOVE_PANGOLIN_DNS_SERVER_FROM_CONFIGD_CMD}" "Removing pangolin DNS server from configd dynamic store..." | |
| if [ $? -ne 0 ]; then | |
| log ERROR "Failed to remove pangolin DNS server from configd dynamic store." | |
| exit 1 | |
| fi | |
| log INFO "Removed pangolin DNS server from configd dynamic store." | |
| } | |
| # -- main script execution -- | |
| check_pangolin_vpn_connection | |
| if [ $? -eq 0 ]; then | |
| log INFO "Pangolin VPN is connected. Exiting script." | |
| else | |
| log INFO "Pangolin VPN is not connected. Proceeding to check whether the pangolin DNS server is still in use..." | |
| check_pangolin_dns_server_in_use | |
| if [ $? -eq 0 ]; then | |
| log INFO "Pangolin DNS server is still in use. Proceeding to check whether the pangolin DNS server is reachable..." | |
| check_dns_server | |
| if [ $? -eq 0 ]; then | |
| log INFO "Pangolin DNS server is reachable. Everything is fine. Exiting script." | |
| exit 0 | |
| else | |
| log INFO "Pangolin DNS server is not reachable. Attempting to clean up the DNS settings..." | |
| check_pangolin_dns_server_in_configd | |
| if [ $? -eq 0 ]; then | |
| log INFO "Pangolin DNS server is set in configd dynamic store. Attempting to remove it..." | |
| remove_pangolin_dns_server_from_configd | |
| if [ $? -ne 0 ]; then | |
| log ERROR "Failed to remove pangolin DNS server from configd dynamic store. Exiting script." | |
| exit 1 | |
| fi | |
| fi | |
| check_dns_server_in_resolv_conf | |
| if [ $? -eq 0 ]; then | |
| log INFO "Pangolin DNS server is set in /etc/resolv.conf. Attempting to remove it..." | |
| remove_pangolin_dns_server_from_resolv_conf | |
| if [ $? -ne 0 ]; then | |
| log ERROR "Failed to remove pangolin DNS server from /etc/resolv.conf. Exiting script." | |
| exit 1 | |
| fi | |
| fi | |
| clear_dns_cache | |
| log INFO "DNS settings cleaned up. Exiting script." | |
| exit 0 | |
| fi | |
| else | |
| log INFO "Pangolin DNS server does not appear to be in use, so no action is needed. Exiting script." | |
| exit 0 | |
| fi | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment