def _do_socks5_connect_response(self, data): # response: # +-----+-----+-------+------+----------+----------+ # | VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | # +-----+-----+-------+------+----------+----------+ # | 1 | 1 | X'00' | 1 | Variable | 2 | # +-----+-----+-------+------+----------+----------+ # if len(data) < 10: settings.PROTO_LOG.error(constants.ERROR_MSG_NOT_ENOUGHT_DATA_FOR.format('socks5 method connect response')) self.transport.close() return False version, reply_code, _ = STRUCT_BBB.unpack_from(data) if reply_code == constants.SOCKS5_REPLY_SUCCEEDED: try: addr = Socks5AddrHeader() length = addr.from_bytes(data[STRUCT_BBB.size:]) return True except ValueError: settings.PROTO_LOG.exception('Fail to parse addr') self.transport.close() return False else: # When a reply (REP value other than X'00') indicates a failure, the # SOCKS server MUST terminate the TCP connection shortly after sending # the reply. Here we close the client too. settings.PROTO_LOG.error('error code for connect response:%d', reply_code) self.transport.close() return False
def conn_completed(future): # socks5 states: In the reply to a CONNECT, BND.PORT contains the port number that # the server assigned to connect to the target host, while BND.ADDR contains the associated IP address. ret, (foward_addr, foward_port) = future.result() if not ret: self._write_error_socks_response_with_reply_code( constants.SOCKS5_REPLY_NETWORK_UNREACHABLE) return False else: # If the reply code (REP value of X'00') indicates a success, # and the request was either a BIND or a CONNECT, the client may now start passing data. self._write_succeed_socks_response_with_addr( Socks5AddrHeader( addr=foward_addr, port=foward_port, addr_type=what_type_of_the_address( foward_addr), )) if cmd == constants.SOCKS5_CMD_CONNECT: self.state = STAGE_SOCKS5_TCP_RELAY elif cmd == constants.SOCKS5_CMD_UDP_ASSOCIATE: self.state = STAGE_SOCKS5_UDP_ASSOCIATE # close the connection and go to the UDP relay server for data relay self.transport.close()
def _send_socks5_connect_request(self): # # resquest: # +-----+-----+-------+------+----------+----------+ # | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | # +-----+-----+-------+------+----------+----------+ # | 1 | 1 | X'00' | 1 | Variable | 2 | # +-----+-----+-------+------+----------+----------+ # addr = Socks5AddrHeader() addr.addr = self.target_host addr.port = self.target_port addr.addr_type = what_type_of_the_address(self.target_host) data = STRUCT_BBB.pack(constants.SOCKS5_VERSION, constants.SOCKS5_CMD_CONNECT, constants.SOCKS5_RESERVED_BYTE, ) + addr.to_bytes() self.transport.write(data) return True
def _parse_socks_request(self, data): """ return the CMD and address(extract from DST.addr DST.port), if ERROR, close the transport and return (False, None, None) """ if len(data) < 10: settings.PROTO_LOG.error('no enough data for SOCKS request') self.transport.close() return False version, cmd, _ = STRUCT_BBB.unpack_from(data) addr = Socks5AddrHeader() try: length = addr.from_bytes(data[STRUCT_BBB.size:]) except ValueError: settings.PROTO_LOG.exception('Fail to get addr') self._write_error_socks_response_with_reply_code(constants.SOCKS_REPLY_ADDRESS_TYPE_NOT_SUPPORTED) return False, None, None else: return True, cmd, addr