dls1_server.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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 http.server
  20. import socketserver
  21. import os
  22. import traceback
  23. from other import dlc, utils
  24. import dwc_config
  25. logger = dwc_config.get_logger('Dls1Server')
  26. def handle_post(handler, addr, post):
  27. """Handle unknown path."""
  28. logger.log(logging.WARNING, "Unknown path request %s from %s:%d!",
  29. handler.path, *addr)
  30. handler.send_response(404)
  31. return None
  32. def handle_download_action(handler, dlc_path, post):
  33. """Handle unknown download action request."""
  34. logger.log(logging.WARNING, "Unknown download action: %s", handler.path)
  35. handler.send_response(200)
  36. return None
  37. def handle_download_count(handler, dlc_path, post):
  38. """Handle download count request."""
  39. ret = dlc.download_count(dlc_path, post)
  40. handler.send_response(200)
  41. handler.send_header("Content-type", "text/plain")
  42. handler.send_header("X-DLS-Host", "http://127.0.0.1/")
  43. return ret
  44. def handle_download_list(handler, dlc_path, post):
  45. """Handle download list request."""
  46. ret = dlc.download_list(dlc_path, post)
  47. handler.send_response(200)
  48. handler.send_header("Content-type", "text/plain")
  49. handler.send_header("X-DLS-Host", "http://127.0.0.1/")
  50. return ret
  51. def handle_download_contents(handler, dlc_path, post):
  52. """Handle download contents request."""
  53. ret = dlc.download_contents(dlc_path, post)
  54. if ret is None:
  55. handler.send_response(404)
  56. else:
  57. handler.send_response(200)
  58. handler.send_header("Content-type", "application/x-dsdl")
  59. handler.send_header("Content-Disposition",
  60. 'attachment; filename="%s"' % post["contents"])
  61. handler.send_header("X-DLS-Host", "http://127.0.0.1/")
  62. return ret
  63. def handle_download(handler, addr, post):
  64. """Handle download POST request."""
  65. logger.log(logging.DEBUG, "Download request to %s from %s:%d",
  66. handler.path, *addr)
  67. logger.log(logging.DEBUG, "%s", post)
  68. action = str(post["action"]).lower()
  69. dlc_dir = os.path.abspath("dlc")
  70. dlc_path = os.path.abspath(os.path.join("dlc", post["gamecd"]))
  71. if os.path.commonprefix([dlc_dir, dlc_path]) != dlc_dir:
  72. logging.log(logging.WARNING,
  73. 'Attempted directory traversal attack "%s",'
  74. ' cancelling.', dlc_path)
  75. handler.send_response(403)
  76. return
  77. command = handler.download_actions.get(action, handle_download_action)
  78. ret = command(handler, dlc_path, post)
  79. logger.log(logging.DEBUG, "Download response to %s:%d", *addr)
  80. return ret
  81. class Dls1HTTPServerHandler(http.server.BaseHTTPRequestHandler):
  82. """Nintendo Dls1 server handler."""
  83. post_paths = {
  84. "/download": handle_download
  85. }
  86. download_actions = {
  87. "count": handle_download_count,
  88. "list": handle_download_list,
  89. "contents": handle_download_contents,
  90. }
  91. def version_string(self):
  92. return "Nintendo Wii (http)"
  93. def do_POST(self):
  94. try:
  95. length = int(self.headers['content-length'])
  96. post = utils.qs_to_dict(self.rfile.read(length))
  97. client_address = (
  98. self.headers.get('x-forwarded-for', self.client_address[0]),
  99. self.client_address[1]
  100. )
  101. post['ipaddr'] = client_address[0]
  102. command = self.post_paths.get(self.path, handle_post)
  103. ret = command(self, client_address, post)
  104. if ret is not None:
  105. self.send_header("Content-Length", str(len(ret)))
  106. self.end_headers()
  107. self.wfile.write(ret)
  108. except:
  109. logger.log(logging.ERROR, "Exception occurred on POST request!")
  110. logger.log(logging.ERROR, "%s", traceback.format_exc())
  111. class Dls1HTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
  112. """Threading HTTP server."""
  113. pass
  114. class Dls1Server(object):
  115. def start(self):
  116. address = dwc_config.get_ip_port('Dls1Server')
  117. httpd = Dls1HTTPServer(address, Dls1HTTPServerHandler)
  118. logger.log(logging.INFO, "Now listening for connections on %s:%d...",
  119. *address)
  120. httpd.serve_forever()
  121. if __name__ == "__main__":
  122. dls1 = Dls1Server()
  123. dls1.start()