mirror of
https://github.com/sshuttle/sshuttle.git
synced 2025-07-05 17:20:35 +02:00
Compare commits
3 Commits
sshuttle-0
...
sshuttle-0
Author | SHA1 | Date | |
---|---|---|---|
8fe3592be3 | |||
ba19d9c72d | |||
096bbcc576 |
15
README.md
15
README.md
@ -36,12 +36,18 @@ Prerequisites
|
|||||||
- sudo, su, or logged in as root on your client machine.
|
- sudo, su, or logged in as root on your client machine.
|
||||||
(The server doesn't need admin access.)
|
(The server doesn't need admin access.)
|
||||||
|
|
||||||
- Linux+iptables on your client machine, including at
|
- If you use Linux on your client machine:
|
||||||
|
iptables installed on the client, including at
|
||||||
least the iptables DNAT, REDIRECT, and ttl modules.
|
least the iptables DNAT, REDIRECT, and ttl modules.
|
||||||
This is available by default on most Linux distributions.
|
These are installed by default on most Linux distributions.
|
||||||
(The server doesn't need iptables and doesn't need to be
|
(The server doesn't need iptables and doesn't need to be
|
||||||
Linux.)
|
Linux.)
|
||||||
|
|
||||||
|
- If you use MacOS or BSD on your client machine:
|
||||||
|
Your kernel needs to be compiled with IPFIREWALL_FORWARD
|
||||||
|
(MacOS has this by default) and you need to have ipfw
|
||||||
|
available. (The server doesn't need to be MacOS or BSD.)
|
||||||
|
|
||||||
|
|
||||||
This is how you use it:
|
This is how you use it:
|
||||||
-----------------------
|
-----------------------
|
||||||
@ -58,6 +64,11 @@ That's it! Now your local machine can access the remote network as if you
|
|||||||
were right there! And if your "client" machine is a router, everyone on
|
were right there! And if your "client" machine is a router, everyone on
|
||||||
your local network can make connections to your remote network.
|
your local network can make connections to your remote network.
|
||||||
|
|
||||||
|
You don't need to install sshuttle on the remote server;
|
||||||
|
the remote server just needs to have python available.
|
||||||
|
sshuttle will automatically upload and run its source code
|
||||||
|
to the remote python interpreter.
|
||||||
|
|
||||||
This creates a transparent proxy server on your local machine for all IP
|
This creates a transparent proxy server on your local machine for all IP
|
||||||
addresses that match 0.0.0.0/0. (You can use more specific IP addresses if
|
addresses that match 0.0.0.0/0. (You can use more specific IP addresses if
|
||||||
you want; use any number of IP addresses or subnets to change which
|
you want; use any number of IP addresses or subnets to change which
|
||||||
|
26
assembler.py
Normal file
26
assembler.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import sys, zlib
|
||||||
|
|
||||||
|
z = zlib.decompressobj()
|
||||||
|
mainmod = sys.modules[__name__]
|
||||||
|
while 1:
|
||||||
|
name = sys.stdin.readline().strip()
|
||||||
|
if name:
|
||||||
|
nbytes = int(sys.stdin.readline())
|
||||||
|
if verbosity >= 2:
|
||||||
|
sys.stderr.write('remote assembling %r (%d bytes)\n'
|
||||||
|
% (name, nbytes))
|
||||||
|
content = z.decompress(sys.stdin.read(nbytes))
|
||||||
|
exec compile(content, name, "exec")
|
||||||
|
|
||||||
|
# FIXME: this crushes everything into a single module namespace,
|
||||||
|
# then makes each of the module names point at this one. Gross.
|
||||||
|
assert(name.endswith('.py'))
|
||||||
|
modname = name[:-3]
|
||||||
|
mainmod.__dict__[modname] = mainmod
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
verbose = verbosity
|
||||||
|
sys.stderr.flush()
|
||||||
|
sys.stdout.flush()
|
||||||
|
main()
|
40
client.py
40
client.py
@ -4,23 +4,29 @@ from ssnet import SockWrapper, Handler, Proxy, Mux, MuxWrapper
|
|||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
def original_dst(sock):
|
def original_dst(sock):
|
||||||
SO_ORIGINAL_DST = 80
|
try:
|
||||||
SOCKADDR_MIN = 16
|
SO_ORIGINAL_DST = 80
|
||||||
sockaddr_in = sock.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, SOCKADDR_MIN)
|
SOCKADDR_MIN = 16
|
||||||
(proto, port, a,b,c,d) = struct.unpack('!HHBBBB', sockaddr_in[:8])
|
sockaddr_in = sock.getsockopt(socket.SOL_IP,
|
||||||
assert(socket.htons(proto) == socket.AF_INET)
|
SO_ORIGINAL_DST, SOCKADDR_MIN)
|
||||||
ip = '%d.%d.%d.%d' % (a,b,c,d)
|
(proto, port, a,b,c,d) = struct.unpack('!HHBBBB', sockaddr_in[:8])
|
||||||
return (ip,port)
|
assert(socket.htons(proto) == socket.AF_INET)
|
||||||
|
ip = '%d.%d.%d.%d' % (a,b,c,d)
|
||||||
|
return (ip,port)
|
||||||
|
except socket.error, e:
|
||||||
|
if e.args[0] == errno.ENOPROTOOPT:
|
||||||
|
return sock.getsockname()
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
class IPTables:
|
class FirewallClient:
|
||||||
def __init__(self, port, subnets):
|
def __init__(self, port, subnets):
|
||||||
self.port = port
|
self.port = port
|
||||||
self.subnets = subnets
|
self.subnets = subnets
|
||||||
subnets_str = ['%s/%d' % (ip,width) for ip,width in subnets]
|
subnets_str = ['%s/%d' % (ip,width) for ip,width in subnets]
|
||||||
argvbase = ([sys.argv[0]] +
|
argvbase = ([sys.argv[0]] +
|
||||||
['-v'] * (helpers.verbose or 0) +
|
['-v'] * (helpers.verbose or 0) +
|
||||||
['--iptables', str(port)] + subnets_str)
|
['--firewall', str(port)] + subnets_str)
|
||||||
argv_tries = [
|
argv_tries = [
|
||||||
['sudo'] + argvbase,
|
['sudo'] + argvbase,
|
||||||
['su', '-c', ' '.join(argvbase)],
|
['su', '-c', ' '.join(argvbase)],
|
||||||
@ -47,7 +53,7 @@ class IPTables:
|
|||||||
s1.close()
|
s1.close()
|
||||||
self.pfile = s2.makefile('wb+')
|
self.pfile = s2.makefile('wb+')
|
||||||
if e:
|
if e:
|
||||||
log('Spawning iptables: %r\n' % self.argv)
|
log('Spawning firewall manager: %r\n' % self.argv)
|
||||||
raise Fatal(e)
|
raise Fatal(e)
|
||||||
line = self.pfile.readline()
|
line = self.pfile.readline()
|
||||||
self.check()
|
self.check()
|
||||||
@ -74,7 +80,7 @@ class IPTables:
|
|||||||
raise Fatal('cleanup: %r returned %d' % (self.argv, rv))
|
raise Fatal('cleanup: %r returned %d' % (self.argv, rv))
|
||||||
|
|
||||||
|
|
||||||
def _main(listener, ipt, use_server, remotename):
|
def _main(listener, fw, use_server, remotename):
|
||||||
handlers = []
|
handlers = []
|
||||||
if use_server:
|
if use_server:
|
||||||
if helpers.verbose >= 1:
|
if helpers.verbose >= 1:
|
||||||
@ -98,14 +104,14 @@ def _main(listener, ipt, use_server, remotename):
|
|||||||
|
|
||||||
# we definitely want to do this *after* starting ssh, or we might end
|
# we definitely want to do this *after* starting ssh, or we might end
|
||||||
# up intercepting the ssh connection!
|
# up intercepting the ssh connection!
|
||||||
ipt.start()
|
fw.start()
|
||||||
|
|
||||||
def onaccept():
|
def onaccept():
|
||||||
sock,srcip = listener.accept()
|
sock,srcip = listener.accept()
|
||||||
dstip = original_dst(sock)
|
dstip = original_dst(sock)
|
||||||
debug1('Accept: %r:%r -> %r:%r.\n' % (srcip[0],srcip[1],
|
debug1('Accept: %r:%r -> %r:%r.\n' % (srcip[0],srcip[1],
|
||||||
dstip[0],dstip[1]))
|
dstip[0],dstip[1]))
|
||||||
if dstip == sock.getsockname():
|
if dstip == listener.getsockname():
|
||||||
debug1("-- ignored: that's my address!\n")
|
debug1("-- ignored: that's my address!\n")
|
||||||
sock.close()
|
sock.close()
|
||||||
return
|
return
|
||||||
@ -150,7 +156,7 @@ def main(listenip, use_server, remotename, subnets):
|
|||||||
if listenip[1]:
|
if listenip[1]:
|
||||||
ports = [listenip[1]]
|
ports = [listenip[1]]
|
||||||
else:
|
else:
|
||||||
ports = xrange(12300,65536)
|
ports = xrange(12300,9000,-1)
|
||||||
last_e = None
|
last_e = None
|
||||||
bound = False
|
bound = False
|
||||||
debug2('Binding:')
|
debug2('Binding:')
|
||||||
@ -170,9 +176,9 @@ def main(listenip, use_server, remotename, subnets):
|
|||||||
listenip = listener.getsockname()
|
listenip = listener.getsockname()
|
||||||
debug1('Listening on %r.\n' % (listenip,))
|
debug1('Listening on %r.\n' % (listenip,))
|
||||||
|
|
||||||
ipt = IPTables(listenip[1], subnets)
|
fw = FirewallClient(listenip[1], subnets)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return _main(listener, ipt, use_server, remotename)
|
return _main(listener, fw, use_server, remotename)
|
||||||
finally:
|
finally:
|
||||||
ipt.done()
|
fw.done()
|
||||||
|
@ -3,7 +3,7 @@ import helpers
|
|||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
|
|
||||||
def chain_exists(name):
|
def ipt_chain_exists(name):
|
||||||
argv = ['iptables', '-t', 'nat', '-nL']
|
argv = ['iptables', '-t', 'nat', '-nL']
|
||||||
p = subprocess.Popen(argv, stdout = subprocess.PIPE)
|
p = subprocess.Popen(argv, stdout = subprocess.PIPE)
|
||||||
for line in p.stdout:
|
for line in p.stdout:
|
||||||
@ -22,11 +22,16 @@ def ipt(*args):
|
|||||||
raise Fatal('%r returned %d' % (argv, rv))
|
raise Fatal('%r returned %d' % (argv, rv))
|
||||||
|
|
||||||
|
|
||||||
def do_it(port, subnets):
|
# 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):
|
||||||
chain = 'sshuttle-%s' % port
|
chain = 'sshuttle-%s' % port
|
||||||
|
|
||||||
# basic cleanup/setup of chains
|
# basic cleanup/setup of chains
|
||||||
if chain_exists(chain):
|
if ipt_chain_exists(chain):
|
||||||
ipt('-D', 'OUTPUT', '-j', chain)
|
ipt('-D', 'OUTPUT', '-j', chain)
|
||||||
ipt('-D', 'PREROUTING', '-j', chain)
|
ipt('-D', 'PREROUTING', '-j', chain)
|
||||||
ipt('-F', chain)
|
ipt('-F', chain)
|
||||||
@ -48,33 +53,113 @@ def do_it(port, subnets):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# This is some iptables voodoo for setting up the Linux kernel's transparent
|
def ipfw_rule_exists(n):
|
||||||
# proxying stuff. If subnets is empty, we just delete our sshuttle chain;
|
argv = ['ipfw', 'list']
|
||||||
# otherwise we delete it, then make it from scratch.
|
p = subprocess.Popen(argv, stdout = subprocess.PIPE)
|
||||||
|
for line in p.stdout:
|
||||||
|
if line.startswith('%05d ' % n):
|
||||||
|
if line[5:].find('ipttl 42') < 0:
|
||||||
|
raise Fatal('non-sshuttle ipfw rule #%d already exists!' % n)
|
||||||
|
return True
|
||||||
|
rv = p.wait()
|
||||||
|
if rv:
|
||||||
|
raise Fatal('%r returned %d' % (argv, rv))
|
||||||
|
|
||||||
|
|
||||||
|
def sysctl_get(name):
|
||||||
|
argv = ['sysctl', '-n', name]
|
||||||
|
p = subprocess.Popen(argv, stdout = subprocess.PIPE)
|
||||||
|
line = p.stdout.readline()
|
||||||
|
rv = p.wait()
|
||||||
|
if rv:
|
||||||
|
raise Fatal('%r returned %d' % (argv, rv))
|
||||||
|
if not line:
|
||||||
|
raise Fatal('%r returned no data' % (argv,))
|
||||||
|
assert(line[-1] == '\n')
|
||||||
|
return line[:-1]
|
||||||
|
|
||||||
|
|
||||||
|
def _sysctl_set(name, val):
|
||||||
|
argv = ['sysctl', '-w', '%s=%s' % (name, val)]
|
||||||
|
debug1('>> %s\n' % ' '.join(argv))
|
||||||
|
rv = subprocess.call(argv, stdout = open('/dev/null', 'w'))
|
||||||
|
|
||||||
|
|
||||||
|
_oldctls = []
|
||||||
|
def sysctl_set(name, val):
|
||||||
|
oldval = sysctl_get(name)
|
||||||
|
if str(val) != str(oldval):
|
||||||
|
_oldctls.append((name, oldval))
|
||||||
|
return _sysctl_set(name, val)
|
||||||
|
|
||||||
|
|
||||||
|
def ipfw(*args):
|
||||||
|
argv = ['ipfw', '-q'] + list(args)
|
||||||
|
debug1('>> %s\n' % ' '.join(argv))
|
||||||
|
rv = subprocess.call(argv)
|
||||||
|
if rv:
|
||||||
|
raise Fatal('%r returned %d' % (argv, rv))
|
||||||
|
|
||||||
|
|
||||||
|
def do_ipfw(port, subnets):
|
||||||
|
sport = str(port)
|
||||||
|
|
||||||
|
# cleanup any existing rules
|
||||||
|
if ipfw_rule_exists(port):
|
||||||
|
ipfw('del', sport)
|
||||||
|
|
||||||
|
while _oldctls:
|
||||||
|
(name,oldval) = _oldctls.pop()
|
||||||
|
_sysctl_set(name, oldval)
|
||||||
|
|
||||||
|
if subnets:
|
||||||
|
sysctl_set('net.inet.ip.fw.enable', 1)
|
||||||
|
sysctl_set('net.inet.ip.forwarding', 1)
|
||||||
|
|
||||||
|
# create new subnet entries
|
||||||
|
for snet,swidth in subnets:
|
||||||
|
ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port,
|
||||||
|
'log', 'tcp',
|
||||||
|
'from', 'any', 'to', '%s/%s' % (snet,swidth),
|
||||||
|
'not', 'ipttl', '42')
|
||||||
|
|
||||||
|
|
||||||
|
def program_exists(name):
|
||||||
|
paths = (os.getenv('PATH') or os.defpath).split(os.pathsep)
|
||||||
|
for p in paths:
|
||||||
|
fn = '%s/%s' % (p, name)
|
||||||
|
if os.path.exists(fn):
|
||||||
|
return not os.path.isdir(fn) and os.access(fn, os.X_OK)
|
||||||
|
|
||||||
|
|
||||||
|
# This is some voodoo for setting up the kernel's transparent
|
||||||
|
# proxying stuff. If subnets is empty, we just delete our sshuttle rules;
|
||||||
|
# otherwise we delete it, then make them from scratch.
|
||||||
#
|
#
|
||||||
# We name the chain based on the transproxy port number so that it's possible
|
# This code is supposed to clean up after itself by deleting its rules on
|
||||||
# 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").
|
|
||||||
#
|
|
||||||
# This code 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
|
# 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
|
# supercede it in the transproxy list, at least, so the leftover rules
|
||||||
# chains are mostly harmless.
|
# are hopefully harmless.
|
||||||
def main(port, subnets):
|
def main(port, subnets):
|
||||||
assert(port > 0)
|
assert(port > 0)
|
||||||
assert(port <= 65535)
|
assert(port <= 65535)
|
||||||
|
|
||||||
if os.getuid() != 0:
|
if os.getuid() != 0:
|
||||||
raise Fatal('you must be root (or enable su/sudo) to set up iptables')
|
raise Fatal('you must be root (or enable su/sudo) to set the firewall')
|
||||||
|
|
||||||
|
if program_exists('ipfw'):
|
||||||
|
do_it = do_ipfw
|
||||||
|
elif program_exists('iptables'):
|
||||||
|
do_it = do_iptables
|
||||||
|
else:
|
||||||
|
raise Fatal("can't find either ipfw or iptables; check your PATH")
|
||||||
|
|
||||||
# because of limitations of the 'su' command, the *real* stdin/stdout
|
# because of limitations of the 'su' command, the *real* stdin/stdout
|
||||||
# are both attached to stdout initially. Clone stdout into stdin so we
|
# are both attached to stdout initially. Clone stdout into stdin so we
|
||||||
# can read from it.
|
# can read from it.
|
||||||
os.dup2(1, 0)
|
os.dup2(1, 0)
|
||||||
|
|
||||||
debug1('iptables manager ready.\n')
|
debug1('firewall manager ready.\n')
|
||||||
sys.stdout.write('READY\n')
|
sys.stdout.write('READY\n')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
@ -89,10 +174,10 @@ def main(port, subnets):
|
|||||||
if not line:
|
if not line:
|
||||||
return # parent died; nothing to do
|
return # parent died; nothing to do
|
||||||
if line != 'GO\n':
|
if line != 'GO\n':
|
||||||
raise Fatal('iptables: expected GO but got %r' % line)
|
raise Fatal('firewall: expected GO but got %r' % line)
|
||||||
try:
|
try:
|
||||||
if line:
|
if line:
|
||||||
debug1('iptables manager: starting transproxy.\n')
|
debug1('firewall manager: starting transproxy.\n')
|
||||||
do_it(port, subnets)
|
do_it(port, subnets)
|
||||||
sys.stdout.write('STARTED\n')
|
sys.stdout.write('STARTED\n')
|
||||||
|
|
||||||
@ -111,7 +196,7 @@ def main(port, subnets):
|
|||||||
|
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
debug1('iptables manager: undoing changes.\n')
|
debug1('firewall manager: undoing changes.\n')
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
do_it(port, [])
|
do_it(port, [])
|
10
main.py
10
main.py
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import sys, os, re
|
import sys, os, re
|
||||||
import helpers, options, client, server, iptables
|
import helpers, options, client, server, firewall
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ def parse_ipport(s):
|
|||||||
|
|
||||||
optspec = """
|
optspec = """
|
||||||
sshuttle [-l [ip:]port] [-r [username@]sshserver[:port]] <subnets...>
|
sshuttle [-l [ip:]port] [-r [username@]sshserver[:port]] <subnets...>
|
||||||
sshuttle --iptables <port> <subnets...>
|
sshuttle --firewall <port> <subnets...>
|
||||||
sshuttle --server
|
sshuttle --server
|
||||||
--
|
--
|
||||||
l,listen= transproxy to this ip address and port number [default=0]
|
l,listen= transproxy to this ip address and port number [default=0]
|
||||||
@ -54,7 +54,7 @@ r,remote= ssh hostname (and optional username) of remote sshuttle server
|
|||||||
v,verbose increase debug message verbosity
|
v,verbose increase debug message verbosity
|
||||||
noserver don't use a separate server process (mostly for debugging)
|
noserver don't use a separate server process (mostly for debugging)
|
||||||
server [internal use only]
|
server [internal use only]
|
||||||
iptables [internal use only]
|
firewall [internal use only]
|
||||||
"""
|
"""
|
||||||
o = options.Options('sshuttle', optspec)
|
o = options.Options('sshuttle', optspec)
|
||||||
(opt, flags, extra) = o.parse(sys.argv[1:])
|
(opt, flags, extra) = o.parse(sys.argv[1:])
|
||||||
@ -64,10 +64,10 @@ helpers.verbose = opt.verbose
|
|||||||
try:
|
try:
|
||||||
if opt.server:
|
if opt.server:
|
||||||
sys.exit(server.main())
|
sys.exit(server.main())
|
||||||
elif opt.iptables:
|
elif opt.firewall:
|
||||||
if len(extra) < 1:
|
if len(extra) < 1:
|
||||||
o.fatal('at least one argument expected')
|
o.fatal('at least one argument expected')
|
||||||
sys.exit(iptables.main(int(extra[0]),
|
sys.exit(firewall.main(int(extra[0]),
|
||||||
parse_subnets(extra[1:])))
|
parse_subnets(extra[1:])))
|
||||||
else:
|
else:
|
||||||
if len(extra) < 1:
|
if len(extra) < 1:
|
||||||
|
16
server.py
16
server.py
@ -1,18 +1,20 @@
|
|||||||
import struct, socket, select
|
import struct, socket, select
|
||||||
import ssnet, helpers
|
if not globals().get('skip_imports'):
|
||||||
from ssnet import SockWrapper, Handler, Proxy, Mux, MuxWrapper
|
import ssnet, helpers
|
||||||
from helpers import *
|
from ssnet import SockWrapper, Handler, Proxy, Mux, MuxWrapper
|
||||||
|
from helpers import *
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# synchronization header
|
|
||||||
sys.stdout.write('SSHUTTLE0001')
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
if helpers.verbose >= 1:
|
if helpers.verbose >= 1:
|
||||||
helpers.logprefix = ' s: '
|
helpers.logprefix = ' s: '
|
||||||
else:
|
else:
|
||||||
helpers.logprefix = 'server: '
|
helpers.logprefix = 'server: '
|
||||||
|
|
||||||
|
# synchronization header
|
||||||
|
sys.stdout.write('SSHUTTLE0001')
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
handlers = []
|
handlers = []
|
||||||
mux = Mux(socket.fromfd(sys.stdin.fileno(),
|
mux = Mux(socket.fromfd(sys.stdin.fileno(),
|
||||||
socket.AF_INET, socket.SOCK_STREAM),
|
socket.AF_INET, socket.SOCK_STREAM),
|
||||||
|
64
ssh.py
64
ssh.py
@ -1,7 +1,20 @@
|
|||||||
import sys, os, re, subprocess, socket
|
import sys, os, re, subprocess, socket, zlib
|
||||||
import helpers
|
import helpers
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
|
|
||||||
|
def readfile(name):
|
||||||
|
basedir = os.path.dirname(os.path.abspath(sys.argv[0]))
|
||||||
|
fullname = os.path.join(basedir, name)
|
||||||
|
return open(fullname, 'rb').read()
|
||||||
|
|
||||||
|
|
||||||
|
def empackage(z, filename):
|
||||||
|
content = z.compress(readfile(filename))
|
||||||
|
content += z.flush(zlib.Z_SYNC_FLUSH)
|
||||||
|
return '%s\n%d\n%s' % (filename,len(content), content)
|
||||||
|
|
||||||
|
|
||||||
def connect(rhostport):
|
def connect(rhostport):
|
||||||
main_exe = sys.argv[0]
|
main_exe = sys.argv[0]
|
||||||
l = (rhostport or '').split(':', 1)
|
l = (rhostport or '').split(':', 1)
|
||||||
@ -9,45 +22,42 @@ def connect(rhostport):
|
|||||||
portl = []
|
portl = []
|
||||||
if len(l) > 1:
|
if len(l) > 1:
|
||||||
portl = ['-p', str(int(l[1]))]
|
portl = ['-p', str(int(l[1]))]
|
||||||
nicedir = os.path.split(os.path.abspath(main_exe))[0]
|
|
||||||
nicedir = re.sub(r':', "_", nicedir)
|
|
||||||
myhome = os.path.expanduser('~') + '/'
|
|
||||||
if nicedir.startswith(myhome):
|
|
||||||
nicedir2 = nicedir[len(myhome):]
|
|
||||||
else:
|
|
||||||
nicedir2 = nicedir
|
|
||||||
if rhost == '-':
|
if rhost == '-':
|
||||||
rhost = None
|
rhost = None
|
||||||
|
|
||||||
|
z = zlib.compressobj(1)
|
||||||
|
content = readfile('assembler.py')
|
||||||
|
content2 = (empackage(z, 'helpers.py') +
|
||||||
|
empackage(z, 'ssnet.py') +
|
||||||
|
empackage(z, 'server.py') +
|
||||||
|
"\n")
|
||||||
|
|
||||||
|
pyscript = r"""
|
||||||
|
import sys;
|
||||||
|
skip_imports=1;
|
||||||
|
verbosity=%d;
|
||||||
|
exec compile(sys.stdin.read(%d), "assembler.py", "exec")
|
||||||
|
""" % (helpers.verbose or 0, len(content))
|
||||||
|
pyscript = re.sub(r'\s+', ' ', pyscript.strip())
|
||||||
|
|
||||||
|
|
||||||
if not rhost:
|
if not rhost:
|
||||||
argv = ['sshuttle', '--server'] + ['-v']*(helpers.verbose or 0)
|
argv = ['python', '-c', pyscript]
|
||||||
else:
|
else:
|
||||||
# WARNING: shell quoting security holes are possible here, so we
|
argv = ['ssh'] + portl + [rhost, '--', "python -c '%s'" % pyscript]
|
||||||
# have to be super careful. We have to use 'sh -c' because
|
|
||||||
# csh-derived shells can't handle PATH= notation. We can't
|
|
||||||
# set PATH in advance, because ssh probably replaces it. We
|
|
||||||
# can't exec *safely* using argv, because *both* ssh and 'sh -c'
|
|
||||||
# allow shellquoting. So we end up having to double-shellquote
|
|
||||||
# stuff here.
|
|
||||||
escapedir = re.sub(r'([^\w/])', r'\\\\\\\1', nicedir)
|
|
||||||
escapedir2 = re.sub(r'([^\w/])', r'\\\\\\\1', nicedir2)
|
|
||||||
cmd = r"""
|
|
||||||
sh -c PATH=%s:'$HOME'/%s:'$PATH exec sshuttle --server%s'
|
|
||||||
""" % (escapedir, escapedir2,
|
|
||||||
' -v' * (helpers.verbose or 0))
|
|
||||||
argv = ['ssh'] + portl + [rhost, '--', cmd.strip()]
|
|
||||||
debug2('executing: %r\n' % argv)
|
|
||||||
(s1,s2) = socket.socketpair()
|
(s1,s2) = socket.socketpair()
|
||||||
def setup():
|
def setup():
|
||||||
# runs in the child process
|
# runs in the child process
|
||||||
s2.close()
|
s2.close()
|
||||||
if not rhost:
|
|
||||||
os.environ['PATH'] = ':'.join([nicedir,
|
|
||||||
os.environ.get('PATH', '')])
|
|
||||||
os.setsid()
|
os.setsid()
|
||||||
s1a,s1b = os.dup(s1.fileno()), os.dup(s1.fileno())
|
s1a,s1b = os.dup(s1.fileno()), os.dup(s1.fileno())
|
||||||
s1.close()
|
s1.close()
|
||||||
|
debug2('executing: %r\n' % argv)
|
||||||
p = subprocess.Popen(argv, stdin=s1a, stdout=s1b, preexec_fn=setup,
|
p = subprocess.Popen(argv, stdin=s1a, stdout=s1b, preexec_fn=setup,
|
||||||
close_fds=True)
|
close_fds=True)
|
||||||
os.close(s1a)
|
os.close(s1a)
|
||||||
os.close(s1b)
|
os.close(s1b)
|
||||||
|
s2.sendall(content)
|
||||||
|
s2.sendall(content2)
|
||||||
return p, s2
|
return p, s2
|
||||||
|
9
ssnet.py
9
ssnet.py
@ -1,5 +1,6 @@
|
|||||||
import struct, socket, errno, select
|
import struct, socket, errno, select
|
||||||
from helpers import *
|
if not globals().get('skip_imports'):
|
||||||
|
from helpers import *
|
||||||
|
|
||||||
HDR_LEN = 8
|
HDR_LEN = 8
|
||||||
|
|
||||||
@ -71,14 +72,17 @@ class SockWrapper:
|
|||||||
def try_connect(self):
|
def try_connect(self):
|
||||||
if not self.connect_to:
|
if not self.connect_to:
|
||||||
return # already connected
|
return # already connected
|
||||||
self.rsock.setsockopt(socket.SOL_IP, socket.IP_TTL, 42)
|
|
||||||
self.rsock.setblocking(False)
|
self.rsock.setblocking(False)
|
||||||
try:
|
try:
|
||||||
self.rsock.connect(self.connect_to)
|
self.rsock.connect(self.connect_to)
|
||||||
|
# connected successfully (Linux)
|
||||||
self.connect_to = None
|
self.connect_to = None
|
||||||
except socket.error, e:
|
except socket.error, e:
|
||||||
if e.args[0] in [errno.EINPROGRESS, errno.EALREADY]:
|
if e.args[0] in [errno.EINPROGRESS, errno.EALREADY]:
|
||||||
pass # not connected yet
|
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]:
|
elif e.args[0] in [errno.ECONNREFUSED, errno.ETIMEDOUT]:
|
||||||
# a "normal" kind of error
|
# a "normal" kind of error
|
||||||
self.connect_to = None
|
self.connect_to = None
|
||||||
@ -387,6 +391,7 @@ class MuxWrapper(SockWrapper):
|
|||||||
def connect_dst(ip, port):
|
def connect_dst(ip, port):
|
||||||
debug2('Connecting to %s:%d\n' % (ip, port))
|
debug2('Connecting to %s:%d\n' % (ip, port))
|
||||||
outsock = socket.socket()
|
outsock = socket.socket()
|
||||||
|
outsock.setsockopt(socket.SOL_IP, socket.IP_TTL, 42)
|
||||||
return SockWrapper(outsock, outsock,
|
return SockWrapper(outsock, outsock,
|
||||||
connect_to = (ip,port),
|
connect_to = (ip,port),
|
||||||
peername = '%s:%d' % (ip,port))
|
peername = '%s:%d' % (ip,port))
|
||||||
|
Reference in New Issue
Block a user