Skip to content

Instantly share code, notes, and snippets.

@cunneen
Last active August 17, 2026 11:46
Show Gist options
  • Select an option

  • Save cunneen/f21d8301ec06673b7684ebae02ecef64 to your computer and use it in GitHub Desktop.

Select an option

Save cunneen/f21d8301ec06673b7684ebae02ecef64 to your computer and use it in GitHub Desktop.
Pangolin Clear DNS settings on Mac
#!/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