def _try_handshake(self): """ Try to read a HANDSHAKE request :return: False if command could not been processes due to lack of bytes, True otherwise """ try: request, offset = socks5_serializer.unpack_serializable( MethodsRequest, self.buffer) except PackError: # No (complete) HANDSHAKE received, so dont do anything return False # Consume the buffer self.buffer = self.buffer[offset:] # Only accept NO AUTH if request.version != SOCKS_VERSION or 0x00 not in request.methods: self._logger.error("Client has sent INVALID METHOD REQUEST") self.buffer = '' self.close() else: self._logger.info("Client has sent METHOD REQUEST") # Respond that we would like to use NO AUTHENTICATION (0x00) if self.state is not ConnectionState.CONNECTED: response = socks5_serializer.pack_serializable( MethodsResponse(SOCKS_VERSION, 0)) self.transport.write(response) # We are connected now, the next incoming message will be a REQUEST self.state = ConnectionState.CONNECTED return True
def datagram_received(self, data, _): try: request, _ = socks5_serializer.unpack_serializable(UdpPacket, data) except PackError: self.logger.warning("Error while decoding packet", exc_info=True) else: self.callback(request.data, request.destination)
def _try_request(self): """ Try to consume a REQUEST message and respond whether we will accept the request. Will setup a TCP relay or an UDP socket to accommodate TCP RELAY and UDP ASSOCIATE requests. After a TCP relay is set up the handler will deactivate itself and change the Connection to a TcpRelayConnection. Further data will be passed on to that handler. :return: False if command could not been processes due to lack of bytes, True otherwise """ self._logger.debug("Client has sent PROXY REQUEST") try: request, offset = socks5_serializer.unpack_serializable( CommandRequest, self.buffer) except PackError: return False self.buffer = self.buffer[offset:] self.state = ConnectionState.PROXY_REQUEST_RECEIVED try: if request.cmd == REQ_CMD_UDP_ASSOCIATE: ensure_future(self.on_udp_associate_request(request)) elif request.cmd == REQ_CMD_BIND: payload = CommandResponse(SOCKS_VERSION, REP_SUCCEEDED, 0, ("127.0.0.1", 1081)) response = socks5_serializer.pack_serializable(payload) self.transport.write(response) self.state = ConnectionState.PROXY_REQUEST_ACCEPTED elif request.cmd == REQ_CMD_CONNECT: self._logger.info("Accepting TCP CONNECT request to %s:%d", *request.destination) self.connect_to = request.destination payload = CommandResponse(SOCKS_VERSION, REP_SUCCEEDED, 0, ("127.0.0.1", 1081)) response = socks5_serializer.pack_serializable(payload) self.transport.write(response) else: self.deny_request(request, "CMD not recognized") except: payload = CommandResponse(SOCKS_VERSION, REP_COMMAND_NOT_SUPPORTED, 0, ("0.0.0.0", 0)) response = socks5_serializer.pack_serializable(payload) self.transport.write(response) self._logger.exception( "Exception thrown, returning unsupported command response") return True
async def _login(self): self.transport, _ = await get_event_loop().create_connection( lambda: self, *self.proxy_addr) request = MethodsRequest(SOCKS_VERSION, [SOCKS_AUTH_ANON]) data = await self._send(socks5_serializer.pack_serializable(request)) response, _ = socks5_serializer.unpack_serializable( MethodsResponse, data) if response.version != SOCKS_VERSION or response.method != SOCKS_AUTH_ANON: raise Socks5Error('Unsupported proxy server')
def test_encode_decode_udp_packet(): rsv = 0 frag = 0 address = DomainAddress('tracker1.good-tracker.com', 8084) data = b'0x000' encoded = socks5_serializer.pack_serializable( UdpPacket(rsv, frag, address, data)) decoded, _ = socks5_serializer.unpack_serializable(UdpPacket, encoded) assert rsv == decoded.rsv assert frag == decoded.frag assert address == decoded.destination address = DomainAddress('tracker1.unicode-tracker\xc4\xe95\x11$\x00', 8084) encoded = socks5_serializer.pack_serializable( UdpPacket(rsv, frag, address, data)) decoded, _ = socks5_serializer.unpack_serializable(UdpPacket, encoded) assert rsv == decoded.rsv assert frag == decoded.frag assert address == decoded.destination
def test_encode_decode_command_request(): rsv = 0 address = DomainAddress('tracker1.good-tracker.com', 8084) rep = 0 version = 5 encoded = socks5_serializer.pack_serializable( CommandRequest(version, rep, rsv, address)) decoded, _ = socks5_serializer.unpack_serializable(CommandRequest, encoded) assert version == decoded.version assert rsv == decoded.rsv assert address == decoded.destination address = DomainAddress('tracker1.unicode-tracker\xc4\xe95\x11$\x00', 8084) encoded = socks5_serializer.pack_serializable( CommandResponse(version, rep, rsv, address)) decoded, _ = socks5_serializer.unpack_serializable(CommandResponse, encoded) assert version == decoded.version assert rsv == decoded.rsv assert address == decoded.bind
async def _connect_tcp(self, target_addr): try: socket.inet_aton(target_addr[0]) except (ValueError, OSError): target_addr = DomainAddress(*target_addr) request = CommandRequest(SOCKS_VERSION, REQ_CMD_CONNECT, 0, target_addr) data = await self._send(socks5_serializer.pack_serializable(request)) response, _ = socks5_serializer.unpack_serializable( CommandResponse, data) if response.version != SOCKS_VERSION: raise Socks5Error('Unsupported proxy server') if response.reply > 0: raise Socks5Error('TCP connect failed') self.connected_to = target_addr
def test_decode_command_request_fail(): badly_encoded = b'\x05\x00\x00\x03 tracker1.invalid-tracker\xc4\xe95\x11$\x00\x1f\x94' with pytest.raises(PackError): socks5_serializer.unpack_serializable(CommandRequest, badly_encoded) with pytest.raises(PackError): # Invalid address type socks5_serializer.unpack_serializable(CommandRequest, struct.pack("!BBBB", 5, 0, 0, 5)) with pytest.raises(PackError): # IPv6 socks5_serializer.unpack_serializable(CommandRequest, struct.pack("!BBBB", 5, 0, 0, 4))
async def _associate_udp(self, local_addr=None): local_addr = local_addr or ('127.0.0.1', 0) connection = Socks5ClientUDPConnection(self.callback) transport, _ = await get_event_loop().create_datagram_endpoint( lambda: connection, local_addr=local_addr) sock = transport.get_extra_info("socket") request = CommandRequest(SOCKS_VERSION, REQ_CMD_UDP_ASSOCIATE, 0, sock.getsockname()) data = await self._send(socks5_serializer.pack_serializable(request)) response, _ = socks5_serializer.unpack_serializable( CommandResponse, data) connection.proxy_udp_addr = response.bind if response.version != SOCKS_VERSION: raise Socks5Error('Unsupported proxy server') if response.reply > 0: raise Socks5Error('UDP associate failed') self.connection = connection
def datagram_received(self, data, source): # If remote_address was not set before, use first one if self.remote_udp_address is None: self.remote_udp_address = source if self.remote_udp_address == source: try: request, _ = socks5_serializer.unpack_serializable( UdpPacket, data) except PackError: self._logger.warning("Cannot serialize UDP packet") return False if request.frag == 0 and request.destination: return self.socksconnection.socksserver.output_stream.on_socks5_udp_data( self, request) self._logger.debug( "No support for fragmented data or without destination host, dropping" ) else: self._logger.debug("Ignoring data from %s:%d, is not %s:%d", *source, *self.remote_udp_address) return False
def test_decode_udp_packet_fail(): # try decoding badly encoded udp packet, should raise an exception in Python3 badly_encoded_packet = b'\x00\x00\x00\x03 tracker1.invalid-tracker\xc4\xe95\x11$\x00\x1f\x940x000' with pytest.raises(PackError): socks5_serializer.unpack_serializable(UdpPacket, badly_encoded_packet)
def datagram_received(self, data, _): request, _ = socks5_serializer.unpack_serializable(UdpPacket, data) self.callback(request.data, request.destination)