Console endianness, natneg report command fix

- Added endianness based on console. This allows the Wii to connect.
- Fixed a bug where the report command was returning the wrong buffer in
natneg.
This commit is contained in:
polaris 2014-04-11 18:09:22 -04:00
parent 25ff93469b
commit f64b1e1c60
4 changed files with 51 additions and 20 deletions

View File

@ -55,6 +55,9 @@ class GameSpyBackendServer(object):
address = ("127.0.0.1", 27500)
password = ""
#self.server_list['mariokartwii'] = []
#self.server_list['mariokartwii'].append({'__session__': 1113766873, 'eb': '5000', 'ev': '5000', 'publicport': '61784', 'localip1': '190.190.190.190', 'gamename': 'mariokartwii', 'maxplayers': '11', 'dwc_mtype': '0', 'publicip': '167772642', 'dwc_mver': '90', 'rk': 'vs_123', 'natneg': '1', 'dwc_groupid': '0', 'localip0': '10.0.1.226', 'dwc_pid': '1', 'localport': '61784', 'statechanged': '1', 'dwc_suspend': '1', 'dwc_eval': '1', 'numplayers': '0', 'dwc_hoststate': '0', 'p': '0'})
logger.log(logging.INFO, "Started server on %s:%d..." % (address[0], address[1]))
manager = GameSpyServerDatabase(address = address, authkey = password)
@ -293,13 +296,13 @@ class GameSpyBackendServer(object):
values.append({ 'value': int(lval & rval), 'type': TokenType.NUMBER})
if match == True:
#print "Matched: %s %s %s" % (l['value'], op, r['value'])
print "Matched: %s %s %s" % (l['value'], op, r['value'])
matched_filters += 1
elif op != "&": # & doesn't need to be matched, so don't display a message for it
#print "Not matched: %s %s %s" % (l['value'], op, r['value'])
print "Not matched: %s %s %s" % (l['value'], op, r['value'])
pass
#print "Matched %d/%d" % (matched_filters, filter_count)
print "Matched %d/%d" % (matched_filters, filter_count)
# Add the server if everything was matched
if matched_filters == filter_count:
@ -344,7 +347,7 @@ class GameSpyBackendServer(object):
return servers
def update_server_list(self, gameid, session, value):
def update_server_list(self, gameid, session, value, console):
# Make sure the user isn't hosting multiple servers or there isn't some left over server information that
# never got handled properly (game crashed, etc).
self.delete_server(gameid, session)
@ -355,6 +358,7 @@ class GameSpyBackendServer(object):
# Add new server
value['__session__'] = session
value['__console__'] = console
logger.log(logging.DEBUG, "Added %s to the server list for %s" % (value, gameid))
self.server_list[gameid].append(value)

View File

@ -20,7 +20,7 @@ class GameSpyNatNegServer(object):
self.secret_key_list = gs_utils.generate_secret_keys("gslist.cfg")
def start(self):
# Start QR server
# 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)
@ -32,6 +32,7 @@ class GameSpyNatNegServer(object):
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]):
@ -44,6 +45,12 @@ class GameSpyNatNegServer(object):
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])
@ -56,8 +63,6 @@ class GameSpyNatNegServer(object):
self.session_list[gameid][session_id][client_id]['addr'] = addr
clients = len(self.session_list[gameid][session_id])
#if client_id in session_list[gameid][session_id]:
# clients -= 1
if clients > 0:
# Someone else is waiting to connect, send message
@ -81,11 +86,6 @@ class GameSpyNatNegServer(object):
logger.log(logging.DEBUG, "")
#session_list[gameid][session_id][client_id]['connected'] = True
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)
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]))
@ -97,15 +97,15 @@ class GameSpyNatNegServer(object):
if client_id not in self.session_list[gameid][session_id]:
pass
#session_list[gameid][session_id][client_id]['connected'] = True
#self.session_list[gameid][session_id][client_id]['connected'] = True
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[7] = 0x0e # Report response
s.sendto(output, addr)
recv_data[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]))

View File

@ -5,6 +5,7 @@ import logging
import socket
import struct
import gamespy.gs_utility as gs_utils
import gamespy.gs_database as gs_database
import other.utils as utils
from multiprocessing.managers import BaseManager
@ -27,6 +28,8 @@ class GameSpyQRServer(object):
self.secretkey = "" # Parse gslist.cfg later
self.sent_challenge = False
self.address = address
self.console = 0
self.playerid= 0
def __init__(self):
self.sessions = {}
@ -36,6 +39,8 @@ class GameSpyQRServer(object):
self.secret_key_list = gs_utils.generate_secret_keys("gslist.cfg")
#self.log(logging.DEBUG, address, "Generated list of secret game keys...")
self.db = gs_database.GamespyDatabase()
GameSpyServerDatabase.register("update_server_list")
GameSpyServerDatabase.register("delete_server")
@ -193,6 +198,17 @@ class GameSpyQRServer(object):
self.sessions[session_id].secretkey = self.secret_key_list[k['gamename']]
#print "Got secret key %s for %s" % (self.sessions[session_id].secretkey, k['gamename'])
if self.sessions[session_id].playerid == 0 and "dwc_pid" in k:
# Get the player's id and then query the profile to figure out what console they are on.
# The endianness of some server data depends on the endianness of the console, so we must be able
# to account for that.
self.sessions[session_id].playerid = int(k['dwc_pid'])
profile = self.db.get_profile_from_profileid(self.sessions[session_id].playerid)
if "console" in profile:
self.sessions[session_id].console = profile['console']
if self.sessions[session_id].sent_challenge == False:
addr_hex = ''.join(["%02X" % int(x) for x in address[0].split('.')])
port_hex = "%04X" % int(address[1])
@ -218,7 +234,7 @@ class GameSpyQRServer(object):
# dwc_mtype = 3 is used when looking for a friends only game (possibly other uses too).
# Some memory could be saved by clearing out any unwanted fields from k before sending.
self.server_manager.update_server_list(k['gamename'] , session_id, k)
self.server_manager.update_server_list(k['gamename'] , session_id, k, self.sessions[session_id].console)
elif k['statechanged'] == "2": # Close server
self.server_manager.delete_server(k['gamename'] , session_id)
#self.sessions.pop(session_id)

View File

@ -74,6 +74,7 @@ class Session(LineReceiver):
self.forward_to_client = False
self.forward_client = ()
self.secret_key_list = secret_key_list # Don't waste time parsing every session, so just accept it from the parent
self.console = 0
manager_address = ("127.0.0.1", 27500)
manager_password = ""
@ -101,18 +102,22 @@ 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?
ip = str(ctypes.c_int32(utils.get_int(bytearray([int(x) for x in self.forward_client[0].split('.')]), 0)).value)
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, server)
logger.log(logging.DEBUG, "find_server_by_address returned: %s" % server)
if server == None:
pass
#print "%s %s" % (ip, server['publicip'])
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
@ -254,7 +259,10 @@ class Session(LineReceiver):
if "natneg" in server_info:
flags |= ServerListFlags.CONNECT_NEGOTIATE_FLAG
flags_buffer += utils.get_bytes_from_int(int(server_info['publicip']))
if self.console != 0:
flags_buffer += utils.get_bytes_from_int_be(int(server_info['publicip'])) # Wii
else:
flags_buffer += utils.get_bytes_from_int(int(server_info['publicip'])) # DS
flags |= ServerListFlags.NONSTANDARD_PORT_FLAG
flags_buffer += utils.get_bytes_from_short_be(int(server_info['publicport']))
@ -303,6 +311,9 @@ class Session(LineReceiver):
#print "Requested was empty"
server = {}
if "__console__" in server:
self.console = int(server['__console__'])
# Generate binary server list data
data = self.generate_server_list_data(self.address, fields, server)
logger.log(logging.DEBUG, utils.pretty_print_hex(data))