support server on Windows

This commit is contained in:
nom3ad 2024-04-14 14:09:17 +05:30 committed by Brian May
parent ace8642950
commit 51287dc4db
4 changed files with 43 additions and 13 deletions

View File

@ -281,7 +281,7 @@ class RWPair:
class SocketRWShim: class SocketRWShim:
__slots__ = ('_r', '_w', '_on_end', '_s1', '_s2', '_t1', '_t2') __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._r = r
self._w = w self._w = w
self._on_end = on_end self._on_end = on_end

View File

@ -14,7 +14,7 @@ import sshuttle.hostwatch as hostwatch
import subprocess as ssubprocess import subprocess as ssubprocess
from sshuttle.ssnet import Handler, Proxy, Mux, MuxWrapper from sshuttle.ssnet import Handler, Proxy, Mux, MuxWrapper
from sshuttle.helpers import b, log, debug1, debug2, debug3, Fatal, \ 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): def _ipmatch(ipstr):
@ -79,6 +79,20 @@ def _route_iproute(line):
return ipw, int(mask) 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): def _list_routes(argv, extract_route):
# FIXME: IPv4 only # FIXME: IPv4 only
p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, env=get_env()) p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, env=get_env())
@ -101,14 +115,17 @@ def _list_routes(argv, extract_route):
def list_routes(): def list_routes():
if which('ip'): if sys.platform == 'win32':
routes = _list_routes(['ip', 'route'], _route_iproute) routes = _list_routes(['route', 'PRINT', '-4'], _route_windows)
elif which('netstat'):
routes = _list_routes(['netstat', '-rn'], _route_netstat)
else: else:
log('WARNING: Neither "ip" nor "netstat" were found on the server. ' if which('ip'):
'--auto-nets feature will not work.') routes = _list_routes(['ip', 'route'], _route_iproute)
routes = [] 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: for (family, ip, width) in routes:
if not ip.startswith('0.') and not ip.startswith('127.'): 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() sys.stdout.flush()
handlers = [] 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) handlers.append(mux)
debug1('auto-nets:' + str(auto_nets)) debug1('auto-nets:' + str(auto_nets))

View File

@ -115,8 +115,8 @@ def connect(ssh_cmd, rhostport, python, stderr, add_cmd_delimiter, options):
pyscript = r""" pyscript = r"""
import sys, os; import sys, os;
verbosity=%d; verbosity=%d;
stdin = os.fdopen(0, "rb"); stdin = os.fdopen(0, 'rb');
exec(compile(stdin.read(%d), "assembler.py", "exec")); exec(compile(stdin.read(%d), 'assembler.py', 'exec'));
sys.exit(98); sys.exit(98);
""" % (helpers.verbose or 0, len(content)) """ % (helpers.verbose or 0, len(content))
pyscript = re.sub(r'\s+', ' ', pyscript.strip()) pyscript = re.sub(r'\s+', ' ', pyscript.strip())
@ -135,7 +135,7 @@ def connect(ssh_cmd, rhostport, python, stderr, add_cmd_delimiter, options):
else: else:
portl = [] portl = []
if python: if python:
pycmd = "'%s' -c '%s'" % (python, pyscript) pycmd = '"%s" -c "%s"' % (python, pyscript)
else: else:
# By default, we run the following code in a shell. # By default, we run the following code in a shell.
# However, with restricted shells and other unusual # However, with restricted shells and other unusual

View File

@ -168,6 +168,8 @@ class SockWrapper:
debug3('%r: fixed connect result: %s' % (self, e)) debug3('%r: fixed connect result: %s' % (self, 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 sys.platform == 'win32' and e.args[0] == errno.WSAEWOULDBLOCK:
pass # not connected yet
elif e.args[0] == 0: elif e.args[0] == 0:
# connected successfully (weird Linux bug?) # connected successfully (weird Linux bug?)
# Sometimes Linux seems to return EINVAL when it isn't # 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)' debug2(' > channel=%d cmd=%s len=%d (fullness=%d)'
% (channel, cmd_to_name.get(cmd, hex(cmd)), % (channel, cmd_to_name.get(cmd, hex(cmd)),
len(data), self.fullness)) len(data), self.fullness))
# debug3('>>> data: %r' % data)
self.fullness += len(data) self.fullness += len(data)
def got_packet(self, channel, cmd, data): def got_packet(self, channel, cmd, data):
debug2('< channel=%d cmd=%s len=%d' debug2('< channel=%d cmd=%s len=%d'
% (channel, cmd_to_name.get(cmd, hex(cmd)), len(data))) % (channel, cmd_to_name.get(cmd, hex(cmd)), len(data)))
# debug3('<<< data: %r' % data)
if cmd == CMD_PING: if cmd == CMD_PING:
self.send(0, CMD_PONG, data) self.send(0, CMD_PONG, data)
elif cmd == CMD_PONG: elif cmd == CMD_PONG: