Wednesday, November 09, 2011

Fun with Applescript, VPN and Proxy

I have a lot of digital pictures and home movies, and I'm supposed to be smart enough not to lose them by mistake or hardware failure. So part of my backup process is to backup files off-site across the internet using Crashplan.

This worked fine at first, but over time something happened and my computers just couldn't see each other unless I connected over a VPN. Bringing up the VPN is a manual step (enough of them and you won't have backups anymore), and so is restarting it when it drops, which happens at least once an hour.

If I don't have too much data, then I can start the VPN, nudge Crashplan to notice it right away, and get my files sent off-site. But if there is a lot, home broadband upload speed is slow, and not much will be done before the VPN gets knocked down. Then the backup will stop until I come back and fix it, which probably won't happen. Usually when I see that I have hundreds of megs or more of stuff not finishing, I will just bring my computer to the other site, plug it into gigabit ethernet LAN where it can finish pretty quickly.

After staying up too late on the internet the other night, I found out how to automatically restart the VPN when it falls down. So now I can just leave the computer on at home and bigger backups get farther without babysitting.

It's just an Applescript, created with "Applescript Editor" that comes with every Mac, "Saved As" format "Application," with the checkbox for "Stay Open" checked. I saved it under my Home directory in ~/Applications/restartVPN.app

on idle
    tell application "System Events"
        tell current location of network preferences
            set myConnection to the service "WHATEVER THE SERVICENAME IS"
            if current configuration of myConnection is not connected then
                connect myConnection
            end if
        end tell
        return 60
    end tell
end idle

The "WHATEVER THE SERVICENAME IS" is whatever the VPN service is called in the list of services under System Preferences, Network. If it's a long name, it might be shortened in the list with an ellipsis but you can see the full name by either hitting the "Advanced" button and looking at the top of the next pane, or by checking the "Show VPN status in menubar," then clicking that menubar icon.

Under Lion, if you need to add routes for the VPN, you put them in /etc/ppp/ip-up


#!/bin/sh
if [[ "$5" == "123.456.789.253" ]]; then
/sbin/route add -net 10.10.10.0/24 123.456.789.253
fi

Now whenever I start the VPN connection, I also invoke Spotlight (Command+Spacebar), start typing "restartVPN.app" and after a couple of letters, when that result jumps to the top of the list, hit the Enter key and the will app run until I quit it. With VPN disconnections of less than 60 seconds, Crashplan can keep uploading to the other side for as long as the machine is on.

Hungry for more Applescript, I thought about how many manual steps there are to setup system wide proxying through an ssh tunnel. First, open Terminal, run "ssh -D 9999 me@somewhereelse" to connect to the other host (using ssh keys) where I want my tunneled traffic to come out. Then System Preferences, Network, click my current servicename (typically Wi-Fi), Advanced, Proxies, then enable checkbox for "SOCKS proxy" ("SOCKS proxy server" on that pane should have "localhost:9999"), then "OK" and then finally "Apply."

What a hassle, but then, SOCKS aware apps like Firefox will automatically send their traffic through the tunnel, so my IP address while browsing will be the IP of the place I ssh'd to, not the IP assigned by my Internet Service Provider at home. However, not all programs are SOCKS aware. Transmission, uTorrent and wget are not. When they connect to places on the net, they do it with your real, not proxied, IP. Safari, Vuze and Xtorrent are: TCP only reveals your proxy IP to those you connect to. Curl can do SOCKS, but you have to ask it special, and even when the tunnel is down, it still acts like it is there, which was confusing to me and I didn't spend much time trying to figure it out. 

So, to automate setting and unsetting the system wide SOCKS proxy,  here's a bash shell script. It scans to see if it's already on or not. If it's on, it turns it off. If it's off, it turns it on, kills any ssh process that looks like a leftover from an old tunnel, creates a new ssh tunnel, and prints the current status and my IP address as seen from the other side of any tunnel in a Growl notification.

The script uses "osacript" in order to run some Applescript to launch Safari and read a webpage to figure out the IP address I look like on the internet. The Applescript is because Safari will use the tunnel if it is there -- wget cannot (and curl was just weird).

#!/bin/bash

device="Wi-Fi"
#device="Ethernet"

function myip {
osascript <<
EOF

property myURL : "http://automation.whatismyip.com/n09230945.asp"

tell application "Safari"
 

    if (count documents) = 0 then
        make new document with properties {URL:myURL}
    else
        set URL of document 1 to myURL
    end if 



    launch
    repeat until exists (window 1)
    end repeat

    repeat with w in (get every window)
    set miniaturized of w to true
    end repeat

    tell window 1
        delay 1
        set mySrc to source of the current tab
        return mySrc
    end tell

end tell
EOF
}

if [[ `scutil --proxy | grep SOCKSEnable | awk '{ print $3 }'` == "1" ]]; then
    networksetup -setsocksfirewallproxystate "$device" off
    proxyState="disabled"
else
    networksetup -setsocksfirewallproxy "$device" 127.0.0.1 9999 off
    kill `lsof -i 4TCP@localhost:9999 -P -sTCP:LISTEN -a -c /^ssh$/ | awk '{ print $2 }' | tail -1`; 2> /dev/null
    ssh -N -D 9999 me@somewhereelse &
    proxyState="enabled"
    sleep 5
fi

if [[ -e /usr/local/bin/growlnotify ]]; then
       /usr/local/bin/growlnotify -m "IP: `myip`." "SOCKS Proxy: $proxyState"
else
    echo "SOCKS Proxy $proxyState. IP: `myip`."
fi


This shell script could just be run from a Terminal window, but I decided to turn it into an "application" with Automator.app so that I could invoke it with a Quicksilver keyboard shortcut. That is Command+Spacebar, type "automator" (click it or hit Enter when "Automator" jumps to the top), then Command+N, click "Application" and "Choose" from the "Choose a type" dialogue, then drag the "Run Shell Script" action into the big empty workflow area, erase the default "cat" text from the input box and replace it with the full path to where you saved the shell script, like /Users/whoeveryouare/togglesox.sh then "File", "Save" as format "application." I saved mine as  togglesox.app under the Applications directory below my home directory.

Then go get really frustrated trying to remember how to set a Trigger for a keystroke combination in Quicksilver while Lion acts buggy and freezes Quicksilver or makes it disappear. The keystroke should "open" /Users/whoeveryouare/togglesox.app or wherever you saved it. Alfred or regular OSX Keyboard Shortcuts could also launch it.

So now I can do Command+Shift+p and a little Growl notice will popup on my screen telling me "Proxy Enabled; IP address [other side of my tunnel]." Do it again and Growl shows "Proxy Disabled; IP address [given by my ISP ]."

I also found http://checkmytorrentip.com/ to be helpful in telling you whether your torrent client is going through your tunnel or not, since I just found out that a checkbox setting I enabled months ago in Transmission preferences for something about "SOCKS proxy" only referred to trackers, not to peers, and that setting is now gone and feature removed in the current version.

All this took way more time to get working than could possibly be justified, so please make some use of it.