diff --git a/sshuttle/helpers.py b/sshuttle/helpers.py index c1f4baf..85ff057 100644 --- a/sshuttle/helpers.py +++ b/sshuttle/helpers.py @@ -281,7 +281,7 @@ class RWPair: class SocketRWShim: __slots__ = ('_r', '_w', '_on_end', '_s1', '_s2', '_t1', '_t2') - def __init__(self, r, w, on_end=None) -> None: + def __init__(self, r, w, on_end=None): self._r = r self._w = w self._on_end = on_end diff --git a/sshuttle/server.py b/sshuttle/server.py index ddca1f3..62c156f 100644 --- a/sshuttle/server.py +++ b/sshuttle/server.py @@ -14,7 +14,7 @@ import sshuttle.hostwatch as hostwatch import subprocess as ssubprocess from sshuttle.ssnet import Handler, Proxy, Mux, MuxWrapper from sshuttle.helpers import b, log, debug1, debug2, debug3, Fatal, \ - resolvconf_random_nameserver, which, get_env + resolvconf_random_nameserver, which, get_env, SocketRWShim def _ipmatch(ipstr): @@ -79,6 +79,20 @@ def _route_iproute(line): return ipw, int(mask) +def _route_windows(line): + if " On-link " not in line: + return None, None + dest, net_mask = re.split(r'\s+', line.strip())[:2] + if net_mask == "255.255.255.255": + return None, None + for p in ('127.', '0.', '224.', '169.254.'): + if dest.startswith(p): + return None, None + ipw = _ipmatch(dest) + mask = _maskbits(_ipmatch(net_mask)) + return ipw, mask + + def _list_routes(argv, extract_route): # FIXME: IPv4 only p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, env=get_env()) @@ -101,14 +115,17 @@ def _list_routes(argv, extract_route): def list_routes(): - if which('ip'): - routes = _list_routes(['ip', 'route'], _route_iproute) - elif which('netstat'): - routes = _list_routes(['netstat', '-rn'], _route_netstat) + if sys.platform == 'win32': + routes = _list_routes(['route', 'PRINT', '-4'], _route_windows) else: - log('WARNING: Neither "ip" nor "netstat" were found on the server. ' - '--auto-nets feature will not work.') - routes = [] + if which('ip'): + routes = _list_routes(['ip', 'route'], _route_iproute) + elif which('netstat'): + routes = _list_routes(['netstat', '-rn'], _route_netstat) + else: + log('WARNING: Neither "ip" nor "netstat" were found on the server. ' + '--auto-nets feature will not work.') + routes = [] for (family, ip, width) in routes: if not ip.startswith('0.') and not ip.startswith('127.'): @@ -282,7 +299,16 @@ def main(latency_control, latency_buffer_size, auto_hosts, to_nameserver, sys.stdout.flush() handlers = [] - mux = Mux(io.FileIO(0, mode='r'), io.FileIO(1, mode='w')) + # get unbuffered stdin and stdout in binary mode. Equivalent to stdin.buffer/stdout.buffer (Only available in Python 3) + r, w = io.FileIO(0, mode='r'), io.FileIO(1, mode='w') + if sys.platform == 'win32': + def _deferred_exit(): + time.sleep(1) # give enough time to write logs to stderr + os._exit(23) + shim = SocketRWShim(r, w, on_end=_deferred_exit) + mux = Mux(*shim.makefiles()) + else: + mux = Mux(r, w) handlers.append(mux) debug1('auto-nets:' + str(auto_nets)) diff --git a/sshuttle/ssh.py b/sshuttle/ssh.py index 8c37fa2..d7967c5 100644 --- a/sshuttle/ssh.py +++ b/sshuttle/ssh.py @@ -115,8 +115,8 @@ def connect(ssh_cmd, rhostport, python, stderr, add_cmd_delimiter, options): pyscript = r""" import sys, os; verbosity=%d; - stdin = os.fdopen(0, "rb"); - exec(compile(stdin.read(%d), "assembler.py", "exec")); + stdin = os.fdopen(0, 'rb'); + exec(compile(stdin.read(%d), 'assembler.py', 'exec')); sys.exit(98); """ % (helpers.verbose or 0, len(content)) pyscript = re.sub(r'\s+', ' ', pyscript.strip()) @@ -135,7 +135,7 @@ def connect(ssh_cmd, rhostport, python, stderr, add_cmd_delimiter, options): else: portl = [] if python: - pycmd = "'%s' -c '%s'" % (python, pyscript) + pycmd = '"%s" -c "%s"' % (python, pyscript) else: # By default, we run the following code in a shell. # However, with restricted shells and other unusual diff --git a/sshuttle/ssnet.py b/sshuttle/ssnet.py index 9438ce8..7b69cc8 100644 --- a/sshuttle/ssnet.py +++ b/sshuttle/ssnet.py @@ -168,6 +168,8 @@ class SockWrapper: debug3('%r: fixed connect result: %s' % (self, e)) if e.args[0] in [errno.EINPROGRESS, errno.EALREADY]: pass # not connected yet + elif sys.platform == 'win32' and e.args[0] == errno.WSAEWOULDBLOCK: + pass # not connected yet elif e.args[0] == 0: # connected successfully (weird Linux bug?) # Sometimes Linux seems to return EINVAL when it isn't @@ -382,11 +384,13 @@ class Mux(Handler): debug2(' > channel=%d cmd=%s len=%d (fullness=%d)' % (channel, cmd_to_name.get(cmd, hex(cmd)), len(data), self.fullness)) + # debug3('>>> data: %r' % data) self.fullness += len(data) def got_packet(self, channel, cmd, data): debug2('< channel=%d cmd=%s len=%d' % (channel, cmd_to_name.get(cmd, hex(cmd)), len(data))) + # debug3('<<< data: %r' % data) if cmd == CMD_PING: self.send(0, CMD_PONG, data) elif cmd == CMD_PONG: