From df89afe365573bc448b3cc09b2d6b7a4abafcc84 Mon Sep 17 00:00:00 2001 From: vieira Date: Sat, 5 Dec 2015 03:40:26 +0000 Subject: [PATCH] Backward compatibility with Python 2.4 (server) It is often the case that the user has no administrative control over the server that is being used. As such it is important to support as many versions as possible, at least on the remote server end. These fixes will allow sshuttle to be used with servers that have only python 2.4 or python 2.6 installed while hopefully not breaking the compatibility with 2.7 and 3.5. --- sshuttle/assembler.py | 5 ++-- sshuttle/helpers.py | 13 +++++++++- sshuttle/hostwatch.py | 17 ++++++++----- sshuttle/server.py | 12 ++++++---- sshuttle/ssnet.py | 56 ++++++++++++++++++++++++------------------- 5 files changed, 66 insertions(+), 37 deletions(-) diff --git a/sshuttle/assembler.py b/sshuttle/assembler.py index 37cde42..86ee82f 100644 --- a/sshuttle/assembler.py +++ b/sshuttle/assembler.py @@ -13,8 +13,9 @@ while 1: content = z.decompress(sys.stdin.read(nbytes)) module = imp.new_module(name) - parent, _, parent_name = name.rpartition(".") - if parent != "": + parents = name.rsplit(".", 1) + if len(parents) == 2: + parent, parent_name = parents setattr(sys.modules[parent], parent_name, module) code = compile(content, name, "exec") diff --git a/sshuttle/helpers.py b/sshuttle/helpers.py index b23e030..2324b2b 100644 --- a/sshuttle/helpers.py +++ b/sshuttle/helpers.py @@ -5,6 +5,16 @@ import errno logprefix = '' verbose = 0 +if sys.version_info[0] == 3: + binary_type = bytes + + def b(s): + return s.encode("latin-1") +else: + binary_type = str + + def b(s): + return s def log(s): try: @@ -62,7 +72,8 @@ def islocal(ip, family): try: try: sock.bind((ip, 0)) - except socket.error as e: + except socket.error: + _, e = sys.exc_info()[:2] if e.args[0] == errno.EADDRNOTAVAIL: return False # not a local IP else: diff --git a/sshuttle/hostwatch.py b/sshuttle/hostwatch.py index 66d93ea..5311210 100644 --- a/sshuttle/hostwatch.py +++ b/sshuttle/hostwatch.py @@ -22,7 +22,8 @@ hostnames = {} queue = {} try: null = open('/dev/null', 'wb') -except IOError as e: +except IOError: + _, e = sys.exc_info()[:2] log('warning: %s\n' % e) null = os.popen("sh -c 'while read x; do :; done'", 'wb', 4096) @@ -38,7 +39,7 @@ def write_host_cache(): for name, ip in sorted(hostnames.items()): f.write('%s,%s\n' % (name, ip)) f.close() - os.chmod(tmpname, 0o600) + os.chmod(tmpname, 384) # 600 in octal, 'rw-------' os.rename(tmpname, CACHEFILE) finally: try: @@ -50,7 +51,8 @@ def write_host_cache(): def read_host_cache(): try: f = open(CACHEFILE) - except IOError as e: + except IOError: + _, e = sys.exc_info()[:2] if e.errno == errno.ENOENT: return else: @@ -124,7 +126,8 @@ def _check_netstat(): p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null) content = p.stdout.read() p.wait() - except OSError as e: + except OSError: + _, e = sys.exc_info()[:2] log('%r failed: %r\n' % (argv, e)) return @@ -144,7 +147,8 @@ def _check_smb(hostname): p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null) lines = p.stdout.readlines() p.wait() - except OSError as e: + except OSError: + _, e = sys.exc_info()[:2] log('%r failed: %r\n' % (argv, e)) _smb_ok = False return @@ -201,7 +205,8 @@ def _check_nmb(hostname, is_workgroup, is_master): p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null) lines = p.stdout.readlines() rv = p.wait() - except OSError as e: + except OSError: + _, e = sys.exc_info()[:2] log('%r failed: %r\n' % (argv, e)) _nmb_ok = False return diff --git a/sshuttle/server.py b/sshuttle/server.py index ae9639f..fe38af1 100644 --- a/sshuttle/server.py +++ b/sshuttle/server.py @@ -147,7 +147,8 @@ class DnsProxy(Handler): try: sock.send(self.request) self.socks.append(sock) - except socket.error as e: + except socket.error: + _, e = sys.exc_info()[:2] if e.args[0] in ssnet.NET_ERRS: # might have been spurious; try again. # Note: these errors sometimes are reported by recv(), @@ -164,7 +165,8 @@ class DnsProxy(Handler): try: data = sock.recv(4096) - except socket.error as e: + except socket.error: + _, e = sys.exc_info()[:2] self.socks.remove(sock) del self.peers[sock] @@ -199,14 +201,16 @@ class UdpProxy(Handler): debug2('UDP: sending to %r port %d\n' % dstip) try: self.sock.sendto(data, dstip) - except socket.error as e: + except socket.error: + _, e = sys.exc_info()[:2] log('UDP send to %r port %d: %s\n' % (dstip[0], dstip[1], e)) return def callback(self, sock): try: data, peer = sock.recvfrom(4096) - except socket.error as e: + except socket.error: + _, e = sys.exc_info()[:2] log('UDP recv from %r port %d: %s\n' % (peer[0], peer[1], e)) return debug2('UDP response: %d bytes\n' % len(data)) diff --git a/sshuttle/ssnet.py b/sshuttle/ssnet.py index 218e7b5..fe1de1e 100644 --- a/sshuttle/ssnet.py +++ b/sshuttle/ssnet.py @@ -1,9 +1,10 @@ +import sys import struct import socket import errno import select import os -from sshuttle.helpers import log, debug1, debug2, debug3, Fatal +from sshuttle.helpers import b, binary_type, log, debug1, debug2, debug3, Fatal MAX_CHANNEL = 65535 @@ -75,7 +76,8 @@ def _fds(l): def _nb_clean(func, *args): try: return func(*args) - except OSError as e: + except OSError: + _, e = sys.exc_info()[:2] if e.errno not in (errno.EWOULDBLOCK, errno.EAGAIN): raise else: @@ -88,7 +90,8 @@ def _try_peername(sock): pn = sock.getpeername() if pn: return '%s:%s' % (pn[0], pn[1]) - except socket.error as e: + except socket.error: + _, e = sys.exc_info()[:2] if e.args[0] not in (errno.ENOTCONN, errno.ENOTSOCK): raise return 'unknown' @@ -144,7 +147,8 @@ class SockWrapper: self.rsock.connect(self.connect_to) # connected successfully (Linux) self.connect_to = None - except socket.error as e: + except socket.error: + _, e = sys.exc_info()[:2] debug3('%r: connect result: %s\n' % (self, e)) if e.args[0] == errno.EINVAL: # this is what happens when you call connect() on a socket @@ -191,7 +195,8 @@ class SockWrapper: self.shut_write = True try: self.wsock.shutdown(SHUT_WR) - except socket.error as e: + except socket.error: + _, e = sys.exc_info()[:2] self.seterr('nowrite: %s' % e) def too_full(self): @@ -203,7 +208,8 @@ class SockWrapper: self.wsock.setblocking(False) try: return _nb_clean(os.write, self.wsock.fileno(), buf) - except OSError as e: + except OSError: + _, e = sys.exc_info()[:2] if e.errno == errno.EPIPE: debug1('%r: uwrite: got EPIPE\n' % self) self.nowrite() @@ -225,9 +231,10 @@ class SockWrapper: self.rsock.setblocking(False) try: return _nb_clean(os.read, self.rsock.fileno(), 65536) - except OSError as e: + except OSError: + _, e = sys.exc_info()[:2] self.seterr('uread: %s' % e) - return b'' # unexpected error... we'll call it EOF + return b('') # unexpected error... we'll call it EOF def fill(self): if self.buf: @@ -235,7 +242,7 @@ class SockWrapper: rb = self.uread() if rb: self.buf.append(rb) - if rb == b'': # empty string means EOF; None means temporarily empty + if rb == b(''): # empty string means EOF; None means temporarily empty self.noread() def copy_to(self, outwrap): @@ -333,11 +340,11 @@ class Mux(Handler): self.channels = {} self.chani = 0 self.want = 0 - self.inbuf = b'' + self.inbuf = b('') self.outbuf = [] self.fullness = 0 self.too_full = False - self.send(0, CMD_PING, b'chicken') + self.send(0, CMD_PING, b('chicken')) def next_channel(self): # channel 0 is special, so we never allocate it @@ -357,7 +364,7 @@ class Mux(Handler): def check_fullness(self): if self.fullness > 32768: if not self.too_full: - self.send(0, CMD_PING, b'rttest') + self.send(0, CMD_PING, b('rttest')) self.too_full = True # ob = [] # for b in self.outbuf: @@ -366,9 +373,9 @@ class Mux(Handler): # log('outbuf: %d %r\n' % (self.amount_queued(), ob)) def send(self, channel, cmd, data): - assert isinstance(data, bytes) + assert isinstance(data, binary_type) assert len(data) <= 65535 - p = struct.pack('!ccHHH', b'S', b'S', channel, cmd, len(data)) + data + p = struct.pack('!ccHHH', b('S'), b('S'), channel, cmd, len(data)) + data self.outbuf.append(p) debug2(' > channel=%d cmd=%s len=%d (fullness=%d)\n' % (channel, cmd_to_name.get(cmd, hex(cmd)), @@ -434,14 +441,15 @@ class Mux(Handler): def fill(self): self.rsock.setblocking(False) try: - b = _nb_clean(os.read, self.rsock.fileno(), 32768) - except OSError as e: + read = _nb_clean(os.read, self.rsock.fileno(), 32768) + except OSError: + _, e = sys.exc_info()[:2] raise Fatal('other end: %r' % e) # log('<<< %r\n' % b) - if b == b'': # EOF + if read == b(''): # EOF self.ok = False - if b: - self.inbuf += b + if read: + self.inbuf += read def handle(self): self.fill() @@ -451,8 +459,8 @@ class Mux(Handler): if len(self.inbuf) >= (self.want or HDR_LEN): (s1, s2, channel, cmd, datalen) = \ struct.unpack('!ccHHH', self.inbuf[:HDR_LEN]) - assert(s1 == b'S') - assert(s2 == b'S') + assert(s1 == b('S')) + assert(s2 == b('S')) self.want = datalen + HDR_LEN if self.want and len(self.inbuf) >= self.want: data = self.inbuf[HDR_LEN:self.want] @@ -496,14 +504,14 @@ class MuxWrapper(SockWrapper): if not self.shut_read: debug2('%r: done reading\n' % self) self.shut_read = True - self.mux.send(self.channel, CMD_TCP_STOP_SENDING, b'') + self.mux.send(self.channel, CMD_TCP_STOP_SENDING, b('')) self.maybe_close() def nowrite(self): if not self.shut_write: debug2('%r: done writing\n' % self) self.shut_write = True - self.mux.send(self.channel, CMD_TCP_EOF, b'') + self.mux.send(self.channel, CMD_TCP_EOF, b('')) self.maybe_close() def maybe_close(self): @@ -526,7 +534,7 @@ class MuxWrapper(SockWrapper): def uread(self): if self.shut_read: - return b'' # EOF + return b('') # EOF else: return None # no data available right now