diff --git a/gamespy_backend_server.py b/gamespy_backend_server.py index 8e76b4f..00d3a39 100644 --- a/gamespy_backend_server.py +++ b/gamespy_backend_server.py @@ -50,6 +50,7 @@ class GameSpyBackendServer(object): GameSpyServerDatabase.register("get_server_list", callable=lambda:self.server_list) GameSpyServerDatabase.register("find_servers", callable=self.find_servers) GameSpyServerDatabase.register("find_server_by_address", callable=self.find_server_by_address) + GameSpyServerDatabase.register("find_server_by_local_address", callable=self.find_server_by_local_address) GameSpyServerDatabase.register("update_server_list", callable=self.update_server_list) GameSpyServerDatabase.register("delete_server", callable=self.delete_server) @@ -390,6 +391,29 @@ class GameSpyBackendServer(object): return None + def find_server_by_local_address(self, publicip, localip, localport, gameid = None): + if gameid == None: + # Search all servers + for gameid in self.server_list: + for server in self.server_list[gameid]: + logger.log(logging.DEBUG, "publicip: %s == %s ? %d" % (server['publicip'], publicip, server['publicip'] == publicip)) + if server['publicip'] == publicip and server['localport'] == str(localport): + for x in range(0, 10): + s = 'localip%d' % x + if s in server: + if server[s] == localip: + return server + else: + for server in self.server_list[gameid]: + logger.log(logging.DEBUG, "publicip: %s == %s ? %d" % (server['publicip'], publicip, server['publicip'] == publicip)) + if server['publicip'] == publicip and server['localport'] == str(localport): + for x in range(0, 10): + s = 'localip%d' % x + if s in server: + if server[s] == localip: + return server + return None + if __name__ == '__main__': freeze_support() diff --git a/gamespy_natneg_server.py b/gamespy_natneg_server.py index 6bf0f6b..b2c290c 100644 --- a/gamespy_natneg_server.py +++ b/gamespy_natneg_server.py @@ -3,10 +3,13 @@ import logging import socket +import ctypes import struct import gamespy.gs_utility as gs_utils import other.utils as utils +from multiprocessing.managers import BaseManager + # Logger settings logger_output_to_console = True logger_output_to_file = True @@ -14,11 +17,25 @@ logger_name = "GameSpyNatNegServer" logger_filename = "gamespy_natneg_server.log" logger = utils.create_logger(logger_name, logger_filename, -1, logger_output_to_console, logger_output_to_file) +class GameSpyServerDatabase(BaseManager): + pass + +GameSpyServerDatabase.register("get_server_list") +GameSpyServerDatabase.register("modify_server_list") +GameSpyServerDatabase.register("find_servers") +GameSpyServerDatabase.register("find_server_by_address") +GameSpyServerDatabase.register("find_server_by_local_address") + class GameSpyNatNegServer(object): def __init__(self): self.session_list = {} self.secret_key_list = gs_utils.generate_secret_keys("gslist.cfg") + manager_address = ("127.0.0.1", 27500) + manager_password = "" + self.server_manager = GameSpyServerDatabase(address = manager_address, authkey= manager_password) + self.server_manager.connect() + 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) @@ -54,46 +71,72 @@ class GameSpyNatNegServer(object): 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': '' } + 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: + 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(self.session_list[gameid][session_id][client]['addr'][1]) + 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'])) + 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)) - logger.log(logging.DEBUG, "") # 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(self.session_list[gameid][session_id][client_id]['addr'][1]) + 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'])) + 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)) - logger.log(logging.DEBUG, "") elif recv_data[7] == '\x06': # Was able to connect client_id = "%02x" % ord(recv_data[13]) @@ -106,7 +149,7 @@ class GameSpyNatNegServer(object): if client_id not in self.session_list[gameid][session_id]: pass - #self.session_list[gameid][session_id][client_id]['connected'] = True + 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]) @@ -135,6 +178,31 @@ class GameSpyNatNegServer(object): 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 get_server_info(self, gameid, session_id, client_id): + console = 0 + ipstr = self.session_list[gameid][session_id][client_id]['addr'][0] + + if console != 0: + ip = str(ctypes.c_int32(utils.get_int_be(bytearray([int(x) for x in ipstr.split('.')]), 0)).value) # Wii + console = 0 + else: + ip = str(ctypes.c_int32(utils.get_int(bytearray([int(x) for x in ipstr.split('.')]), 0)).value) # DS + console = 1 + + serveraddr = self.server_manager.find_server_by_local_address(ip, self.session_list[gameid][session_id][client_id]['localaddr'][0], self.session_list[gameid][session_id][client_id]['localaddr'][1], self.session_list[gameid][session_id][client_id]['gameid'])._getvalue() + + if serveraddr == None: + if console != 0: + ip = str(ctypes.c_int32(utils.get_int_be(bytearray([int(x) for x in ipstr.split('.')]), 0)).value) # Wii + console = 0 + else: + ip = str(ctypes.c_int32(utils.get_int(bytearray([int(x) for x in ipstr.split('.')]), 0)).value) # DS + console = 1 + + serveraddr = self.server_manager.find_server_by_local_address(ip, self.session_list[gameid][session_id][client_id]['localaddr'][0], self.session_list[gameid][session_id][client_id]['localaddr'][1], self.session_list[gameid][session_id][client_id]['gameid'])._getvalue() + + return serveraddr + if __name__ == "__main__": natneg_server = GameSpyNatNegServer() natneg_server.start() \ No newline at end of file diff --git a/gamespy_server_browser_server.py b/gamespy_server_browser_server.py index 21bb04d..2ddaea3 100644 --- a/gamespy_server_browser_server.py +++ b/gamespy_server_browser_server.py @@ -364,6 +364,7 @@ class Session(LineReceiver): # #self.server_cache.pop((publicip + str(self.forward_client[1]))) server = self.server_manager.find_server_by_address(ip, self.forward_client[1])._getvalue() + self.log(logging.DEBUG, "find_server_in_cache is returning: %s %s" % (server, ip)) return server, ip @@ -371,6 +372,9 @@ class Session(LineReceiver): # 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.forward_client == None or len(self.forward_client) != 2: + return + server, ip = self.find_server_in_cache(self.forward_client[0], self.forward_client[1], self.console) if server == None: @@ -389,7 +393,7 @@ class Session(LineReceiver): self.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 + natneg_session = int(utils.generate_random_hex_str(8), 16) # Quick and lazy way to get a random 32bit integer. Replace with something else later output = bytearray([0xfe, 0xfd, 0x06]) output += utils.get_bytes_from_int(server['__session__'])