From 76359bc71ce4d850cb5778026dd359a0ee3cb6f8 Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Tue, 1 Feb 2011 03:57:29 -0800 Subject: [PATCH] MacOS precompiled app package for sshuttle-0.50a-2-ga238f76 --- .../Resources/English.lproj/MainMenu.nib | Bin 27923 -> 29570 bytes .../Contents/Resources/askpass.pyc | Bin 0 -> 1146 bytes Sshuttle VPN.app/Contents/Resources/main.py | 33 ++-- Sshuttle VPN.app/Contents/Resources/models.py | 26 +++ .../Contents/Resources/models.pyc | Bin 0 -> 8985 bytes Sshuttle VPN.app/Contents/Resources/my.pyc | Bin 0 -> 2809 bytes .../Contents/Resources/sshuttle/client.py | 81 ++++++--- .../Contents/Resources/sshuttle/client.pyc | Bin 0 -> 13665 bytes .../Resources/sshuttle/compat/__init__.pyc | Bin 0 -> 188 bytes .../Resources/sshuttle/compat/ssubprocess.pyc | Bin 0 -> 36231 bytes .../Contents/Resources/sshuttle/firewall.py | 161 ++++++++++++++++-- .../Contents/Resources/sshuttle/firewall.pyc | Bin 0 -> 12663 bytes .../Contents/Resources/sshuttle/helpers.py | 40 ++++- .../Contents/Resources/sshuttle/helpers.pyc | Bin 0 -> 3118 bytes .../Contents/Resources/sshuttle/hostwatch.pyc | Bin 0 -> 9157 bytes .../Contents/Resources/sshuttle/main.py | 13 +- .../Contents/Resources/sshuttle/options.py | 9 +- .../Contents/Resources/sshuttle/options.pyc | Bin 0 -> 8735 bytes .../Contents/Resources/sshuttle/server.py | 40 ++++- .../Contents/Resources/sshuttle/server.pyc | Bin 0 -> 9224 bytes .../Contents/Resources/sshuttle/ssh.py | 14 +- .../Contents/Resources/sshuttle/ssh.pyc | Bin 0 -> 3963 bytes .../Contents/Resources/sshuttle/sshuttle | 13 +- .../Contents/Resources/sshuttle/ssnet.py | 10 +- .../Contents/Resources/sshuttle/ssnet.pyc | Bin 0 -> 21065 bytes .../Contents/Resources/sshuttle/ssyslog.pyc | Bin 0 -> 965 bytes Sshuttle VPN.app/Contents/Resources/stupid.py | 14 -- 27 files changed, 362 insertions(+), 92 deletions(-) create mode 100644 Sshuttle VPN.app/Contents/Resources/askpass.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/models.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/my.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/sshuttle/client.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/sshuttle/compat/__init__.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/sshuttle/compat/ssubprocess.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/sshuttle/helpers.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/sshuttle/hostwatch.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/sshuttle/options.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/sshuttle/server.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.pyc create mode 100644 Sshuttle VPN.app/Contents/Resources/sshuttle/ssyslog.pyc delete mode 100644 Sshuttle VPN.app/Contents/Resources/stupid.py diff --git a/Sshuttle VPN.app/Contents/Resources/English.lproj/MainMenu.nib b/Sshuttle VPN.app/Contents/Resources/English.lproj/MainMenu.nib index 302f9fbd892003f94656e586fde7a33415d04d40..e0e3d32e13d2d9c419ad8598ee925ab16d4b567e 100644 GIT binary patch literal 29570 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 QCJ4>-61L+RLdQ-30Qr`H{{R30 literal 27923 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

I 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 z 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 RM*2|?bM9l5{~s~z{{ipL3T*%Y diff --git a/Sshuttle VPN.app/Contents/Resources/askpass.pyc b/Sshuttle VPN.app/Contents/Resources/askpass.pyc new file mode 100644 index 0000000000000000000000000000000000000000..180ae1a925dc1de54afcf9f518e564e51667ac19 GIT binary patch literal 1146 zcmcIiO>Yx15FKwmo3=u#1P4ybN1Mt1%r-I@V|_$H}D<&r69oC|98Z_0qpLyz66%qv%j{ z_j}PjaZ4PCj_8V`VEaC|pcPjbv|1b=uy5=W9N%X1PxSv)j$ID+Y++TE>lePG;Y~b= F{sN&X1DOB- literal 0 HcmV?d00001 diff --git a/Sshuttle VPN.app/Contents/Resources/main.py b/Sshuttle VPN.app/Contents/Resources/main.py index baa290d..cadf0d9 100644 --- a/Sshuttle VPN.app/Contents/Resources/main.py +++ b/Sshuttle VPN.app/Contents/Resources/main.py @@ -2,7 +2,8 @@ import sys, os, pty from AppKit import * import my, models, askpass -def sshuttle_args(host, auto_nets, auto_hosts, nets, debug): +def sshuttle_args(host, auto_nets, auto_hosts, dns, nets, debug, + no_latency_control): argv = [my.bundle_path('sshuttle/sshuttle', ''), '-r', host] assert(argv[0]) if debug: @@ -11,6 +12,10 @@ def sshuttle_args(host, auto_nets, auto_hosts, nets, debug): argv.append('--auto-nets') if auto_hosts: argv.append('--auto-hosts') + if dns: + argv.append('--dns') + if no_latency_control: + argv.append('--no-latency-control') argv += nets return argv @@ -131,6 +136,7 @@ class SshuttleController(NSObject): prefsWindow = objc.IBOutlet() serversController = objc.IBOutlet() logField = objc.IBOutlet() + noLatencyControlField = objc.IBOutlet() servers = [] conns = {} @@ -159,8 +165,11 @@ class SshuttleController(NSObject): conn = Runner(sshuttle_args(host, auto_nets = nets_mode == models.NET_AUTO, auto_hosts = server.autoHosts(), + dns = server.useDns(), nets = manual_nets, - debug = self.debugField.state()), + debug = self.debugField.state(), + no_latency_control + = self.noLatencyControlField.state()), logfunc=logfunc, promptfunc=promptfunc, serverobj=server) self.conns[host] = conn @@ -213,6 +222,7 @@ class SshuttleController(NSObject): if len(self.servers): for i in self.servers: host = i.host() + title = i.title() want = i.wantConnect() connected = i.connected() numnets = len(list(i.nets())) @@ -222,9 +232,9 @@ class SshuttleController(NSObject): additem('Connect %s (no routes)' % host, None, i) elif want: any_conn = i - additem('Disconnect %s' % host, self.cmd_disconnect, i) + additem('Disconnect %s' % title, self.cmd_disconnect, i) else: - additem('Connect %s' % host, self.cmd_connect, i) + additem('Connect %s' % title, self.cmd_connect, i) if not want: msg = 'Off' elif i.error(): @@ -236,12 +246,6 @@ class SshuttleController(NSObject): msg = 'Connecting...' any_inprogress = i addnote(' State: %s' % msg) - if i.autoNets() == 0: - addnote(' Routes: All') - elif i.autoNets() == 2: - addnote(' Routes: Auto') - else: - addnote(' Routes: Custom') else: addnote('No servers defined yet') @@ -279,13 +283,15 @@ class SshuttleController(NSObject): net.setWidth_(width) nl.append(net) - autoNets = s.get('autoNets', 1) - autoHosts = s.get('autoHosts', 1) + autoNets = s.get('autoNets', models.NET_AUTO) + autoHosts = s.get('autoHosts', True) + useDns = s.get('useDns', autoNets == models.NET_ALL) srv = models.SshuttleServer.alloc().init() srv.setHost_(host) srv.setAutoNets_(autoNets) srv.setAutoHosts_(autoHosts) srv.setNets_(nl) + srv.setUseDns_(useDns) sl.append(srv) self.serversController.addObjects_(sl) self.serversController.setSelectionIndex_(0) @@ -303,7 +309,8 @@ class SshuttleController(NSObject): d = dict(host=s.host(), nets=nets, autoNets=s.autoNets(), - autoHosts=s.autoHosts()) + autoHosts=s.autoHosts(), + useDns=s.useDns()) l.append(d) my.Defaults().setObject_forKey_(l, 'servers') self.fill_menu() diff --git a/Sshuttle VPN.app/Contents/Resources/models.py b/Sshuttle VPN.app/Contents/Resources/models.py index 858975e..ad8e538 100644 --- a/Sshuttle VPN.app/Contents/Resources/models.py +++ b/Sshuttle VPN.app/Contents/Resources/models.py @@ -92,11 +92,28 @@ class SshuttleServer(NSObject): if self.autoNets() == NET_MANUAL and not len(list(self.nets())): return False return True + + def title(self): + host = self.host() + if not host: + return host + an = self.autoNets() + suffix = "" + if an == NET_ALL: + suffix = " (all traffic)" + elif an == NET_MANUAL: + n = self.nets() + suffix = ' (%d subnet%s)' % (len(n), len(n)!=1 and 's' or '') + return self.host() + suffix + def setTitle_(self, v): + # title is always auto-generated + config_changed() def host(self): return getattr(self, '_k_host', None) def setHost_(self, v): self._k_host = v + self.setTitle_(None) config_changed() @objc.accessor def validateHost_error_(self, value, error): @@ -109,6 +126,7 @@ class SshuttleServer(NSObject): return getattr(self, '_k_nets', []) def setNets_(self, v): self._k_nets = v + self.setTitle_(None) config_changed() def netsHidden(self): #print 'checking netsHidden' @@ -122,6 +140,8 @@ class SshuttleServer(NSObject): def setAutoNets_(self, v): self._k_autoNets = v self.setNetsHidden_(-1) + self.setUseDns_(v == NET_ALL) + self.setTitle_(None) config_changed() def autoHosts(self): @@ -129,3 +149,9 @@ class SshuttleServer(NSObject): def setAutoHosts_(self, v): self._k_autoHosts = v config_changed() + + def useDns(self): + return getattr(self, '_k_useDns', False) + def setUseDns_(self, v): + self._k_useDns = v + config_changed() diff --git a/Sshuttle VPN.app/Contents/Resources/models.pyc b/Sshuttle VPN.app/Contents/Resources/models.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc9c70ae111255bedb6600c13e53bcb09240feb0 GIT binary patch literal 8985 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 b5ji$=C9}?4dA1BaEYxait5qE9s->?1KOJ=; literal 0 HcmV?d00001 diff --git a/Sshuttle VPN.app/Contents/Resources/my.pyc b/Sshuttle VPN.app/Contents/Resources/my.pyc new file mode 100644 index 0000000000000000000000000000000000000000..112d1e698ac946be2b7a80fb50fac41b2a661ec3 GIT binary patch literal 2809 zcmcguU2hvz5S{C<*h!OC<)cjt!hA$>D;mEbRiJ=q6WSuciPoV-d9gOTYiE=7uGxDn zu#`Lnk@kf@!*AdZA>QGf*-cz{H;(V@+_~d>XU;t{o8SLjUH;>*r{4uqe(HFBiD7?) z2=O&?B64u08#yp)ugFP7_Dhed9MmMN$}e!OOITB-Az@vWB?%jlmnx;(kzpLMQ?;H~q@T92QkA$S zab4yv4LZV$E(pD^hx9s2*v>T}di}@KG)yALIL3aQmeX69PJDOtB+I?XX45=~2H9|b zHi-s~fo0a!N*7g*vp99vV987GJ^Z*sPhC#^afIkFn@u`+hzh$4t^2)=a@+MMQTmgg z=UtlzU2Bh~&LvTI8b28OL1w%C($apu*W2_blkRqwx+rzFdl{bXj%S;bnOni0hnmxX z2{bUZ6k?Xlok~D_IT*bn`wo;jUJ5B9C1d~@sFjG(-*KN_f5&&eBW?U7$pZHlv^b63 zi`X4Kp1H^lk|-Uyqk$$2eSWca!MQU?Mz@BiCHP`A;eS!7Oc)u0fXML7kaZ*zrlcgj zdz#=RiLLAK-aPU{FBtj9fs3=$&u8vkSi`F+5xBgU-Qklsi3YlvN2Ym1Y{B{Sh`nzS z^BYhs(GYXp1Wbq%4!!fwpy1AtHc(*N6=n_y&PC2DQq&Q2mQq7bbrlY0;6?R{5n4ms zG6QuBd^pP^KXhE{^}inbqsU`bxK)TtlJJHG-VInf{DGpQT#0^m3i}~hq?UL*`X3ran5^^D(9gry7v|Zh zwvZ0c#MzgDv>6A3EGvP4b6m%;4h4IWQg8gkzYk#iP}Ym6?S`A zE;+zWdNG7tAyvghiT5d#m%8>{D$yS15X0WZHk>O zQOu`l8mFW7M&9lW6MyviMt-jYds$12Lt8A@RPpms(VU0VG|-lHg2p;3de1@tXT*b1 z*2Tj%A;hep?3U*oVvvf=M^K97QIzWd=3#ZKXuc>uV1Xk0F9_fpa^(@)+V^I=hsWFd zS(aGWg7ML0@+?XwQLeK9E&0*VMR|b`FWzfY@L;tq1OVp8qd&p3Y&$nh5dxRI4=CQJ z_>h7t#VQ+?##XGhIaO!+EhJPDe= 1: @@ -200,7 +187,8 @@ def _main(listener, fw, ssh_cmd, remotename, python, seed_hosts, auto_nets, try: (serverproc, serversock) = ssh.connect(ssh_cmd, remotename, python, - stderr=ssyslog._p and ssyslog._p.stdin) + stderr=ssyslog._p and ssyslog._p.stdin, + options=dict(latency_control=latency_control)) except socket.error, e: if e.args[0] == errno.EPIPE: raise Fatal("failed to establish ssh session (1)") @@ -280,7 +268,7 @@ def _main(listener, fw, ssh_cmd, remotename, python, seed_hosts, auto_nets, 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]): + if dstip[1] == listener.getsockname()[1] and islocal(dstip[0]): debug1("-- ignored: that's my address!\n") sock.close() return @@ -290,6 +278,30 @@ def _main(listener, fw, ssh_cmd, remotename, python, seed_hosts, auto_nets, handlers.append(Proxy(SockWrapper(sock, sock), outwrap)) handlers.append(Handler([listener], onaccept)) + 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)) + if seed_hosts != None: debug1('seed_hosts: %r\n' % seed_hosts) mux.send(0, ssnet.CMD_HOST_REQ, '\n'.join(seed_hosts)) @@ -300,11 +312,13 @@ def _main(listener, fw, ssh_cmd, remotename, python, seed_hosts, auto_nets, raise Fatal('server died with error code %d' % rv) ssnet.runonce(handlers, mux) + if latency_control: + mux.check_fullness() mux.callback() - mux.check_fullness() -def main(listenip, ssh_cmd, remotename, python, seed_hosts, auto_nets, +def main(listenip, ssh_cmd, remotename, python, latency_control, dns, + seed_hosts, auto_nets, subnets_include, subnets_exclude, syslog, daemon, pidfile): if syslog: ssyslog.start_syslog() @@ -315,8 +329,7 @@ def main(listenip, ssh_cmd, remotename, python, seed_hosts, auto_nets, log("%s\n" % e) return 5 debug1('Starting sshuttle proxy.\n') - listener = socket.socket() - listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + if listenip[1]: ports = [listenip[1]] else: @@ -326,8 +339,13 @@ def main(listenip, ssh_cmd, remotename, python, seed_hosts, auto_nets, debug2('Binding:') for port in ports: debug2(' %d' % port) + listener = socket.socket() + listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + dnslistener = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + dnslistener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: listener.bind((listenip[0], port)) + dnslistener.bind((listenip[0], port)) bound = True break except socket.error, e: @@ -340,11 +358,20 @@ def main(listenip, ssh_cmd, remotename, python, seed_hosts, auto_nets, listenip = listener.getsockname() debug1('Listening on %r.\n' % (listenip,)) - fw = FirewallClient(listenip[1], subnets_include, subnets_exclude) + if dns: + dnsip = dnslistener.getsockname() + debug1('DNS listening on %r.\n' % (dnsip,)) + dnsport = dnsip[1] + else: + dnsport = 0 + dnslistener = None + + fw = FirewallClient(listenip[1], subnets_include, subnets_exclude, dnsport) try: return _main(listener, fw, ssh_cmd, remotename, - python, seed_hosts, auto_nets, syslog, daemon) + python, latency_control, dnslistener, + seed_hosts, auto_nets, syslog, daemon) finally: try: if daemon: diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/client.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/client.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64b7efb857342f845b716fa03dc616ab25cf0f8a GIT binary patch literal 13665 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 Hvi!dRb0Szk literal 0 HcmV?d00001 diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py index 044ac52..c7557ed 100644 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py @@ -1,8 +1,11 @@ -import re, errno +import re, errno, socket, select, struct import compat.ssubprocess as ssubprocess import helpers, ssyslog from helpers import * +# python doesn't have a definition for this +IPPROTO_DIVERT = 254 + def ipt_chain_exists(name): argv = ['iptables', '-t', 'nat', '-nL'] @@ -23,12 +26,33 @@ def ipt(*args): raise Fatal('%r returned %d' % (argv, rv)) +_no_ttl_module = False +def ipt_ttl(*args): + global _no_ttl_module + if not _no_ttl_module: + # we avoid infinite loops by generating server-side connections + # with ttl 42. This makes the client side not recapture those + # connections, in case client == server. + try: + argsplus = list(args) + ['-m', 'ttl', '!', '--ttl', '42'] + ipt(*argsplus) + except Fatal: + ipt(*args) + # we only get here if the non-ttl attempt succeeds + log('sshuttle: warning: your iptables is missing ' + 'the ttl module.\n') + _no_ttl_module = True + else: + ipt(*args) + + + # We name the chain based on the transproxy port number so that it's possible # to run multiple copies of sshuttle at the same time. Of course, the # multiple copies shouldn't have overlapping subnets, or only the most- # recently-started one will win (because we use "-I OUTPUT 1" instead of # "-A OUTPUT"). -def do_iptables(port, subnets): +def do_iptables(port, dnsport, subnets): chain = 'sshuttle-%s' % port # basic cleanup/setup of chains @@ -38,12 +62,13 @@ def do_iptables(port, subnets): ipt('-F', chain) ipt('-X', chain) - if subnets: + if subnets or dnsport: ipt('-N', chain) ipt('-F', chain) ipt('-I', 'OUTPUT', '1', '-j', chain) ipt('-I', 'PREROUTING', '1', '-j', chain) + if subnets: # create new subnet entries. Note that we're sorting in a very # particular order: we need to go from most-specific (largest swidth) # to least-specific, and at any given level of specificity, we want @@ -55,12 +80,19 @@ def do_iptables(port, subnets): '--dest', '%s/%s' % (snet,swidth), '-p', 'tcp') else: - ipt('-A', chain, '-j', 'REDIRECT', - '--dest', '%s/%s' % (snet,swidth), - '-p', 'tcp', - '--to-ports', str(port), - '-m', 'ttl', '!', '--ttl', '42' # to prevent infinite loops - ) + ipt_ttl('-A', chain, '-j', 'REDIRECT', + '--dest', '%s/%s' % (snet,swidth), + '-p', 'tcp', + '--to-ports', str(port)) + + if dnsport: + nslist = resolvconf_nameservers() + for ip in nslist: + ipt_ttl('-A', chain, '-j', 'REDIRECT', + '--dest', '%s/32' % ip, + '-p', 'udp', + '--dport', '53', + '--to-ports', str(dnsport)) def ipfw_rule_exists(n): @@ -69,7 +101,7 @@ def ipfw_rule_exists(n): found = False for line in p.stdout: if line.startswith('%05d ' % n): - if not ('ipttl 42 setup keep-state' in line + if not ('ipttl 42' in line or ('skipto %d' % (n+1)) in line or 'check-state' in line): log('non-sshuttle ipfw rule: %r\n' % line.strip()) @@ -116,6 +148,39 @@ def sysctl_set(name, val): if val != oldval: _changedctls.append(name) return _sysctl_set(name, val) + + +def _udp_unpack(p): + src = (socket.inet_ntoa(p[12:16]), struct.unpack('!H', p[20:22])[0]) + dst = (socket.inet_ntoa(p[16:20]), struct.unpack('!H', p[22:24])[0]) + return src, dst + + +def _udp_repack(p, src, dst): + addrs = socket.inet_aton(src[0]) + socket.inet_aton(dst[0]) + ports = struct.pack('!HH', src[1], dst[1]) + return p[:12] + addrs + ports + p[24:] + + +_real_dns_server = [None] +def _handle_diversion(divertsock, dnsport): + p,tag = divertsock.recvfrom(4096) + src,dst = _udp_unpack(p) + debug3('got diverted packet from %r to %r\n' % (src, dst)) + if dst[1] == 53: + # outgoing DNS + debug3('...packet is a DNS request.\n') + _real_dns_server[0] = dst + dst = ('127.0.0.1', dnsport) + elif src[1] == dnsport: + if islocal(src[0]): + debug3('...packet is a DNS response.\n') + src = _real_dns_server[0] + else: + log('weird?! unexpected divert from %r to %r\n' % (src, dst)) + assert(0) + newp = _udp_repack(p, src, dst) + divertsock.sendto(newp, tag) def ipfw(*args): @@ -126,7 +191,7 @@ def ipfw(*args): raise Fatal('%r returned %d' % (argv, rv)) -def do_ipfw(port, subnets): +def do_ipfw(port, dnsport, subnets): sport = str(port) xsport = str(port+1) @@ -139,13 +204,14 @@ def do_ipfw(port, subnets): oldval = _oldctls[name] _sysctl_set(name, oldval) - if subnets: + if subnets or dnsport: sysctl_set('net.inet.ip.fw.enable', 1) sysctl_set('net.inet.ip.scopedroute', 0) ipfw('add', sport, 'check-state', 'ip', 'from', 'any', 'to', 'any') - + + if subnets: # create new subnet entries for swidth,sexclude,snet in sorted(subnets, reverse=True): if sexclude: @@ -158,6 +224,65 @@ def do_ipfw(port, subnets): 'from', 'any', 'to', '%s/%s' % (snet,swidth), 'not', 'ipttl', '42', 'keep-state', 'setup') + # This part is much crazier than it is on Linux, because MacOS (at least + # 10.6, and probably other versions, and maybe FreeBSD too) doesn't + # correctly fixup the dstip/dstport for UDP packets when it puts them + # through a 'fwd' rule. It also doesn't fixup the srcip/srcport in the + # response packet. In Linux iptables, all that happens magically for us, + # so we just redirect the packets and relax. + # + # On MacOS, we have to fix the ports ourselves. For that, we use a + # 'divert' socket, which receives raw packets and lets us mangle them. + # + # Here's how it works. Let's say the local DNS server is 1.1.1.1:53, + # and the remote DNS server is 2.2.2.2:53, and the local transproxy port + # is 10.0.0.1:12300, and a client machine is making a request from + # 10.0.0.5:9999. We see a packet like this: + # 10.0.0.5:9999 -> 1.1.1.1:53 + # Since the destip:port matches one of our local nameservers, it will + # match a 'fwd' rule, thus grabbing it on the local machine. However, + # the local kernel will then see a packet addressed to *:53 and + # not know what to do with it; there's nobody listening on port 53. Thus, + # we divert it, rewriting it into this: + # 10.0.0.5:9999 -> 10.0.0.1:12300 + # This gets proxied out to the server, which sends it to 2.2.2.2:53, + # and the answer comes back, and the proxy sends it back out like this: + # 10.0.0.1:12300 -> 10.0.0.5:9999 + # But that's wrong! The original machine expected an answer from + # 1.1.1.1:53, so we have to divert the *answer* and rewrite it: + # 1.1.1.1:53 -> 10.0.0.5:9999 + # + # See? Easy stuff. + if dnsport: + divertsock = socket.socket(socket.AF_INET, socket.SOCK_RAW, + IPPROTO_DIVERT) + divertsock.bind(('0.0.0.0', port)) # IP field is ignored + + nslist = resolvconf_nameservers() + for ip in nslist: + # relabel and then catch outgoing DNS requests + ipfw('add', sport, 'divert', sport, + 'log', 'udp', + 'from', 'any', 'to', '%s/32' % ip, '53', + 'not', 'ipttl', '42') + # relabel DNS responses + ipfw('add', sport, 'divert', sport, + 'log', 'udp', + 'from', 'any', str(dnsport), 'to', 'any', + 'not', 'ipttl', '42') + + def do_wait(): + while 1: + r,w,x = select.select([sys.stdin, divertsock], [], []) + if divertsock in r: + _handle_diversion(divertsock, dnsport) + if sys.stdin in r: + return + else: + do_wait = None + + return do_wait + def program_exists(name): paths = (os.getenv('PATH') or os.defpath).split(os.pathsep) @@ -166,6 +291,7 @@ def program_exists(name): if os.path.exists(fn): return not os.path.isdir(fn) and os.access(fn, os.X_OK) + hostmap = {} def rewrite_etc_hosts(port): HOSTSFILE='/etc/hosts' @@ -216,9 +342,11 @@ def restore_etc_hosts(port): # exit. In case that fails, it's not the end of the world; future runs will # supercede it in the transproxy list, at least, so the leftover rules # are hopefully harmless. -def main(port, syslog): +def main(port, dnsport, syslog): assert(port > 0) assert(port <= 65535) + assert(dnsport >= 0) + assert(dnsport <= 65535) if os.getuid() != 0: raise Fatal('you must be root (or enable su/sudo) to set the firewall') @@ -272,7 +400,7 @@ def main(port, syslog): try: if line: debug1('firewall manager: starting transproxy.\n') - do_it(port, subnets) + do_wait = do_it(port, dnsport, subnets) sys.stdout.write('STARTED\n') try: @@ -286,6 +414,7 @@ def main(port, syslog): # to stay running so that we don't need a *second* password # authentication at shutdown time - that cleanup is important! while 1: + if do_wait: do_wait() line = sys.stdin.readline(128) if line.startswith('HOST '): (name,ip) = line[5:].strip().split(',', 1) @@ -300,5 +429,5 @@ def main(port, syslog): debug1('firewall manager: undoing changes.\n') except: pass - do_it(port, []) + do_it(port, 0, []) restore_etc_hosts(port) diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7539b6fd006890ac8be159375d9b017a25d6139a GIT binary patch literal 12663 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 2 and words[0] == 'nameserver': + l.append(words[1]) + return l + + +def resolvconf_random_nameserver(): + l = resolvconf_nameservers() + if l: + if len(l) > 1: + # don't import this unless we really need it + import random + random.shuffle(l) + return l[0] + else: + return '127.0.0.1' + + +def islocal(ip): + sock = socket.socket() + try: + try: + sock.bind((ip, 0)) + except socket.error, e: + if e.args[0] == errno.EADDRNOTAVAIL: + return False # not a local IP + else: + raise + finally: + sock.close() + return True # it's a local IP, or there would have been an error + + diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/helpers.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/helpers.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ced3de353d57126ba796c8215bdc98cba89767f7 GIT binary patch literal 3118 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 aWe2mp+5T)7e)_X-W?#%^vu)Y-Z1z7-@)V~4 literal 0 HcmV?d00001 diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/main.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/main.py index 66954f8..e76e596 100755 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/main.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/main.py @@ -54,12 +54,14 @@ sshuttle --hostwatch l,listen= transproxy to this ip address and port number [127.0.0.1:0] H,auto-hosts scan for remote hostnames and update local /etc/hosts N,auto-nets automatically determine subnets to route +dns capture local DNS requests and forward to the remote DNS server python= path to python interpreter on the remote server [python] r,remote= ssh hostname (and optional username) of remote sshuttle server x,exclude= exclude this subnet (can be used more than once) v,verbose increase debug message verbosity e,ssh-cmd= the command to use to connect to the remote [ssh] seed-hosts= with -H, use these hostnames for initial scan (comma-separated) +no-latency-control sacrifice latency to improve bandwidth benchmarks D,daemon run in the background as a daemon syslog send log messages to syslog (default if you use --daemon) pidfile= pidfile name (only if using --daemon) [./sshuttle.pid] @@ -67,7 +69,7 @@ server (internal use only) firewall (internal use only) hostwatch (internal use only) """ -o = options.Options('sshuttle', optspec) +o = options.Options(optspec) (opt, flags, extra) = o.parse(sys.argv[1:]) if opt.daemon: @@ -78,11 +80,12 @@ try: if opt.server: if len(extra) != 0: o.fatal('no arguments expected') + server.latency_control = opt.latency_control sys.exit(server.main()) elif opt.firewall: - if len(extra) != 1: - o.fatal('exactly one argument expected') - sys.exit(firewall.main(int(extra[0]), opt.syslog)) + if len(extra) != 2: + o.fatal('exactly two arguments expected') + sys.exit(firewall.main(int(extra[0]), int(extra[1]), opt.syslog)) elif opt.hostwatch: sys.exit(hostwatch.hw_main(extra)) else: @@ -108,6 +111,8 @@ try: opt.ssh_cmd, remotename, opt.python, + opt.latency_control, + opt.dns, sh, opt.auto_nets, parse_subnets(includes), diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/options.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/options.py index 25322fb..2f3fff6 100644 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/options.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/options.py @@ -76,9 +76,8 @@ class Options: By default, the parser function is getopt.gnu_getopt, and the abort behaviour is to exit the program. """ - def __init__(self, exe, optspec, optfunc=getopt.gnu_getopt, + def __init__(self, optspec, optfunc=getopt.gnu_getopt, onabort=_default_onabort): - self.exe = exe self.optspec = optspec self._onabort = onabort self.optfunc = optfunc @@ -122,8 +121,8 @@ class Options: defval = None flagl = flags.split(',') flagl_nice = [] - for f in flagl: - f,dvi = _remove_negative_kv(f, _intify(defval)) + for _f in flagl: + f,dvi = _remove_negative_kv(_f, _intify(defval)) self._aliases[f] = _remove_negative_k(flagl[0]) self._hasparms[f] = has_parm self._defaults[f] = dvi @@ -135,7 +134,7 @@ class Options: self._aliases[f_nice] = _remove_negative_k(flagl[0]) self._longopts.append(f + (has_parm and '=' or '')) self._longopts.append('no-' + f) - flagl_nice.append('--' + f) + flagl_nice.append('--' + _f) flags_nice = ', '.join(flagl_nice) if has_parm: flags_nice += ' ...' diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/options.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/options.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99ebce52e0e72606ab6a0c10a186538385420eee GIT binary patch literal 8735 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 GJN-Y_-jo0U literal 0 HcmV?d00001 diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/server.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/server.py index 24dd462..45bc2fc 100644 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/server.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/server.py @@ -1,4 +1,4 @@ -import re, struct, socket, select, traceback +import re, struct, socket, select, traceback, time if not globals().get('skip_imports'): import ssnet, helpers, hostwatch import compat.ssubprocess as ssubprocess @@ -106,11 +106,31 @@ class Hostwatch: self.sock = None +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.sock.setsockopt(socket.SOL_IP, socket.IP_TTL, 42) + self.sock.connect((resolvconf_random_nameserver(), 53)) + self.sock.send(request) + + def callback(self): + data = self.sock.recv(4096) + debug2('DNS response: %d bytes\n' % len(data)) + self.mux.send(self.chan, ssnet.CMD_DNS_RESPONSE, data) + self.ok = False + + def main(): if helpers.verbose >= 1: helpers.logprefix = ' s: ' else: helpers.logprefix = 'server: ' + debug1('latency control setting = %r\n' % latency_control) routes = list(list_routes()) debug1('available routes:\n') @@ -164,6 +184,14 @@ def main(): handlers.append(Proxy(MuxWrapper(mux, channel), outwrap)) mux.new_channel = new_channel + dnshandlers = {} + def dns_req(channel, data): + debug2('Incoming DNS request.\n') + h = DnsProxy(mux, channel, data) + handlers.append(h) + dnshandlers[channel] = h + mux.got_dns_req = dns_req + while mux.ok: if hw.pid: assert(hw.pid > 0) @@ -172,5 +200,13 @@ def main(): raise Fatal('hostwatch exited unexpectedly: code 0x%04x\n' % rv) ssnet.runonce(handlers, mux) - mux.check_fullness() + if latency_control: + mux.check_fullness() mux.callback() + + if dnshandlers: + now = time.time() + for channel,h in dnshandlers.items(): + if h.timeout < now or not h.ok: + del dnshandlers[channel] + h.ok = False diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/server.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/server.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e4a14bf9a58c4ba5ab123df62ab79bbb27dd9b1 GIT binary patch literal 9224 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 literal 0 HcmV?d00001 diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.py index ac7f411..9a6270a 100644 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.py @@ -14,14 +14,16 @@ def readfile(name): raise Exception("can't find file %r in any of %r" % (name, path)) -def empackage(z, filename): +def empackage(z, filename, data=None): (path,basename) = os.path.split(filename) - content = z.compress(readfile(filename)) + if not data: + data = readfile(filename) + content = z.compress(data) content += z.flush(zlib.Z_SYNC_FLUSH) - return '%s\n%d\n%s' % (basename,len(content), content) + return '%s\n%d\n%s' % (basename, len(content), content) -def connect(ssh_cmd, rhostport, python, stderr): +def connect(ssh_cmd, rhostport, python, stderr, options): main_exe = sys.argv[0] portl = [] @@ -52,7 +54,9 @@ def connect(ssh_cmd, rhostport, python, stderr): z = zlib.compressobj(1) content = readfile('assembler.py') - content2 = (empackage(z, 'helpers.py') + + optdata = ''.join("%s=%r\n" % (k,v) for (k,v) in options.items()) + content2 = (empackage(z, 'cmdline_options.py', optdata) + + empackage(z, 'helpers.py') + empackage(z, 'compat/ssubprocess.py') + empackage(z, 'ssnet.py') + empackage(z, 'hostwatch.py') + diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6118b39772a10d5b3cbc0417bf2d9c03c6ff65ec GIT binary patch literal 3963 zcmcgvUvnGB4aXiS(xm=NmT1X#eDw^+bW(}2ADn3-XWBST>P)Kf9Fw?F<57=yM{1?x z-T8K@$eH6y<^KB){TS^>=m+Qr=yTsYd2fEek+xbr#}s!7EU*9;z+$0){%5iIkMBSI zbwJZk6Td&lFz1*;d=;G#9lU}S9jMv5LkG?buFyec23P5zI)mp(*XW=&L+f-<7ucbb zI(-pV=q03IVKrfuUP5}4!Z}%-=fz&920QeB@!9VvfR?AX1D@wFnED9A{1uZ0ry}jl zfC||v={c6Ob*>2CiK0T$Pzga%YvRz^XH+=!@w1O;0{&7$jfx5tMCT4osvj&%n* z2{FZy+s<8`xPJ1&O$R_a^E3X`;4DO$Z;xypK&bP?A4LNDeIx4?teY1lqn{5?Wyf)< z6U%Oj&a|;5G0mbxh(O?|M%+mo( zGMTRdb-y%GZn{2HeCy|V*W^Lhn4__^anv2_osl1;rrVpYxL-Zl-}SSs`zTE;j51v> zGU+%EBJ+9|w(n*yL@680j$#*>71pfUYDLwlrZ&{Nx~2lQl8?*YdW2z+zOxWI9DT&( z8^pE3p+%Uo6@(KpEAzvlM3F-1wGcUF=BU6ID{}-t>N*Q}539&3#YKka|P)r%Ohhf(+2U_97znmi*?^FVCmqn z_x1jx!%sf{viB=kOB6?mTn!1xcV$4}7YQ04J@l<#YGX?{gsKwBtPCCj8ixLH_zE&6k80#}uU zOO%%Q-gFt}95*O+VOX7mihDJsKnT7=*j}UO|04DL-x$A8Y!nnFPsNCre~ksKCwzDwgORb+kkm`4p`w0>Wzq z+g~Fq7m-y#!Zj4aj5I0$)gniT!XRJ|yZ;e{TJO-S2jlwOi6%fN!9im#0 zWj9D~vO+E=>+~0*KjH2o7q{t;aPd{LEz;MhfQ?&X7wo^Z;!HMZ0t3Oc%}mN(r|>!z ztMYg#dxP{%#=;v^Y)}Yzlft*Cz>#lpoeo`otIpNjq$Sd~nGGjWtO^JW1oCH`6`RC$ zGXx<3rDzd|I90Fn6I{IM3k9ew_3z_Bor2G#)cn?P9oxwS*(s3+a-n@=qESDNa@6X8 zcU3UB6d4|OF`eD!I{NIoI1Emg<^h)go@KXwrJmR5Dbl^Hp@yq?+dORN&Hb`gu|Rn| zci$EDCo-SDsX}JBknIhlBs$CT4}S(kj^7}&1~?dnu}-4HRGv`-Cm8Ofj-oijd&uYp zbD_@r7Ejx_pXDiDIiXx(Stf}rvvF}e!gK$vZv%e!L@nObQ}L`}&o<59cy|Bqqeoeq zTNf?B2bXB(L}!O3gL!!GG9A+UsnPbu!*+O?8l6Rf%eLc*=|KLjv7t_O`8Dpe!@ECq zZ(of5ZQ#7)_T9^7a16gO??~j_GJOJh(D2BY=#F^cPTSmZcLJAx1MHCxpM4#}EMY?0 zYFu_)8I-o3G>6j`kzbP_PR+D6kqqXnHTNY|e&jZVwVTL}v!@KOUUI}>cFEZ4WcWTh zyQaMK;zsGt0Hx@WIAzcXFzewUk=9I3aySUhj4#Ub4)=~8Ul5NwA(8coA49F2Jsl@j z`iC#_apZA!PPOGa^ID8@7>v#37yY3y@25%RagyR0C^MBilC*y;ZHmW9Dq^&aM#j6z zC@Y_4xWHu=MP-K@m0HIA((p7miOK^S0W%*xCA&x1!f_^S+}tIp$AJ<&jQZo@eX;1m zr4c)xNE^#0pt%YCIPP!@A$A~{4sjvHe>qY>9W{f)eh>gk{=&-4_J6J3vfq z8AU0U<|r2TnS0*byk8=9&!7JGMOh|!EGh4CVf~Ranq3UDlPIv;tnGK0NG+b9k>UCFLuhiAL!=q7|SKDe$Emz*b zjwSULrT&C;;hB#5Z-(V`82H=hT bx3V`%!*Lva$Q4Mc+8$20pqiCNbD{AAbPPFQ literal 0 HcmV?d00001 diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/sshuttle b/Sshuttle VPN.app/Contents/Resources/sshuttle/sshuttle index 66954f8..e76e596 100755 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/sshuttle +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/sshuttle @@ -54,12 +54,14 @@ sshuttle --hostwatch l,listen= transproxy to this ip address and port number [127.0.0.1:0] H,auto-hosts scan for remote hostnames and update local /etc/hosts N,auto-nets automatically determine subnets to route +dns capture local DNS requests and forward to the remote DNS server python= path to python interpreter on the remote server [python] r,remote= ssh hostname (and optional username) of remote sshuttle server x,exclude= exclude this subnet (can be used more than once) v,verbose increase debug message verbosity e,ssh-cmd= the command to use to connect to the remote [ssh] seed-hosts= with -H, use these hostnames for initial scan (comma-separated) +no-latency-control sacrifice latency to improve bandwidth benchmarks D,daemon run in the background as a daemon syslog send log messages to syslog (default if you use --daemon) pidfile= pidfile name (only if using --daemon) [./sshuttle.pid] @@ -67,7 +69,7 @@ server (internal use only) firewall (internal use only) hostwatch (internal use only) """ -o = options.Options('sshuttle', optspec) +o = options.Options(optspec) (opt, flags, extra) = o.parse(sys.argv[1:]) if opt.daemon: @@ -78,11 +80,12 @@ try: if opt.server: if len(extra) != 0: o.fatal('no arguments expected') + server.latency_control = opt.latency_control sys.exit(server.main()) elif opt.firewall: - if len(extra) != 1: - o.fatal('exactly one argument expected') - sys.exit(firewall.main(int(extra[0]), opt.syslog)) + if len(extra) != 2: + o.fatal('exactly two arguments expected') + sys.exit(firewall.main(int(extra[0]), int(extra[1]), opt.syslog)) elif opt.hostwatch: sys.exit(hostwatch.hw_main(extra)) else: @@ -108,6 +111,8 @@ try: opt.ssh_cmd, remotename, opt.python, + opt.latency_control, + opt.dns, sh, opt.auto_nets, parse_subnets(includes), diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py index 62fa378..554d870 100644 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py @@ -21,6 +21,8 @@ CMD_DATA = 0x4206 CMD_ROUTES = 0x4207 CMD_HOST_REQ = 0x4208 CMD_HOST_LIST = 0x4209 +CMD_DNS_REQ = 0x420a +CMD_DNS_RESPONSE = 0x420b cmd_to_name = { CMD_EXIT: 'EXIT', @@ -33,6 +35,8 @@ cmd_to_name = { CMD_ROUTES: 'ROUTES', CMD_HOST_REQ: 'HOST_REQ', CMD_HOST_LIST: 'HOST_LIST', + CMD_DNS_REQ: 'DNS_REQ', + CMD_DNS_RESPONSE: 'DNS_RESPONSE', } @@ -281,7 +285,7 @@ class Mux(Handler): Handler.__init__(self, [rsock, wsock]) self.rsock = rsock self.wsock = wsock - self.new_channel = self.got_routes = None + self.new_channel = self.got_dns_req = self.got_routes = None self.got_host_req = self.got_host_list = None self.channels = {} self.chani = 0 @@ -343,6 +347,10 @@ class Mux(Handler): assert(not self.channels.get(channel)) if self.new_channel: self.new_channel(channel, data) + elif cmd == CMD_DNS_REQ: + assert(not self.channels.get(channel)) + if self.got_dns_req: + self.got_dns_req(channel, data) elif cmd == CMD_ROUTES: if self.got_routes: self.got_routes(data) diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a810bc30d1ce5ee79c85e539673492a0ac9631b6 GIT binary patch literal 21065 zcmd5^TWlQHc|Nnt+pb96NRgr?%PYx@Da(?4i<2m_C7Glpq0*8bQlZR5S+90Rjs(pZ}bhAOHJM{)hj0;Y!8D{|4}T4p;Pj&$$5qJ$K8wr8Moi zB`?X(xTQ>z&bpIf$hf~KpvT`=gDcDZ24E$w!}PPeqj1-snRUKi|kOT#YM zcS!XjJa?pe)hX?7k7d+*{ zM^*PkntRvR<6{eQ}c07;bU{flj!@@RK3;;Ypv*%A4c_!W+jZ;t)i$FwoW#-^vF^<01IFq zM6&aSyb4E3%yF2SVF`(4=k9oJ^CIUtef^xflW|*PoFu23A+_&AuCx9q*v$`7A>Mh=%Z?&4$8yl@~rr89yv0jAV zNsiOs#Rg2(ffcb`w{dtgh@Rn)_5wJs&+B)6Ue+7)c6y^;zKzjdlwM%H6@aas#3d&G zn_!Q{+B~oZ76R5T0Bb<6XK2NQAVq{(cN1s@kM%q69p}~q=K&FrOEO$9setlPA~`^h zHS}zE zRaV1t?F?=rvXTR3c6z(LgI<4TtW6s!64n6~+}24URXP@I4GMDjh|LB1@F)i^K!`VjVQBVhvyLUif@orwElLbGk~G7@JwY&F8LN!*JfQN402Y_(zv)|-9-mHctG9{`vs&Mr_UAa{oVm@*ef6 znP5Wx1QWZQKR-!EtB@4DIJzlZ5d_tNy)c#!m65_AA=xgKSrnJDWHu&xD9}uRbNXBl zQ~kOhpf2WZ&b@`&NaWpHJ+1-83jE-{3P;KM7}V{Mv}{0Cz>yTiQxOSVX@=!iwYFTS zg|`bGcr8p$1O;$gA>6D+t^6@f$7DUaU8!%>TK+*a_YZMC>@(b~i1C_YBhLADntG+Y z8ilg3v{5BIGHB&dLwC2E)mEr3Zfx9CcBNjcg_TyRRrimxd-86=7@|KpC9Wiqk$#E7 z)tmk)RP~=G_#nXx09|Bb4zyIN)~c;iX#weI1i)qTP&PUJ?G_w2l|#Ai!6m*R!y8|G zIOPiv5?P5xCs{MDegHI<>cPEbB^6RXgPq1CrtD8NPZt8X6(Tdq+6>pq)!MPVY!yqP z?(2z3Xy(nLp5TCJ|E1=i@lPTt-^KqhQ@Fb1p-?NAN|->Y1kAXING48460=H`MGh1m zuOmUU81&p5Kv}G}l?xQ!g}6bZ*N#G+OTJLqve{5d994)_)PjmIX*AnAIzK7wMttDW zi6A0|@cR^!9O(R_6t^mMFe9A;V@3{E7!=3DY3L#*jXj%Tqgg7gU>t@@DAuve5%0ZT zEoI42NyQTDB`c51vLrnrS%r0iQ-Wnsb;uF!bjxrU z`&_*Te)kn~#M^atNjyMYSO$CCd+Y30KYIlC3Jwd72<~&sed_;_j!rniQHqVceLVFY zASm@kO@0!^1XWJ!6KnHAt6rFB=A$v7pNc8TZidmuY6}i)GcTWLk`+wFKgZNr0-7Sp zoNNTBd}9@U0($r9n9E#=ALCB_3Fdx)pgDSbF z=F+@eC@u{xjH{HE$cS3aOuj-G!~ayht6cnES2T-O8%LkaKasbN3wumu@wk>*A z=6<2M5k3riBwqVG1|-$*fS{db&N&N`QV1ZI#t8Vh8SMKYU~5EmvUJzL1(cS>^$_CT zhd_wL9a1H6XIJn^fjQ4yMO5>yr3fV6w;_-!mk5l=(Xj^Rf^Ra1dtX9xnW-xTbeQC2 z(IE@4#1N~`U%a|dTJ$^QIgW&}}@*wn(_z+oVVz+Qm=!7f-y zvMQ8*KV2HC+bp99OJJY@aZj;h8Jcsk5rIGSsFn$4KO~auXONw=ABjZ`Jf9?(fPrjN z;AC(YjRH_+H)P>Y7nbX-!pz*IxtV-K|31kE+i^8y3R~X!dEQDbA3;j1&Uz$GsGfW| zO#4|$YS~mI`t?sUkDOr2lGrmx;(4YH19Z`$q7t_`egmn9{#Jz+or{+-LXP z$Gtj$${<<;p&T&kJ0Pu*)9hx#xa1TIe?ovHSs9`j`%U8P4-fcl;y~j5Ch}vC&1a}2EnMLdJ*nSTQ7oX z=i``{{0y+E=OXI74Vn~cKp-2qqWw00l-ocHWB&TRoaw)c0@)ij?_kSw$nU^^ml==p zpb7mM61K_|-*xgHc+Nv98Hm>uYhDE)mmyBt>82`j8wa@%u(cXcImU_#U?T}Sa5aTZ z7gm5a0&Y_i#77kUj{(}+hYaaoBar0NL2IK+^4`4BJ%$wp5ldeEYm6hcAH9k!jWFQN z;b(D?X@se-BN1cjT!IlGV7&e;?x1sJ&VfhfK`kR#Yqfa~wfR_jcZMk1OoEAZEIQMo z@j-^i?H1TZQ!4oND{uoWbFjYO%Cm-cJXbql+t7|-0K4P~UudUvYI)hHH+*q66{aog zKg==;DFWrU5kiY~ZoSpbs4SmfsW-L|;rTlx6_|G3ez=qeJuF|mefTR*?naUSD1d@O zrP5kG*jPo7*&O|ku=Jw@FA|(1pa*Mf0a=JS-YyUjBgT!`X>|T40ou%`)Pvq=@#$30 zu=<{|B<@)j0mph0nm&@jM=X!F3xtwTov0MvQ?-$!VhPELBjEYM@*9Gq%+e8GC z%v>Q?^GbC9tuAyRIIILFh5^FTaM|v`#1L>U_QlSKM5gjhRZ=w3rpss+`Ol6D<+)6x{Ub7jhh@>Z-rc4FxOK$6R>mLD9KN2IA!zo7ef=-;M)e8ylAXIs? zS_^{{C(_k^)j<_VYo9n#S*=G&;Us4zMYmdC*80#8AR@&^yYi;FnPpT}ee)JUR_BKV zH`xbiXPZ-Rc8_cli+|>m_X0S$ijQm8a2L3h0pp^Lmm1JVh$#+mNbdo)+oh^ITI_W^ z_#beIzo5nXay`-kbItnZ7R6luh|ZUBMMnXEWjoUGE)ueGOlVt-i5~8iWKl2GRHUl- zlu8_YYPOa}R~`xY4(f?%z^q#~Sw%5#luRLgY+$^&ppc5^Bx?#h5QFKd_UWI1oPW#$FjqwJD#J;=b*( z#cg{O9LMBM;j=pgE1~*o*Hgy?yUoY#2Hg$N>uVqe+HQkfxam}vKR~;Qv|T=}C(?BE z9WQmzaQpL-b?@_$(SANgcc14YH-7#ZFMU4RZGV2dZ#=()IT#@EMQTMwNN@8Yw1Y%d zZwrIxnUYKKG*k4e%pTGP_~@`hp>&a9s0#iASM)dl4sWR1 z2~9nMW1Y-I{z-W!xk-5^E-^+*naFbKyvF5H_@H=_h4#I!{$yH?fHZ;-W*|@vrXlW2 zp$uDapbbdYZif4Nnq`t^;*n0aN6i6OpG%*Hn<=}|I0ow;<8 z>jd{&Puu%fd^x;bs;rc;Yrm?E4uk*$ur0VEk-t%Xr!9v@Y;8bvr|r}C*Q(@e}EE6_zLBo+@+3b_(=SoHF2z!-G3oe@Z&^fYyJ%1-oGH$NG>?oh4M*XQV-gMWJ9 z1%Uh?%5X^s{iXHc8?dh2+cUPnhId;(>(B^lp_9gdDMQAZc_Ub`D9cA_}R~5 zV<(hvHp{i;(BDLfe;Ytzh_gr?0iJ$lSJy&Nr+b(cbJfDlR>F+OiDky0TxN)CZs3^v zR>Eu+xhZB(;s!ziWNsc70v(V$s;CY`1vrJ&;=~Th*LUmOVtk}d5@XZ;6uPjr2tIK22~1;Gt~K zkV7gfVda)KXsAI>6IE0x4lI+0aHi1%W=gmJ2@=rlIDZAs!19<}qBQvEjX12IQCJ^b z0Y?`|1IF;5s$Zu$t@>SP`m7s6?19L&Z5^H7(jGr@!GHsFaoGqCiZ~G_Bz+#6?Ql1H zY$cDo9^9s-Pzf**l<8g!xF|`M1wCA;T)cP@^-vZ)30Qck5bN+4aYm@J7Q8qS6>$2N zcaw=~#m%w2g6HD(sMYlEqOH%svaP^d&5cS+dbfc{kWaC&5b$MIk)2!%5ct!HQU7gb zQd(BRO`lBB5muoNNvsO1U`XmWD7VTQccXi%#V4E2kSh?109OpcRhk5E^dV~5!5tj3 zxZ^n}Zg2+;uoQ?bS25Ez{Xkmr%gt;|IncB>Ij=;b!W}^tPW+)7>~!rXIKH6_M&Sz< zZBY*%7z7RA9mKjm?`q$($~%%19&wdHSNpP+45{Qrlj)sWPs<(*YC~#Qx*@e&XSg{Y z_z=e9T?eA@#<0#9Bv2|LrFtnw2LaC%-d7y+GCMjX(+n$N_02FS(67O%iKtPpMPXhJKD?>| zT^@Vep>Vs}T7fy{9&PN5osB+#2PM^#HzDq+n7C;oqosCYQU%aYN z!wCc&;!(&hnN0!eg#Fy3VDgiU1Nu}E{%;YyP4L?UQZKR(6v4J1hc`dkex6iETE_Q8 zw9=BgAqwKxL*%VUSNTnLMs396_)QN#VkRwQ8*M(Mm|N$N{>@6*;0CPR4Aw|6=JV7{ zi}p6nC35^Xk>jrbw3V9wS>}C?;PV6(mf|w-pd;g{GfveUwq~i%Ug6WB$Bw}@V#pw` zrJpSf58!S;@{->?4*SrBy5~2u7xBtI?}Qx#qQgpKFD>{ZB&a#?A7UZPB^LtQS)VdV zf<@$}+M0X_s6QyZxPcvNKufCv4G8ZGB&Lc(LJRh!?4C1l^@E*VqptSi)XSOmFF}vO zVTt$SFpi_0m9-xiMhSFs-_;q1Y5Y>^Z(EVQ<pgKLBHVU>PED(MRwq*==ALOCcMKS zY(X1xsmGiexG$jEcG7Mgp)H4vh8q)?L37Fq8u=Cx9_)cd<2w@L@!^!1q;}GTIu@$% z8mI=;)6QW51I9sbux?twAlx}Dn)M*Uhk9^uVD3QEP7=M1nNS|<$$`DNs_eS7~`S@|&YaCL`TK4VO9INOBEdPhL6eF*Ul zahD54I!=@l3M;pm7@&l=`BS-u5GzR|sg%_YAgJ~RVr^xhTT)2RMYT{+fO+~vZ+j;Q zHjjHgT5^I$av1u}VbJ1WTb|x~86yd8KhYjqFX2(iLog%D5sVPWld@T_keS5dZv+9qqX_+RhhZ$E6%I9A7S|N6=ve@+aEvx^4jH!+Pw?0R zOA>mpBo#q&V7yaIEorhQ1<1JhFG|S#>%<62YAZa+4QEensM#y9Aui#ZJ-3|jDiSae zkdM3f?vGkEV2j7C-*ZTYrC3cLN>B*gb<~4nih-mkBrlnvu`NKds^O6+^uIwMjQ#;q zzX%wuU>x5jMrr(h8zQepTs6_av^t+;FZ}juZmV+h0N>@4t*Ix&aU(whSGK zBN|9RTiNSp@GI=~GlT^GfAMF8or&&B7JwJocM4bZ8UVx-Obf09Lvry6h-C&8qlt6p zMk!g-^eZekRxlZN2DnFVX@GtfEkSY)lyL+xr2_j7Ih0=f# zxwg>-N+%o6B!`_+6*fMK+lLX^()p-A=fubxJen#!#|d$Sn$QRmB(dg1XwGy(XSz#x zsi}Dy+>ZC0%uRg(nMNaa)$LuZ7xBy=a8e|SYfmZVf9|i@d>VKY*PIL-pkNmE3e8>6 z5o#)NiUbPesBY81<}lKevQuu>w3|EqKpI2Z*$_^45(I?;Yc~%0nSN zHy;0rSQ%_~0LN=&``_hgUBy4g6zBqyyK{Yypq^d*aK? z*$`ex;EM@^1z3Ac1n~Fif9|Bv& z?_DBh(MDJFC`J%}rvYy-uB~DD#fT?gyS}l7{jX!T;`|P(*(swxW{OJ5|1JSp#r8|S z$`ppz=^TBId7oz5x<+yM3iA?e2rxKJbRCVlkV{F>F(I6Y#Je(9MzAV zo+5YJS#0*)o`_Np+`0cIp82Po%?J{3?gwz}It%xne_7o1RmkgETsd4*xa7-&qs&PH zcO~!J()kl_@$f$!3j{3WWfN+@PpZVu)+NCTYmN;JoHT*lCB0=b^U^Fu4QtB2e}Le* z-iO7PKoDDe5<_W!)FAyvNMIU?PPAUs)5-+zzVc9Zz0pz(V1D*;>C&8%m*z?f3zu~m zT|T$s71ahJEjZSPujwdUfn5SNZyPJLS>1yoXB0PnD z6`?|>pFlKQhpIVV^sT5V_~C!=JKf_|Ph$vnjolgGiJ&_ini;u;+b(k203LT{ zwY+vCD8KaI7z($1?2YHevfh~GO198^aRT4NAlM}6-2O(OA79QmfL4Wqk9Weh?$f|_ z0-_0a4M7T9h&~ta#U*@DrB&F#`E}TWLb?HnQ!q&O zxh<_R zVF~PgfHJUW!4i>@MNIp}i0qg^mYtBDiXR+5Mc6`NQijYCi5X{7Mv4M9a0`wc7!$hg z>YnfSM$>hH9LIsIs&CH=>93IF5Oim2)3_sRL3;t}PNjQap~1CxJNo2I4D_iv?sO`h y1|dX;c1yQywI2?GY=7<=GfX50t#||R^0P>bAGfaRYx7C|A{N72bs8sXHu(>z>D>bW literal 0 HcmV?d00001 diff --git a/Sshuttle VPN.app/Contents/Resources/stupid.py b/Sshuttle VPN.app/Contents/Resources/stupid.py deleted file mode 100644 index fdb1e0b..0000000 --- a/Sshuttle VPN.app/Contents/Resources/stupid.py +++ /dev/null @@ -1,14 +0,0 @@ -import os - -pid = os.fork() -if pid == 0: - # child - try: - os.setsid() - #os.execvp('sudo', ['sudo', 'SSH_ASKPASS=%s' % os.path.abspath('askpass.py'), 'ssh', 'afterlife', 'ls']) - os.execvp('ssh', ['ssh', 'afterlife', 'ls']) - finally: - os._exit(44) -else: - # parent - os.wait()