dwc_network_server_emulator/dls1_server.py
Sepalani d3eed4c430 DLS1 server added
Split NAS's DLS1 code
2018-02-20 17:28:09 +01:00

162 lines
5.0 KiB
Python

"""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 BaseHTTPServer
import SocketServer
import os
import traceback
from other import dlc, utils
import dwc_config
logger = dwc_config.get_logger('Dls1Server')
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_download_action(handler, dlc_path, post):
"""Handle unknown download action request."""
logger.log(logging.WARNING, "Unknown download action: %s", handler.path)
handler.send_response(200)
return None
def handle_download_count(handler, dlc_path, post):
"""Handle download count request."""
ret = dlc.download_count(dlc_path, post)
handler.send_response(200)
handler.send_header("Content-type", "text/plain")
handler.send_header("X-DLS-Host", "http://127.0.0.1/")
return ret
def handle_download_list(handler, dlc_path, post):
"""Handle download list request."""
ret = dlc.download_list(dlc_path, post)
handler.send_response(200)
handler.send_header("Content-type", "text/plain")
handler.send_header("X-DLS-Host", "http://127.0.0.1/")
return ret
def handle_download_contents(handler, dlc_path, post):
"""Handle download contents request."""
ret = dlc.download_contents(dlc_path, post)
if ret is None:
handler.send_response(404)
else:
handler.send_response(200)
handler.send_header("Content-type", "application/x-dsdl")
handler.send_header("Content-Disposition",
'attachment; filename="%s"' % post["contents"])
handler.send_header("X-DLS-Host", "http://127.0.0.1/")
return ret
def handle_download(handler, addr, post):
"""Handle download POST request."""
logger.log(logging.DEBUG, "Download request to %s from %s:%d",
handler.path, *addr)
logger.log(logging.DEBUG, "%s", post)
action = str(post["action"]).lower()
dlc_dir = os.path.abspath("dlc")
dlc_path = os.path.abspath(os.path.join("dlc", post["gamecd"]))
if os.path.commonprefix([dlc_dir, dlc_path]) != dlc_dir:
logging.log(logging.WARNING,
'Attempted directory traversal attack "%s",'
' cancelling.', dlc_path)
handler.send_response(403)
return
command = handler.download_actions.get(action, handle_download_action)
ret = command(handler, dlc_path, post)
logger.log(logging.DEBUG, "Download response to %s:%d", *addr)
return ret
class Dls1HTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""Nintendo Dls1 server handler."""
post_paths = {
"/download": handle_download
}
download_actions = {
"count": handle_download_count,
"list": handle_download_list,
"contents": handle_download_contents,
}
def version_string(self):
return "Nintendo Wii (http)"
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 Dls1HTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
"""Threading HTTP server."""
pass
class Dls1Server(object):
def start(self):
address = dwc_config.get_ip_port('Dls1Server')
httpd = Dls1HTTPServer(address, Dls1HTTPServerHandler)
logger.log(logging.INFO, "Now listening for connections on %s:%d...",
*address)
httpd.serve_forever()
if __name__ == "__main__":
dls1 = Dls1Server()
dls1.start()