Esempio n. 1
0
    def rawDataReceived(self, data):
        # First 2 bytes are the packet size.
        #
        # Third byte is the command byte.
        # According to Openspy-Core:
        #   0x00 - Server list request
        #   0x01 - Server info request
        #   0x02 - Send message request
        #   0x03 - Keep alive reply
        #   0x04 - Map loop request (?)
        #   0x05 - Player search request
        #
        # For Tetris DS, at the very least 0x00 and 0x02 need to be implemented.
        if self.forward_to_client:
            if self.forward_packet == None:
                self.forward_packet = data
            else:
                self.forward_packet += data

            if self.header_length + len(self.forward_packet) >= self.expected_packet_length:
                # Is it possible that multiple packets will need to be waited for?
                # Is it possible that more data will be in the last packet than expected?
                self.forward_data_to_client(self.forward_packet, self.forward_client)

                self.forward_to_client = False
                self.forward_client = ()
                self.header_length = 0
                self.expected_packet_length = 0
                self.forward_packet = None
            return

        if data[2] == '\x00': # Server list request
            self.log(logging.DEBUG, "Received server list request from %s:%s..." % (self.address.host, self.address.port))

            # This code is so... not python. The C programmer in me is coming out strong.
            # TODO: Rewrite this section later?
            idx = 3
            list_version = ord(data[idx])
            idx += 1
            encoding_version = ord(data[idx])
            idx += 1
            game_version = utils.get_int(data, idx)
            idx += 4

            query_game = utils.get_string(data, idx)
            idx += len(query_game) + 1
            game_name = utils.get_string(data, idx)
            idx += len(game_name) + 1

            challenge = data[idx:idx+8]
            idx += 8

            filter = utils.get_string(data, idx)
            idx += len(filter) + 1
            fields = utils.get_string(data, idx)
            idx += len(fields) + 1

            options = utils.get_int_be(data, idx)
            idx += 4

            source_ip = 0
            max_servers = 0

            NO_SERVER_LIST = 0x02
            ALTERNATE_SOURCE_IP = 0x08
            LIMIT_RESULT_COUNT = 0x80

            send_ip = False
            if (options & LIMIT_RESULT_COUNT):
                max_servers = utils.get_int(data, idx)
            elif (options & ALTERNATE_SOURCE_IP):
                source_ip = utils.get_int(data, idx)
            elif (options & NO_SERVER_LIST):
                send_ip = True

            if '\\' in fields:
                fields = [x for x in fields.split('\\') if x and not x.isspace()]

            #print "%02x %02x %08x" % (list_version, encoding_version, game_version)
            #print "%s" % query_game
            #print "%s" % game_name
            #print "%s" % challenge
            #print "%s" % filter
            #print "%s" % fields

            #print "%08x" % options
            #print "%d %08x" % (max_servers, source_ip)

            self.log(logging.DEBUG, "list version: %02x / encoding version: %02x / game version: %08x / query game: %s / game name: %s / challenge: %s / filter: %s / fields: %s / options: %08x / max servers: %d / source ip: %08x" % (list_version, encoding_version, game_version, query_game, game_name, challenge, filter, fields, options, max_servers, source_ip))

            # Requesting ip and port of client, not server
            if filter == "" or fields == "" or send_ip == True:
                output = bytearray([int(x) for x in self.address.host.split('.')])
                output += utils.get_bytes_from_short_be(6500) # Does this ever change?

                enc = gs_utils.EncTypeX()
                output_enc = enc.encrypt(self.secret_key_list[game_name], challenge, output)

                self.transport.write(bytes(output_enc))

                self.log(logging.DEBUG, "Responding with own IP and game port...")
                self.log(logging.DEBUG, utils.pretty_print_hex(output))
            else:
                self.find_server(query_game, filter, fields, max_servers, game_name, challenge)



        elif data[2] == '\x02': # Send message request
            packet_len = utils.get_short_be(data, 0)
            dest_addr = '.'.join(["%d" % ord(x) for x in data[3:7]])
            dest_port = utils.get_short_be(data, 7) # What's the pythonic way to do this? unpack?
            dest = (dest_addr, dest_port)

            self.log(logging.DEBUG, "Received send message request from %s:%s to %s:%d... expecting %d byte packet." % (self.address.host, self.address.port, dest_addr, dest_port, packet_len))
            self.log(logging.DEBUG, utils.pretty_print_hex(bytearray(data)))

            if packet_len == len(data):
                # Contains entire packet, send immediately.
                self.forward_data_to_client(data[3:], dest)
                
                self.forward_to_client = False
                self.forward_client = ()
                self.header_length = 0
                self.expected_packet_length = 0
                self.forward_packet = None
            else:
                self.forward_to_client = True
                self.forward_client = dest
                self.header_length = len(data)
                self.expected_packet_length = packet_len

        elif data[2] == '\x03': # Keep alive reply
            self.log(logging.DEBUG, "Received keep alive from %s:%s..." % (self.address.host, self.address.port))

        else:
            self.log(logging.DEBUG, "Received unknown command (%02x) from %s:%s..." % (ord(data[2]), self.address.host, self.address.port))
            self.log(logging.DEBUG, utils.pretty_print_hex(bytearray(data)))
            self.log(logging.DEBUG, utils.pretty_print_hex(data))
    def handle_packet(self, recv_data, addr):
        logger.log(logging.DEBUG, "Connection from %s:%d..." % (addr[0], addr[1]))
        logger.log(logging.DEBUG, utils.pretty_print_hex(recv_data))

        # Make sure it's a legal packet
        if recv_data[0:6] != bytearray([0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2]):
            return

        session_id = struct.unpack("<I", recv_data[8:12])[0]
        session_id_raw = recv_data[8:12]

        # Handle commands
        if recv_data[7] == '\x00':
            logger.log(logging.DEBUG, "Received initialization from %s:%s..." % (addr[0], addr[1]))

            output = bytearray(recv_data[0:14])
            output += bytearray([0xff, 0xff, 0x6d, 0x16, 0xb5, 0x7d, 0xea ]) # Checked with Tetris DS, Mario Kart DS, and Metroid Prime Hunters, and this seems to be the standard response to 0x00
            output[7] = 0x01 # Initialization response
            self.write_queue.put((output, addr))

            # Try to connect to the server
            gameid = utils.get_string(recv_data, 0x15)
            client_id = "%02x" % ord(recv_data[13])

            localip_raw = recv_data[15:19]
            localip_int_le = utils.get_int(recv_data, 15)
            localip_int_be = utils.get_int_be(recv_data, 15)
            localip = '.'.join(["%d" % ord(x) for x in localip_raw])
            localport_raw = recv_data[19:21]
            localport = utils.get_short_be(localport_raw, 0)
            localaddr = (localip, localport, localip_int_le, localip_int_be)

            if session_id not in self.session_list:
                self.session_list[session_id] = {}
            if client_id not in self.session_list[session_id]:
                self.session_list[session_id][client_id] = { 'connected': False, 'addr': '', 'localaddr': None, 'serveraddr': None, 'gameid': None }

            self.session_list[session_id][client_id]['gameid'] = gameid
            self.session_list[session_id][client_id]['addr'] = addr
            self.session_list[session_id][client_id]['localaddr'] = localaddr
            clients = len(self.session_list[session_id])

            for client in self.session_list[session_id]:
                if self.session_list[session_id][client]['connected'] == False: # and self.session_list[session_id][client]['localaddr'][1] != 0:
                    if client == client_id:
                        continue

                    #if self.session_list[session_id][client]['serveraddr'] == None:
                    serveraddr = self.get_server_info(gameid, session_id, client)
                    if serveraddr == None:
                        serveraddr = self.get_server_info_alt(gameid, session_id, client)

                    self.session_list[session_id][client]['serveraddr'] = serveraddr
                    logger.log(logging.DEBUG, "Found server from local ip/port: %s from %d" % (serveraddr, session_id))

                    publicport = self.session_list[session_id][client]['addr'][1]
                    if self.session_list[session_id][client]['localaddr'][1] != 0:
                        publicport = self.session_list[session_id][client]['localaddr'][1]

                    if self.session_list[session_id][client]['serveraddr'] != None:
                        publicport = int(self.session_list[session_id][client]['serveraddr']['publicport'])

                    # Send to requesting client
                    output = bytearray(recv_data[0:12])
                    output += bytearray([int(x) for x in self.session_list[session_id][client]['addr'][0].split('.')])
                    output += utils.get_bytes_from_short_be(publicport)

                    output += bytearray([0x42, 0x00]) # Unknown, always seems to be \x42\x00
                    output[7] = 0x05
                    #self.write_queue.put((output, (self.session_list[session_id][client_id]['addr'])))
                    self.write_queue.put((output, (self.session_list[session_id][client_id]['addr'][0], self.session_list[session_id][client_id]['addr'][1])))

                    logger.log(logging.DEBUG, "Sent connection request to %s:%d..." % (self.session_list[session_id][client_id]['addr'][0], self.session_list[session_id][client_id]['addr'][1]))
                    logger.log(logging.DEBUG, utils.pretty_print_hex(output))

                    # Send to other client
                    #if self.session_list[session_id][client_id]['serveraddr'] == None:
                    serveraddr = self.get_server_info(gameid, session_id, client_id)
                    if serveraddr == None:
                        serveraddr = self.get_server_info_alt(gameid, session_id, client)

                    self.session_list[session_id][client_id]['serveraddr'] = serveraddr
                    logger.log(logging.DEBUG, "Found server 2 from local ip/port: %s from %d" % (serveraddr, session_id))

                    publicport = self.session_list[session_id][client_id]['addr'][1]
                    if self.session_list[session_id][client_id]['localaddr'][1] != 0:
                        publicport = self.session_list[session_id][client_id]['localaddr'][1]

                    if self.session_list[session_id][client_id]['serveraddr'] != None:
                        publicport = int(self.session_list[session_id][client_id]['serveraddr']['publicport'])

                    output = bytearray(recv_data[0:12])
                    output += bytearray([int(x) for x in self.session_list[session_id][client_id]['addr'][0].split('.')])
                    output += utils.get_bytes_from_short_be(publicport)

                    output += bytearray([0x42, 0x00]) # Unknown, always seems to be \x42\x00
                    output[7] = 0x05
                    #self.write_queue.put((output, (self.session_list[session_id][client]['addr'])))
                    self.write_queue.put((output, (self.session_list[session_id][client]['addr'][0], self.session_list[session_id][client]['addr'][1])))

                    logger.log(logging.DEBUG, "Sent connection request to %s:%d..." % (self.session_list[session_id][client]['addr'][0], self.session_list[session_id][client]['addr'][1]))
                    logger.log(logging.DEBUG, utils.pretty_print_hex(output))

        elif recv_data[7] == '\x06': # Was able to connect
            client_id = "%02x" % ord(recv_data[13])
            logger.log(logging.DEBUG, "Received connected command from %s:%s..." % (addr[0], addr[1]))

            if session_id not in self.session_list:
                return
            if client_id not in self.session_list[session_id]:
                return

            self.session_list[session_id][client_id]['connected'] = True

        elif recv_data[7] == '\x0a': # Address check. Note: UNTESTED!
            client_id = "%02x" % ord(recv_data[13])
            logger.log(logging.DEBUG, "Received address check command from %s:%s..." % (addr[0], addr[1]))

            output = bytearray(recv_data[0:15])
            output += bytearray([int(x) for x in addr[0].split('.')])
            output += utils.get_bytes_from_short_be(addr[1])
            output += bytearray(recv_data[len(output):])

            output[7] = 0x0b
            self.write_queue.put((output, addr))

            logger.log(logging.DEBUG, "Sent address check response to %s:%d..." % (addr[0], addr[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(output))

        elif recv_data[7] == '\x0c': # Natify
            port_type = "%02x" % ord(recv_data[12])
            logger.log(logging.DEBUG, "Received natify command from %s:%s..." % (addr[0], addr[1]))

            output = bytearray(recv_data)
            output[7] = 0x02 # ERT Test
            self.write_queue.put((output, addr))

            logger.log(logging.DEBUG, "Sent natify response to %s:%d..." % (addr[0], addr[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(output))

        elif recv_data[7] == '\x0d':
            client_id = "%02x" % ord(recv_data[13])
            logger.log(logging.DEBUG, "Received report command from %s:%s..." % (addr[0], addr[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(recv_data))

            output = bytearray(recv_data)
            output[7] = 0x0e # Report response
            self.write_queue.put((recv_data, addr))

        else: # Was able to connect
            logger.log(logging.DEBUG, "Received unknown command %02x from %s:%s..." % (ord(recv_data[7]), addr[0], addr[1]))
    def handle_packet(self, recv_data, addr):
        logger.log(logging.DEBUG, "Connection from %s:%d..." % (addr[0], addr[1]))
        logger.log(logging.DEBUG, utils.pretty_print_hex(recv_data))

        # Make sure it's a legal packet
        if recv_data[0:6] != bytearray([0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2]):
            return

        session_id = struct.unpack("<I", recv_data[8:12])[0]
        session_id_raw = recv_data[8:12]

        # Handle commands
        if recv_data[7] == '\x00':
            logger.log(logging.DEBUG, "Received initialization from %s:%s..." % (addr[0], addr[1]))

            output = bytearray(recv_data[0:14])
            output += bytearray([0xff, 0xff, 0x6d, 0x16, 0xb5, 0x7d, 0xea ]) # Checked with Tetris DS, Mario Kart DS, and Metroid Prime Hunters, and this seems to be the standard response to 0x00
            output[7] = 0x01 # Initialization response
            self.write_queue.put((output, addr))

            # Try to connect to the server
            gameid = utils.get_string(recv_data, 0x15)
            client_id = "%02x" % ord(recv_data[13])

            localip_raw = recv_data[15:19]
            localip_int_le = utils.get_int(recv_data, 15)
            localip_int_be = utils.get_int_be(recv_data, 15)
            localip = '.'.join(["%d" % ord(x) for x in localip_raw])
            localport_raw = recv_data[19:21]
            localport = utils.get_short_be(localport_raw, 0)
            localaddr = (localip, localport, localip_int_le, localip_int_be)

            if session_id not in self.session_list:
                self.session_list[session_id] = {}
            if client_id not in self.session_list[session_id]:
                self.session_list[session_id][client_id] = { 'connected': False, 'addr': '', 'localaddr': None, 'serveraddr': None, 'gameid': None }

            self.session_list[session_id][client_id]['gameid'] = gameid
            self.session_list[session_id][client_id]['addr'] = addr
            self.session_list[session_id][client_id]['localaddr'] = localaddr
            clients = len(self.session_list[session_id])

            for client in self.session_list[session_id]:
                if self.session_list[session_id][client]['connected'] == False: # and self.session_list[session_id][client]['localaddr'][1] != 0:
                    if client == client_id:
                        continue

                    #if self.session_list[session_id][client]['serveraddr'] == None:
                    serveraddr = self.get_server_info(gameid, session_id, client)
                    if serveraddr == None:
                        serveraddr = self.get_server_info_alt(gameid, session_id, client)

                    self.session_list[session_id][client]['serveraddr'] = serveraddr
                    logger.log(logging.DEBUG, "Found server from local ip/port: %s from %d" % (serveraddr, session_id))

                    publicport = self.session_list[session_id][client]['addr'][1]
                    if self.session_list[session_id][client]['localaddr'][1] != 0:
                        publicport = self.session_list[session_id][client]['localaddr'][1]

                    if self.session_list[session_id][client]['serveraddr'] != None:
                        publicport = int(self.session_list[session_id][client]['serveraddr']['publicport'])

                    # Send to requesting client
                    output = bytearray(recv_data[0:12])
                    output += bytearray([int(x) for x in self.session_list[session_id][client]['addr'][0].split('.')])
                    output += utils.get_bytes_from_short_be(publicport)

                    output += bytearray([0x42, 0x00]) # Unknown, always seems to be \x42\x00
                    output[7] = 0x05
                    #self.write_queue.put((output, (self.session_list[session_id][client_id]['addr'])))
                    self.write_queue.put((output, (self.session_list[session_id][client_id]['addr'][0], self.session_list[session_id][client_id]['addr'][1])))

                    logger.log(logging.DEBUG, "Sent connection request to %s:%d..." % (self.session_list[session_id][client_id]['addr'][0], self.session_list[session_id][client_id]['addr'][1]))
                    logger.log(logging.DEBUG, utils.pretty_print_hex(output))

                    # Send to other client
                    #if self.session_list[session_id][client_id]['serveraddr'] == None:
                    serveraddr = self.get_server_info(gameid, session_id, client_id)
                    if serveraddr == None:
                        serveraddr = self.get_server_info_alt(gameid, session_id, client)

                    self.session_list[session_id][client_id]['serveraddr'] = serveraddr
                    logger.log(logging.DEBUG, "Found server 2 from local ip/port: %s from %d" % (serveraddr, session_id))

                    publicport = self.session_list[session_id][client_id]['addr'][1]
                    if self.session_list[session_id][client_id]['localaddr'][1] != 0:
                        publicport = self.session_list[session_id][client_id]['localaddr'][1]

                    if self.session_list[session_id][client_id]['serveraddr'] != None:
                        publicport = int(self.session_list[session_id][client_id]['serveraddr']['publicport'])

                    output = bytearray(recv_data[0:12])
                    output += bytearray([int(x) for x in self.session_list[session_id][client_id]['addr'][0].split('.')])
                    output += utils.get_bytes_from_short_be(publicport)

                    output += bytearray([0x42, 0x00]) # Unknown, always seems to be \x42\x00
                    output[7] = 0x05
                    #self.write_queue.put((output, (self.session_list[session_id][client]['addr'])))
                    self.write_queue.put((output, (self.session_list[session_id][client]['addr'][0], self.session_list[session_id][client]['addr'][1])))

                    logger.log(logging.DEBUG, "Sent connection request to %s:%d..." % (self.session_list[session_id][client]['addr'][0], self.session_list[session_id][client]['addr'][1]))
                    logger.log(logging.DEBUG, utils.pretty_print_hex(output))

        elif recv_data[7] == '\x06': # Was able to connect
            client_id = "%02x" % ord(recv_data[13])
            logger.log(logging.DEBUG, "Received connected command from %s:%s..." % (addr[0], addr[1]))

            if session_id not in self.session_list:
                return
            if client_id not in self.session_list[session_id]:
                return

            self.session_list[session_id][client_id]['connected'] = True

        elif recv_data[7] == '\x0a': # Address check. Note: UNTESTED!
            client_id = "%02x" % ord(recv_data[13])
            logger.log(logging.DEBUG, "Received address check command from %s:%s..." % (addr[0], addr[1]))

            output = bytearray(recv_data[0:15])
            output += bytearray([int(x) for x in addr[0].split('.')])
            output += utils.get_bytes_from_short_be(addr[1])
            output += bytearray(recv_data[len(output):])

            output[7] = 0x0b
            self.write_queue.put((output, addr))

            logger.log(logging.DEBUG, "Sent address check response to %s:%d..." % (addr[0], addr[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(output))

        elif recv_data[7] == '\x0c': # Natify
            port_type = "%02x" % ord(recv_data[12])
            logger.log(logging.DEBUG, "Received natify command from %s:%s..." % (addr[0], addr[1]))

            output = bytearray(recv_data)
            output[7] = 0x02 # ERT Test
            self.write_queue.put((output, addr))

            logger.log(logging.DEBUG, "Sent natify response to %s:%d..." % (addr[0], addr[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(output))

        elif recv_data[7] == '\x0d': # Report
            logger.log(logging.DEBUG, "Received report command from %s:%s..." % (addr[0], addr[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(recv_data))

            # Report response
            output = bytearray(recv_data[:21])
            output[7] = 0x0e # Report response
            output[14] = 0 # Clear byte to match real server's response
            self.write_queue.put((output, addr))

        elif recv_data[7] == '\x0f':
            # Natneg v4 command thanks to Pipian.
            # Only seems to be used in very few DS games (namely, Pokemon Black/White/Black 2/White 2).
            logger.log(logging.DEBUG, "Received pre-init command from %s:%s..." % (addr[0], addr[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(recv_data))

            session = utils.get_int(recv_data[-4:],0)

            # Report response
            output = bytearray(recv_data[:-4]) + bytearray([0, 0, 0, 0])
            output[7] = 0x10 # Pre-init response

            if session == 0:
                # What's the correct behavior when session == 0?
                output[13] = 2
            elif session in self.natneg_preinit_session:
                # Should this be sent to both clients or just the one that connected most recently?
                # I can't tell from a one sided packet capture of Pokemon.
                # For the time being, send to both clients just in case.
                output[13] = 2
                self.write_queue.put((output, self.natneg_preinit_session[session]))

                output[12] = (1, 0)[output[12]] # Swap the index
                del self.natneg_preinit_session[session]
            else:
                output[13] = 0
                self.natneg_preinit_session[session] = addr

            self.write_queue.put((output, addr))

        else: # Was able to connect
            logger.log(logging.DEBUG, "Received unknown command %02x from %s:%s..." % (ord(recv_data[7]), addr[0], addr[1]))
    def start(self):
        # Start natneg server
        address = (
            '0.0.0.0', 27901
        )  # accessible to outside connections (use this if you don't know what you're doing)

        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.bind(address)

        logger.log(
            logging.INFO,
            "Server is now listening on %s:%s..." % (address[0], address[1]))

        while 1:
            recv_data, addr = s.recvfrom(2048)

            logger.log(logging.DEBUG,
                       "Connection from %s:%d..." % (addr[0], addr[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(recv_data))

            # Make sure it's a legal packet
            if recv_data[0:6] != bytearray(
                [0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2]):
                continue

            session_id = struct.unpack("<I", recv_data[8:12])[0]
            session_id_raw = recv_data[8:12]

            # Handle commands
            if recv_data[7] == '\x00':
                logger.log(
                    logging.DEBUG, "Received initialization from %s:%s..." %
                    (addr[0], addr[1]))

                output = bytearray(recv_data[0:14])
                output += bytearray(
                    [0xff, 0xff, 0x6d, 0x16, 0xb5, 0x7d, 0xea]
                )  # Checked with Tetris DS, Mario Kart DS, and Metroid Prime Hunters, and this seems to be the standard response to 0x00
                output[7] = 0x01  # Initialization response
                s.sendto(output, addr)

                # Try to connect to the server
                gameid = utils.get_string(recv_data, 0x16)
                client_id = "%02x" % ord(recv_data[13])

                localip_raw = recv_data[15:19]
                localip = '.'.join(["%d" % ord(x) for x in localip_raw])
                localport_raw = recv_data[19:21]
                localport = utils.get_short_be(localport_raw, 0)
                localaddr = (localip, localport)

                if gameid not in self.session_list:
                    self.session_list[gameid] = {}
                if session_id not in self.session_list[gameid]:
                    self.session_list[gameid][session_id] = {}
                if client_id not in self.session_list[gameid][session_id]:
                    self.session_list[gameid][session_id][client_id] = {
                        'connected': False,
                        'addr': '',
                        'localaddr': None,
                        'serveraddr': None,
                        'gameid': None
                    }

                self.session_list[gameid][session_id][client_id][
                    'gameid'] = utils.get_string(recv_data[21:], 0)
                self.session_list[gameid][session_id][client_id]['addr'] = addr
                self.session_list[gameid][session_id][client_id][
                    'localaddr'] = localaddr
                clients = len(self.session_list[gameid][session_id])

                for client in self.session_list[gameid][session_id]:
                    if self.session_list[gameid][session_id][client][
                            'connected'] == False and self.session_list[gameid][
                                session_id][client]['localaddr'][1] != 0:
                        if client == client_id:
                            continue

                        if self.session_list[gameid][session_id][client][
                                'serveraddr'] == None:
                            serveraddr = self.get_server_info(
                                gameid, session_id, client)
                            self.session_list[gameid][session_id][client][
                                'serveraddr'] = serveraddr
                            logger.log(
                                logging.DEBUG,
                                "Found server from local ip/port: %s" %
                                serveraddr)

                        publicport = 0
                        if self.session_list[gameid][session_id][client][
                                'serveraddr'] != None:
                            publicport = int(
                                self.session_list[gameid][session_id][client]
                                ['serveraddr']['publicport'])

                        # Send to requesting client
                        output = bytearray(recv_data[0:12])
                        output += bytearray([
                            int(x) for x in self.session_list[gameid]
                            [session_id][client]['addr'][0].split('.')
                        ])
                        output += utils.get_bytes_from_short_be(publicport)

                        output += bytearray(
                            [0x42,
                             0x00])  # Unknown, always seems to be \x42\x00
                        output[7] = 0x05
                        #s.sendto(output, (self.session_list[gameid][session_id][client_id]['addr']))
                        s.sendto(
                            output,
                            (self.session_list[gameid][session_id][client_id]
                             ['addr'][0], self.session_list[gameid][session_id]
                             [client_id]['addr'][1]))

                        logger.log(
                            logging.DEBUG,
                            "Sent connection request to %s:%d..." %
                            (self.session_list[gameid][session_id][client_id]
                             ['addr'][0], self.session_list[gameid][session_id]
                             [client_id]['addr'][1]))
                        logger.log(logging.DEBUG,
                                   utils.pretty_print_hex(output))

                        # Send to other client
                        if self.session_list[gameid][session_id][client_id][
                                'serveraddr'] == None:
                            serveraddr = self.get_server_info(
                                gameid, session_id, client_id)
                            self.session_list[gameid][session_id][client_id][
                                'serveraddr'] = serveraddr
                            logger.log(
                                logging.DEBUG,
                                "Found server 2 from local ip/port: %s" %
                                serveraddr)

                        publicport = 0
                        if self.session_list[gameid][session_id][client_id][
                                'serveraddr'] != None:
                            publicport = int(
                                self.session_list[gameid][session_id]
                                [client_id]['serveraddr']['publicport'])

                        output = bytearray(recv_data[0:12])
                        output += bytearray([
                            int(x) for x in self.session_list[gameid]
                            [session_id][client_id]['addr'][0].split('.')
                        ])
                        output += utils.get_bytes_from_short_be(publicport)

                        output += bytearray(
                            [0x42,
                             0x00])  # Unknown, always seems to be \x42\x00
                        output[7] = 0x05
                        #s.sendto(output, (self.session_list[gameid][session_id][client]['addr']))
                        s.sendto(output,
                                 (self.session_list[gameid][session_id][client]
                                  ['addr'][0], self.session_list[gameid]
                                  [session_id][client]['addr'][1]))

                        logger.log(
                            logging.DEBUG,
                            "Sent connection request to %s:%d..." %
                            (self.session_list[gameid][session_id][client]
                             ['addr'][0], self.session_list[gameid][session_id]
                             [client]['addr'][1]))
                        logger.log(logging.DEBUG,
                                   utils.pretty_print_hex(output))

            elif recv_data[7] == '\x06':  # Was able to connect
                client_id = "%02x" % ord(recv_data[13])
                logger.log(
                    logging.DEBUG, "Received connected command from %s:%s..." %
                    (addr[0], addr[1]))

                if gameid not in self.session_list:
                    pass
                if session_id not in self.session_list[gameid]:
                    pass
                if client_id not in self.session_list[gameid][session_id]:
                    pass

                self.session_list[gameid][session_id][client_id][
                    'connected'] = True

            elif recv_data[7] == '\x0a':  # Address check. Note: UNTESTED!
                client_id = "%02x" % ord(recv_data[13])
                logger.log(
                    logging.DEBUG,
                    "Received address check command from %s:%s..." %
                    (addr[0], addr[1]))

                output = bytearray(recv_data[0:15])
                output += bytearray([int(x) for x in addr[0].split('.')])
                output += utils.get_bytes_from_short_be(addr[1])
                output += bytearray(recv_data[len(output):])

                output[7] = 0x0b
                s.sendto(output, addr)

                logger.log(
                    logging.DEBUG, "Sent address check response to %s:%d..." %
                    (self.session_list[gameid][session_id][client]['addr'][0],
                     self.session_list[gameid][session_id][client]['addr'][1]))
                logger.log(logging.DEBUG, utils.pretty_print_hex(output))

            elif recv_data[7] == '\x0d':
                client_id = "%02x" % ord(recv_data[13])
                logger.log(
                    logging.DEBUG, "Received report command from %s:%s..." %
                    (addr[0], addr[1]))
                logger.log(logging.DEBUG, utils.pretty_print_hex(recv_data))

                output = bytearray(recv_data)
                output[7] = 0x0e  # Report response
                s.sendto(recv_data, addr)

            else:  # Was able to connect
                logger.log(
                    logging.DEBUG,
                    "Received unknown command %02x from %s:%s..." %
                    (ord(recv_data[7]), addr[0], addr[1]))
    def rawDataReceived(self, data):
        # First 2 bytes are the packet size.
        #
        # Third byte is the command byte.
        # According to Openspy-Core:
        #   0x00 - Server list request
        #   0x01 - Server info request
        #   0x02 - Send message request
        #   0x03 - Keep alive reply
        #   0x04 - Map loop request (?)
        #   0x05 - Player search request
        #
        # For Tetris DS, at the very least 0x00 and 0x02 need to be implemented.

        if self.forward_to_client:
            self.forward_to_client = False

            # Find session id of server
            # Iterate through the list of servers sent to the client and match by IP and port.
            # Is there a better way to determine this information?
            if self.console != 0:
                ip = str(ctypes.c_int32(utils.get_int_be(bytearray([int(x) for x in self.forward_client[0].split('.')]), 0)).value) # Wii
            else:
                ip = str(ctypes.c_int32(utils.get_int(bytearray([int(x) for x in self.forward_client[0].split('.')]), 0)).value) # DS

            logger.log(logging.DEBUG, "Trying to send message to %s:%d..." % (self.forward_client[0], self.forward_client[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(bytearray(data)))

            # Get server based on ip/port
            server = self.server_manager.find_server_by_address(ip, self.forward_client[1])._getvalue()
            logger.log(logging.DEBUG, "find_server_by_address returned: %s" % server)

            if server == None:
                pass

            logger.log(logging.DEBUG, "%s %s" % (ip, server['publicip']))
            if server['publicip'] == ip and server['publicport'] == str(self.forward_client[1]):
                # Send command to server to get it to connect to natneg
                natneg_session = int(utils.generate_random_hex_str(8), 16) # Quick and lazy way to get a random 32bit integer. Replace with something else late.r

                output = bytearray([0xfe, 0xfd, 0x06])
                output += utils.get_bytes_from_int(server['__session__'])
                output += bytearray(utils.get_bytes_from_int(natneg_session))
                output += bytearray(data)

                client_s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                client_s.sendto(output, self.forward_client)
                logger.log(logging.DEBUG, "Forwarded data to %s:%s..." % (self.forward_client[0], self.forward_client[1]))
            return

        if data[2] == '\x00': # Server list request
            logger.log(logging.DEBUG, "Received server list request from %s:%s..." % (self.address.host, self.address.port))

            # This code is so... not python. The C programmer in me is coming out strong.
            # TODO: Rewrite this section later?
            idx = 3
            list_version = ord(data[idx])
            idx += 1
            encoding_version = ord(data[idx])
            idx += 1
            game_version = utils.get_int(data, idx)
            idx += 4

            query_game = utils.get_string(data, idx)
            idx += len(query_game) + 1
            game_name = utils.get_string(data, idx)
            idx += len(game_name) + 1

            challenge = data[idx:idx+8]
            idx += 8

            filter = utils.get_string(data, idx)
            idx += len(filter) + 1
            fields = utils.get_string(data, idx)
            idx += len(fields) + 1

            options = utils.get_int_be(data, idx)
            idx += 4

            source_ip = 0
            max_servers = 0

            ALTERNATE_SOURCE_IP = 0x08
            LIMIT_RESULT_COUNT = 0x80
            if (options & LIMIT_RESULT_COUNT):
                max_servers = utils.get_int(data, idx)
            elif (options & ALTERNATE_SOURCE_IP):
                source_ip = utils.get_int(data, idx)

            if '\\' in fields:
                fields = [x for x in fields.split('\\') if x and not x.isspace()]

            #print "%02x %02x %08x" % (list_version, encoding_version, game_version)
            #print "%s" % query_game
            #print "%s" % game_name
            #print "%s" % challenge
            #print "%s" % filter
            #print "%s" % fields

            #print "%08x" % options
            #print "%d %08x" % (max_servers, source_ip)

            logger.log(logging.DEBUG, "list version: %02x / encoding version: %02x / game version: %08x / query game: %s / game name: %s / challenge: %s / filter: %s / fields: %s / options: %08x / max servers: %d / source ip: %08x" % (list_version, encoding_version, game_version, query_game, game_name, challenge, filter, fields, options, max_servers, source_ip))

            # Requesting ip and port of client, not server
            if filter == "" or fields == "":
                output = bytearray([int(x) for x in self.address.host.split('.')])
                output += utils.get_bytes_from_short_be(self.address.port)
                self.transport.write(bytes(output))
                logger.log(logging.DEBUG, "Responding with own IP and port...")
                logger.log(logging.DEBUG, utils.pretty_print_hex(output))
            else:
                self.find_server(query_game, filter, fields, max_servers, game_name, challenge)



        elif data[2] == '\x02': # Send message request
            dest_addr = '.'.join(["%d" % ord(x) for x in data[3:7]])
            dest_port = utils.get_short_be(data, 7) # What's the pythonic way to do this? unpack?
            dest = (dest_addr, dest_port)

            logger.log(logging.DEBUG, "Received send message request from %s:%s to %s:%d..." % (self.address.host, self.address.port, dest_addr, dest_port))
            logger.log(logging.DEBUG, utils.pretty_print_hex(bytearray(data)))

            self.forward_to_client = True
            self.forward_client = dest

        elif data[2] == '\x03': # Keep alive reply
            logger.log(logging.DEBUG, "Received keep alive from %s:%s..." % (self.address.host, self.address.port))

        else:
            logger.log(logging.DEBUG, "Received unknown command (%02x) from %s:%s..." % (ord(data[2]), self.address.host, self.address.port))
            logger.log(logging.DEBUG, utils.pretty_print_hex(bytearray(data)))
            logger.log(logging.DEBUG, utils.pretty_print_hex(data))
    def start(self):
        # Start natneg server
        address = ('0.0.0.0', 27901)  # accessible to outside connections (use this if you don't know what you're doing)

        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.bind(address)

        logger.log(logging.INFO, "Server is now listening on %s:%s..." % (address[0], address[1]))

        while 1:
            recv_data, addr = s.recvfrom(2048)

            logger.log(logging.DEBUG, "Connection from %s:%d..." % (addr[0], addr[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(recv_data))

            # Make sure it's a legal packet
            if recv_data[0:6] != bytearray([0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2]):
                continue

            session_id = struct.unpack("<I", recv_data[8:12])[0]
            session_id_raw = recv_data[8:12]

            # Handle commands
            if recv_data[7] == '\x00':
                logger.log(logging.DEBUG, "Received initialization from %s:%s..." % (addr[0], addr[1]))

                output = bytearray(recv_data[0:14])
                output += bytearray([0xff, 0xff, 0x6d, 0x16, 0xb5, 0x7d, 0xea ]) # Checked with Tetris DS, Mario Kart DS, and Metroid Prime Hunters, and this seems to be the standard response to 0x00
                output[7] = 0x01 # Initialization response
                s.sendto(output, addr)

                # Try to connect to the server
                gameid = utils.get_string(recv_data, 0x16)
                client_id = "%02x" % ord(recv_data[13])

                localip_raw = recv_data[15:19]
                localip = '.'.join(["%d" % ord(x) for x in localip_raw])
                localport_raw = recv_data[19:21]
                localport = utils.get_short_be(localport_raw, 0)
                localaddr = (localip, localport)

                if gameid not in self.session_list:
                    self.session_list[gameid] = {}
                if session_id not in self.session_list[gameid]:
                    self.session_list[gameid][session_id] = {}
                if client_id not in self.session_list[gameid][session_id]:
                    self.session_list[gameid][session_id][client_id] = { 'connected': False, 'addr': '', 'localaddr': None, 'serveraddr': None, 'gameid': None }

                self.session_list[gameid][session_id][client_id]['gameid'] = utils.get_string(recv_data[21:], 0)
                self.session_list[gameid][session_id][client_id]['addr'] = addr
                self.session_list[gameid][session_id][client_id]['localaddr'] = localaddr
                clients = len(self.session_list[gameid][session_id])

                for client in self.session_list[gameid][session_id]:
                    if self.session_list[gameid][session_id][client]['connected'] == False and self.session_list[gameid][session_id][client]['localaddr'][1] != 0:
                        if client == client_id:
                            continue

                        if self.session_list[gameid][session_id][client]['serveraddr'] == None:
                            serveraddr = self.get_server_info(gameid, session_id, client)
                            self.session_list[gameid][session_id][client]['serveraddr'] = serveraddr
                            logger.log(logging.DEBUG, "Found server from local ip/port: %s" % serveraddr)

                        publicport = self.session_list[gameid][session_id][client]['addr'][1]
                        if self.session_list[gameid][session_id][client]['localaddr'][1] != 0:
                            publicport = self.session_list[gameid][session_id][client]['localaddr'][1]

                        if self.session_list[gameid][session_id][client]['serveraddr'] != None:
                            publicport = int(self.session_list[gameid][session_id][client]['serveraddr']['publicport'])

                        # Send to requesting client
                        output = bytearray(recv_data[0:12])
                        output += bytearray([int(x) for x in self.session_list[gameid][session_id][client]['addr'][0].split('.')])
                        output += utils.get_bytes_from_short_be(publicport)

                        output += bytearray([0x42, 0x00]) # Unknown, always seems to be \x42\x00
                        output[7] = 0x05
                        #s.sendto(output, (self.session_list[gameid][session_id][client_id]['addr']))
                        s.sendto(output, (self.session_list[gameid][session_id][client_id]['addr'][0], self.session_list[gameid][session_id][client_id]['addr'][1]))

                        logger.log(logging.DEBUG, "Sent connection request to %s:%d..." % (self.session_list[gameid][session_id][client_id]['addr'][0], self.session_list[gameid][session_id][client_id]['addr'][1]))
                        logger.log(logging.DEBUG, utils.pretty_print_hex(output))

                        # Send to other client
                        if self.session_list[gameid][session_id][client_id]['serveraddr'] == None:
                            serveraddr = self.get_server_info(gameid, session_id, client_id)
                            self.session_list[gameid][session_id][client_id]['serveraddr'] = serveraddr
                            logger.log(logging.DEBUG, "Found server 2 from local ip/port: %s" % serveraddr)

                        publicport = self.session_list[gameid][session_id][client_id]['addr'][1]
                        if self.session_list[gameid][session_id][client_id]['localaddr'][1] != 0:
                            publicport = self.session_list[gameid][session_id][client_id]['localaddr'][1]

                        if self.session_list[gameid][session_id][client_id]['serveraddr'] != None:
                            publicport = int(self.session_list[gameid][session_id][client_id]['serveraddr']['publicport'])

                        output = bytearray(recv_data[0:12])
                        output += bytearray([int(x) for x in self.session_list[gameid][session_id][client_id]['addr'][0].split('.')])
                        output += utils.get_bytes_from_short_be(publicport)

                        output += bytearray([0x42, 0x00]) # Unknown, always seems to be \x42\x00
                        output[7] = 0x05
                        #s.sendto(output, (self.session_list[gameid][session_id][client]['addr']))
                        s.sendto(output, (self.session_list[gameid][session_id][client]['addr'][0], self.session_list[gameid][session_id][client]['addr'][1]))

                        logger.log(logging.DEBUG, "Sent connection request to %s:%d..." % (self.session_list[gameid][session_id][client]['addr'][0], self.session_list[gameid][session_id][client]['addr'][1]))
                        logger.log(logging.DEBUG, utils.pretty_print_hex(output))

            elif recv_data[7] == '\x06': # Was able to connect
                client_id = "%02x" % ord(recv_data[13])
                logger.log(logging.DEBUG, "Received connected command from %s:%s..." % (addr[0], addr[1]))

                if gameid not in self.session_list:
                    pass
                if session_id not in self.session_list[gameid]:
                    pass
                if client_id not in self.session_list[gameid][session_id]:
                    pass

                self.session_list[gameid][session_id][client_id]['connected'] = True

            elif recv_data[7] == '\x0a': # Address check. Note: UNTESTED!
                client_id = "%02x" % ord(recv_data[13])
                logger.log(logging.DEBUG, "Received address check command from %s:%s..." % (addr[0], addr[1]))

                output = bytearray(recv_data[0:15])
                output += bytearray([int(x) for x in addr[0].split('.')])
                output += utils.get_bytes_from_short_be(addr[1])
                output += bytearray(recv_data[len(output):])

                output[7] = 0x0b
                s.sendto(output, addr)

                logger.log(logging.DEBUG, "Sent address check response to %s:%d..." % (self.session_list[gameid][session_id][client]['addr'][0], self.session_list[gameid][session_id][client]['addr'][1]))
                logger.log(logging.DEBUG, utils.pretty_print_hex(output))

            elif recv_data[7] == '\x0d':
                client_id = "%02x" % ord(recv_data[13])
                logger.log(logging.DEBUG, "Received report command from %s:%s..." % (addr[0], addr[1]))
                logger.log(logging.DEBUG, utils.pretty_print_hex(recv_data))

                output = bytearray(recv_data)
                output[7] = 0x0e # Report response
                s.sendto(recv_data, addr)

            else: # Was able to connect
                logger.log(logging.DEBUG, "Received unknown command %02x from %s:%s..." % (ord(recv_data[7]), addr[0], addr[1]))
    def handle_packet(self, s, recv_data, addr):
        logger.log(logging.DEBUG,
                   "Connection from %s:%d..." % (addr[0], addr[1]))
        logger.log(logging.DEBUG, utils.pretty_print_hex(recv_data))
        time.sleep(0.05)

        # Make sure it's a legal packet
        if recv_data[0:6] != bytearray([0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2]):
            return

        session_id = struct.unpack("<I", recv_data[8:12])[0]
        session_id_raw = recv_data[8:12]

        # Handle commands
        if recv_data[7] == '\x00':
            logger.log(
                logging.DEBUG,
                "Received initialization from %s:%s..." % (addr[0], addr[1]))

            output = bytearray(recv_data[0:14])
            output += bytearray(
                [0xff, 0xff, 0x6d, 0x16, 0xb5, 0x7d, 0xea]
            )  # Checked with Tetris DS, Mario Kart DS, and Metroid Prime Hunters, and this seems to be the standard response to 0x00
            output[7] = 0x01  # Initialization response
            s.sendto(output, addr)

            # Try to connect to the server
            gameid = utils.get_string(recv_data, 0x15)
            client_id = "%02x" % ord(recv_data[13])

            localip_raw = recv_data[15:19]
            localip_int_le = utils.get_int(recv_data, 15)
            localip_int_be = utils.get_int_be(recv_data, 15)
            localip = '.'.join(["%d" % ord(x) for x in localip_raw])
            localport_raw = recv_data[19:21]
            localport = utils.get_short_be(localport_raw, 0)
            localaddr = (localip, localport, localip_int_le, localip_int_be)

            if session_id not in self.session_list:
                self.session_list[session_id] = {}
            if client_id not in self.session_list[session_id]:
                self.session_list[session_id][client_id] = {
                    'connected': False,
                    'addr': '',
                    'localaddr': None,
                    'serveraddr': None,
                    'gameid': None
                }

            self.session_list[session_id][client_id]['gameid'] = gameid
            self.session_list[session_id][client_id]['addr'] = addr
            self.session_list[session_id][client_id]['localaddr'] = localaddr
            clients = len(self.session_list[session_id])

            for client in self.session_list[session_id]:
                if self.session_list[session_id][client][
                        'connected'] == False:  # and self.session_list[session_id][client]['localaddr'][1] != 0:
                    if client == client_id:
                        continue

                    #if self.session_list[session_id][client]['serveraddr'] == None:
                    serveraddr = self.get_server_info(gameid, session_id,
                                                      client)
                    if serveraddr == None:
                        serveraddr = self.get_server_info_alt(
                            gameid, session_id, client)

                    self.session_list[session_id][client][
                        'serveraddr'] = serveraddr
                    logger.log(
                        logging.DEBUG,
                        "Found server from local ip/port: %s from %d" %
                        (serveraddr, session_id))

                    publicport = self.session_list[session_id][client]['addr'][
                        1]
                    if self.session_list[session_id][client]['localaddr'][
                            1] != 0:
                        publicport = self.session_list[session_id][client][
                            'localaddr'][1]

                    if self.session_list[session_id][client][
                            'serveraddr'] != None:
                        publicport = int(self.session_list[session_id][client]
                                         ['serveraddr']['publicport'])

                    # Send to requesting client
                    output = bytearray(recv_data[0:12])
                    output += bytearray([
                        int(x) for x in self.session_list[session_id][client]
                        ['addr'][0].split('.')
                    ])
                    output += utils.get_bytes_from_short_be(publicport)

                    output += bytearray(
                        [0x42, 0x00])  # Unknown, always seems to be \x42\x00
                    output[7] = 0x05
                    #s.sendto(output, (self.session_list[session_id][client_id]['addr']))
                    s.sendto(
                        output,
                        (self.session_list[session_id][client_id]['addr'][0],
                         self.session_list[session_id][client_id]['addr'][1]))

                    logger.log(
                        logging.DEBUG, "Sent connection request to %s:%d..." %
                        (self.session_list[session_id][client_id]['addr'][0],
                         self.session_list[session_id][client_id]['addr'][1]))
                    logger.log(logging.DEBUG, utils.pretty_print_hex(output))

                    # Send to other client
                    #if self.session_list[session_id][client_id]['serveraddr'] == None:
                    serveraddr = self.get_server_info(gameid, session_id,
                                                      client_id)
                    if serveraddr == None:
                        serveraddr = self.get_server_info_alt(
                            gameid, session_id, client)

                    self.session_list[session_id][client_id][
                        'serveraddr'] = serveraddr
                    logger.log(
                        logging.DEBUG,
                        "Found server 2 from local ip/port: %s from %d" %
                        (serveraddr, session_id))

                    publicport = self.session_list[session_id][client_id][
                        'addr'][1]
                    if self.session_list[session_id][client_id]['localaddr'][
                            1] != 0:
                        publicport = self.session_list[session_id][client_id][
                            'localaddr'][1]

                    if self.session_list[session_id][client_id][
                            'serveraddr'] != None:
                        publicport = int(
                            self.session_list[session_id][client_id]
                            ['serveraddr']['publicport'])

                    output = bytearray(recv_data[0:12])
                    output += bytearray([
                        int(x) for x in self.session_list[session_id]
                        [client_id]['addr'][0].split('.')
                    ])
                    output += utils.get_bytes_from_short_be(publicport)

                    output += bytearray(
                        [0x42, 0x00])  # Unknown, always seems to be \x42\x00
                    output[7] = 0x05
                    #s.sendto(output, (self.session_list[session_id][client]['addr']))
                    s.sendto(
                        output,
                        (self.session_list[session_id][client]['addr'][0],
                         self.session_list[session_id][client]['addr'][1]))

                    logger.log(
                        logging.DEBUG, "Sent connection request to %s:%d..." %
                        (self.session_list[session_id][client]['addr'][0],
                         self.session_list[session_id][client]['addr'][1]))
                    logger.log(logging.DEBUG, utils.pretty_print_hex(output))

        elif recv_data[7] == '\x06':  # Was able to connect
            client_id = "%02x" % ord(recv_data[13])
            logger.log(
                logging.DEBUG, "Received connected command from %s:%s..." %
                (addr[0], addr[1]))

            if session_id not in self.session_list:
                return
            if client_id not in self.session_list[session_id]:
                return

            self.session_list[session_id][client_id]['connected'] = True

        elif recv_data[7] == '\x0a':  # Address check. Note: UNTESTED!
            client_id = "%02x" % ord(recv_data[13])
            logger.log(
                logging.DEBUG, "Received address check command from %s:%s..." %
                (addr[0], addr[1]))

            output = bytearray(recv_data[0:15])
            output += bytearray([int(x) for x in addr[0].split('.')])
            output += utils.get_bytes_from_short_be(addr[1])
            output += bytearray(recv_data[len(output):])

            output[7] = 0x0b
            s.sendto(output, addr)

            logger.log(
                logging.DEBUG,
                "Sent address check response to %s:%d..." % (addr[0], addr[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(output))

        elif recv_data[7] == '\x0c':  # Natify
            port_type = "%02x" % ord(recv_data[12])
            logger.log(
                logging.DEBUG,
                "Received natify command from %s:%s..." % (addr[0], addr[1]))

            output = bytearray(recv_data)
            output[7] = 0x02  # ERT Test
            s.sendto(output, addr)

            logger.log(logging.DEBUG,
                       "Sent natify response to %s:%d..." % (addr[0], addr[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(output))

        elif recv_data[7] == '\x0d':
            client_id = "%02x" % ord(recv_data[13])
            logger.log(
                logging.DEBUG,
                "Received report command from %s:%s..." % (addr[0], addr[1]))
            logger.log(logging.DEBUG, utils.pretty_print_hex(recv_data))

            output = bytearray(recv_data)
            output[7] = 0x0e  # Report response
            s.sendto(recv_data, addr)

        else:  # Was able to connect
            logger.log(
                logging.DEBUG, "Received unknown command %02x from %s:%s..." %
                (ord(recv_data[7]), addr[0], addr[1]))