configuration-templates – Rev 25
?pathlinks?
#!/bin/sh
###########################################################################
## Copyright (C) Wizardry and Steamworks 2017 - License: GNU GPLv3 ##
## Please see: http://www.gnu.org/licenses/gpl.html for legal details, ##
## rights of fair usage, the disclaimer and warranty conditions. ##
###########################################################################
# openvpn-osx-tuntap.sh #
# Up / Down script for OpenVPN running on OSX as a client. #
# #
# The OpenVPN server is expected to serve clients with IP addresses via #
# a previously configured DHCP server. #
# #
# Note that this script does not replace the default route but instead #
# uses the Mac OS capability of using scoped DNS resolution in order to #
# make the remote network available whilst preserving the default route. #
# #
# To use this script on an OS X OpenVPN client, the following changes to #
# your OpenVPN configuration are required: #
# #
# up-restart #
# up openvpn-osx-tuntap.sh #
# down openvpn-osx-tuntap.sh #
# ipchange openvpn-osx-tuntap.sh #
# #
# where openvpn-osx-tuntap.sh is the filesystem path to this script. #
# #
# Based on: #
# - 2006-09-21, Ben Low - original version #
# - Nick Williams - for TunnelBrick #
# - Jonathan K. Bullard - additions for Mountain Lion #
###########################################################################
###########################################################################
# CONFIGURATION #
###########################################################################
# A MAC address for the tunnel interface.
STATIC_TUNTAP_MAC_ADDRESS="42:75:e2:67:43:db"
# Whether to prepend domain names instead of replacing the exiting ones.
PREPEND_DOMAIN_NAME="false"
###########################################################################
# INTERNALS #
###########################################################################
# Regular expression for matching IP addresses.
IPRX="(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
# Domain name regular expression.
DOMRX="(?:[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]|[A-Za-z0-9])"
###########################################################################
# Utility function to flush the DNS cache on various Mac OS releases. #
###########################################################################
flushDNSCache()
{
if [ "${OSVER}" = "10.4" ] ; then
if [ -f /usr/sbin/lookupd ] ; then
set +e # we will catch errors from lookupd
/usr/sbin/lookupd -flushcache
set -e # bash should again fail on errors
fi
else
if [ -f /usr/bin/dscacheutil ] ; then
set +e # we will catch errors from dscacheutil
/usr/bin/dscacheutil -flushcache
set -e # bash should again fail on errors
fi
if [ -f /usr/sbin/discoveryutil ] ; then
set +e # we will catch errors from discoveryutil
/usr/sbin/discoveryutil udnsflushcaches
/usr/sbin/discoveryutil mdnsflushcache
set -e # bash should again fail on errors
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 [ -z "${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
set -e # bash should again fail on errors
fi
fi
fi
}
###########################################################################
# Sets all dynamic DHCP options on the tuntap interface. #
###########################################################################
setDnsServersAndDomainName()
{
readonly PSID="DHCP-$dev"
# Set up the DYN_* variables to contain what is asked for (dynamically, by a 'push' directive, for example)
declare -a vDNS=("${!1}")
declare -a vSMB=("${!3}")
declare -a vSD=("${!4}")
if [ ${#vDNS[*]} -eq 0 ] ; then
readonly DYN_DNS_SA=""
else
readonly DYN_DNS_SA="${!1}"
fi
if [ ${#vSMB[*]} -eq 0 ] ; then
readonly DYN_SMB_WA=""
else
readonly DYN_SMB_WA="${!3}"
fi
if [ ${#vSD[*]} -eq 0 ] ; then
readonly DYN_DNS_SD=""
else
readonly DYN_DNS_SD="${!4}"
fi
DYN_DNS_DN="$2"
# set up the FIN_* variables with what we want to set things to
# Three FIN_* variables are simple -- no aggregation is done for them
if [ ! -z "${DYN_DNS_DN}" ] ; then
readonly FIN_DNS_DN="${DYN_DNS_DN}"
else
readonly FIN_DNS_DN=""
fi
if [ ! -z "${DYN_SMB_NN}" ] ; then
readonly FIN_SMB_NN="${DYN_SMB_NN}"
else
readonly FIN_SMB_NN=""
fi
if [ ! -z "${DYN_SMB_WG}" ] ; then
readonly FIN_SMB_WG="${DYN_SMB_WG}"
else
readonly FIN_SMB_WG=""
fi
# DNS ServerAddresses (FIN_DNS_SA) are aggregated for 10.4 and 10.5
if [ ${#vDNS[*]} -eq 0 ] ; then
readonly FIN_DNS_SA=""
else
case "${OSVER}" in
10.4 | 10.5 )
# We need to remove duplicate DNS entries, so that our reference list matches MacOSX's
SDNS="$( echo -n "${DYN_DNS_SA}" | tr ' ' '\n' )"
i=0
for n in "${vDNS[@]}" ; do
if echo -n "${SDNS}" | grep -q "${n}" ; then
unset vDNS[${i}]
fi
let i++
done
if [ ${#vDNS[*]} -gt 0 ] ; then
readonly FIN_DNS_SA="$( echo -n "${DYN_DNS_SA}" | sed s/"${vDNS[*]}"//g )"
else
readonly FIN_DNS_SA="${DYN_DNS_SA}"
fi
;;
* )
# Do nothing - in 10.6 and higher -- we don't aggregate our configurations, apparently
readonly FIN_DNS_SA="${DYN_DNS_SA}"
;;
esac
fi
# SMB WINSAddresses (FIN_SMB_WA) are aggregated for 10.4 and 10.5
if [ ${#vSMB[*]} -eq 0 ] ; then
readonly FIN_SMB_WA=""
else
case "${OSVER}" in
10.4 | 10.5 )
# We need to remove duplicate SMB entries, so that our reference list matches MacOSX's
SSMB="$( echo -n "${DYN_SMB_WA}" | tr ' ' '\n' )"
i=0
for n in "${vSMB[@]}" ; do
if echo -n "${SSMB}" | grep -q "${n}" ; then
unset vSMB[${i}]
fi
let i++
done
if [ ${#vSMB[*]} -gt 0 ] ; then
readonly FIN_SMB_WA="$( echo -n "${DYN_SMB_WA}" | sed s/"${vSMB[*]}"//g )"
else
readonly FIN_SMB_WA="${DYN_SMB_WA}"
fi
;;
* )
# Do nothing - in 10.6 and higher -- we don't aggregate our configurations, apparently
readonly FIN_SMB_WA="${DYN_SMB_WA}"
;;
esac
fi
# DNS SearchDomains (FIN_DNS_SD) is treated specially
#
# OLD BEHAVIOR:
# if SearchDomains was not set manually, we set SearchDomains to the DomainName
# else
# In OS X 10.4-10.5, we add the DomainName to the end of any manual SearchDomains (unless it is already there)
# In OS X 10.6+, if SearchDomains was entered manually, we ignore the DomainName
# else we set SearchDomains to the DomainName
#
# NEW BEHAVIOR (done if ARG_PREPEND_DOMAIN_NAME is "true"):
#
# if SearchDomains was entered manually, we do nothing
# else we PREpend new SearchDomains (if any) to the existing SearchDomains (NOT replacing them)
# and PREpend DomainName to that
#
# (done if ARG_PREPEND_DOMAIN_NAME is "false" and there are new SearchDomains from DOMAIN-SEARCH):
#
# if SearchDomains was entered manually, we do nothing
# else we PREpend any new SearchDomains to the existing SearchDomains (NOT replacing them)
#
# This behavior is meant to behave like Linux with Network Manager and Windows
if "${PREPEND_DOMAIN_NAME}" ; then
if [ ! -z "${DYN_DNS_SD}" ] ; then
readonly TMP_DNS_SD="${DYN_DNS_SD}"
if [ ! -z "${FIN_DNS_DN}" -a "${FIN_DNS_DN}" != "localdomain" ]; then
if ! echo -n "${TMP_DNS_SD}" | tr ' ' '\n' | grep -q "${FIN_DNS_DN}" ; then
readonly FIN_DNS_SD="$( echo -n "${FIN_DNS_DN}" | sed s/"${TMP_DNS_SD}"//g )"
else
readonly FIN_DNS_SD="${TMP_DNS_SD}"
fi
else
readonly FIN_DNS_SD="${TMP_DNS_SD}"
fi
else
readonly FIN_DNS_SD="${DYN_DNS_SD}"
fi
else
if [ ! -z "${DYN_DNS_SD}" ] ; then
readonly FIN_DNS_SD="${DYN_DNS_SD}"
else
if [ ! -z "${FIN_DNS_DN}" -a "${FIN_DNS_DN}" != "localdomain" ] ; then
case "${OSVER}" in
10.4 | 10.5 )
readonly FIN_DNS_SD="${FIN_DNS_DN}"
;;
* )
readonly FIN_DNS_SD="${FIN_DNS_DN}"
;;
esac
else
readonly FIN_DNS_SD=""
fi
fi
fi
# Set up SKP_* variables to inhibit scutil from making some changes
# SKP_DNS_* and SKP_SMB_* are used to comment out individual items
# that are not being set
if [ -z "${FIN_DNS_DN}" ] ; then
SKP_DNS_DN="#"
else
SKP_DNS_DN=""
fi
if [ -z "${FIN_DNS_SA}" ] ; then
SKP_DNS_SA="#"
else
SKP_DNS_SA=""
fi
if [ -z "${FIN_DNS_SD}" ] ; then
SKP_DNS_SD="#"
else
SKP_DNS_SD=""
fi
if [ -z "${FIN_SMB_NN}" ] ; then
SKP_SMB_NN="#"
else
SKP_SMB_NN=""
fi
if [ -z "${FIN_SMB_WG}" ] ; then
SKP_SMB_WG="#"
else
SKP_SMB_WG=""
fi
if [ -z "${FIN_SMB_WA}" ] ; then
SKP_SMB_WA="#"
else
SKP_SMB_WA=""
fi
# if any DNS items should be set, set all that have values
if [ "${SKP_DNS_DN}${SKP_DNS_SA}${SKP_DNS_SD}" = "###" ] ; then
readonly SKP_DNS="#"
else
readonly SKP_DNS=""
if [ ! -z "${FIN_DNS_DN}" ] ; then
SKP_DNS_DN=""
fi
if [ ! -z "${FIN_DNS_SA}" ] ; then
SKP_DNS_SA=""
fi
if [ ! -z "${FIN_DNS_SD}" ] ; then
SKP_DNS_SD=""
fi
fi
# if any SMB items should be set, set all that have values
if [ "${SKP_SMB_NN}${SKP_SMB_WG}${SKP_SMB_WA}" = "###" ] ; then
readonly SKP_SMB="#"
else
readonly SKP_SMB=""
if [ ! -z "${FIN_SMB_NN}" ] ; then
SKP_SMB_NN=""
fi
if [ ! -z "${FIN_SMB_WG}" ] ; then
SKP_SMB_WG=""
fi
if [ ! -z "${FIN_SMB_WA}" ] ; then
SKP_SMB_WA=""
fi
fi
readonly SKP_DNS_SA SKP_DNS_SD SKP_DNS_DN
readonly SKP_SMB_NN SKP_SMB_WG SKP_SMB_WA
# special-case fiddling:
# 10.8+ : ServerAddresses and SearchDomains must be set via the Setup:
# key in addition to the State: key
# 10.7 : if ServerAddresses or SearchDomains are manually set,
# ServerAddresses and SearchDomains must be similarly set with the
# Setup: key in addition to the State: key
case "${OSVER}" in
10.4 | 10.5 | 10.6 | 10.7 )
readonly SKP_SETUP_DNS="#"
;;
* )
readonly SKP_SETUP_DNS=""
;;
esac
# Set all parameters.
/usr/sbin/scutil >/dev/null 2>&1 <<-EOF
open
# Initialize the new DNS map via State:
${SKP_DNS}d.init
${SKP_DNS}${SKP_DNS_SA}d.add ServerAddresses * ${FIN_DNS_SA}
${SKP_DNS}${SKP_DNS_SD}d.add SearchDomains * ${FIN_DNS_SD}
${SKP_DNS}${SKP_DNS_DN}d.add DomainName ${FIN_DNS_DN}
${SKP_DNS}${SKP_DNS_DN}d.add SupplementalMatchDomains * ${FIN_DNS_DN}
${SKP_DNS}set State:/Network/Service/${PSID}/DNS
# If necessary, initialize the new DNS map via Setup: also
${SKP_SETUP_DNS}${SKP_DNS}d.init
${SKP_SETUP_DNS}${SKP_DNS}${SKP_DNS_SA}d.add ServerAddresses * ${FIN_DNS_SA}
${SKP_SETUP_DNS}${SKP_DNS}${SKP_DNS_SD}d.add SearchDomains * ${FIN_DNS_SD}
${SKP_SETUP_DNS}${SKP_DNS}${SKP_DNS_DN}d.add DomainName ${FIN_DNS_DN}
${SKP_SETUP_DNS}${SKP_DNS}set Setup:/Network/Service/${PSID}/DNS
# Initialize the SMB map
${SKP_SMB}d.init
${SKP_SMB}${SKP_SMB_NN}d.add NetBIOSName ${FIN_SMB_NN}
${SKP_SMB}${SKP_SMB_WG}d.add Workgroup ${FIN_SMB_WG}
${SKP_SMB}${SKP_SMB_WA}d.add WINSAddresses * ${FIN_SMB_WA}
${SKP_SMB}set State:/Network/Service/${PSID}/SMB
quit
EOF
}
# If OpenVPN has not brought up the device, then terminate.
if [ -z "$dev" ]; then
echo "$0: \$dev not defined, exiting";
exit 1;
fi
# OpenVPN passes $script_type set to the script method.
case "$script_type" in
ipchange)
# Set the MAC address for the tuntap device for static DHCP bindings
/sbin/ifconfig "$dev" ether $STATIC_TUNTAP_MAC_ADDRESS
# Set the interface to NONE
/usr/sbin/ipconfig set "$dev" NONE
# Set the interface to DHCP
/usr/sbin/ipconfig set "$dev" DHCP
;;
up)
# Set the MAC address for the tuntap device for static DHCP bindings
/sbin/ifconfig "$dev" ether $STATIC_TUNTAP_MAC_ADDRESS
# Set the interface to NONE
/usr/sbin/ipconfig set "$dev" NONE
# Set the interface to DHCP
/usr/sbin/ipconfig set "$dev" DHCP
{
# Issue the waitall command - even if it does not wait.
/usr/sbin/ipconfig waitall
unset PACKET
# Spin and check for packet from the tap device
set +e
n=0
while [ -z "$PACKET" -a $n -lt 60 ] ; do
PACKET="$( /usr/sbin/ipconfig getpacket "$dev" )"
let n++
sleep 1
done
set -e
# Get packet to set options
if [ -z "$PACKET" ]; then
exit 1
fi
unset DOMAIN_NAME
unset DOMAIN_NAME_SERVERS
unset SEARCH_DOMAINS
unset WINS_SERVERS
set +e
# Get domain name
DOMAIN_NAME="$( echo -n "$PACKET" | grep "domain_name " | grep -Eo ": $DOMRX" | grep -Eo "$DOMRX" | tr -d [:space:] )"
# Get nameservers
DOMAIN_NAME_SERVERS_INDEX=1
for DOMAIN_NAME_SERVER in $( echo -n "$PACKET" | grep "domain_name_server" | grep -Eo "\{($IPRX)(, $IPRX)*\}" | grep -Eo "($IPRX)" ); do
DOMAIN_NAME_SERVERS[DOMAIN_NAME_SERVERS_INDEX-1]=$DOMAIN_NAME_SERVER
let DOMAIN_NAME_SERVERS_INDEX++
done
# Get search domains
SEARCH_DOMAINS_INDEX=1
for SEARCH_DOMAIN in $( echo -n "$PACKET" | grep "search_domain" | grep -Eo "\{($DOMRX)(, $DOMRX)*\}" | grep -Eo "($DOMRX)" ); do
SEARCH_DOMAINS[SEARCH_DOMAINS_INDEX-1]=$SEARCH_DOMAIN
let SEARCH_DOMAINS_INDEX++
done
# Get WINS servers
WINS_SERVERS_INDEX=1
for WINS_SERVER in $( echo -n "$PACKET" | grep "nb_over_tcpip_name_server" | grep -Eo "\{($IPRX)(, $IPRX)*\}" | grep -Eo "($IPRX)" ); do
WINS_SERVERS[WINS_SERVERS_INDEX-1]=$WINS_SERVER
let WINS_SERVERS_INDEX++
done
if [ ${#DOMAIN_NAME_SERVERS[*]} -gt 0 -a "$DOMAIN_NAME" ]; then
setDnsServersAndDomainName DOMAIN_NAME_SERVERS[@] "$DOMAIN_NAME" WINS_SERVERS[@] SEARCH_DOMAINS[@]
elif [ ${#DOMAIN_NAME_SERVERS[*]} -gt 0 ]; then
setDnsServersAndDomainName DOMAIN_NAME_SERVERS[@] "$DEFAULT_DOMAIN_NAME" WINS_SERVERS[@] SEARCH_DOMAINS[@]
else
exit 1
fi
set -e
sleep 1
flushDNSCache
exit 0
} &
;;
down)
sleep 1
/usr/sbin/scutil >/dev/null 2>&1 <<-EOF
open
remove State:/Network/Service/DHCP-$dev/IPv4
remove State:/Network/Service/DHCP-$dev/DNS
close
EOF
flushDNSCache
exit 0
;;
*)
echo "$0: invalid script_type" && exit 1
;;
esac