2010-10-01 21:06:56 +02:00
|
|
|
import sys, os, re, socket, zlib
|
|
|
|
import compat.ssubprocess as ssubprocess
|
2011-01-26 06:07:01 +01:00
|
|
|
import helpers
|
ssh.py: support finding sshuttle in "$HOME/.../sshuttle"
If you ran sshuttle from /home/apenwarr/sshuttle/sshuttle, we would
automatically add /home/apenwarr/sshuttle to the PATH before trying to
execute sshuttle on the remote machine. That way, if you install it in the
same place on two computers, the client would still be able to start the
server.
Someone reported, though, that if they installed the client in
/home/apenwarr/sshuttle/shuttle, and the server in /root/sshuttle/sshuttle,
then used "-r root@servername", it wasn't able to find the program.
Similar problems would happen if you're apenwarr at home and averyp at work.
So what we now do is add *two* directories to the PATH:
/home/apenwarr/sshuttle and $HOME/sshuttle, where $HOME is the value of
$HOME on the *server*, not the client. So it'll find it in either place.
2010-05-03 03:24:31 +02:00
|
|
|
from helpers import *
|
2010-05-01 22:15:37 +02:00
|
|
|
|
2010-05-05 05:21:16 +02:00
|
|
|
|
|
|
|
def readfile(name):
|
|
|
|
basedir = os.path.dirname(os.path.abspath(sys.argv[0]))
|
2010-05-12 19:48:37 +02:00
|
|
|
path = [basedir] + sys.path
|
|
|
|
for d in path:
|
|
|
|
fullname = os.path.join(d, name)
|
|
|
|
if os.path.exists(fullname):
|
|
|
|
return open(fullname, 'rb').read()
|
|
|
|
raise Exception("can't find file %r in any of %r" % (name, path))
|
2010-05-05 05:21:16 +02:00
|
|
|
|
|
|
|
|
2011-01-26 06:07:01 +01:00
|
|
|
def empackage(z, filename, data=None):
|
2010-10-01 21:06:56 +02:00
|
|
|
(path,basename) = os.path.split(filename)
|
2011-01-26 06:07:01 +01:00
|
|
|
if not data:
|
|
|
|
data = readfile(filename)
|
|
|
|
content = z.compress(data)
|
2010-05-05 05:21:16 +02:00
|
|
|
content += z.flush(zlib.Z_SYNC_FLUSH)
|
2011-01-26 06:07:01 +01:00
|
|
|
return '%s\n%d\n%s' % (basename, len(content), content)
|
2010-05-05 05:21:16 +02:00
|
|
|
|
|
|
|
|
2011-01-26 06:07:01 +01:00
|
|
|
def connect(ssh_cmd, rhostport, python, stderr, options):
|
2010-05-01 22:15:37 +02:00
|
|
|
main_exe = sys.argv[0]
|
2010-05-04 19:07:51 +02:00
|
|
|
portl = []
|
2010-11-20 00:11:58 +01:00
|
|
|
|
|
|
|
rhostIsIPv6 = False
|
2010-12-10 04:19:15 +01:00
|
|
|
if (rhostport or '').count(':') > 1:
|
2010-11-20 00:11:58 +01:00
|
|
|
rhostIsIPv6 = True
|
|
|
|
if rhostport.count(']') or rhostport.count('['):
|
|
|
|
result = rhostport.split(']')
|
|
|
|
rhost = result[0].strip('[')
|
|
|
|
if len(result) > 1:
|
|
|
|
result[1] = result[1].strip(':')
|
|
|
|
if result[1] is not '':
|
|
|
|
portl = ['-p', str(int(result[1]))]
|
|
|
|
else: # can't disambiguate IPv6 colons and a port number. pass the hostname through.
|
|
|
|
rhost = rhostport
|
|
|
|
else: # IPv4
|
|
|
|
l = (rhostport or '').split(':', 1)
|
|
|
|
rhost = l[0]
|
|
|
|
if len(l) > 1:
|
|
|
|
portl = ['-p', str(int(l[1]))]
|
2010-05-05 05:21:16 +02:00
|
|
|
|
2010-05-01 22:15:37 +02:00
|
|
|
if rhost == '-':
|
|
|
|
rhost = None
|
2010-05-05 05:21:16 +02:00
|
|
|
|
2010-11-20 00:11:58 +01:00
|
|
|
ipv6flag = []
|
|
|
|
if rhostIsIPv6:
|
|
|
|
ipv6flag = ['-6']
|
|
|
|
|
2010-05-05 05:21:16 +02:00
|
|
|
z = zlib.compressobj(1)
|
|
|
|
content = readfile('assembler.py')
|
2011-01-26 06:07:01 +01:00
|
|
|
optdata = ''.join("%s=%r\n" % (k,v) for (k,v) in options.items())
|
|
|
|
content2 = (empackage(z, 'cmdline_options.py', optdata) +
|
|
|
|
empackage(z, 'helpers.py') +
|
2010-10-01 21:06:56 +02:00
|
|
|
empackage(z, 'compat/ssubprocess.py') +
|
2010-05-05 05:21:16 +02:00
|
|
|
empackage(z, 'ssnet.py') +
|
Added new --auto-hosts and --seed-hosts options to the client.
Now if you use --auto-hosts (-H), the client will ask the server to spawn a
hostwatcher to add names. That, in turn, will send names back to the
server, which sends them back to the client, which sends them to the
firewall subprocess, which will write them to /etc/hosts. Whew!
Only the firewall process can write to /etc/hosts, of course, because only
he's running as root.
Since the name discovery process is kind of slow, we cache the names in
~/.sshuttle.hosts on the remote server.
Right now, most of the names are discovered using nmblookup and smbclient,
as well as by reading the existing entries in /etc/hosts. What would really
be nice would be to query active directory or mdns somehow... but I don't
really know how those work, so this is what you get for now :) It's pretty
neat, at least.
2010-05-08 09:03:12 +02:00
|
|
|
empackage(z, 'hostwatch.py') +
|
2010-05-05 05:21:16 +02:00
|
|
|
empackage(z, 'server.py') +
|
|
|
|
"\n")
|
|
|
|
|
|
|
|
pyscript = r"""
|
|
|
|
import sys;
|
|
|
|
skip_imports=1;
|
|
|
|
verbosity=%d;
|
|
|
|
exec compile(sys.stdin.read(%d), "assembler.py", "exec")
|
2011-01-26 06:07:01 +01:00
|
|
|
""" % (helpers.verbose or 0, len(content))
|
2010-05-05 05:21:16 +02:00
|
|
|
pyscript = re.sub(r'\s+', ' ', pyscript.strip())
|
|
|
|
|
|
|
|
|
2010-05-01 22:15:37 +02:00
|
|
|
if not rhost:
|
2011-02-07 09:32:16 +01:00
|
|
|
# ignore the --python argument when running locally; we already know
|
|
|
|
# which python version works.
|
|
|
|
argv = [sys.argv[1], '-c', pyscript]
|
2010-05-01 22:15:37 +02:00
|
|
|
else:
|
2010-11-09 09:17:01 +01:00
|
|
|
if ssh_cmd:
|
|
|
|
sshl = ssh_cmd.split(' ')
|
|
|
|
else:
|
|
|
|
sshl = ['ssh']
|
2011-02-07 09:32:16 +01:00
|
|
|
if python:
|
|
|
|
pycmd = "'%s' -c '%s'" % (python, pyscript)
|
|
|
|
else:
|
|
|
|
pycmd = ("P=python2; $P -V 2>/dev/null || P=python; "
|
|
|
|
"\"$P\" -c '%s'") % pyscript
|
2010-11-09 09:17:01 +01:00
|
|
|
argv = (sshl +
|
|
|
|
portl +
|
2010-11-20 00:11:58 +01:00
|
|
|
ipv6flag +
|
2011-02-07 09:32:16 +01:00
|
|
|
[rhost, '--', pycmd])
|
2010-05-02 05:14:42 +02:00
|
|
|
(s1,s2) = socket.socketpair()
|
2010-05-01 22:15:37 +02:00
|
|
|
def setup():
|
|
|
|
# runs in the child process
|
2010-05-02 05:14:42 +02:00
|
|
|
s2.close()
|
|
|
|
s1a,s1b = os.dup(s1.fileno()), os.dup(s1.fileno())
|
|
|
|
s1.close()
|
2010-05-05 05:21:16 +02:00
|
|
|
debug2('executing: %r\n' % argv)
|
2010-10-01 21:06:56 +02:00
|
|
|
p = ssubprocess.Popen(argv, stdin=s1a, stdout=s1b, preexec_fn=setup,
|
2011-01-01 07:54:07 +01:00
|
|
|
close_fds=True, stderr=stderr)
|
2010-05-02 05:14:42 +02:00
|
|
|
os.close(s1a)
|
|
|
|
os.close(s1b)
|
2010-05-05 05:21:16 +02:00
|
|
|
s2.sendall(content)
|
|
|
|
s2.sendall(content2)
|
2010-05-02 05:14:42 +02:00
|
|
|
return p, s2
|