123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- """DWC Network Server Emulator
- Copyright (C) 2014 polaris-
- Copyright (C) 2014 ToadKing
- Copyright (C) 2014 AdmiralCurtiss
- Copyright (C) 2014 msoucy
- Copyright (C) 2015 Sepalani
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- """
- import logging
- import time
- import http.server
- import socketserver
- import traceback
- from gamespy import gs_database
- from other import utils
- import dwc_config
- logger = dwc_config.get_logger('NasServer')
- def handle_post(handler, addr, post):
- """Handle unknown path."""
- logger.log(logging.WARNING, "Unknown path request %s from %s:%d!",
- handler.path, *addr)
- handler.send_response(404)
- return None
- def handle_ac_action(handler, db, addr, post):
- """Handle unknown ac action request."""
- logger.log(logging.WARNING, "Unknown ac action: %s", handler.path)
- return {}
- def handle_ac_acctcreate(handler, db, addr, post):
- """Handle ac acctcreate request.
- TODO: Test for duplicate accounts.
- """
- if db.is_banned(post):
- ret = {
- "retry": "1",
- "returncd": "3913",
- "locator": "gamespy.com",
- "reason": "User banned."
- }
- logger.log(logging.DEBUG, "Acctcreate denied for banned user %s",
- str(post))
- else:
- ret = {
- "retry": "0",
- "returncd": "002",
- "userid": db.get_next_available_userid()
- }
- logger.log(logging.DEBUG, "Acctcreate response to %s:%d", *addr)
- logger.log(logging.DEBUG, "%s", ret)
- return ret
- def handle_ac_login(handler, db, addr, post):
- """Handle ac login request."""
- new_post = {}
- for key, value in post.items():
- # do something with value
- if(isinstance(key, (bytes, bytearray))):
- key = key.decode('utf-8')
- if(isinstance(value, (bytes, bytearray))):
- value = value.decode('utf-8')
- new_post[key] = value
- post=new_post
- if db.is_banned(post):
- ret = {
- "retry": "1",
- "returncd": "3914",
- "locator": "gamespy.com",
- "reason": "User banned."
- }
- logger.log(logging.DEBUG, "Login denied for banned user %s", str(post))
- # Un-comment these lines to enable console registration feature
- # elif not db.pending(post):
- # logger.log(logging.DEBUG, "Login denied - Unknown console %s", post)
- # ret = {
- # "retry": "1",
- # "returncd": "3921",
- # "locator": "gamespy.com",
- # }
- # elif not db.registered(post):
- # logger.log(logging.DEBUG, "Login denied - console pending %s", post)
- # ret = {
- # "retry": "1",
- # "returncd": "3888",
- # "locator": "gamespy.com",
- # }
- else:
- challenge = utils.generate_random_str(8)
- post["challenge"] = challenge
- authtoken = db.generate_authtoken(post["userid"], post)
- ret = {
- "retry": "0",
- "returncd": "001",
- "locator": "gamespy.com",
- "challenge": challenge,
- "token": authtoken,
- }
- logger.log(logging.DEBUG, "Login response to %s:%d", *addr)
- logger.log(logging.DEBUG, "%s", ret)
- return ret
- def handle_ac_svcloc(handler, db, addr, post):
- """Handle ac svcloc request."""
- new_post = {}
- for key, value in post.items():
- # do something with value
- if(isinstance(key, (bytes, bytearray))):
- key = key.decode('utf-8')
- if(isinstance(value, (bytes, bytearray))):
- value = value.decode('utf-8')
- new_post[key] = value
- post=new_post
- # Get service based on service id number
- ret = {
- "retry": "0",
- "returncd": "007",
- "statusdata": "Y"
- }
- authtoken = db.generate_authtoken(post["userid"], post)
- if 'svc' in post:
- if post["svc"] in ("9000", "9001"):
- # DLC host = 9000
- # In case the client's DNS isn't redirecting to
- # dls1.nintendowifi.net
- # NB: NAS config overrides this if set
- svchost = dwc_config.get_svchost('NasServer')
- ret["svchost"] = svchost if svchost else handler.headers['host']
- # Brawl has 2 host headers which Apache chokes
- # on, so only return the first one or else it
- # won't work
- ret["svchost"] = ret["svchost"].split(',')[0]
- if post["svc"] == 9000:
- ret["token"] = authtoken
- else:
- ret["servicetoken"] = authtoken
- elif post["svc"] == "0000":
- # Pokemon requests this for some things
- ret["servicetoken"] = authtoken
- ret["svchost"] = "n/a"
- else:
- # Empty svc - Fix Error Code 24101 (Boom Street)
- ret["svchost"] = "n/a"
- ret["servicetoken"] = authtoken
- logger.log(logging.DEBUG, "Svcloc response to %s:%d", *addr)
- logger.log(logging.DEBUG, "%s", ret)
- return ret
- def handle_ac(handler, addr, post):
- """Handle ac POST request."""
- logger.log(logging.DEBUG, "Ac request to %s from %s:%d",
- handler.path, *addr)
- logger.log(logging.DEBUG, "%s", post)
- action = (post[b"action"]).lower().decode("utf-8")
- command = handler.ac_actions.get(action, handle_ac_action)
- ret = command(handler, gs_database.GamespyDatabase(), addr, post)
- ret.update({"datetime": time.strftime("%Y%m%d%H%M%S")})
- handler.send_response(200)
- handler.send_header("Content-type", "text/plain")
- handler.send_header("NODE", "wifiappe1")
- return utils.dict_to_qs(ret).encode()
- def handle_pr(handler, addr, post):
- """Handle pr POST request."""
- logger.log(logging.DEBUG, "Pr request to %s from %s:%d",
- handler.path, *addr)
- logger.log(logging.DEBUG, "%s", post)
- words = len(post["words"].split('\t'))
- wordsret = "0" * words
- ret = {
- "prwords": wordsret,
- "returncd": "000",
- "datetime": time.strftime("%Y%m%d%H%M%S")
- }
- for l in "ACEJKP":
- ret["prwords" + l] = wordsret
- handler.send_response(200)
- handler.send_header("Content-type", "text/plain")
- handler.send_header("NODE", "wifiappe1")
- logger.log(logging.DEBUG, "Pr response to %s:%d", *addr)
- logger.log(logging.DEBUG, "%s", ret)
- return utils.dict_to_qs(ret)
- class NasHTTPServerHandler(http.server.BaseHTTPRequestHandler):
- """Nintendo NAS server handler."""
- post_paths = {
- "/ac": handle_ac,
- "/pr": handle_pr,
- }
- ac_actions = {
- "acctcreate": handle_ac_acctcreate,
- "login": handle_ac_login,
- "svcloc": handle_ac_svcloc,
- }
- def version_string(self):
- return "Nintendo Wii (http)"
- def do_GET(self):
- """Handle GET request."""
- try:
- # conntest server
- self.send_response(200)
- self.send_header("Content-type", "text/html")
- self.send_header("X-Organization", "Nintendo")
- self.send_header("Server", "BigIP")
- self.end_headers()
- self.wfile.write("ok")
- except:
- logger.log(logging.ERROR, "Exception occurred on GET request!")
- logger.log(logging.ERROR, "%s", traceback.format_exc())
- def do_POST(self):
- try:
- length = int(self.headers['content-length'])
- post = utils.qs_to_dict(self.rfile.read(length))
- client_address = (
- self.headers.get('x-forwarded-for', self.client_address[0]),
- self.client_address[1]
- )
- post['ipaddr'] = client_address[0]
- command = self.post_paths.get(self.path, handle_post)
- ret = command(self, client_address, post)
- if ret is not None:
- self.send_header("Content-Length", str(len(ret)))
- self.end_headers()
- self.wfile.write(ret)
- except:
- logger.log(logging.ERROR, "Exception occurred on POST request!")
- logger.log(logging.ERROR, "%s", traceback.format_exc())
- class NasHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
- """Threading HTTP server."""
- pass
- class NasServer(object):
- def start(self):
- address = dwc_config.get_ip_port('NasServer')
- httpd = NasHTTPServer(address, NasHTTPServerHandler)
- logger.log(logging.INFO, "Now listening for connections on %s:%d...",
- *address)
- httpd.serve_forever()
- if __name__ == "__main__":
- nas = NasServer()
- nas.start()
|