class LocalTransfer(object): """ client <==> local <==> remote """ def __init__(self, loop, sock, addr, dns_resolver): cfg = LocalConfigManager.get_config() self._encryptor = Encryptor(cfg['password'], cfg['method']) self._loop = loop self._client = Client(sock, addr, loop, self._encryptor) self._remote = None self._dns_resolver = dns_resolver self._last_active = time.time() self._raddr = cfg['remote_address'] self._laddr = cfg['local_address'] self._saddr = None @property def closed(self): return self._client is None @property def last_active(self): return self._last_active @property def display_name(self): client = '%s:%s' % (self._client.ipaddr, self._client.port) if self._saddr is not None: server = '%s:%s' % (self._saddr.hostname, self._saddr.port) else: server = 'None' return '%s <==> %s' % (client, server) def start(self): self._client.start(MainLoop.EVENT_READ, self.handle_event) def handle_event(self, sock, event): if self.closed: return self._last_active = time.time() if sock == self._client.socket: if event & MainLoop.EVENT_ERROR: self.stop(warning='client %s:%s error' % (self._client.ipaddr, self._client.port)) return self._handle_client(event) elif sock == self._remote.socket: if event & MainLoop.EVENT_ERROR: self.stop(warning='remote %s:%s error' % (self._remote.ipaddr, self._remote.port)) return self._handle_remote(event) @stop_transfer_if_fail def _handle_client(self, event): """handle the client events""" data = None if event & MainLoop.EVENT_READ: data = self._client.read() if data == b'': self.stop(info='%s closed by client' % self.display_name) return if self._client.state == ClientState.UDP_ASSOC: return elif self._client.state in (ClientState.INIT, ClientState.ADDR)\ and not data: """in state INIT or ADDR, supposed to receive data from client""" self.stop(info='client %s:%s closed' % (self._client.ipaddr, self._client.port)) return if self._client.state == ClientState.INIT: """ receive HELLO message from client, shall we verify it ? send a HELLO back """ self._client.write(b'\x05\00') # HELLO self._client.state = ClientState.ADDR elif self._client.state == ClientState.ADDR: """ receive the server addr, give client a feedback and connect to remote """ vsn, cmd, atype, server_addr, server_port = parse_tcp_request(data) if cmd == SOCKS5Command.UDP_ASSOCIATE: DEBUG('UDP associate') self._client.write(build_tcp_reply(5, 0, 0, self._client.ipaddr, self._client.port)) self._client.state = ClientState.UDP_ASSOC # just wait for the client to disconnect return elif cmd != SOCKS5Command.CONNECT: raise UnknownCommandException(cmd) server_addr = tostr(server_addr) INFO('connecting %s:%d from %s:%d' % (server_addr, server_port, self._client.ipaddr, self._client.port)) self._saddr = Address(server_addr, server_port) # forward address to remote self._client.write(build_tcp_reply(5, 0, 0, self._laddr.ipaddr, self._laddr.port)) self._client.state = ClientState.DNS self._remote = Remote(None, self._raddr, self._loop, self._encryptor) self._remote.write(data[3:]) if self._raddr.ipaddr: # ipaddr self._connect_to_remote() else: self._dns_resolver.resolve(self._raddr.hostname, self._dns_resolved) elif data and self._remote: self._remote.write(data) if event & MainLoop.EVENT_WRITE: """some data unsent""" self._client.write() @stop_transfer_if_fail def _handle_remote(self, event): """handle remote events""" if event & MainLoop.EVENT_READ: data = self._remote.read() if data == b'': self.stop(info='%s closed by remote' % self.display_name) return self._client.write(data) if event & MainLoop.EVENT_WRITE: self._remote.write() self._client.state = ClientState.CONNECTED @stop_transfer_if_fail def _dns_resolved(self, result, error): """remote ip address is resolved""" if self._client is None: # ignore DNS resolve callback if transfer already closed return elif error: self.stop(warning=error) return self._raddr.ipaddr = result[1] self._connect_to_remote() def _connect_to_remote(self): ipaddr = self._raddr.ipaddr port = self._raddr.port self._remote.socket = socket.socket(ipaddr.family, socket.SOCK_STREAM, socket.SOL_TCP) self._remote.connect((ipaddr.compressed, port)) self._remote.start(MainLoop.EVENT_READ | MainLoop.EVENT_WRITE, self.handle_event) def stop(self, info=None, warning=None): """stop transfer""" if self._client is None: return if info: INFO(info) elif warning: WARN(warning) self._client.close() self._client = None if self._remote: self._remote.close()