#!/bin/bash
# script for managing SOCKSv5 proxies via ssh and networksetup
# dre@mac.com, 2/18/05

###### Requirements
# An account on an SSH server that is reachable from the Internet.
# SSH host key authentication configured between your mac and that server
# --see http://www.macosxhints.com/article.php?story=20011207004643312
# Administrator access on your workstation
# OpenSSH 3.7.1, which supplies SOCKSv5 support; fink can help
# --with fink installed, upgrade openssh with: sudo fink install openssh
# networksetup command, not part of Mac OS X, but is included with ARD 2 
# --see http://www.macosxhints.com/article.php?story=20041215002018178
###### Security
# This script requires sudo (admin) access in order to use networksetup. You
# will be prompted for your password when required. Do not run the entire
# script with sudo or as root! 
###### Instructions
# 1. Verify that you meet the above requirements.
# 2. Download this script and place it somewhere; e.g. ~/bin or /usr/local/bin.
# 3. Make it executable: chmod +x /path/to/socks. 
# 4. Customize the user variables below.
# 5. Execute the script: /path/to/socks

###### start user variables section; customize these as needed
# path to networksetup command; this is accurate with ARD 2 installed
NETWORKSETUP='/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Support/networksetup'

# local entry port for socks proxy
PORT=4242

# host and user for remote socks proxy endpoint
HOST='core.dreness.com'
USER='andre'

# retry SSH tunnel if it collapses
RETRY='yes'
RETRY_PERIOD='15'

# disable socks proxy if it fails while in retry mode
# setting this to no means that connections will fail rather than be unproxied
DISABLE_ON_FAIL='no'

# path to 'stroke', the port scan utility built into Network Utility
STROKE="/Applications/Utilities/Network Utility.app/Contents/Resources/stroke"
###### end user variables section

SSH_STRING="ssh -q -D $PORT $USER@$HOST -f -N"

ISADMIN=`id | grep "(admin)" | awk '{print $1}'`
if [ ! $ISADMIN ]
	then echo "you are not an admin!"
	exit;
fi

# subroutines
StartProxy()
{
	GetPid
	TestReachability
	if [ $PID ]
		then echo "Error: socks proxy is already running with pid $PID"
		exit
	fi
	$SSH_STRING || echo "Failed to initiate SSH process!"
	sudo $NETWORKSETUP -setsocksfirewallproxy "$NETWORKSERVICE" localhost $PORT || echo "networksetup failed!"

	#QueryStatus
}

StopProxy()
{
	GetPid
	sudo $NETWORKSETUP -setsocksfirewallproxystate "$NETWORKSERVICE" off
	#if [ ! $PID ]
	#	then echo "Error: socks proxy is not running!" ; exit
	#fi
	DEAD=`kill $PID >/dev/null 2>&1 || echo "couldn't kill ssh proc!"`
	# kill off a backgrounded socks as well (if RETRY=yes)
	SOCKS_PID=`ps x | grep "socks start" | awk '{print $1}'`
	DEAD=`kill $SOCKS_PID >/dev/null 2>&1 || echo "couldn't kill off backgrounded socks!"`
	#QueryStatus
}

QueryStatus()
{
        sudo $NETWORKSETUP -getsocksfirewallproxy "$NETWORKSERVICE"
        proxy_proc=`ps x | grep "$SSH_STRING" | grep -v grep`
        if [ "$proxy_proc" != "" ]
                then echo "$proxy_proc"
                else echo "SSH tunnel not active."
        fi
}

GetServiceName()  # thanks to hayne @ macosxhints for improvements here
{
scutil_query()
{
key=$1
scutil<<EOT
open
get $key
d.show
close
EOT
}

SERVICE_GUID=`scutil_query State:/Network/Global/IPv4 | grep "PrimaryService" | awk '{print $3}'`

NETWORKSERVICE=`scutil_query Setup:/Network/Service/$SERVICE_GUID | grep "UserDefinedName" | awk -F': ' '{print $2}'`
}

TestReachability ()
{
	# test for reachability of proxy host
	ARE_YOU_THERE=`"$STROKE" $HOST 22 22 | grep "Open Port" | awk '{print $1}'`
	if [ ! $ARE_YOU_THERE ]
	then echo "proxy host unavailable!"
		if [ $DISABLE_ON_FAIL == 'yes' ] 
		then StopProxy
		fi
	exit
	fi
}

RetrySSH()
{
if [ $RETRY == "yes" ]
	then
	while true
	do GetPid
	if [ $PID ]
		then sleep $RETRY_PERIOD
		else echo "ssh tunnel collapsed, diggin' a new one..."
		TestReachability
		$SSH_STRING || echo "couldn't build ssh tunnel!"
	fi
	done
fi 
}

GetPid()
{
	# get process ID of running proxy, if any
	PID=`ps x | grep "$SSH_STRING" | grep -v grep | awk '{print $1}'`
}

# fetch the network service name and current PID of ssh tunnel, if any
GetPid
GetServiceName

# process command line arguments
case "$1" in
start)
	StartProxy
	RetrySSH	
	;;
stop)
	StopProxy
	;;
status)
	QueryStatus
	;;
*)
	echo 'usage: socks ( start | stop | status )' ; exit
	;;
esac
