move NAS server to python, save authentication data in database instead of challenge key

This commit is contained in:
Toad King
2014-04-18 01:40:50 -04:00
parent d1af97646d
commit 5ceff7e29f
6 changed files with 172 additions and 17 deletions

View File

@@ -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()

View File

@@ -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):

View File

@@ -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:

View File

@@ -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

View File

@@ -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
View 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()