forked from Hay1tsme/artemis
sao: backport changes from diana
This commit is contained in:
@ -1,17 +1,20 @@
|
||||
from typing import Tuple, Dict, List
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
from starlette.responses import Response, PlainTextResponse, FileResponse
|
||||
from starlette.routing import Route
|
||||
import yaml
|
||||
import logging, coloredlogs
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
from os import path
|
||||
from Crypto.Cipher import Blowfish
|
||||
from hashlib import md5
|
||||
from Crypto.Hash import MD5
|
||||
import secrets
|
||||
import traceback
|
||||
import sys
|
||||
|
||||
from core import CoreConfig, Utils
|
||||
from core import CoreConfig
|
||||
from core.title import BaseServlet
|
||||
from core.utils import Utils
|
||||
from titles.sao.config import SaoConfig
|
||||
from titles.sao.const import SaoConstants
|
||||
from titles.sao.base import SaoBase
|
||||
@ -55,13 +58,15 @@ class SaoServlet(BaseServlet):
|
||||
self.static_hash = None
|
||||
|
||||
if self.game_cfg.hash.verify_hash:
|
||||
self.static_hash = md5(self.game_cfg.hash.hash_base.encode()).digest() # Greate hashing guys, really validates the data
|
||||
|
||||
self.static_hash = MD5.new(self.game_cfg.hash.hash_base.encode()).digest() # Greate hashing guys, really validates the data
|
||||
|
||||
def get_routes(self) -> List[Route]:
|
||||
return [
|
||||
Route("/{datecode:int}/proto/if/{category:str}/{endpoint:str}", self.render_POST, methods=['POST'])
|
||||
Route("/{datecode:int}/proto/if/{category:str}/{endpoint:str}", self.render_POST, methods=['POST']),
|
||||
Route("/saoresource/{resource_type:str}/{resource_id:int}/{endpoint:str}", self.handle_resource),
|
||||
Route("/system_status.php", self.render_system_status),
|
||||
]
|
||||
|
||||
|
||||
@classmethod
|
||||
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
|
||||
game_cfg = SaoConfig()
|
||||
@ -75,29 +80,25 @@ class SaoServlet(BaseServlet):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
||||
port_ssl = Utils.get_title_port_ssl(self.core_cfg)
|
||||
port_normal = Utils.get_title_port(self.core_cfg)
|
||||
|
||||
proto = "http"
|
||||
port = f":{port_normal}" if port_normal != 80 else ""
|
||||
def get_allnet_info(self, game_id: str, int_ver: int, serial: str) -> Tuple[str, str]:
|
||||
if self.core_cfg.server.is_using_proxy:
|
||||
return (f"https://{self.core_cfg.server.hostname}/", "")
|
||||
|
||||
if self.game_cfg.server.use_https:
|
||||
proto = "https"
|
||||
port = f":{port_ssl}" if port_ssl != 443 else ""
|
||||
return (f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/", "")
|
||||
|
||||
return (f"{proto}://{self.core_cfg.server.hostname}{port}/", "")
|
||||
|
||||
|
||||
def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]:
|
||||
def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, List[str], List[str]]:
|
||||
if not self.game_cfg.server.enable:
|
||||
return (False, [], [])
|
||||
|
||||
return (True, SaoConstants.GAME_CDS, SaoConstants.NETID_PREFIX)
|
||||
|
||||
async def render_system_status(self, request: Request) -> bytes:
|
||||
return PlainTextResponse("open")
|
||||
|
||||
async def render_POST(self, request: Request) -> bytes:
|
||||
endpoint = request.path_params.get('endpoint', '')
|
||||
endpoint = request.path_params['endpoint']
|
||||
ip = Utils.get_ip_addr(request)
|
||||
iv = b""
|
||||
|
||||
req_raw = await request.body()
|
||||
@ -122,10 +123,23 @@ class SaoServlet(BaseServlet):
|
||||
else:
|
||||
req_data = req_raw[40:]
|
||||
|
||||
handler = getattr(self.base, f"handle_{cmd_str}", self.base.handle_noop)
|
||||
self.logger.info(f"{endpoint} - {cmd_str} request")
|
||||
self.logger.debug(f"Request: {req_raw.hex()}")
|
||||
resp = await handler(req_header, req_data)
|
||||
self.logger.debug(f"{endpoint} ({cmd_str}) Request from {ip}: {req_raw.hex()}")
|
||||
handler = getattr(self.base, f"handle_{cmd_str}", None)
|
||||
if handler is None:
|
||||
self.logger.info(f"Using Generic handler for {endpoint}")
|
||||
handler = self.base.handle_noop
|
||||
|
||||
try:
|
||||
resp = await handler(req_header, req_data, ip)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error handling {endpoint} - {e}")
|
||||
tp, val, tb = sys.exc_info()
|
||||
traceback.print_exception(tp, val, tb, limit=5)
|
||||
with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "sao"), "a") as f:
|
||||
traceback.print_exception(tp, val, tb, limit=5, file=f)
|
||||
|
||||
resp = SaoNoopResponse(req_header.cmd + 1).make()
|
||||
|
||||
if resp is None:
|
||||
resp = SaoNoopResponse(req_header.cmd + 1).make()
|
||||
@ -138,7 +152,7 @@ class SaoServlet(BaseServlet):
|
||||
|
||||
else:
|
||||
self.logger.error(f"Unknown response type {type(resp)}")
|
||||
return Response()
|
||||
return SaoNoopResponse(req_header.cmd + 1).make()
|
||||
|
||||
self.logger.debug(f"Response: {resp.hex()}")
|
||||
|
||||
@ -154,6 +168,17 @@ class SaoServlet(BaseServlet):
|
||||
tmp = struct.pack("!I", crypt_data_len) # does it want the length of the encrypted response??
|
||||
resp = resp[:20] + tmp + iv + data_crypt
|
||||
self.logger.debug(f"Encrypted Response: {resp.hex()}")
|
||||
|
||||
|
||||
return Response(resp, media_type="text/html; charset=utf-8")
|
||||
|
||||
return Response(resp)
|
||||
|
||||
async def handle_resource(self, request: Request) -> bytes:
|
||||
# TODO: better guard against path traversal attacks
|
||||
resource_type = request.path_params['resource_type'].replace(".\\", "").replace("..\\", "")
|
||||
resource_id = request.path_params['resource_id']
|
||||
endpoint = request.path_params['endpoint'].replace(".\\", "").replace("..\\", "")
|
||||
req_ip = Utils.get_ip_addr(request)
|
||||
|
||||
self.logger.debug(f"{req_ip} requested {resource_type} id {resource_id} {endpoint}")
|
||||
if path.exists(f"./titles/sao/data/{resource_type}/{resource_id}/{endpoint}"):
|
||||
return FileResponse(f"./titles/sao/data/{resource_type}/{resource_id}/{endpoint}")
|
||||
return Response(status_code=404)
|
||||
|
Reference in New Issue
Block a user