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]))