diff options
Diffstat (limited to 'pkg/osx/client.down.sh')
-rwxr-xr-x | pkg/osx/client.down.sh | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/pkg/osx/client.down.sh b/pkg/osx/client.down.sh new file mode 100755 index 00000000..1e173bba --- /dev/null +++ b/pkg/osx/client.down.sh @@ -0,0 +1,426 @@ +#!/bin/bash -e +# Note: must be bash; uses bash-specific tricks +# +# ****************************************************************************************************************** +# Copyright By Tunnelblick. Redistributed with Bitmask under the GPL. +# This Tunnelblick script does everything! It handles TUN and TAP interfaces, +# pushed configurations and DHCP leases. :) +# +# This is the "Down" version of the script, executed after the connection is +# closed. +# +# Created by: Nick Williams (using original code and parts of old Tblk scripts) +# +# ****************************************************************************************************************** + +# @param String message - The message to log +logMessage() +{ + echo "${@}" +} + +# @param String message - The message to log +logDebugMessage() +{ + echo "${@}" > /dev/null +} + +trim() +{ +echo ${@} +} + +# @param String list - list of network service names, output from disable_ipv6() +restore_ipv6() { + + # Undoes the actions performed by the disable_ipv6() routine in client.up.tunnelblick.sh by restoring the IPv6 + # 'automatic' setting for each network service for which that routine disabled IPv6. + # + # $1 must contain the output from disable_ipv6() -- the list of network services. + # + # This routine outputs log messages describing its activities. + + if [ "$1" = "" ] ; then + exit + fi + + printf %s "$1 +" | \ + while IFS= read -r ripv6_service ; do + networksetup -setv6automatic "$ripv6_service" + logMessage "Re-enabled IPv6 (automatic) for '$ripv6_service'" + done +} + +########################################################################################## +flushDNSCache() +{ + if ${ARG_FLUSH_DNS_CACHE} ; then + set +e # "grep" will return error status (1) if no matches are found, so don't fail on individual errors + readonly OSVER="$(sw_vers | grep 'ProductVersion:' | grep -o '10\.[0-9]*')" + set -e # We instruct bash that it CAN again fail on errors + if [ "${OSVER}" = "10.4" ] ; then + + if [ -f /usr/sbin/lookupd ] ; then + set +e # we will catch errors from lookupd + /usr/sbin/lookupd -flushcache + if [ $? != 0 ] ; then + logMessage "WARNING: Unable to flush the DNS cache via lookupd" + else + logMessage "Flushed the DNS cache via lookupd" + fi + set -e # bash should again fail on errors + else + logMessage "WARNING: /usr/sbin/lookupd not present. Not flushing the DNS cache" + fi + + else + + if [ -f /usr/bin/dscacheutil ] ; then + set +e # we will catch errors from dscacheutil + /usr/bin/dscacheutil -flushcache + if [ $? != 0 ] ; then + logMessage "WARNING: Unable to flush the DNS cache via dscacheutil" + else + logMessage "Flushed the DNS cache via dscacheutil" + fi + set -e # bash should again fail on errors + else + logMessage "WARNING: /usr/bin/dscacheutil not present. Not flushing the DNS cache via dscacheutil" + fi + + if [ -f /usr/sbin/discoveryutil ] ; then + set +e # we will catch errors from discoveryutil + /usr/sbin/discoveryutil udnsflushcaches + if [ $? != 0 ] ; then + logMessage "WARNING: Unable to flush the DNS cache via discoveryutil udnsflushcaches" + else + logMessage "Flushed the DNS cache via discoveryutil udnsflushcaches" + fi + /usr/sbin/discoveryutil mdnsflushcache + if [ $? != 0 ] ; then + logMessage "WARNING: Unable to flush the DNS cache via discoveryutil mdnsflushcache" + else + logMessage "Flushed the DNS cache via discoveryutil mdnsflushcache" + fi + set -e # bash should again fail on errors + else + logMessage "/usr/sbin/discoveryutil not present. Not flushing the DNS cache via discoveryutil" + fi + + set +e # "grep" will return error status (1) if no matches are found, so don't fail on individual errors + hands_off_ps="$( ps -ax | grep HandsOffDaemon | grep -v grep.HandsOffDaemon )" + set -e # We instruct bash that it CAN again fail on errors + if [ "${hands_off_ps}" = "" ] ; then + if [ -f /usr/bin/killall ] ; then + set +e # ignore errors if mDNSResponder isn't currently running + /usr/bin/killall -HUP mDNSResponder + if [ $? != 0 ] ; then + logMessage "mDNSResponder not running. Not notifying it that the DNS cache was flushed" + else + logMessage "Notified mDNSResponder that the DNS cache was flushed" + fi + set -e # bash should again fail on errors + else + logMessage "WARNING: /usr/bin/killall not present. Not notifying mDNSResponder that the DNS cache was flushed" + fi + else + logMessage "WARNING: Hands Off is running. Not notifying mDNSResponder that the DNS cache was flushed" + fi + + fi + fi +} + +########################################################################################## +resetPrimaryInterface() +{ + set +e # "grep" will return error status (1) if no matches are found, so don't fail on individual errors + WIFI_INTERFACE="$(networksetup -listallhardwareports | awk '$3=="Wi-Fi" {getline; print $2}')" + if [ "${WIFI_INTERFACE}" == "" ] ; then + WIFI_INTERFACE="$(networksetup -listallhardwareports | awk '$3=="AirPort" {getline; print $2}')" + fi + PINTERFACE="$( scutil <<-EOF | + open + show State:/Network/Global/IPv4 + quit +EOF + grep PrimaryInterface | sed -e 's/.*PrimaryInterface : //' + )" + set -e # resume abort on error + + if [ "${PINTERFACE}" != "" ] ; then + if [ "${PINTERFACE}" == "${WIFI_INTERFACE}" -a "${OSVER}" != "10.4" -a -f /usr/sbin/networksetup ] ; then + if [ "${OSVER}" == "10.5" ] ; then + logMessage "Resetting primary interface '${PINTERFACE}' via networksetup -setairportpower off/on..." + /usr/sbin/networksetup -setairportpower off + sleep 2 + /usr/sbin/networksetup -setairportpower on + else + logMessage "Resetting primary interface '${PINTERFACE}' via networksetup -setairportpower ${PINTERFACE} off/on..." + /usr/sbin/networksetup -setairportpower "${PINTERFACE}" off + sleep 2 + /usr/sbin/networksetup -setairportpower "${PINTERFACE}" on + fi + else + if [ -f /sbin/ifconfig ] ; then + logMessage "Resetting primary interface '${PINTERFACE}' via ifconfig ${PINTERFACE} down/up..." + /sbin/ifconfig "${PINTERFACE}" down + sleep 2 + /sbin/ifconfig "${PINTERFACE}" up + else + logMessage "WARNING: Not resetting primary interface because /sbin/ifconfig does not exist." + fi + fi + else + logMessage "WARNING: Not resetting primary interface because it cannot be found." + fi +} + +########################################################################################## +trap "" TSTP +trap "" HUP +trap "" INT +export PATH="/bin:/sbin:/usr/sbin:/usr/bin" + +readonly OUR_NAME=$(basename "${0}") + +logMessage "**********************************************" +logMessage "Start of output from ${OUR_NAME}" + +# Remove the flag file that indicates we need to run the down script + +if [ -e "/tmp/bitmask-downscript-needs-to-be-run.txt" ] ; then + rm -f "/tmp/bitmask-downscript-needs-to-be-run.txt" +fi + +# Test for the "-r" Bitmask option (Reset primary interface after disconnecting) because we _always_ need its value. +# Usually we get the value for that option (and the other options) from State:/Network/OpenVPN, +# but that key may not exist (because, for example, there were no DNS changes). +# So we get the value from the Bitmask options passed to this script by OpenVPN. +# +# We do the same thing for the -f Bitmask option (Flush DNS cache after connecting or disconnecting) +ARG_RESET_PRIMARY_INTERFACE_ON_DISCONNECT="false" +ARG_FLUSH_DNS_CACHE="false" +while [ {$#} ] ; do + if [ "${1:0:1}" != "-" ] ; then # Bitmask arguments start with "-" and come first + break # so if this one doesn't start with "-" we are done processing Bitmask arguments + fi + if [ "$1" = "-r" ] ; then + ARG_RESET_PRIMARY_INTERFACE_ON_DISCONNECT="true" + else + if [ "$1" = "-f" ] ; then + ARG_FLUSH_DNS_CACHE="true" + fi + fi + shift # Shift arguments to examine the next option (if there is one) +done + +# Quick check - is the configuration there? +if ! scutil -w State:/Network/OpenVPN &>/dev/null -t 1 ; then + # Configuration isn't there + logMessage "WARNING: Not restoring DNS settings because no saved Bitmask DNS information was found." + + flushDNSCache + + if ${ARG_RESET_PRIMARY_INTERFACE_ON_DISCONNECT} ; then + resetPrimaryInterface + fi + logMessage "End of output from ${OUR_NAME}" + logMessage "**********************************************" + exit 0 +fi + +# Get info saved by the up script +TUNNELBLICK_CONFIG="$( scutil <<-EOF + open + show State:/Network/OpenVPN + quit +EOF +)" + +ARG_MONITOR_NETWORK_CONFIGURATION="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*MonitorNetwork :' | sed -e 's/^.*: //g')" +LEASEWATCHER_PLIST_PATH="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*LeaseWatcherPlistPath :' | sed -e 's/^.*: //g')" +REMOVE_LEASEWATCHER_PLIST="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RemoveLeaseWatcherPlist :' | sed -e 's/^.*: //g')" +PSID="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*Service :' | sed -e 's/^.*: //g')" +# Don't need: SCRIPT_LOG_FILE="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*ScriptLogFile :' | sed -e 's/^.*: //g')" +# Don't need: ARG_RESTORE_ON_DNS_RESET="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RestoreOnDNSReset :' | sed -e 's/^.*: //g')" +# Don't need: ARG_RESTORE_ON_WINS_RESET="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RestoreOnWINSReset :' | sed -e 's/^.*: //g')" +# Don't need: PROCESS="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*PID :' | sed -e 's/^.*: //g')" +# Don't need: ARG_IGNORE_OPTION_FLAGS="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*IgnoreOptionFlags :' | sed -e 's/^.*: //g')" +ARG_TAP="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*IsTapInterface :' | sed -e 's/^.*: //g')" +ARG_FLUSH_DNS_CACHE="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*FlushDNSCache :' | sed -e 's/^.*: //g')" +ARG_RESET_PRIMARY_INTERFACE_ON_DISCONNECT="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*ResetPrimaryInterface :' | sed -e 's/^.*: //g')" +bRouteGatewayIsDhcp="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RouteGatewayIsDhcp :' | sed -e 's/^.*: //g')" +bTapDeviceHasBeenSetNone="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*TapDeviceHasBeenSetNone :' | sed -e 's/^.*: //g')" +bAlsoUsingSetupKeys="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*bAlsoUsingSetupKeys :' | sed -e 's/^.*: //g')" +sTunnelDevice="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*TunnelDevice :' | sed -e 's/^.*: //g')" + +# Note: '\n' was translated into '\t', so we translate it back (it was done because grep and sed only work with single lines) +sRestoreIpv6Services="$(echo "${TUNNELBLICK_CONFIG}" | grep -i '^[[:space:]]*RestoreIpv6Services :' | sed -e 's/^.*: //g' | tr '\t' '\n')" + +# Remove leasewatcher +if ${ARG_MONITOR_NETWORK_CONFIGURATION} ; then + launchctl unload "${LEASEWATCHER_PLIST_PATH}" + if ${REMOVE_LEASEWATCHER_PLIST} ; then + rm -f "${LEASEWATCHER_PLIST_PATH}" + fi + logMessage "Cancelled monitoring of system configuration changes" +fi + +if ${ARG_TAP} ; then + if [ "$bRouteGatewayIsDhcp" == "true" ]; then + if [ "$bTapDeviceHasBeenSetNone" == "false" ]; then + if [ -z "$dev" ]; then + # If $dev is not defined, then use TunnelDevice, which was set from $dev by client.up.tunnelblick.sh + # ($def is not defined when this script is called from MenuController to clean up when exiting Bitmask) + if [ -n "${sTunnelDevice}" ]; then + logMessage "WARNING: \$dev not defined; using TunnelDevice: ${sTunnelDevice}" + set +e + ipconfig set "${sTunnelDevice}" NONE 2>/dev/null + set -e + logMessage "Released the DHCP lease via ipconfig set ${sTunnelDevice} NONE." + else + logMessage "WARNING: Cannot configure TAP interface to NONE without \$dev or State:/Network/OpenVPN/TunnelDevice being defined. Device may not have disconnected properly." + fi + else + set +e + ipconfig set "$dev" NONE 2>/dev/null + set -e + logMessage "Released the DHCP lease via ipconfig set $dev NONE." + fi + fi + fi +fi + +# Issue warning if the primary service ID has changed +set +e # "grep" will return error status (1) if no matches are found, so don't fail if not found +PSID_CURRENT="$( scutil <<-EOF | + open + show State:/Network/OpenVPN + quit +EOF +grep 'Service : ' | sed -e 's/.*Service : //' +)" +set -e # resume abort on error +if [ "${PSID}" != "${PSID_CURRENT}" ] ; then + logMessage "Ignoring change of Network Primary Service from ${PSID} to ${PSID_CURRENT}" +fi + +# Restore configurations +DNS_OLD="$( scutil <<-EOF + open + show State:/Network/OpenVPN/OldDNS + quit +EOF +)" +SMB_OLD="$( scutil <<-EOF + open + show State:/Network/OpenVPN/OldSMB + quit +EOF +)" +DNS_OLD_SETUP="$( scutil <<-EOF + open + show State:/Network/OpenVPN/OldDNSSetup + quit +EOF +)" +TB_NO_SUCH_KEY="<dictionary> { + BitmaskNoSuchKey : true +}" + +if [ "${DNS_OLD}" = "${TB_NO_SUCH_KEY}" ] ; then + scutil <<-EOF + open + remove State:/Network/Service/${PSID}/DNS + quit +EOF +else + scutil <<-EOF + open + get State:/Network/OpenVPN/OldDNS + set State:/Network/Service/${PSID}/DNS + quit +EOF +fi + +if [ "${DNS_OLD_SETUP}" = "${TB_NO_SUCH_KEY}" ] ; then + if ${bAlsoUsingSetupKeys} ; then + logDebugMessage "DEBUG: Removing 'Setup:' DNS key" + scutil <<-EOF + open + remove Setup:/Network/Service/${PSID}/DNS + quit +EOF + else + logDebugMessage "DEBUG: Not removing 'Setup:' DNS key" + fi +else + if ${bAlsoUsingSetupKeys} ; then + logDebugMessage "DEBUG: Restoring 'Setup:' DNS key" + scutil <<-EOF + open + get State:/Network/OpenVPN/OldDNSSetup + set Setup:/Network/Service/${PSID}/DNS + quit +EOF + else + logDebugMessage "DEBUG: Not restoring 'Setup:' DNS key" + fi +fi + +if [ "${SMB_OLD}" = "${TB_NO_SUCH_KEY}" ] ; then + scutil > /dev/null <<-EOF + open + remove State:/Network/Service/${PSID}/SMB + quit +EOF +else + scutil > /dev/null <<-EOF + open + get State:/Network/OpenVPN/OldSMB + set State:/Network/Service/${PSID}/SMB + quit +EOF +fi + +logMessage "Restored the DNS and SMB configurations" + +set +e # "grep" will return error status (1) if no matches are found, so don't fail if not found +new_resolver_contents="$( grep -v '#' < /etc/resolv.conf )" +set -e # resume abort on error +logDebugMessage "DEBUG:" +logDebugMessage "DEBUG: /etc/resolve = ${new_resolver_contents}" + +set +e # scutil --dns will return error status in case dns is already down, so don't fail if no dns found +scutil_dns="$( scutil --dns)" +set -e # resume abort on error +logDebugMessage "DEBUG:" +logDebugMessage "DEBUG: scutil --dns = ${scutil_dns}" +logDebugMessage "DEBUG:" + +restore_ipv6 "$sRestoreIpv6Services" + +flushDNSCache + +# Remove our system configuration data +scutil <<-EOF + open + remove State:/Network/OpenVPN/OldDNS + remove State:/Network/OpenVPN/OldSMB + remove State:/Network/OpenVPN/OldDNSSetup + remove State:/Network/OpenVPN/DNS + remove State:/Network/OpenVPN/SMB + remove State:/Network/OpenVPN + quit +EOF + +if ${ARG_RESET_PRIMARY_INTERFACE_ON_DISCONNECT} ; then + resetPrimaryInterface +fi + +logMessage "End of output from ${OUR_NAME}" +logMessage "**********************************************" + +exit 0 |