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.

Monday, April 04, 2011

Stop form input from capturing/ignoring certain keypresses

Too many web forms have some stupid javascript that tries to limit what keys you can press while you are focused in a certain input element. For example, in my bank billpay system, they have javascript that makes it so you can only type numbers while you are in the "Zip Code" field. When they do that, it disables me from using the CMD+v to paste a zip code in the field, because "CMD+v" is not a number.

Here is a sample of the javascript they use:

function numbersonly(myfield, e)
{
    var key, keychar;
    if (e) key = e.which;
    else return true;
   
    // allow control keys
    if ((key==null) || (key==0) || (key==8) ||
         (key==9) || (key==13) || (key==27) )
        return true;

    // numbers
    else if ((("0123456789").indexOf(keychar) > -1))  return true;
    else return false;
}

Then they have HTML like this:

<input onKeyPress="return numbersonly(this, event)" type="text" />

So, if you are a Windows user, you probably are allowed to CONTROL+v to paste a zip code. But Mac users just see the "Edit" menu flicker for a moment and their paste command (keycode 118) is dropped on the floor. Neither can you "CMD+," (keycode 44) to get Application Preferences, or use the metakey to do any other Firefox action while in that form field. It should validate only when the user is ready to submit, not while typing.

Install Greasemonkey and rip that crap out with this script:

// ==UserScript==
// @name           Get Off My Keypress
// @namespace      http://gomk.amulder.modwest.com/gomk.user.js
// @description    stop websites from setting event handlers to capture your keypresses
// @include        https://sitethatbothersyou.com/*
// @require  http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js
// ==/UserScript==

$(document).ready(function() {
    // unsafeWindow.console.log("testing firebug after jq");     
    $("input").each(function(i, elem) {
        // unsafeWindow.console.log(i + $(elem).attr("name"));
        var h = elem.getAttribute("onKeyPress");
        // might return "return numbersonly(this, event)"
        if(h) elem.setAttribute("onKeyPress", "return true");
    });     
}); 

Having to use Greasemonkey is really more complicated than it should be. We should be able to use the new CAPS (capabilities) security policy settings built into Firefox 4 to grant noAccess to specific websites to set onkeypress events or read what keys you are pushing on. I tried several iterations using the "Control de Scripts" extension to block these:

HTMLInputElement.onKeyPress
Window.numbersonly
Window.onKeyPress
Window.onkeypress
event.preventDefault 

but none worked. I think this is because I don't really grasp what an event handler is or where it is in the DOM. Maybe someone else can comment to get a solution that is native to the browser, without needing a whole extension just to do this one simple thing.

Saturday, March 26, 2011

Review of OWC 480G SSD laptop drive

After waiting for SSD notebook drives to get big enough that I could fit everything on it that I carry around on my Hitachi 500G 7200 rpm mechanical drive, OWC finally came out with a 480G SSD last summer that was over a thousand dollars. Just recently, they lowered the price a lot and I got mine for a total of $908, including Fedex 2 Day shipping and a $25 rebate for using Amazon Checkout when I bought it through their website.

I think the price change is related to recent Sandforce chip changes and maybe also that the next rev Sandforce 2000, reportedly twice as fast as these, may be only 8 weeks away.

I got the drive on time from OWC and, unlike the Seagate that just came in an antistatic bag like a McDonald hamburger, this one came in nice retail packaging. Since I mention the Seagate, I should also say that this Hitachi that I eventually replaced it with was slightly slower, but without the confidence eroding clicking, and maybe less pinwheels until recently.

Before installing the new SSD drive, I ran some crude benchmark tests, then used SuperDuper! to clone my old internal drive to my external backup, a firewire 800 G-Drive Mini, which are the nicest bus-powered enclosures I have seen. After making a bootable backup, I opened the laptop to install the new SSD drive, then booted from the firewire backup.

Upon logging in, OSX offered to "initialize" the unrecognized/unformatted internal SSD drive. When I said yes, it opened Disk Utility, where I clicked the disk, named it, and erased/formated it as MacOS Extended (Journaled). Then I launched SuperDuper! and told it to restore everything from the external backup drive that I had booted from onto the empty internal SSD. That process read data off the 5400rpm backup drive and over the firewire at about 50MB/s (according to the "Disk Activity" graph in Activity Monitor). While 300 gigs went from here to there I did laundry and played with my baby.

Once my backup was restored to the internal SSD, I rebooted and here is the comparison:

Boot time comparison
7200rpm OWC SSD
Apple logo 38 sec (but probably with unset startup drive system preference setting) 4 sec (after setting startup drive in system preferences)
Login Window +30 sec +13 sec
Boot Total 68 sec 17 sec
Login and launch all startup items, including Firefox +80 sec +9 sec
Usable Total 148 sec (2.5 minutes) 26 seconds

I was pretty impressed with how fast it launched all my stuff after I logged in. The OWC website linked above has a graph showing powerup to desktop in 19 seconds. Mine's not doing that, but I am still happy with it.  Correction on March 28,2011: Thanks to the article on macperformanceblog that tells you to set your startup disk after upgrading a hard drive, my machine really does go from off to login window in < 20 seconds! If I can type my password fast enough, my machine is totally ready to use in a total of < 35 seconds!

Here are 2 more before/after comparisons.

Xbench

dd test
Writing to /test on the 7200 rpm drive:
#sudo time dd if=/dev/zero of=/Volumes/Macintosh\ HD/test bs=1024k
[waited a while, then CNTRL+c]
97679048704 bytes transferred in 1565.832047 secs (62381562 bytes/sec)

Reading /test on the 7200 rpm drive:
#sudo time dd of=/dev/null if=/Volumes/Macintosh\ HD/test bs=1024k
[waited a while, then CNTRL+C]
39557529600 bytes transferred in 543.910842 secs (72727967 bytes/sec)
#sudo rm /test

Writing to /test on the SSD:
#sudo time dd if=/dev/zero of=/Volumes/Macintosh\ SSD/test bs=1024k count=16384;
17179869184 bytes transferred in 66.070450 secs (260023493 bytes/sec)

Reading /test on the SSD:
#sudo time dd of=/dev/null if=/Volumes/Macintosh\ SSD/test bs=1024k
17179869184 bytes transferred in 61.126414 secs (281054753 bytes/sec)
#sudo rm /test

So that tells me compared to the 7200rpm Hitachi writing 59MB/s and reading 69MB/s, the SSD's 248MB/s writes and 268MB/s reads are about 4 times faster. Plus, the battery runtime that the battery monitor is reporting now looks about an hour longer than it would have been with the mechanical drive.

This and the cheap 8 Gig RAM upgrade also from OWC put an end to any other hardware upgrades for this computer.