From d5c3aa61b8b77d3b12647bb15a83ba51a5f4d312 Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Sun, 8 Jan 2012 19:16:05 -0500 Subject: [PATCH] MacOS precompiled app package for sshuttle-0.60 --- Sshuttle VPN.app/Contents/MacOS/Sshuttle | Bin 25248 -> 37568 bytes .../Resources/English.lproj/MainMenu.nib | Bin 28750 -> 27144 bytes .../Contents/Resources/askpass.pyc | Bin 1146 -> 0 bytes Sshuttle VPN.app/Contents/Resources/main.py | 10 +- Sshuttle VPN.app/Contents/Resources/models.py | 7 +- .../Contents/Resources/models.pyc | Bin 9502 -> 0 bytes Sshuttle VPN.app/Contents/Resources/my.pyc | Bin 2809 -> 0 bytes .../Contents/Resources/sshuttle/client.py | 120 ++++++++-------- .../Contents/Resources/sshuttle/firewall.py | 130 ++++++++++++++---- .../Contents/Resources/sshuttle/helpers.py | 7 +- .../Contents/Resources/sshuttle/main.py | 8 ++ .../Contents/Resources/sshuttle/ssnet.py | 11 ++ 12 files changed, 204 insertions(+), 89 deletions(-) delete mode 100644 Sshuttle VPN.app/Contents/Resources/askpass.pyc delete mode 100644 Sshuttle VPN.app/Contents/Resources/models.pyc delete mode 100644 Sshuttle VPN.app/Contents/Resources/my.pyc mode change 100644 => 100755 Sshuttle VPN.app/Contents/Resources/sshuttle/main.py diff --git a/Sshuttle VPN.app/Contents/MacOS/Sshuttle b/Sshuttle VPN.app/Contents/MacOS/Sshuttle index f45f4e2e4b5ba0c0fa91f2b950c527c7a36eccdf..334693bce34a1bd861422c89ea9d54c9ca219d47 100755 GIT binary patch literal 37568 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 uBRG)L6~=mQV{qUQ3=eAE!!8(Y*o~> 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 diff --git a/Sshuttle VPN.app/Contents/Resources/askpass.pyc b/Sshuttle VPN.app/Contents/Resources/askpass.pyc deleted file mode 100644 index 3be75fcc6ba460e1fca2781fdcf469d861f148f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1146 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 diff --git a/Sshuttle VPN.app/Contents/Resources/my.pyc b/Sshuttle VPN.app/Contents/Resources/my.pyc deleted file mode 100644 index 188ff607de9910106db9e60a0a1f37a25341e75b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2809 zcmcguU2hvz5S{C<*h!OC;iF9p!hA$>D;mEbRiJ=q6G{=_MC;I^yjYvvwX?~3*X+F( zSW2FPNc+N{;Scbe_ycgx>?SU}8^?Ec?%eUcGv}U}%^!cQF0bc*ejiBrspI)2hW!a5 z#Mj8F$lU2SEjrql&SEVRs?UjAVDkIQ+oK1!P6e)Ot~oe%i)LRpOe& zb(y;~=m;;mAoRW-((5c?JJ*Eh^`A`BFo_)F82fQrPH$s6@!j#$EcYUtP4gfcWW$5m zBpNsdmRVOTT~s;A;?&)MB`>}A@#79XbvgCN5u(FvHtFCYD(oJ#?hiJ~ZP%Yf>Cb+i zcWoYYtv#MPmqguZ{BZ0CneFyVOZ)kLZ_A%dx;t6wqSV>$Rd}{Lo^4HLZUuWDX-)$s z(7@DEh*>gsD*^H4VDyUYJ5c6$DWr&$kO5?%Rw71!$9;PJUEleRwDFT93*1}K;xu+I zVt4#x<{~>tqIBer2bwVS`Ni4==guG*-5Q>j;EU0O|3#%TVPpsbBExe-){#t@l9Kf9 zX@Zj^wywi_^T-dqVB{YMF3wUvpSgEo4X>v};ObuXhEL-p8t7&ondT9(1?SHr_P;~S zZ$Pm`L(EMRFd~;Vh5*&~dHT|7PruB9B$!Rw1rP!W$ZRw_xe;2a1kzCHloF>_=pgTH^8Oe}GKm z+(0O0q1-d~On|9=SS$*fG5}csu6ZLNw2pM^^;yTe(a@hJ&bl{Yvd&LKKM!ADm}jHf zLOMVbXI}-ed_C12Nd@CBC}J+h0G!iN)eJt(a8hcZ8Q3coJA{Q|xSs zVm?jNI32Y&^LA&L_@mD^^ZOmx%UWU_+G4q;il2{)<~*FHfwrtuG}ckkdlmvXBOZ*h zE*`cCA!Y?-w>;+%gH&Wbf>I=pqTB#5kE+{6^F{Fi3l!OZK>**7E056DzCYVLI@vkM zvc$R;jE^UiXHhbVa-9We$&ZIF$_s>e@m`yP2dix%05Cru{Ry6B+qq?m5V+)hK=D4s zhZI~XR@tyLwqmu-sXEhdA)%7^yHVoLqHqThANj%QL!Q-y5YsYu>g&~jkaIA+9Sr+1 z#B7Z{vv@J~U!=FChW>f7EZK?}eE8o?S`qVOND>qIg2bv8%`A2LZ^m;!nMPmbd6w&& z(PKyj98eaMHE+RpS?rdl;?`0HqQCcEs+dUVr@0js`^oxa*%BizhUYcEpxP$V$QEfF_^;V;`)LLz={sWN| BY{CEl 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