Source code for baboossh.tunnel

import select
import socket
import struct
from socketserver import StreamRequestHandler, ThreadingTCPServer
import threading

[docs]class SocksProxy(StreamRequestHandler): SOCKS_VERSION = 5 def handle(self): # greeting header # read and unpack 2 bytes from a client header = self.connection.recv(2) version, nmethods = struct.unpack("!BB", header) # socks 5 if version != self.SOCKS_VERSION: print("SOCKS version mismatch. Please use socks5") return assert nmethods > 0 # get available methods methods = self.get_available_methods(nmethods) # accept only NO AUTH auth if 0 not in set(methods): # close connection self.server.close_request(self.request) return # send welcome message self.connection.sendall(struct.pack("!BB", self.SOCKS_VERSION, 0)) # request version, cmd, _, address_type = struct.unpack("!BBBB", self.connection.recv(4)) assert version == self.SOCKS_VERSION if address_type == 1: # IPv4 address = socket.inet_ntoa(self.connection.recv(4)) elif address_type == 3: # Domain name domain_length = ord(self.connection.recv(1)[0]) address = self.connection.recv(domain_length) port = struct.unpack('!H', self.connection.recv(2))[0] # reply try: if cmd == 1: # CONNECT remote = self.server.output.transport.open_channel( kind="direct-tcpip", dest_addr=(address, port), src_addr=("", 0) ) else: self.server.close_request(self.request) addr = struct.unpack("!I", socket.inet_aton(address))[0] port = int(port) reply = struct.pack("!BBBBIH", self.SOCKS_VERSION, 0, 0, address_type, addr, port) except Exception as err: print(err) # return connection refused error reply = self.generate_failed_reply(address_type, 5) self.connection.sendall(reply) # establish data exchange if reply[1] == 0 and cmd == 1: self.exchange_loop(self.connection, remote) self.server.close_request(self.request) def get_available_methods(self, n): methods = [] for i in range(n): methods.append(ord(self.connection.recv(1))) return methods def generate_failed_reply(self, address_type, error_number): return struct.pack("!BBBBIH", self.SOCKS_VERSION, error_number, 0, address_type, 0, 0) def exchange_loop(self, client, remote): while True: # wait until client or remote is available for read r, w, e = select.select([client, remote], [], []) if client in r: data = client.recv(4096) if remote.send(data) <= 0: break if remote in r: data = remote.recv(4096) if client.send(data) <= 0: break
[docs]class Tunnel(): """Open SOCKS tunnel to a :class:`Host` Each tunnel is opened in distinct thread so that BabooSSH is still usable while the tunnel is open. Attributes: connection (:class:`.Connection`): the tunnel exit port (int): the tunnel entrance (local) port. Uses a random port if none is provided. """
[docs] def __init__(self, connection, port=None): self.connection = connection if port is None: port = 0 self.connection.open() self.connection.used_by_tunnels.append(self) self.server = ThreadingTCPServer(('127.0.0.1', port), SocksProxy) self.server.output = self.connection.conn ip, newport = self.server.server_address self.port = newport self.thread = threading.Thread(target=self.server.serve_forever) self.thread.start() print("Tunnel to "+str(self.connection)+" open on port "+str(self.port))
[docs] def close(self): """Close a previously opened port""" try: self.connection.used_by_tunnels.remove(self) except: pass self.server.shutdown() print("Tunnel port "+str(self.port)+" closed")
def __str__(self): return str(self.port)+"->"+str(self.connection)