diff --git a/Sshuttle VPN.app/Contents/Resources/English.lproj/MainMenu.nib b/Sshuttle VPN.app/Contents/Resources/English.lproj/MainMenu.nib index e0e3d32..42d0ed4 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 index 180ae1a..aa27822 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/askpass.pyc and b/Sshuttle VPN.app/Contents/Resources/askpass.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/main.py b/Sshuttle VPN.app/Contents/Resources/main.py index cadf0d9..3e6c2a1 100644 --- a/Sshuttle VPN.app/Contents/Resources/main.py +++ b/Sshuttle VPN.app/Contents/Resources/main.py @@ -136,7 +136,7 @@ class SshuttleController(NSObject): prefsWindow = objc.IBOutlet() serversController = objc.IBOutlet() logField = objc.IBOutlet() - noLatencyControlField = objc.IBOutlet() + latencyControlField = objc.IBOutlet() servers = [] conns = {} @@ -162,14 +162,14 @@ class SshuttleController(NSObject): manual_nets = ['0/0'] else: manual_nets = [] + noLatencyControl = (server.latencyControl() != models.LAT_INTERACTIVE) 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(), - no_latency_control - = self.noLatencyControlField.state()), + no_latency_control = noLatencyControl), logfunc=logfunc, promptfunc=promptfunc, serverobj=server) self.conns[host] = conn @@ -286,12 +286,14 @@ class SshuttleController(NSObject): autoNets = s.get('autoNets', models.NET_AUTO) autoHosts = s.get('autoHosts', True) useDns = s.get('useDns', autoNets == models.NET_ALL) + latencyControl = s.get('latencyControl', models.LAT_INTERACTIVE) srv = models.SshuttleServer.alloc().init() srv.setHost_(host) srv.setAutoNets_(autoNets) srv.setAutoHosts_(autoHosts) srv.setNets_(nl) srv.setUseDns_(useDns) + srv.setLatencyControl_(latencyControl) sl.append(srv) self.serversController.addObjects_(sl) self.serversController.setSelectionIndex_(0) @@ -310,7 +312,8 @@ class SshuttleController(NSObject): nets=nets, autoNets=s.autoNets(), autoHosts=s.autoHosts(), - useDns=s.useDns()) + useDns=s.useDns(), + latencyControl=s.latencyControl()) l.append(d) my.Defaults().setObject_forKey_(l, 'servers') self.fill_menu() @@ -322,6 +325,11 @@ class SshuttleController(NSObject): tf('Determine automatically') tf('Custom...') + self.latencyControlField.removeAllItems() + tf = self.latencyControlField.addItemWithTitle_ + tf('Fast transfer') + tf('Low latency') + # Hmm, even when I mark this as !enabled in the .nib, it still comes # through as enabled. So let's just disable it here (since we don't # support this feature yet). diff --git a/Sshuttle VPN.app/Contents/Resources/models.py b/Sshuttle VPN.app/Contents/Resources/models.py index ad8e538..c4cbe8b 100644 --- a/Sshuttle VPN.app/Contents/Resources/models.py +++ b/Sshuttle VPN.app/Contents/Resources/models.py @@ -58,6 +58,9 @@ NET_ALL = 0 NET_AUTO = 1 NET_MANUAL = 2 +LAT_BANDWIDTH = 0 +LAT_INTERACTIVE = 1 + class SshuttleServer(NSObject): def init(self): self = super(SshuttleServer, self).init() @@ -155,3 +158,9 @@ class SshuttleServer(NSObject): def setUseDns_(self, v): self._k_useDns = v config_changed() + + def latencyControl(self): + return getattr(self, '_k_latencyControl', LAT_INTERACTIVE) + def setLatencyControl_(self, v): + self._k_latencyControl = v + config_changed() diff --git a/Sshuttle VPN.app/Contents/Resources/models.pyc b/Sshuttle VPN.app/Contents/Resources/models.pyc index bc9c70a..11b7144 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/models.pyc 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 index 112d1e6..e3c56a9 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/my.pyc 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 1ade5d9..fa93c26 100644 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/client.py @@ -273,6 +273,10 @@ def _main(listener, fw, ssh_cmd, remotename, python, latency_control, 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)) diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/client.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/client.pyc index 64b7efb..23f3bbe 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/sshuttle/client.pyc and b/Sshuttle VPN.app/Contents/Resources/sshuttle/client.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/__init__.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/__init__.pyc index 736e5ec..463e907 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/__init__.pyc and b/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/__init__.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/ssubprocess.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/ssubprocess.pyc index dcb10bd..6509e71 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/ssubprocess.pyc and b/Sshuttle VPN.app/Contents/Resources/sshuttle/compat/ssubprocess.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py index c7557ed..7767d43 100644 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.py @@ -131,11 +131,11 @@ def _fill_oldctls(prefix): def _sysctl_set(name, val): argv = ['sysctl', '-w', '%s=%s' % (name, val)] debug1('>> %s\n' % ' '.join(argv)) - rv = ssubprocess.call(argv, stdout = open('/dev/null', 'w')) + return ssubprocess.call(argv, stdout = open('/dev/null', 'w')) _changedctls = [] -def sysctl_set(name, val): +def sysctl_set(name, val, permanent=False): PREFIX = 'net.inet.ip' assert(name.startswith(PREFIX + '.')) val = str(val) @@ -146,8 +146,16 @@ def sysctl_set(name, val): return oldval = _oldctls[name] if val != oldval: - _changedctls.append(name) - return _sysctl_set(name, val) + 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) def _udp_unpack(p): @@ -206,7 +214,7 @@ def do_ipfw(port, dnsport, subnets): if subnets or dnsport: sysctl_set('net.inet.ip.fw.enable', 1) - sysctl_set('net.inet.ip.scopedroute', 0) + sysctl_set('net.inet.ip.scopedroute', 0, permanent=True) ipfw('add', sport, 'check-state', 'ip', 'from', 'any', 'to', 'any') diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.pyc index 7539b6f..3682bb1 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.pyc and b/Sshuttle VPN.app/Contents/Resources/sshuttle/firewall.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/helpers.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/helpers.pyc index ced3de3..d84b651 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/sshuttle/helpers.pyc and b/Sshuttle VPN.app/Contents/Resources/sshuttle/helpers.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/hostwatch.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/hostwatch.pyc index 5dbb1cc..de2a0ef 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/sshuttle/hostwatch.pyc and b/Sshuttle VPN.app/Contents/Resources/sshuttle/hostwatch.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/main.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/main.py index e76e596..3fe1e53 100755 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/main.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/main.py @@ -62,6 +62,7 @@ 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 +wrap= restart counting channel numbers after this number (for testing) 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] @@ -74,6 +75,9 @@ o = options.Options(optspec) if opt.daemon: opt.syslog = 1 +if opt.wrap: + import ssnet + ssnet.MAX_CHANNEL = int(opt.wrap) helpers.verbose = opt.verbose try: diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/options.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/options.pyc index 99ebce5..4a2df17 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/sshuttle/options.pyc and b/Sshuttle VPN.app/Contents/Resources/sshuttle/options.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/server.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/server.pyc index 6e4a14b..6c46ecc 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/sshuttle/server.pyc and b/Sshuttle VPN.app/Contents/Resources/sshuttle/server.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.pyc index 6118b39..fbcb512 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.pyc and b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssh.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/sshuttle b/Sshuttle VPN.app/Contents/Resources/sshuttle/sshuttle index e76e596..3fe1e53 100755 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/sshuttle +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/sshuttle @@ -62,6 +62,7 @@ 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 +wrap= restart counting channel numbers after this number (for testing) 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] @@ -74,6 +75,9 @@ o = options.Options(optspec) if opt.daemon: opt.syslog = 1 +if opt.wrap: + import ssnet + ssnet.MAX_CHANNEL = int(opt.wrap) helpers.verbose = opt.verbose try: diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py index 554d870..2145431 100644 --- a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.py @@ -1,6 +1,8 @@ import struct, socket, errno, select if not globals().get('skip_imports'): from helpers import * + +MAX_CHANNEL = 65535 # these don't exist in the socket module in python 2.3! SHUT_RD = 0 @@ -300,7 +302,7 @@ class Mux(Handler): # channel 0 is special, so we never allocate it for timeout in xrange(1024): self.chani += 1 - if self.chani > 65535: + if self.chani > MAX_CHANNEL: self.chani = 1 if not self.channels.get(self.chani): return self.chani diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.pyc index a810bc3..e162b8e 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.pyc and b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssnet.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssyslog.pyc b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssyslog.pyc index b5ac023..acbfc5e 100644 Binary files a/Sshuttle VPN.app/Contents/Resources/sshuttle/ssyslog.pyc and b/Sshuttle VPN.app/Contents/Resources/sshuttle/ssyslog.pyc differ diff --git a/Sshuttle VPN.app/Contents/Resources/sshuttle/stresstest.py b/Sshuttle VPN.app/Contents/Resources/sshuttle/stresstest.py new file mode 100755 index 0000000..f42df09 --- /dev/null +++ b/Sshuttle VPN.app/Contents/Resources/sshuttle/stresstest.py @@ -0,0 +1,86 @@ +#!/usr/bin/python +import sys, os, socket, select, struct, time + +listener = socket.socket() +listener.bind(('127.0.0.1', 0)) +listener.listen(500) + +servers = [] +clients = [] +remain = {} + +NUMCLIENTS = 50 +count = 0 + + +while 1: + if len(clients) < NUMCLIENTS: + c = socket.socket() + c.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + c.bind(('0.0.0.0', 0)) + c.connect(listener.getsockname()) + count += 1 + if count >= 16384: + count = 1 + print 'cli CREATING %d' % count + b = struct.pack('I', count) + 'x'*count + remain[c] = count + print 'cli >> %r' % len(b) + c.send(b) + c.shutdown(socket.SHUT_WR) + clients.append(c) + r = [listener] + time.sleep(0.1) + else: + r = [listener]+servers+clients + print 'select(%d)' % len(r) + r,w,x = select.select(r, [], [], 5) + assert(r) + for i in r: + if i == listener: + s,addr = listener.accept() + servers.append(s) + elif i in servers: + b = i.recv(4096) + print 'srv << %r' % len(b) + if not i in remain: + assert(len(b) >= 4) + want = struct.unpack('I', b[:4])[0] + b = b[4:] + #i.send('y'*want) + else: + want = remain[i] + if want < len(b): + print 'weird wanted %d bytes, got %d: %r' % (want, len(b), b) + assert(want >= len(b)) + want -= len(b) + remain[i] = want + if not b: # EOF + if want: + print 'weird: eof but wanted %d more' % want + assert(want == 0) + i.close() + servers.remove(i) + del remain[i] + else: + print 'srv >> %r' % len(b) + i.send('y'*len(b)) + if not want: + i.shutdown(socket.SHUT_WR) + elif i in clients: + b = i.recv(4096) + print 'cli << %r' % len(b) + want = remain[i] + if want < len(b): + print 'weird wanted %d bytes, got %d: %r' % (want, len(b), b) + assert(want >= len(b)) + want -= len(b) + remain[i] = want + if not b: # EOF + if want: + print 'weird: eof but wanted %d more' % want + assert(want == 0) + i.close() + clients.remove(i) + del remain[i] +listener.accept()