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:
__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

View File

@ -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))

View File

@ -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

View File

@ -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: