summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAvery Pennarun <apenwarr@gmail.com>2010-05-01 21:50:43 -0400
committerAvery Pennarun <apenwarr@gmail.com>2010-05-01 21:50:43 -0400
commit9f514d7a1507018b337b56f376fd2e9022d75641 (patch)
treedd8892a410205acadb17e0413a218d4cbbc60a53
parentad459e29187f751294257ae1a8c34eec6eaf8757 (diff)
Smarter listenport selection.
Now if we aren't given an explicit port, we always initiate the port search at 12300 and count upward looking for an available port. Normally the kernel will assign us a random port, but that's not ideal in our case because we'd like to use the same port numbers whenever possible; that avoids piling up crap inside iptables in the (hopefully unlikely) event that we die without cleaning up correctly.
-rw-r--r--client.py25
-rw-r--r--iptables.py15
-rwxr-xr-xmain.py4
3 files changed, 38 insertions, 6 deletions
diff --git a/client.py b/client.py
index 54fd64b..1e49e83 100644
--- a/client.py
+++ b/client.py
@@ -1,4 +1,4 @@
-import struct, socket, select, subprocess
+import struct, socket, select, subprocess, errno
from ssnet import SockWrapper, Handler, Proxy
from helpers import *
@@ -58,9 +58,28 @@ def main(listenip, remotename, subnets):
log('Starting sshuttle proxy.\n')
listener = socket.socket()
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- listener.bind(listenip)
+ if listenip[1]:
+ ports = [listenip[1]]
+ else:
+ ports = xrange(12300,65536)
+ last_e = None
+ bound = False
+ log('Binding:')
+ for port in ports:
+ log(' %d' % port)
+ try:
+ listener.bind((listenip[0], port))
+ bound = True
+ break
+ except socket.error, e:
+ last_e = e
+ log('\n')
+ if not bound:
+ assert(last_e)
+ raise last_e
listener.listen(10)
- log('Listening on %r.\n' % (listener.getsockname(),))
+ listenip = listener.getsockname()
+ log('Listening on %r.\n' % (listenip,))
iptables_setup(listenip[1], subnets)
diff --git a/iptables.py b/iptables.py
index a65ae9d..5bb6133 100644
--- a/iptables.py
+++ b/iptables.py
@@ -21,7 +21,20 @@ def ipt(*args):
raise Exception('%r returned %d' % (argv, rv))
-# FIXME: this prints scary-looking errors
+# This is some iptables voodoo for setting up the Linux kernel's transparent
+# proxying stuff. If subnets is empty, we just delete our sshuttle chain;
+# otherwise we delete it, then make it from scratch.
+#
+# 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").
+#
+# sshuttle is supposed to clean up after itself by deleting extra chains on
+# 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 iptables
+# chains are mostly harmless.
def main(port, subnets):
assert(port > 0)
assert(port <= 65535)
diff --git a/main.py b/main.py
index 0beaf0b..350239e 100755
--- a/main.py
+++ b/main.py
@@ -66,10 +66,10 @@ elif opt.iptables:
parse_subnets(extra[1:])))
else:
if len(extra) < 1:
- o.fatal('at least one argument expected')
+ o.fatal('at least one subnet expected')
remotename = extra[0]
if remotename == '' or remotename == '-':
remotename = None
- sys.exit(client.main(parse_ipport(opt.listen or '0.0.0.0:1234'),
+ sys.exit(client.main(parse_ipport(opt.listen or '0.0.0.0:0'),
remotename,
parse_subnets(extra)))