mirror of
https://github.com/barronwaffles/dwc_network_server_emulator.git
synced 2026-08-27 13:24:03 -05:00
move NAS server to python, save authentication data in database instead of challenge key
This commit is contained in:
@@ -52,6 +52,10 @@ class GamespyDatabase(object):
|
||||
logger.log(-1, q)
|
||||
c.execute(q)
|
||||
|
||||
q = "CREATE TABLE nas_logins (userid TEXT, authtoken TEXT, data TEXT)"
|
||||
logger.log(-1, q)
|
||||
c.execute(q)
|
||||
|
||||
self.conn.commit()
|
||||
|
||||
def get_dict(self, row):
|
||||
@@ -361,6 +365,58 @@ class GamespyDatabase(object):
|
||||
|
||||
return sessions
|
||||
|
||||
# nas server functions
|
||||
def get_nas_login(self, authtoken):
|
||||
q = "SELECT data FROM nas_logins WHERE authtoken = ?"
|
||||
q2 = q.replace("?", "%s") % (authtoken)
|
||||
logger.log(-1, q2)
|
||||
|
||||
c = self.conn.cursor()
|
||||
c.execute(q, [authtoken])
|
||||
r = self.get_dict(c.fetchone())
|
||||
c.close()
|
||||
|
||||
if r == None:
|
||||
return None
|
||||
else:
|
||||
return r["data"]
|
||||
|
||||
def generate_authtoken(self, userid, data):
|
||||
size = 128
|
||||
authtoken = "NDS" + utils.generate_random_str(size)
|
||||
|
||||
q = "SELECT authtoken FROM nas_logins WHERE authtoken = ?"
|
||||
q2 = q.replace("?", "%s") % (authtoken)
|
||||
logger.log(-1, q2)
|
||||
|
||||
c = self.conn.cursor()
|
||||
for r in c.execute(q, [authtoken]):
|
||||
authtoken = "NDS" + utils.generate_random_str(size)
|
||||
|
||||
q = "SELECT * FROM nas_logins WHERE userid LIKE ?"
|
||||
q2 = q.replace("?", "%s") % (userid)
|
||||
logger.log(-1, q2)
|
||||
|
||||
c.execute(q, [authtoken])
|
||||
r = self.get_dict(c.fetchone())
|
||||
|
||||
if r == None: # no row, add it
|
||||
q = "INSERT INTO nas_logins VALUES (?, ?, ?)"
|
||||
q2 = q.replace("?", "%s") % (userid, authtoken, data)
|
||||
logger.log(-1, q2)
|
||||
c.execute(q, [userid, authtoken, data])
|
||||
else:
|
||||
q = "UPDATE nas_logins SET authtoken = ?, data = ? WHERE userid = ?"
|
||||
q2 = q.replace("?", "%s") % (authtoken, data, userid)
|
||||
logger.log(-1, q2)
|
||||
c.execute(q, [authtoken, data, userid])
|
||||
|
||||
c.close()
|
||||
self.conn.commit();
|
||||
|
||||
return authtoken
|
||||
|
||||
|
||||
# Buddy functions
|
||||
def add_buddy(self, userProfileId, buddyProfileId):
|
||||
now = int(time.time())
|
||||
@@ -502,9 +558,9 @@ class GamespyDatabase(object):
|
||||
c.execute(q, [data, profileid, dindex, ptype])
|
||||
self.conn.commit()
|
||||
|
||||
def pd_get(self, profileid, dindex, ptype, data):
|
||||
def pd_get(self, profileid, dindex, ptype):
|
||||
q = "SELECT * FROM gamestat_profile WHERE profileid = ? AND dindex = ? AND ptype = ?"
|
||||
q2 = q.replace("?", "%s") % (dindex, ptype, data)
|
||||
q2 = q.replace("?", "%s") % (profileid, dindex, ptype)
|
||||
logger.log(-1, q2)
|
||||
|
||||
c = self.conn.cursor()
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import time
|
||||
import other.utils as utils
|
||||
|
||||
import other.utils as utils
|
||||
|
||||
def generate_secret_keys(filename="gslist.cfg"):
|
||||
key_file = open(filename)
|
||||
@@ -64,20 +65,15 @@ def prepare_rc4_base64(_key, _data):
|
||||
data.append(0)
|
||||
return base64.b64encode(buffer(data))
|
||||
|
||||
# Parse my custom authtoken generated by the emulated nas.nintendowifi.net/ac
|
||||
def parse_authtoken(authtoken):
|
||||
# get the login data from nas.nintendowifi.net/ac from an authtoken
|
||||
def parse_authtoken(authtoken, db):
|
||||
messages = {}
|
||||
nas_data = db.get_nas_login(authtoken)
|
||||
|
||||
if authtoken[:3] == "NDS":
|
||||
authtoken = authtoken[3:]
|
||||
if nas_data == None:
|
||||
return None
|
||||
|
||||
dec = base64.standard_b64decode(authtoken)
|
||||
|
||||
for item in dec.split('|'):
|
||||
s = item.split('\\')
|
||||
messages[s[0]] = s[1]
|
||||
|
||||
return messages
|
||||
return json.loads(nas_data)
|
||||
|
||||
|
||||
def generate_response(challenge, ac_challenge, secretkey, authtoken):
|
||||
|
||||
@@ -146,7 +146,7 @@ class Gamestats(LineReceiver):
|
||||
self.transport.write(bytes(msg))
|
||||
|
||||
def perform_authp(self, data_parsed):
|
||||
authtoken_parsed = gs_utils.parse_authtoken(data_parsed['authtoken'])
|
||||
authtoken_parsed = gs_utils.parse_authtoken(data_parsed['authtoken'], self.db)
|
||||
#print authtoken_parsed
|
||||
|
||||
if "lid" in data_parsed:
|
||||
|
||||
@@ -154,7 +154,7 @@ class PlayerSession(LineReceiver):
|
||||
self.log(logging.ERROR, "Found unknown command, don't know how to handle '%s'." % data_parsed['__cmd__'])
|
||||
|
||||
def perform_login(self, data_parsed):
|
||||
authtoken_parsed = gs_utils.parse_authtoken(data_parsed['authtoken'])
|
||||
authtoken_parsed = gs_utils.parse_authtoken(data_parsed['authtoken'], self.db)
|
||||
#print authtoken_parsed
|
||||
|
||||
# Track what console is connecting and save it in the database during user creation just in case we can use
|
||||
|
||||
@@ -5,6 +5,7 @@ from gamespy_natneg_server import GameSpyNatNegServer
|
||||
from gamespy_qr_server import GameSpyQRServer
|
||||
from gamespy_server_browser_server import GameSpyServerBrowserServer
|
||||
from gamespy_gamestats_server import GameSpyGamestatsServer
|
||||
from nintendo_nas_server import NintendoNasServer
|
||||
|
||||
import threading
|
||||
|
||||
@@ -36,6 +37,10 @@ def start_natneg_server():
|
||||
natneg_server = GameSpyNatNegServer()
|
||||
natneg_server.start()
|
||||
|
||||
def start_nas_server():
|
||||
nas_server = NintendoNasServer()
|
||||
nas_server.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
backend_server_thread = threading.Thread(target=start_backend_server)
|
||||
backend_server_thread.start()
|
||||
@@ -56,4 +61,7 @@ if __name__ == "__main__":
|
||||
#server_browser_server_thread.start()
|
||||
|
||||
natneg_server_thread = threading.Thread(target=start_natneg_server)
|
||||
natneg_server_thread.start()
|
||||
natneg_server_thread.start()
|
||||
|
||||
nas_server_thread = threading.Thread(target=start_nas_server)
|
||||
nas_server_thread.start()
|
||||
|
||||
95
nintendo_nas_server.py
Normal file
95
nintendo_nas_server.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
import urllib
|
||||
import urlparse
|
||||
import BaseHTTPServer
|
||||
|
||||
import gamespy.gs_database as gs_database
|
||||
import gamespy.gs_utility as gs_utils
|
||||
import other.utils as utils
|
||||
|
||||
logger_output_to_console = True
|
||||
logger_output_to_file = True
|
||||
logger_name = "NintendoNasServer"
|
||||
logger_filename = "nintendo_nas_server.log"
|
||||
logger = utils.create_logger(logger_name, logger_filename, -1, logger_output_to_console, logger_output_to_file)
|
||||
|
||||
#address = ("0.0.0.0", 80)
|
||||
address = ("127.0.0.1", 9000)
|
||||
|
||||
class NintendoNasServer(object):
|
||||
def start(self):
|
||||
httpd = NintendoNasHTTPServer((address[0], address[1]), NintendoNasHTTPServerHandler)
|
||||
logger.log(logging.INFO, "Now listening for connections on %s:%d...", address[0], address[1])
|
||||
httpd.serve_forever()
|
||||
|
||||
class NintendoNasHTTPServer(BaseHTTPServer.HTTPServer):
|
||||
def __init__(self, server_address, RequestHandlerClass):
|
||||
self.db = gs_database.GamespyDatabase()
|
||||
BaseHTTPServer.HTTPServer.__init__(self, server_address, RequestHandlerClass)
|
||||
|
||||
class NintendoNasHTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
if self.path == "/ac":
|
||||
length = int(self.headers['content-length'])
|
||||
post = urlparse.parse_qs(self.rfile.read(length))
|
||||
|
||||
for k, v in post.iteritems():
|
||||
post[k] = self.base64_dec(v[0])
|
||||
|
||||
logger.log(logging.DEBUG, "Request to %s from %s", self.path, self.client_address)
|
||||
logger.log(logging.DEBUG, post)
|
||||
ret = {}
|
||||
ret["datetime"] = time.strftime("%Y%m%d%H%M%S")
|
||||
ret["retry"] = "0"
|
||||
action = post["action"]
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.send_header("Server", "Nintendo Wii (http)")
|
||||
self.send_header("NODE", "wifiappe3")
|
||||
self.end_headers()
|
||||
|
||||
if action == "acctcreate":
|
||||
# TODO: test for duplicate accounts
|
||||
ret["returncd"] = "002"
|
||||
|
||||
logger.log(logging.DEBUG, "acctcreate response to %s", self.client_address)
|
||||
logger.log(logging.DEBUG, ret)
|
||||
|
||||
for k, v in ret.iteritems():
|
||||
ret[k] = self.base64_enc(v)
|
||||
|
||||
self.wfile.write(urllib.urlencode(ret).replace("%2A", "*"))
|
||||
elif action == "login":
|
||||
ret["returncd"] = "001"
|
||||
ret["locator"] = "gamespy.com"
|
||||
challenge = utils.generate_random_str(8)
|
||||
ret["challenge"] = challenge
|
||||
post["challenge"] = challenge
|
||||
authtoken = self.server.db.generate_authtoken(post["userid"], json.dumps(post))
|
||||
ret["token"] = authtoken
|
||||
|
||||
logger.log(logging.DEBUG, "login response to %s", self.client_address)
|
||||
logger.log(logging.DEBUG, ret)
|
||||
|
||||
for k, v in ret.iteritems():
|
||||
ret[k] = self.base64_enc(v)
|
||||
|
||||
self.wfile.write(urllib.urlencode(ret).replace("%2A", "*"))
|
||||
|
||||
#elif self.path == "/pr":
|
||||
# TODO
|
||||
#elif self.path == "/download":
|
||||
# TODO
|
||||
|
||||
def base64_dec(self, data):
|
||||
return base64.b64decode(data.replace("*", "="))
|
||||
|
||||
def base64_enc(self, data):
|
||||
return base64.b64encode(data).replace("=", "*")
|
||||
|
||||
if __name__ == "__main__":
|
||||
nas = NintendoNasServer()
|
||||
nas.start()
|
||||
Reference in New Issue
Block a user