diff --git a/Sshuttle VPN.app/Contents/MacOS/Sshuttle b/Sshuttle VPN.app/Contents/MacOS/Sshuttle
index f45f4e2..334693b 100755
Binary files a/Sshuttle VPN.app/Contents/MacOS/Sshuttle and b/Sshuttle VPN.app/Contents/MacOS/Sshuttle differ
diff --git a/Sshuttle VPN.app/Contents/Resources/English.lproj/MainMenu.nib b/Sshuttle VPN.app/Contents/Resources/English.lproj/MainMenu.nib
index af813ea..53b254c 100644
Binary files a/Sshuttle VPN.app/Contents/Resources/English.lproj/MainMenu.nib and b/Sshuttle VPN.app/Contents/Resources/English.lproj/MainMenu.nib differ
diff --git a/Sshuttle VPN.app/Contents/Resources/askpass.pyc b/Sshuttle VPN.app/Contents/Resources/askpass.pyc
deleted file mode 100644
index 3be75fc..0000000
Binary files a/Sshuttle VPN.app/Contents/Resources/askpass.pyc and /dev/null differ
diff --git a/Sshuttle VPN.app/Contents/Resources/main.py b/Sshuttle VPN.app/Contents/Resources/main.py
index 3e6c2a1..fc67a34 100644
--- a/Sshuttle VPN.app/Contents/Resources/main.py	
+++ b/Sshuttle VPN.app/Contents/Resources/main.py	
@@ -78,6 +78,11 @@ class Runner:
             if pid == self.pid:
                 if os.WIFEXITED(code):
                     self.rv = os.WEXITSTATUS(code)
+                    if self.rv == 111:
+                        NSRunAlertPanel('Sshuttle',
+                            'Please restart your computer to finish '
+                            'installing Sshuttle.',
+                            'Restart Later', None, None)
                 else:
                     self.rv = -os.WSTOPSIG(code)
                 self.serverobj.setConnected_(False)
@@ -87,7 +92,10 @@ class Runner:
         return self.rv
 
     def wait(self):
-        return self._try_wait(0)
+        rv = None
+        while rv is None:
+            self.gotdata(None)
+            rv = self._try_wait(os.WNOHANG)
         
     def poll(self):
         return self._try_wait(os.WNOHANG)
diff --git a/Sshuttle VPN.app/Contents/Resources/models.py b/Sshuttle VPN.app/Contents/Resources/models.py
index c4cbe8b..e71fce5 100644
--- a/Sshuttle VPN.app/Contents/Resources/models.py	
+++ b/Sshuttle VPN.app/Contents/Resources/models.py	
@@ -3,6 +3,7 @@ import my
 
 
 configchange_callback = setconnect_callback = None
+objc_validator = objc.signature('@@:N^@o^@')
 
 
 def config_changed():
@@ -39,7 +40,7 @@ class SshuttleNet(NSObject):
     def setSubnet_(self, v):
         self._k_subnet = v
         config_changed()
-    @objc.accessor
+    @objc_validator
     def validateSubnet_error_(self, value, error):
         #print 'validateSubnet!'
         return True, _validate_ip(value), error
@@ -49,7 +50,7 @@ class SshuttleNet(NSObject):
     def setWidth_(self, v):
         self._k_width = v
         config_changed()
-    @objc.accessor
+    @objc_validator
     def validateWidth_error_(self, value, error):
         #print 'validateWidth!'
         return True, _validate_width(value), error
@@ -118,7 +119,7 @@ class SshuttleServer(NSObject):
         self._k_host = v
         self.setTitle_(None)
         config_changed()
-    @objc.accessor
+    @objc_validator
     def validateHost_error_(self, value, error):
         #print 'validatehost! %r %r %r' % (self, value, error)
         while value.startswith('-'):
diff --git a/Sshuttle VPN.app/Contents/Resources/models.pyc b/Sshuttle VPN.app/Contents/Resources/models.pyc
deleted file mode 100644
index 6163bd5..0000000
Binary files a/Sshuttle VPN.app/Contents/Resources/models.pyc and /dev/null differ
diff --git a/Sshuttle VPN.app/Contents/Resources/my.pyc b/Sshuttle VPN.app/Contents/Resources/my.pyc
deleted file mode 100644
index 188ff60..0000000
Binary files a/Sshuttle VPN.app/Contents/Resources/my.pyc and /dev/null differ
diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py
index 449a75a..06fc573 100644
--- a/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py	
+++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py	
@@ -171,10 +171,71 @@ class FirewallClient:
     def done(self):
         self.pfile.close()
         rv = self.p.wait()
-        if rv:
+        if rv == EXITCODE_NEEDS_REBOOT:
+            raise FatalNeedsReboot()
+        elif rv:
             raise Fatal('cleanup: %r returned %d' % (self.argv, rv))
 
 
+def onaccept(listener, mux, handlers):
+    global _extra_fd
+    try:
+        sock,srcip = listener.accept()
+    except socket.error, e:
+        if e.args[0] in [errno.EMFILE, errno.ENFILE]:
+            debug1('Rejected incoming connection: too many open files!\n')
+            # free up an fd so we can eat the connection
+            os.close(_extra_fd)
+            try:
+                sock,srcip = listener.accept()
+                sock.close()
+            finally:
+                _extra_fd = os.open('/dev/null', os.O_RDONLY)
+            return
+        else:
+            raise
+    dstip = original_dst(sock)
+    debug1('Accept: %s:%r -> %s:%r.\n' % (srcip[0],srcip[1],
+                                          dstip[0],dstip[1]))
+    if dstip[1] == listener.getsockname()[1] and islocal(dstip[0]):
+        debug1("-- ignored: that's my address!\n")
+        sock.close()
+        return
+    chan = mux.next_channel()
+    if not chan:
+        log('warning: too many open channels.  Discarded connection.\n')
+        sock.close()
+        return
+    mux.send(chan, ssnet.CMD_CONNECT, '%s,%s' % dstip)
+    outwrap = MuxWrapper(mux, chan)
+    handlers.append(Proxy(SockWrapper(sock, sock), outwrap))
+
+
+dnsreqs = {}
+def dns_done(chan, data):
+    peer,sock,timeout = dnsreqs.get(chan) or (None,None,None)
+    debug3('dns_done: channel=%r peer=%r\n' % (chan, peer))
+    if peer:
+        del dnsreqs[chan]
+        debug3('doing sendto %r\n' % (peer,))
+        sock.sendto(data, peer)
+
+
+def ondns(listener, mux, handlers):
+    pkt,peer = listener.recvfrom(4096)
+    now = time.time()
+    if pkt:
+        debug1('DNS request from %r: %d bytes\n' % (peer, len(pkt)))
+        chan = mux.next_channel()
+        dnsreqs[chan] = peer,listener,now+30
+        mux.send(chan, ssnet.CMD_DNS_REQ, pkt)
+        mux.channels[chan] = lambda cmd,data: dns_done(chan,data)
+    for chan,(peer,sock,timeout) in dnsreqs.items():
+        if timeout < now:
+            del dnsreqs[chan]
+    debug3('Remaining DNS requests: %d\n' % len(dnsreqs))
+
+
 def _main(listener, fw, ssh_cmd, remotename, python, latency_control,
           dnslistener, seed_hosts, auto_nets,
           syslog, daemon):
@@ -255,63 +316,10 @@ def _main(listener, fw, ssh_cmd, remotename, python, latency_control,
                 fw.sethostip(name, ip)
     mux.got_host_list = onhostlist
 
-    def onaccept():
-        global _extra_fd
-        try:
-            sock,srcip = listener.accept()
-        except socket.error, e:
-            if e.args[0] in [errno.EMFILE, errno.ENFILE]:
-                debug1('Rejected incoming connection: too many open files!\n')
-                # free up an fd so we can eat the connection
-                os.close(_extra_fd)
-                try:
-                    sock,srcip = listener.accept()
-                    sock.close()
-                finally:
-                    _extra_fd = os.open('/dev/null', os.O_RDONLY)
-                return
-            else:
-                raise
-        dstip = original_dst(sock)
-        debug1('Accept: %s:%r -> %s:%r.\n' % (srcip[0],srcip[1],
-                                              dstip[0],dstip[1]))
-        if dstip[1] == listener.getsockname()[1] and islocal(dstip[0]):
-            debug1("-- ignored: that's my address!\n")
-            sock.close()
-            return
-        chan = mux.next_channel()
-        if not chan:
-            log('warning: too many open channels.  Discarded connection.\n')
-            sock.close()
-            return
-        mux.send(chan, ssnet.CMD_CONNECT, '%s,%s' % dstip)
-        outwrap = MuxWrapper(mux, chan)
-        handlers.append(Proxy(SockWrapper(sock, sock), outwrap))
-    handlers.append(Handler([listener], onaccept))
+    handlers.append(Handler([listener], lambda: onaccept(listener, mux, handlers)))
 
-    dnsreqs = {}
-    def dns_done(chan, data):
-        peer,timeout = dnsreqs.get(chan) or (None,None)
-        debug3('dns_done: channel=%r peer=%r\n' % (chan, peer))
-        if peer:
-            del dnsreqs[chan]
-            debug3('doing sendto %r\n' % (peer,))
-            dnslistener.sendto(data, peer)
-    def ondns():
-        pkt,peer = dnslistener.recvfrom(4096)
-        now = time.time()
-        if pkt:
-            debug1('DNS request from %r: %d bytes\n' % (peer, len(pkt)))
-            chan = mux.next_channel()
-            dnsreqs[chan] = peer,now+30
-            mux.send(chan, ssnet.CMD_DNS_REQ, pkt)
-            mux.channels[chan] = lambda cmd,data: dns_done(chan,data)
-        for chan,(peer,timeout) in dnsreqs.items():
-            if timeout < now:
-                del dnsreqs[chan]
-        debug3('Remaining DNS requests: %d\n' % len(dnsreqs))
     if dnslistener:
-        handlers.append(Handler([dnslistener], ondns))
+        handlers.append(Handler([dnslistener], lambda: ondns(dnslistener, mux, handlers)))
 
     if seed_hosts != None:
         debug1('seed_hosts: %r\n' % seed_hosts)
diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py
index 4fd8c79..452715e 100644
--- a/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py	
+++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py	
@@ -1,4 +1,4 @@
-import re, errno, socket, select, struct
+import re, errno, socket, select, signal, struct
 import compat.ssubprocess as ssubprocess
 import helpers, ssyslog
 from helpers import *
@@ -6,6 +6,12 @@ from helpers import *
 # python doesn't have a definition for this
 IPPROTO_DIVERT = 254
 
+# return values from sysctl_set
+SUCCESS = 0
+SAME = 1
+FAILED = -1
+NONEXIST = -2
+
 
 def nonfatal(func, *args):
     try:
@@ -14,6 +20,14 @@ def nonfatal(func, *args):
         log('error: %s\n' % e)
 
 
+def _call(argv):
+    debug1('>> %s\n' % ' '.join(argv))
+    rv = ssubprocess.call(argv)
+    if rv:
+        raise Fatal('%r returned %d' % (argv, rv))
+    return rv
+
+
 def ipt_chain_exists(name):
     argv = ['iptables', '-t', 'nat', '-nL']
     p = ssubprocess.Popen(argv, stdout = ssubprocess.PIPE)
@@ -27,10 +41,7 @@ def ipt_chain_exists(name):
 
 def ipt(*args):
     argv = ['iptables', '-t', 'nat'] + list(args)
-    debug1('>> %s\n' % ' '.join(argv))
-    rv = ssubprocess.call(argv)
-    if rv:
-        raise Fatal('%r returned %d' % (argv, rv))
+    _call(argv)
 
 
 _no_ttl_module = False
@@ -135,6 +146,42 @@ def _fill_oldctls(prefix):
         raise Fatal('%r returned no data' % (argv,))
 
 
+KERNEL_FLAGS_PATH = '/Library/Preferences/SystemConfiguration/com.apple.Boot'
+KERNEL_FLAGS_NAME = 'Kernel Flags'
+def _defaults_read_kernel_flags():
+    argv = ['defaults', 'read', KERNEL_FLAGS_PATH, KERNEL_FLAGS_NAME]
+    debug1('>> %s\n' % ' '.join(argv))
+    p = ssubprocess.Popen(argv, stdout = ssubprocess.PIPE)
+    flagstr = p.stdout.read().strip()
+    rv = p.wait()
+    if rv:
+        raise Fatal('%r returned %d' % (argv, rv))
+    flags = flagstr and flagstr.split(' ') or []
+    return flags
+
+
+def _defaults_write_kernel_flags(flags):
+    flagstr = ' '.join(flags)
+    argv = ['defaults', 'write', KERNEL_FLAGS_PATH, KERNEL_FLAGS_NAME,
+            flagstr]
+    _call(argv)
+    argv = ['plutil', '-convert', 'xml1', KERNEL_FLAGS_PATH + '.plist']
+    _call(argv)
+    
+
+
+def defaults_write_kernel_flag(name, val):
+    flags = _defaults_read_kernel_flags()
+    found = 0
+    for i in range(len(flags)):
+        if flags[i].startswith('%s=' % name):
+            found += 1
+            flags[i] = '%s=%s' % (name, val)
+    if not found:
+        flags.insert(0, '%s=%s' % (name, val))
+    _defaults_write_kernel_flags(flags)
+
+
 def _sysctl_set(name, val):
     argv = ['sysctl', '-w', '%s=%s' % (name, val)]
     debug1('>> %s\n' % ' '.join(argv))
@@ -150,20 +197,24 @@ def sysctl_set(name, val, permanent=False):
         _fill_oldctls(PREFIX)
     if not (name in _oldctls):
         debug1('>> No such sysctl: %r\n' % name)
-        return False
+        return NONEXIST
     oldval = _oldctls[name]
-    if val != oldval:
-        rv = _sysctl_set(name, val)
-        if rv==0 and permanent:
-            debug1('>>   ...saving permanently in /etc/sysctl.conf\n')
-            f = open('/etc/sysctl.conf', 'a')
-            f.write('\n'
-                    '# Added by sshuttle\n'
-                    '%s=%s\n' % (name, val))
-            f.close()
-        else:
-            _changedctls.append(name)
-        return True
+    if val == oldval:
+        return SAME
+
+    rv = _sysctl_set(name, val)
+    if rv != 0:
+        return FAILED
+    if permanent:
+        debug1('>>   ...saving permanently in /etc/sysctl.conf\n')
+        f = open('/etc/sysctl.conf', 'a')
+        f.write('\n'
+                '# Added by sshuttle\n'
+                '%s=%s\n' % (name, val))
+        f.close()
+    else:
+        _changedctls.append(name)
+    return SUCCESS
 
 
 def _udp_unpack(p):
@@ -201,10 +252,7 @@ def _handle_diversion(divertsock, dnsport):
 
 def ipfw(*args):
     argv = ['ipfw', '-q'] + list(args)
-    debug1('>> %s\n' % ' '.join(argv))
-    rv = ssubprocess.call(argv)
-    if rv:
-        raise Fatal('%r returned %d' % (argv, rv))
+    _call(argv)
 
 
 def do_ipfw(port, dnsport, subnets):
@@ -222,8 +270,14 @@ def do_ipfw(port, dnsport, subnets):
 
     if subnets or dnsport:
         sysctl_set('net.inet.ip.fw.enable', 1)
-        changed = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True)
-        if changed:
+
+        # This seems to be needed on MacOS 10.6 and 10.7.  For more
+        # information, see:
+        #   http://groups.google.com/group/sshuttle/browse_thread/thread/bc32562e17987b25/6d3aa2bb30a1edab
+        # and
+        #   http://serverfault.com/questions/138622/transparent-proxying-leaves-sockets-with-syn-rcvd-in-macos-x-10-6-snow-leopard
+        changeflag = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True)
+        if changeflag == SUCCESS:
             log("\n"
                 "        WARNING: ONE-TIME NETWORK DISRUPTION:\n"
                 "        =====================================\n"
@@ -234,6 +288,21 @@ def do_ipfw(port, dnsport, subnets):
                 "ethernet port) NOW, then restart sshuttle.  The fix is\n"
                 "permanent; you only have to do this once.\n\n")
             sys.exit(1)
+        elif changeflag == FAILED:
+            # On MacOS 10.7, the scopedroute sysctl became read-only, so
+            # we have to fix it using a kernel boot parameter instead,
+            # which requires rebooting.  For more, see:
+            #   http://groups.google.com/group/sshuttle/browse_thread/thread/a42505ca33e1de80/e5e8f3e5a92d25f7
+            log('Updating kernel boot flags.\n')
+            defaults_write_kernel_flag('net.inet.ip.scopedroute', 0)
+            log("\n"
+                "        YOU MUST REBOOT TO USE SSHUTTLE\n"
+                "        ===============================\n"
+                "sshuttle has changed a MacOS kernel boot-time setting\n"
+                "to work around a bug in MacOS 10.7 Lion.  You will need\n"
+                "to reboot before it takes effect.  You only have to\n"
+                "do this once.\n\n")
+            sys.exit(EXITCODE_NEEDS_REBOOT)
 
         ipfw('add', sport, 'check-state', 'ip',
              'from', 'any', 'to', 'any')
@@ -243,11 +312,11 @@ def do_ipfw(port, dnsport, subnets):
         for swidth,sexclude,snet in sorted(subnets, reverse=True):
             if sexclude:
                 ipfw('add', sport, 'skipto', xsport,
-                     'log', 'tcp',
+                     'tcp',
                      'from', 'any', 'to', '%s/%s' % (snet,swidth))
             else:
                 ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port,
-                     'log', 'tcp',
+                     'tcp',
                      'from', 'any', 'to', '%s/%s' % (snet,swidth),
                      'not', 'ipttl', '42', 'keep-state', 'setup')
 
@@ -289,12 +358,12 @@ def do_ipfw(port, dnsport, subnets):
         for ip in nslist:
             # relabel and then catch outgoing DNS requests
             ipfw('add', sport, 'divert', sport,
-                 'log', 'udp',
+                 'udp',
                  'from', 'any', 'to', '%s/32' % ip, '53',
                  'not', 'ipttl', '42')
         # relabel DNS responses
         ipfw('add', sport, 'divert', sport,
-             'log', 'udp',
+             'udp',
              'from', 'any', str(dnsport), 'to', 'any',
              'not', 'ipttl', '42')
 
@@ -398,6 +467,11 @@ def main(port, dnsport, syslog):
     sys.stdout.write('READY\n')
     sys.stdout.flush()
 
+    # don't disappear if our controlling terminal or stdout/stderr
+    # disappears; we still have to clean up.
+    signal.signal(signal.SIGHUP, signal.SIG_IGN)
+    signal.signal(signal.SIGPIPE, signal.SIG_IGN)
+
     # ctrl-c shouldn't be passed along to me.  When the main sshuttle dies,
     # I'll die automatically.
     os.setsid()
diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/helpers.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/helpers.py
index c169d0c..45a028b 100644
--- a/Sshuttle VPN.app/Contents/Resources/sshuttle/helpers.py	
+++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/helpers.py	
@@ -1,4 +1,4 @@
-import sys, os, socket
+import sys, os, socket, errno
 
 logprefix = ''
 verbose = 0
@@ -30,6 +30,11 @@ class Fatal(Exception):
     pass
 
 
+EXITCODE_NEEDS_REBOOT = 111
+class FatalNeedsReboot(Fatal):
+    pass
+
+
 def list_contains_any(l, sub):
     for i in sub:
         if i in l:
diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/main.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/main.py
old mode 100644
new mode 100755
index 1cf00af..daf3b87
--- a/Sshuttle VPN.app/Contents/Resources/sshuttle/main.py	
+++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/main.py	
@@ -63,6 +63,7 @@ seed-hosts= with -H, use these hostnames for initial scan (comma-separated)
 no-latency-control  sacrifice latency to improve bandwidth benchmarks
 wrap=      restart counting channel numbers after this number (for testing)
 D,daemon   run in the background as a daemon
+V,version  print sshuttle's version number
 syslog     send log messages to syslog (default if you use --daemon)
 pidfile=   pidfile name (only if using --daemon) [./sshuttle.pid]
 server     (internal use only)
@@ -72,6 +73,10 @@ hostwatch  (internal use only)
 o = options.Options(optspec)
 (opt, flags, extra) = o.parse(sys.argv[2:])
 
+if opt.version:
+    import version
+    print version.TAG
+    sys.exit(0)
 if opt.daemon:
     opt.syslog = 1
 if opt.wrap:
@@ -121,6 +126,9 @@ try:
                              parse_subnets(includes),
                              parse_subnets(excludes),
                              opt.syslog, opt.daemon, opt.pidfile))
+except FatalNeedsReboot, e:
+    log('You must reboot before using sshuttle.\n')
+    sys.exit(EXITCODE_NEEDS_REBOOT)
 except Fatal, e:
     log('fatal: %s\n' % e)
     sys.exit(99)
diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py
index b6d73c2..a636ce1 100644
--- a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py	
+++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py	
@@ -152,6 +152,17 @@ class SockWrapper:
                 debug3('%r: fixed connect result: %s\n' % (self, e))
             if e.args[0] in [errno.EINPROGRESS, errno.EALREADY]:
                 pass  # not connected yet
+            elif e.args[0] == 0:
+                # connected successfully (weird Linux bug?)
+                # Sometimes Linux seems to return EINVAL when it isn't
+                # invalid.  This *may* be caused by a race condition
+                # between connect() and getsockopt(SO_ERROR) (ie. it
+                # finishes connecting in between the two, so there is no
+                # longer an error).  However, I'm not sure of that.
+                #
+                # I did get at least one report that the problem went away
+                # when we added this, however.
+                self.connect_to = None
             elif e.args[0] == errno.EISCONN:
                 # connected successfully (BSD)
                 self.connect_to = None