diff --git a/Sshuttle VPN.app/Contents/MacOS/Sshuttle b/Sshuttle VPN.app/Contents/MacOS/Sshuttle index 76f25e2..f45f4e2 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 349dc68..af813ea 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 new file mode 100644 index 0000000..3be75fc Binary files /dev/null and b/Sshuttle VPN.app/Contents/Resources/askpass.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/models.pyc b/Sshuttle VPN.app/Contents/Resources/models.pyc new file mode 100644 index 0000000..6163bd5 Binary files /dev/null and b/Sshuttle VPN.app/Contents/Resources/models.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/my.pyc b/Sshuttle VPN.app/Contents/Resources/my.pyc new file mode 100644 index 0000000..188ff60 Binary files /dev/null and b/Sshuttle VPN.app/Contents/Resources/my.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py index 0ff5f2b..449a75a 100644 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py @@ -198,7 +198,14 @@ def _main(listener, fw, ssh_cmd, remotename, python, latency_control, handlers.append(mux) expected = 'SSHUTTLE0001' + try: + v = 'x' + while v and v != '\0': + v = serversock.recv(1) + v = 'x' + while v and v != '\0': + v = serversock.recv(1) initstring = serversock.recv(len(expected)) except socket.error, e: if e.args[0] == errno.ECONNRESET: diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/server.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/server.py index 37daf77..e1b327d 100644 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/server.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/server.py @@ -110,23 +110,51 @@ class DnsProxy(Handler): def __init__(self, mux, chan, request): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) Handler.__init__(self, [sock]) - self.sock = sock self.timeout = time.time()+30 self.mux = mux self.chan = chan + self.tries = 0 + self.peer = None + self.request = request + self.sock = sock self.sock.setsockopt(socket.SOL_IP, socket.IP_TTL, 42) - self.sock.connect((resolvconf_random_nameserver(), 53)) - self.sock.send(request) + self.try_send() + + def try_send(self): + if self.tries >= 3: + return + self.tries += 1 + self.peer = resolvconf_random_nameserver() + self.sock.connect((self.peer, 53)) + debug2('DNS: sending to %r\n' % self.peer) + try: + self.sock.send(self.request) + except socket.error, e: + if e.args[0] in ssnet.NET_ERRS: + # might have been spurious; try again. + # Note: these errors sometimes are reported by recv(), + # and sometimes by send(). We have to catch both. + debug2('DNS send to %r: %s\n' % (self.peer, e)) + self.try_send() + return + else: + log('DNS send to %r: %s\n' % (self.peer, e)) + return def callback(self): try: data = self.sock.recv(4096) except socket.error, e: - if e.args[0] == errno.ECONNREFUSED: - debug2('DNS response: ignoring ECONNREFUSED.\n') - return # might have been spurious; wait for a real answer + if e.args[0] in ssnet.NET_ERRS: + # might have been spurious; try again. + # Note: these errors sometimes are reported by recv(), + # and sometimes by send(). We have to catch both. + debug2('DNS recv from %r: %s\n' % (self.peer, e)) + self.try_send() + return else: - raise + log('DNS recv from %r: %s\n' % (self.peer, e)) + return debug2('DNS response: %d bytes\n' % len(data)) self.mux.send(self.chan, ssnet.CMD_DNS_RESPONSE, data) self.ok = False @@ -145,7 +173,7 @@ def main(): debug1(' %s/%d\n' % r) # synchronization header - sys.stdout.write('SSHUTTLE0001') + sys.stdout.write('\0\0SSHUTTLE0001') sys.stdout.flush() handlers = [] diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.py index c7be991..c4bf06e 100644 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.py @@ -85,7 +85,7 @@ def connect(ssh_cmd, rhostport, python, stderr, options): pycmd = "'%s' -c '%s'" % (python, pyscript) else: pycmd = ("P=python2; $P -V 2>/dev/null || P=python; " - "\"$P\" -c '%s'") % pyscript + "exec \"$P\" -c '%s'") % pyscript argv = (sshl + portl + ipv6flag + diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/sshuttle b/Sshuttle VPN.app/Contents/Resources/sshuttle/sshuttle index d832198..2d234d5 100755 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/sshuttle +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/sshuttle @@ -1,5 +1,10 @@ #!/bin/sh -DIR=$(dirname "$0") +EXE=$0 +for i in 1 2 3 4 5 6 7 8 9 10; do + [ -L "$EXE" ] || break + EXE=$(readlink "$EXE") +done +DIR=$(dirname "$EXE") if python2 -V 2>/dev/null; then exec python2 "$DIR/main.py" python2 "$@" else diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py index 2145431..b6d73c2 100644 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py @@ -40,7 +40,11 @@ cmd_to_name = { CMD_DNS_REQ: 'DNS_REQ', CMD_DNS_RESPONSE: 'DNS_RESPONSE', } - + + +NET_ERRS = [errno.ECONNREFUSED, errno.ETIMEDOUT, + errno.EHOSTUNREACH, errno.ENETUNREACH, + errno.EHOSTDOWN, errno.ENETDOWN] def _add(l, elem): @@ -86,7 +90,7 @@ class SockWrapper: def __init__(self, rsock, wsock, connect_to=None, peername=None): global _swcount _swcount += 1 - debug3('creating new SockWrapper (%d now exist\n)' % _swcount) + debug3('creating new SockWrapper (%d now exist)\n' % _swcount) self.exc = None self.rsock = rsock self.wsock = wsock @@ -101,7 +105,7 @@ class SockWrapper: _swcount -= 1 debug1('%r: deleting (%d remain)\n' % (self, _swcount)) if self.exc: - debug1('%r: error was: %r\n' % (self, self.exc)) + debug1('%r: error was: %s\n' % (self, self.exc)) def __repr__(self): if self.rsock == self.wsock: @@ -124,20 +128,34 @@ class SockWrapper: return # already connected self.rsock.setblocking(False) debug3('%r: trying connect to %r\n' % (self, self.connect_to)) + if socket.inet_aton(self.connect_to[0])[0] == '\0': + self.seterr(Exception("Can't connect to %r: " + "IP address starts with zero\n" + % (self.connect_to,))) + self.connect_to = None + return try: self.rsock.connect(self.connect_to) # connected successfully (Linux) self.connect_to = None except socket.error, e: - debug3('%r: connect result: %r\n' % (self, e)) + debug3('%r: connect result: %s\n' % (self, e)) + if e.args[0] == errno.EINVAL: + # this is what happens when you call connect() on a socket + # that is now connected but returned EINPROGRESS last time, + # on BSD, on python pre-2.5.1. We need to use getsockopt() + # to get the "real" error. Later pythons do this + # automatically, so this code won't run. + realerr = self.rsock.getsockopt(socket.SOL_SOCKET, + socket.SO_ERROR) + e = socket.error(realerr, os.strerror(realerr)) + 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] == errno.EISCONN: # connected successfully (BSD) self.connect_to = None - elif e.args[0] in [errno.ECONNREFUSED, errno.ETIMEDOUT, - errno.EHOSTUNREACH, errno.ENETUNREACH, - errno.EACCES, errno.EPERM]: + elif e.args[0] in NET_ERRS + [errno.EACCES, errno.EPERM]: # a "normal" kind of error self.connect_to = None self.seterr(e)