nas_server.py.bak 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. """DWC Network Server Emulator
  2. Copyright (C) 2014 polaris-
  3. Copyright (C) 2014 ToadKing
  4. Copyright (C) 2014 AdmiralCurtiss
  5. Copyright (C) 2014 msoucy
  6. Copyright (C) 2015 Sepalani
  7. This program is free software: you can redistribute it and/or modify
  8. it under the terms of the GNU Affero General Public License as
  9. published by the Free Software Foundation, either version 3 of the
  10. License, or (at your option) any later version.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU Affero General Public License for more details.
  15. You should have received a copy of the GNU Affero General Public License
  16. along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. """
  18. import logging
  19. import time
  20. import BaseHTTPServer
  21. import SocketServer
  22. import traceback
  23. from gamespy import gs_database
  24. from other import utils
  25. import dwc_config
  26. logger = dwc_config.get_logger('NasServer')
  27. def handle_post(handler, addr, post):
  28. """Handle unknown path."""
  29. logger.log(logging.WARNING, "Unknown path request %s from %s:%d!",
  30. handler.path, *addr)
  31. handler.send_response(404)
  32. return None
  33. def handle_ac_action(handler, db, addr, post):
  34. """Handle unknown ac action request."""
  35. logger.log(logging.WARNING, "Unknown ac action: %s", handler.path)
  36. return {}
  37. def handle_ac_acctcreate(handler, db, addr, post):
  38. """Handle ac acctcreate request.
  39. TODO: Test for duplicate accounts.
  40. """
  41. if db.is_banned(post):
  42. ret = {
  43. "retry": "1",
  44. "returncd": "3913",
  45. "locator": "gamespy.com",
  46. "reason": "User banned."
  47. }
  48. logger.log(logging.DEBUG, "Acctcreate denied for banned user %s",
  49. str(post))
  50. else:
  51. ret = {
  52. "retry": "0",
  53. "returncd": "002",
  54. "userid": db.get_next_available_userid()
  55. }
  56. logger.log(logging.DEBUG, "Acctcreate response to %s:%d", *addr)
  57. logger.log(logging.DEBUG, "%s", ret)
  58. return ret
  59. def handle_ac_login(handler, db, addr, post):
  60. """Handle ac login request."""
  61. if db.is_banned(post):
  62. ret = {
  63. "retry": "1",
  64. "returncd": "3914",
  65. "locator": "gamespy.com",
  66. "reason": "User banned."
  67. }
  68. logger.log(logging.DEBUG, "Login denied for banned user %s", str(post))
  69. # Un-comment these lines to enable console registration feature
  70. # elif not db.pending(post):
  71. # logger.log(logging.DEBUG, "Login denied - Unknown console %s", post)
  72. # ret = {
  73. # "retry": "1",
  74. # "returncd": "3921",
  75. # "locator": "gamespy.com",
  76. # }
  77. # elif not db.registered(post):
  78. # logger.log(logging.DEBUG, "Login denied - console pending %s", post)
  79. # ret = {
  80. # "retry": "1",
  81. # "returncd": "3888",
  82. # "locator": "gamespy.com",
  83. # }
  84. else:
  85. challenge = utils.generate_random_str(8)
  86. post["challenge"] = challenge
  87. authtoken = db.generate_authtoken(post["userid"], post)
  88. ret = {
  89. "retry": "0",
  90. "returncd": "001",
  91. "locator": "gamespy.com",
  92. "challenge": challenge,
  93. "token": authtoken,
  94. }
  95. logger.log(logging.DEBUG, "Login response to %s:%d", *addr)
  96. logger.log(logging.DEBUG, "%s", ret)
  97. return ret
  98. def handle_ac_svcloc(handler, db, addr, post):
  99. """Handle ac svcloc request."""
  100. # Get service based on service id number
  101. ret = {
  102. "retry": "0",
  103. "returncd": "007",
  104. "statusdata": "Y"
  105. }
  106. authtoken = db.generate_authtoken(post["userid"], post)
  107. if 'svc' in post:
  108. if post["svc"] in ("9000", "9001"):
  109. # DLC host = 9000
  110. # In case the client's DNS isn't redirecting to
  111. # dls1.nintendowifi.net
  112. # NB: NAS config overrides this if set
  113. svchost = dwc_config.get_svchost('NasServer')
  114. ret["svchost"] = svchost if svchost else handler.headers['host']
  115. # Brawl has 2 host headers which Apache chokes
  116. # on, so only return the first one or else it
  117. # won't work
  118. ret["svchost"] = ret["svchost"].split(',')[0]
  119. if post["svc"] == 9000:
  120. ret["token"] = authtoken
  121. else:
  122. ret["servicetoken"] = authtoken
  123. elif post["svc"] == "0000":
  124. # Pokemon requests this for some things
  125. ret["servicetoken"] = authtoken
  126. ret["svchost"] = "n/a"
  127. else:
  128. # Empty svc - Fix Error Code 24101 (Boom Street)
  129. ret["svchost"] = "n/a"
  130. ret["servicetoken"] = authtoken
  131. logger.log(logging.DEBUG, "Svcloc response to %s:%d", *addr)
  132. logger.log(logging.DEBUG, "%s", ret)
  133. return ret
  134. def handle_ac(handler, addr, post):
  135. """Handle ac POST request."""
  136. logger.log(logging.DEBUG, "Ac request to %s from %s:%d",
  137. handler.path, *addr)
  138. logger.log(logging.DEBUG, "%s", post)
  139. action = str(post["action"]).lower()
  140. command = handler.ac_actions.get(action, handle_ac_action)
  141. ret = command(handler, gs_database.GamespyDatabase(), addr, post)
  142. ret.update({"datetime": time.strftime("%Y%m%d%H%M%S")})
  143. handler.send_response(200)
  144. handler.send_header("Content-type", "text/plain")
  145. handler.send_header("NODE", "wifiappe1")
  146. return utils.dict_to_qs(ret)
  147. def handle_pr(handler, addr, post):
  148. """Handle pr POST request."""
  149. logger.log(logging.DEBUG, "Pr request to %s from %s:%d",
  150. handler.path, *addr)
  151. logger.log(logging.DEBUG, "%s", post)
  152. words = len(post["words"].split('\t'))
  153. wordsret = "0" * words
  154. ret = {
  155. "prwords": wordsret,
  156. "returncd": "000",
  157. "datetime": time.strftime("%Y%m%d%H%M%S")
  158. }
  159. for l in "ACEJKP":
  160. ret["prwords" + l] = wordsret
  161. handler.send_response(200)
  162. handler.send_header("Content-type", "text/plain")
  163. handler.send_header("NODE", "wifiappe1")
  164. logger.log(logging.DEBUG, "Pr response to %s:%d", *addr)
  165. logger.log(logging.DEBUG, "%s", ret)
  166. return utils.dict_to_qs(ret)
  167. class NasHTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  168. """Nintendo NAS server handler."""
  169. post_paths = {
  170. "/ac": handle_ac,
  171. "/pr": handle_pr,
  172. }
  173. ac_actions = {
  174. "acctcreate": handle_ac_acctcreate,
  175. "login": handle_ac_login,
  176. "svcloc": handle_ac_svcloc,
  177. }
  178. def version_string(self):
  179. return "Nintendo Wii (http)"
  180. def do_GET(self):
  181. """Handle GET request."""
  182. try:
  183. # conntest server
  184. self.send_response(200)
  185. self.send_header("Content-type", "text/html")
  186. self.send_header("X-Organization", "Nintendo")
  187. self.send_header("Server", "BigIP")
  188. self.end_headers()
  189. self.wfile.write("ok")
  190. except:
  191. logger.log(logging.ERROR, "Exception occurred on GET request!")
  192. logger.log(logging.ERROR, "%s", traceback.format_exc())
  193. def do_POST(self):
  194. try:
  195. length = int(self.headers['content-length'])
  196. post = utils.qs_to_dict(self.rfile.read(length))
  197. client_address = (
  198. self.headers.get('x-forwarded-for', self.client_address[0]),
  199. self.client_address[1]
  200. )
  201. post['ipaddr'] = client_address[0]
  202. command = self.post_paths.get(self.path, handle_post)
  203. ret = command(self, client_address, post)
  204. if ret is not None:
  205. self.send_header("Content-Length", str(len(ret)))
  206. self.end_headers()
  207. self.wfile.write(ret)
  208. except:
  209. logger.log(logging.ERROR, "Exception occurred on POST request!")
  210. logger.log(logging.ERROR, "%s", traceback.format_exc())
  211. class NasHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
  212. """Threading HTTP server."""
  213. pass
  214. class NasServer(object):
  215. def start(self):
  216. address = dwc_config.get_ip_port('NasServer')
  217. httpd = NasHTTPServer(address, NasHTTPServerHandler)
  218. logger.log(logging.INFO, "Now listening for connections on %s:%d...",
  219. *address)
  220. httpd.serve_forever()
  221. if __name__ == "__main__":
  222. nas = NasServer()
  223. nas.start()