Merge branch 'develop' into diva_handler_classes

This commit is contained in:
Hay1tsme 2023-03-13 02:27:12 -04:00
commit 3076bdf575
22 changed files with 221 additions and 202 deletions

View File

@ -12,8 +12,8 @@ from Crypto.Signature import PKCS1_v1_5
from time import strptime from time import strptime
from core.config import CoreConfig from core.config import CoreConfig
from core.data import Data
from core.utils import Utils from core.utils import Utils
from core.data import Data
from core.const import * from core.const import *
@ -65,11 +65,11 @@ class AllnetServlet:
self.uri_registry[code] = (uri, host) self.uri_registry[code] = (uri, host)
self.logger.info( self.logger.info(
f"Allnet serving {len(self.uri_registry)} games on port {core_cfg.allnet.port}" f"Serving {len(self.uri_registry)} game codes port {core_cfg.allnet.port}"
) )
def handle_poweron(self, request: Request, _: Dict): def handle_poweron(self, request: Request, _: Dict):
request_ip = request.getClientAddress().host request_ip = Utils.get_ip_addr(request)
try: try:
req_dict = self.allnet_req_to_dict(request.content.getvalue()) req_dict = self.allnet_req_to_dict(request.content.getvalue())
if req_dict is None: if req_dict is None:
@ -78,13 +78,7 @@ class AllnetServlet:
req = AllnetPowerOnRequest(req_dict[0]) req = AllnetPowerOnRequest(req_dict[0])
# Validate the request. Currently we only validate the fields we plan on using # Validate the request. Currently we only validate the fields we plan on using
if ( if not req.game_id or not req.ver or not req.serial or not req.ip:
not req.game_id
or not req.ver
or not req.token
or not req.serial
or not req.ip
):
raise AllnetRequestException( raise AllnetRequestException(
f"Bad auth request params from {request_ip} - {vars(req)}" f"Bad auth request params from {request_ip} - {vars(req)}"
) )
@ -94,7 +88,7 @@ class AllnetServlet:
self.logger.error(e) self.logger.error(e)
return b"" return b""
if req.format_ver == 3: if req.format_ver == "3":
resp = AllnetPowerOnResponse3(req.token) resp = AllnetPowerOnResponse3(req.token)
else: else:
resp = AllnetPowerOnResponse2() resp = AllnetPowerOnResponse2()
@ -168,7 +162,7 @@ class AllnetServlet:
return self.dict_to_http_form_string([vars(resp)]).encode("utf-8") return self.dict_to_http_form_string([vars(resp)]).encode("utf-8")
def handle_dlorder(self, request: Request, _: Dict): def handle_dlorder(self, request: Request, _: Dict):
request_ip = request.getClientAddress().host request_ip = Utils.get_ip_addr(request)
try: try:
req_dict = self.allnet_req_to_dict(request.content.getvalue()) req_dict = self.allnet_req_to_dict(request.content.getvalue())
if req_dict is None: if req_dict is None:
@ -261,7 +255,7 @@ class AllnetServlet:
return resp_str.encode("utf-8") return resp_str.encode("utf-8")
def handle_naomitest(self, request: Request, _: Dict) -> bytes: def handle_naomitest(self, request: Request, _: Dict) -> bytes:
self.logger.info(f"Ping from {request.getClientAddress().host}") self.logger.info(f"Ping from {Utils.get_ip_addr(request)}")
return b"naomi ok" return b"naomi ok"
def kvp_to_dict(self, kvp: List[str]) -> List[Dict[str, Any]]: def kvp_to_dict(self, kvp: List[str]) -> List[Dict[str, Any]]:
@ -346,20 +340,16 @@ class AllnetPowerOnRequest:
def __init__(self, req: Dict) -> None: def __init__(self, req: Dict) -> None:
if req is None: if req is None:
raise AllnetRequestException("Request processing failed") raise AllnetRequestException("Request processing failed")
self.game_id: str = req["game_id"] if "game_id" in req else "" self.game_id: str = req.get("game_id", "")
self.ver: str = req["ver"] if "ver" in req else "" self.ver: str = req.get("ver", "")
self.serial: str = req["serial"] if "serial" in req else "" self.serial: str = req.get("serial", "")
self.ip: str = req["ip"] if "ip" in req else "" self.ip: str = req.get("ip", "")
self.firm_ver: str = req["firm_ver"] if "firm_ver" in req else "" self.firm_ver: str = req.get("firm_ver", "")
self.boot_ver: str = req["boot_ver"] if "boot_ver" in req else "" self.boot_ver: str = req.get("boot_ver", "")
self.encode: str = req["encode"] if "encode" in req else "" self.encode: str = req.get("encode", "")
self.hops = int(req.get("hops", "0"))
try: self.format_ver = req.get("format_ver", "2")
self.hops = int(req["hops"]) if "hops" in req else 0 self.token = int(req.get("token", "0"))
self.format_ver = int(req["format_ver"]) if "format_ver" in req else 2
self.token = int(req["token"]) if "token" in req else 0
except ValueError as e:
raise AllnetRequestException(f"Failed to parse int: {e}")
class AllnetPowerOnResponse3: class AllnetPowerOnResponse3:
@ -413,10 +403,10 @@ class AllnetPowerOnResponse2:
class AllnetDownloadOrderRequest: class AllnetDownloadOrderRequest:
def __init__(self, req: Dict) -> None: def __init__(self, req: Dict) -> None:
self.game_id = req["game_id"] if "game_id" in req else "" self.game_id = req.get("game_id", "")
self.ver = req["ver"] if "ver" in req else "" self.ver = req.get("ver", "")
self.serial = req["serial"] if "serial" in req else "" self.serial = req.get("serial", "")
self.encode = req["encode"] if "encode" in req else "" self.encode = req.get("encode", "")
class AllnetDownloadOrderResponse: class AllnetDownloadOrderResponse:

View File

@ -267,24 +267,6 @@ class MuchaConfig:
self.__config, "core", "mucha", "hostname", default="localhost" self.__config, "core", "mucha", "hostname", default="localhost"
) )
@property
def port(self) -> int:
return CoreConfig.get_config_field(
self.__config, "core", "mucha", "port", default=8444
)
@property
def ssl_cert(self) -> str:
return CoreConfig.get_config_field(
self.__config, "core", "mucha", "ssl_cert", default="cert/server.pem"
)
@property
def signing_key(self) -> str:
return CoreConfig.get_config_field(
self.__config, "core", "mucha", "signing_key", default="cert/billing.key"
)
class CoreConfig(dict): class CoreConfig(dict):
def __init__(self) -> None: def __init__(self) -> None:

View File

@ -71,7 +71,8 @@ class Data:
games = Utils.get_all_titles() games = Utils.get_all_titles()
for game_dir, game_mod in games.items(): for game_dir, game_mod in games.items():
try: try:
title_db = game_mod.database(self.config) if hasattr(game_mod, "database") and hasattr(game_mod, "current_schema_version"):
game_mod.database(self.config)
metadata.create_all(self.__engine.connect()) metadata.create_all(self.__engine.connect())
self.base.set_schema_ver( self.base.set_schema_ver(
@ -109,7 +110,8 @@ class Data:
mod = importlib.import_module(f"titles.{dir}") mod = importlib.import_module(f"titles.{dir}")
try: try:
title_db = mod.database(self.config) if hasattr(mod, "database"):
mod.database(self.config)
metadata.drop_all(self.__engine.connect()) metadata.drop_all(self.__engine.connect())
except Exception as e: except Exception as e:

View File

@ -10,9 +10,8 @@ from twisted.python.components import registerAdapter
import jinja2 import jinja2
import bcrypt import bcrypt
from core.config import CoreConfig from core import CoreConfig, Utils
from core.data import Data from core.data import Data
from core.utils import Utils
class IUserSession(Interface): class IUserSession(Interface):
@ -143,7 +142,7 @@ class FE_Gate(FE_Base):
def render_POST(self, request: Request): def render_POST(self, request: Request):
uri = request.uri.decode() uri = request.uri.decode()
ip = request.getClientAddress().host ip = Utils.get_ip_addr(request)
if uri == "/gate/gate.login": if uri == "/gate/gate.login":
access_code: str = request.args[b"access_code"][0].decode() access_code: str = request.args[b"access_code"][0].decode()

View File

@ -6,7 +6,7 @@ from twisted.web.http import Request
from datetime import datetime from datetime import datetime
import pytz import pytz
from core.config import CoreConfig from core import CoreConfig
from core.utils import Utils from core.utils import Utils
@ -47,11 +47,13 @@ class MuchaServlet:
self.mucha_registry.append(game_cd) self.mucha_registry.append(game_cd)
self.logger.info( self.logger.info(
f"Serving {len(self.mucha_registry)} games on port {self.config.mucha.port}" f"Serving {len(self.mucha_registry)} games"
) )
def handle_boardauth(self, request: Request, _: Dict) -> bytes: def handle_boardauth(self, request: Request, _: Dict) -> bytes:
req_dict = self.mucha_preprocess(request.content.getvalue()) req_dict = self.mucha_preprocess(request.content.getvalue())
client_ip = Utils.get_ip_addr(request)
if req_dict is None: if req_dict is None:
self.logger.error( self.logger.error(
f"Error processing mucha request {request.content.getvalue()}" f"Error processing mucha request {request.content.getvalue()}"
@ -61,7 +63,7 @@ class MuchaServlet:
req = MuchaAuthRequest(req_dict) req = MuchaAuthRequest(req_dict)
self.logger.debug(f"Mucha request {vars(req)}") self.logger.debug(f"Mucha request {vars(req)}")
self.logger.info( self.logger.info(
f"Boardauth request from {request.getClientAddress().host} for {req.gameVer}" f"Boardauth request from {client_ip} for {req.gameVer}"
) )
if req.gameCd not in self.mucha_registry: if req.gameCd not in self.mucha_registry:
@ -71,7 +73,7 @@ class MuchaServlet:
# TODO: Decrypt S/N # TODO: Decrypt S/N
resp = MuchaAuthResponse( resp = MuchaAuthResponse(
f"{self.config.mucha.hostname}{':' + self.config.mucha.port if self.config.server.is_develop else ''}" f"{self.config.mucha.hostname}{':' + str(self.config.allnet.port) if self.config.server.is_develop else ''}"
) )
self.logger.debug(f"Mucha response {vars(resp)}") self.logger.debug(f"Mucha response {vars(resp)}")
@ -80,6 +82,8 @@ class MuchaServlet:
def handle_updatecheck(self, request: Request, _: Dict) -> bytes: def handle_updatecheck(self, request: Request, _: Dict) -> bytes:
req_dict = self.mucha_preprocess(request.content.getvalue()) req_dict = self.mucha_preprocess(request.content.getvalue())
client_ip = Utils.get_ip_addr(request)
if req_dict is None: if req_dict is None:
self.logger.error( self.logger.error(
f"Error processing mucha request {request.content.getvalue()}" f"Error processing mucha request {request.content.getvalue()}"
@ -89,7 +93,7 @@ class MuchaServlet:
req = MuchaUpdateRequest(req_dict) req = MuchaUpdateRequest(req_dict)
self.logger.debug(f"Mucha request {vars(req)}") self.logger.debug(f"Mucha request {vars(req)}")
self.logger.info( self.logger.info(
f"Updatecheck request from {request.getClientAddress().host} for {req.gameVer}" f"Updatecheck request from {client_ip} for {req.gameVer}"
) )
if req.gameCd not in self.mucha_registry: if req.gameCd not in self.mucha_registry:
@ -132,24 +136,19 @@ class MuchaServlet:
class MuchaAuthRequest: class MuchaAuthRequest:
def __init__(self, request: Dict) -> None: def __init__(self, request: Dict) -> None:
self.gameVer = ( # gameCd + boardType + countryCd + version
"" if "gameVer" not in request else request["gameVer"] self.gameVer = request.get("gameVer", "")
) # gameCd + boardType + countryCd + version self.sendDate = request.get("sendDate", "") # %Y%m%d
self.sendDate = ( self.serialNum = request.get("serialNum", "")
"" if "sendDate" not in request else request["sendDate"] self.gameCd = request.get("gameCd", "")
) # %Y%m%d self.boardType = request.get("boardType", "")
self.serialNum = "" if "serialNum" not in request else request["serialNum"] self.boardId = request.get("boardId", "")
self.gameCd = "" if "gameCd" not in request else request["gameCd"] self.mac = request.get("mac", "")
self.boardType = "" if "boardType" not in request else request["boardType"] self.placeId = request.get("placeId", "")
self.boardId = "" if "boardId" not in request else request["boardId"] self.storeRouterIp = request.get("storeRouterIp", "")
self.mac = "" if "mac" not in request else request["mac"] self.countryCd = request.get("countryCd", "")
self.placeId = "" if "placeId" not in request else request["placeId"] self.useToken = request.get("useToken", "")
self.storeRouterIp = ( self.allToken = request.get("allToken", "")
"" if "storeRouterIp" not in request else request["storeRouterIp"]
)
self.countryCd = "" if "countryCd" not in request else request["countryCd"]
self.useToken = "" if "useToken" not in request else request["useToken"]
self.allToken = "" if "allToken" not in request else request["allToken"]
class MuchaAuthResponse: class MuchaAuthResponse:
@ -199,14 +198,12 @@ class MuchaAuthResponse:
class MuchaUpdateRequest: class MuchaUpdateRequest:
def __init__(self, request: Dict) -> None: def __init__(self, request: Dict) -> None:
self.gameVer = "" if "gameVer" not in request else request["gameVer"] self.gameVer = request.get("gameVer", "")
self.gameCd = "" if "gameCd" not in request else request["gameCd"] self.gameCd = request.get("gameCd", "")
self.serialNum = "" if "serialNum" not in request else request["serialNum"] self.serialNum = request.get("serialNum", "")
self.countryCd = "" if "countryCd" not in request else request["countryCd"] self.countryCd = request.get("countryCd", "")
self.placeId = "" if "placeId" not in request else request["placeId"] self.placeId = request.get("placeId", "")
self.storeRouterIp = ( self.storeRouterIp = request.get("storeRouterIp", "")
"" if "storeRouterIp" not in request else request["storeRouterIp"]
)
class MuchaUpdateResponse: class MuchaUpdateResponse:

View File

@ -68,7 +68,7 @@ class TitleServlet:
self.logger.error(f"{folder} missing game_code or index in __init__.py") self.logger.error(f"{folder} missing game_code or index in __init__.py")
self.logger.info( self.logger.info(
f"Serving {len(self.title_registry)} game codes on port {core_cfg.title.port}" f"Serving {len(self.title_registry)} game codes {'on port ' + str(core_cfg.title.port) if core_cfg.title.port > 0 else ''}"
) )
def render_GET(self, request: Request, endpoints: dict) -> bytes: def render_GET(self, request: Request, endpoints: dict) -> bytes:

View File

@ -1,5 +1,6 @@
from typing import Dict, Any from typing import Dict, Any
from types import ModuleType from types import ModuleType
from twisted.web.http import Request
import logging import logging
import importlib import importlib
from os import walk from os import walk
@ -21,3 +22,7 @@ class Utils:
logging.getLogger("core").error(f"get_all_titles: {dir} - {e}") logging.getLogger("core").error(f"get_all_titles: {dir} - {e}")
raise raise
return ret return ret
@classmethod
def get_ip_addr(cls, req: Request) -> str:
return req.getAllHeaders()[b"x-forwarded-for"].decode() if b"x-forwarded-for" in req.getAllHeaders() else req.getClientAddress().host

View File

@ -48,6 +48,3 @@ mucha:
enable: False enable: False
hostname: "localhost" hostname: "localhost"
loglevel: "info" loglevel: "info"
port: 8444
ssl_key: "cert/server.key"
ssl_cert: "cert/server.pem"

View File

@ -4,6 +4,8 @@ server {
server_name naominet.jp; server_name naominet.jp;
location / { location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_request_headers on;
proxy_pass http://localhost:8000/; proxy_pass http://localhost:8000/;
} }
} }
@ -14,6 +16,8 @@ server {
server_name your.hostname.here; server_name your.hostname.here;
location / { location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_request_headers on;
proxy_pass http://localhost:8080/; proxy_pass http://localhost:8080/;
} }
} }
@ -75,6 +79,8 @@ server {
ssl_prefer_server_ciphers off; ssl_prefer_server_ciphers off;
location / { location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_request_headers on;
proxy_pass http://localhost:8080/; proxy_pass http://localhost:8080/;
} }
} }
@ -95,6 +101,8 @@ server {
ssl_prefer_server_ciphers off; ssl_prefer_server_ciphers off;
location / { location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_request_headers on;
proxy_pass http://localhost:8080/SDBT/104/; proxy_pass http://localhost:8080/SDBT/104/;
} }
} }
@ -131,6 +139,8 @@ server {
add_header Strict-Transport-Security "max-age=63072000" always; add_header Strict-Transport-Security "max-age=63072000" always;
location / { location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_request_headers on;
proxy_pass http://localhost:8090/; proxy_pass http://localhost:8090/;
} }
} }

View File

@ -3,9 +3,6 @@ server:
enable: True enable: True
loglevel: "info" loglevel: "info"
port: 9000 port: 9000
port_matching: 9001 port_stun: 9001
port_stun: 9002 port_turn: 9002
port_turn: 9003 port_admission: 9003
port_admission: 9004
ssl_cert: cert/pokken.crt
ssl_key: cert/pokken.key

View File

@ -73,7 +73,7 @@ class HttpDispatcher(resource.Resource):
"mucha_updatacheck", "mucha_updatacheck",
"/mucha/updatacheck.do", "/mucha/updatacheck.do",
controller="mucha", controller="mucha",
action="handle_updatacheck", action="handle_updatecheck",
conditions=dict(method=["POST"]), conditions=dict(method=["POST"]),
) )
@ -96,9 +96,11 @@ class HttpDispatcher(resource.Resource):
def render_GET(self, request: Request) -> bytes: def render_GET(self, request: Request) -> bytes:
test = self.map_get.match(request.uri.decode()) test = self.map_get.match(request.uri.decode())
client_ip = Utils.get_ip_addr(request)
if test is None: if test is None:
self.logger.debug( self.logger.debug(
f"Unknown GET endpoint {request.uri.decode()} from {request.getClientAddress().host} to port {request.getHost().port}" f"Unknown GET endpoint {request.uri.decode()} from {client_ip} to port {request.getHost().port}"
) )
request.setResponseCode(404) request.setResponseCode(404)
return b"Endpoint not found." return b"Endpoint not found."
@ -107,9 +109,11 @@ class HttpDispatcher(resource.Resource):
def render_POST(self, request: Request) -> bytes: def render_POST(self, request: Request) -> bytes:
test = self.map_post.match(request.uri.decode()) test = self.map_post.match(request.uri.decode())
client_ip = Utils.get_ip_addr(request)
if test is None: if test is None:
self.logger.debug( self.logger.debug(
f"Unknown POST endpoint {request.uri.decode()} from {request.getClientAddress().host} to port {request.getHost().port}" f"Unknown POST endpoint {request.uri.decode()} from {client_ip} to port {request.getHost().port}"
) )
request.setResponseCode(404) request.setResponseCode(404)
return b"Endpoint not found." return b"Endpoint not found."
@ -163,6 +167,9 @@ if __name__ == "__main__":
if path.exists(f"{args.config}/core.yaml"): if path.exists(f"{args.config}/core.yaml"):
cfg.update(yaml.safe_load(open(f"{args.config}/core.yaml"))) cfg.update(yaml.safe_load(open(f"{args.config}/core.yaml")))
if not path.exists(cfg.server.log_dir):
mkdir(cfg.server.log_dir)
logger = logging.getLogger("core") logger = logging.getLogger("core")
log_fmt_str = "[%(asctime)s] Core | %(levelname)s | %(message)s" log_fmt_str = "[%(asctime)s] Core | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str) log_fmt = logging.Formatter(log_fmt_str)
@ -182,9 +189,6 @@ if __name__ == "__main__":
logger.setLevel(log_lv) logger.setLevel(log_lv)
coloredlogs.install(level=log_lv, logger=logger, fmt=log_fmt_str) coloredlogs.install(level=log_lv, logger=logger, fmt=log_fmt_str)
if not path.exists(cfg.server.log_dir):
mkdir(cfg.server.log_dir)
if not access(cfg.server.log_dir, W_OK): if not access(cfg.server.log_dir, W_OK):
logger.error( logger.error(
f"Log directory {cfg.server.log_dir} NOT writable, please check permissions" f"Log directory {cfg.server.log_dir} NOT writable, please check permissions"

View File

@ -9,8 +9,7 @@ import logging, coloredlogs
from logging.handlers import TimedRotatingFileHandler from logging.handlers import TimedRotatingFileHandler
from typing import List, Optional from typing import List, Optional
from core import CoreConfig from core import CoreConfig, Utils
from core.utils import Utils
class BaseReader: class BaseReader:

View File

@ -11,7 +11,7 @@ from Crypto.Util.Padding import pad
from os import path from os import path
from typing import Tuple from typing import Tuple
from core import CoreConfig from core import CoreConfig, Utils
from titles.chuni.config import ChuniConfig from titles.chuni.config import ChuniConfig
from titles.chuni.const import ChuniConstants from titles.chuni.const import ChuniConstants
from titles.chuni.base import ChuniBase from titles.chuni.base import ChuniBase
@ -111,6 +111,7 @@ class ChuniServlet:
encrtped = False encrtped = False
internal_ver = 0 internal_ver = 0
endpoint = url_split[len(url_split) - 1] endpoint = url_split[len(url_split) - 1]
client_ip = Utils.get_ip_addr(request)
if version < 105: # 1.0 if version < 105: # 1.0
internal_ver = ChuniConstants.VER_CHUNITHM internal_ver = ChuniConstants.VER_CHUNITHM
@ -179,7 +180,7 @@ class ChuniServlet:
req_data = json.loads(unzip) req_data = json.loads(unzip)
self.logger.info( self.logger.info(
f"v{version} {endpoint} request from {request.getClientAddress().host}" f"v{version} {endpoint} request from {client_ip}"
) )
self.logger.debug(req_data) self.logger.debug(req_data)

View File

@ -60,6 +60,9 @@ class ChuniNew(ChuniBase):
"isAou": "false", "isAou": "false",
} }
def handle_remove_token_api_request(self, data: Dict) -> Dict:
return { "returnCode": "1" }
def handle_delete_token_api_request(self, data: Dict) -> Dict: def handle_delete_token_api_request(self, data: Dict) -> Dict:
return {"returnCode": "1"} return {"returnCode": "1"}

View File

@ -26,6 +26,8 @@ class CardMaker136(CardMakerBase):
ret["gameConnectList"][0]["titleUri"] = f"{uri}/SDHD/205/" ret["gameConnectList"][0]["titleUri"] = f"{uri}/SDHD/205/"
ret["gameConnectList"][1]["titleUri"] = f"{uri}/SDEZ/125/" ret["gameConnectList"][1]["titleUri"] = f"{uri}/SDEZ/125/"
ret["gameConnectList"][2]["titleUri"] = f"{uri}/SDDT/135/" ret["gameConnectList"][2]["titleUri"] = f"{uri}/SDDT/135/"
return ret
def handle_get_game_setting_api_request(self, data: Dict) -> Dict: def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
ret = super().handle_get_game_setting_api_request(data) ret = super().handle_get_game_setting_api_request(data)

View File

@ -98,11 +98,11 @@ class CxbServlet(resource.Resource):
).listen(server.Site(CxbServlet(self.core_cfg, self.cfg_dir))) ).listen(server.Site(CxbServlet(self.core_cfg, self.cfg_dir)))
self.logger.info( self.logger.info(
f"Crossbeats title server ready on port {self.game_cfg.server.port} & {self.game_cfg.server.port_secure}" f"Ready on ports {self.game_cfg.server.port} & {self.game_cfg.server.port_secure}"
) )
else: else:
self.logger.info( self.logger.info(
f"Crossbeats title server ready on port {self.game_cfg.server.port}" f"Ready on port {self.game_cfg.server.port}"
) )
def render_POST(self, request: Request): def render_POST(self, request: Request):

View File

@ -298,6 +298,6 @@ class DivaReader(BaseReader):
tree[key] = ( tree[key] = (
value value
if len(vector) == 1 if len(vector) == 1
else self.add_branch(tree[key] if key in tree else {}, vector[1:], value) else self.add_branch(tree.get(key, {}), vector[1:], value)
) )
return tree return tree

View File

@ -1,6 +1,6 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
import json import json, logging
from typing import Any from typing import Any, Dict
from core.config import CoreConfig from core.config import CoreConfig
from titles.pokken.config import PokkenConfig from titles.pokken.config import PokkenConfig
@ -12,6 +12,7 @@ class PokkenBase:
self.core_cfg = core_cfg self.core_cfg = core_cfg
self.game_cfg = game_cfg self.game_cfg = game_cfg
self.version = 0 self.version = 0
self.logger = logging.getLogger("pokken")
def handle_noop(self, request: Any) -> bytes: def handle_noop(self, request: Any) -> bytes:
res = jackal_pb2.Response() res = jackal_pb2.Response()
@ -20,25 +21,26 @@ class PokkenBase:
return res.SerializeToString() return res.SerializeToString()
def handle_ping(self, request: jackal_pb2.PingRequestData) -> bytes: def handle_ping(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response() res = jackal_pb2.Response()
res.result = 1 res.result = 1
res.type = jackal_pb2.MessageType.PING res.type = jackal_pb2.MessageType.PING
return res.SerializeToString() return res.SerializeToString()
def handle_register_pcb(self, request: jackal_pb2.RegisterPcbRequestData) -> bytes: def handle_register_pcb(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response() res = jackal_pb2.Response()
res.result = 1 res.result = 1
res.type = jackal_pb2.MessageType.REGISTER_PCB res.type = jackal_pb2.MessageType.REGISTER_PCB
self.logger.info(f"Register PCB {request.register_pcb.pcb_id}")
regist_pcb = jackal_pb2.RegisterPcbResponseData() regist_pcb = jackal_pb2.RegisterPcbResponseData()
regist_pcb.server_time = int(datetime.now().timestamp() / 1000) regist_pcb.server_time = int(datetime.now().timestamp())
biwa_setting = { biwa_setting = {
"MatchingServer": { "MatchingServer": {
"host": f"https://{self.game_cfg.server.hostname}", "host": f"https://{self.game_cfg.server.hostname}",
"port": self.game_cfg.server.port_matching, "port": self.game_cfg.server.port,
"url": "/matching", "url": "/SDAK/100/matching",
}, },
"StunServer": { "StunServer": {
"addr": self.game_cfg.server.hostname, "addr": self.game_cfg.server.hostname,
@ -60,7 +62,7 @@ class PokkenBase:
return res.SerializeToString() return res.SerializeToString()
def handle_save_ads(self, request: jackal_pb2.SaveAdsRequestData) -> bytes: def handle_save_ads(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response() res = jackal_pb2.Response()
res.result = 1 res.result = 1
res.type = jackal_pb2.MessageType.SAVE_ADS res.type = jackal_pb2.MessageType.SAVE_ADS
@ -68,7 +70,7 @@ class PokkenBase:
return res.SerializeToString() return res.SerializeToString()
def handle_save_client_log( def handle_save_client_log(
self, request: jackal_pb2.SaveClientLogRequestData self, request: jackal_pb2.Request
) -> bytes: ) -> bytes:
res = jackal_pb2.Response() res = jackal_pb2.Response()
res.result = 1 res.result = 1
@ -77,7 +79,7 @@ class PokkenBase:
return res.SerializeToString() return res.SerializeToString()
def handle_check_diagnosis( def handle_check_diagnosis(
self, request: jackal_pb2.CheckDiagnosisRequestData self, request: jackal_pb2.Request
) -> bytes: ) -> bytes:
res = jackal_pb2.Response() res = jackal_pb2.Response()
res.result = 1 res.result = 1
@ -86,7 +88,7 @@ class PokkenBase:
return res.SerializeToString() return res.SerializeToString()
def handle_load_client_settings( def handle_load_client_settings(
self, request: jackal_pb2.CheckDiagnosisRequestData self, request: jackal_pb2.Request
) -> bytes: ) -> bytes:
res = jackal_pb2.Response() res = jackal_pb2.Response()
res.result = 1 res.result = 1
@ -108,3 +110,36 @@ class PokkenBase:
res.load_client_settings.CopyFrom(settings) res.load_client_settings.CopyFrom(settings)
return res.SerializeToString() return res.SerializeToString()
def handle_load_ranking(self, request: jackal_pb2.Request) -> bytes:
res = jackal_pb2.Response()
res.result = 1
res.type = jackal_pb2.MessageType.LOAD_RANKING
ranking = jackal_pb2.LoadRankingResponseData()
ranking.ranking_id = 1
ranking.ranking_start = 0
ranking.ranking_end = 1
ranking.event_end = True
ranking.modify_date = int(datetime.now().timestamp() / 1000)
res.load_ranking.CopyFrom(ranking)
def handle_matching_noop(self, data: Dict = {}, client_ip: str = "127.0.0.1") -> Dict:
return {}
def handle_matching_start_matching(self, data: Dict = {}, client_ip: str = "127.0.0.1") -> Dict:
return {}
def handle_matching_is_matching(self, data: Dict = {}, client_ip: str = "127.0.0.1") -> Dict:
"""
"sessionId":"12345678",
"A":{
"pcb_id": data["data"]["must"]["pcb_id"],
"gip": client_ip
},
"list":[]
"""
return {}
def handle_matching_stop_matching(self, data: Dict = {}, client_ip: str = "127.0.0.1") -> Dict:
return {}

View File

@ -31,43 +31,24 @@ class PokkenServerConfig:
self.__config, "pokken", "server", "port", default=9000 self.__config, "pokken", "server", "port", default=9000
) )
@property
def port_matching(self) -> int:
return CoreConfig.get_config_field(
self.__config, "pokken", "server", "port_matching", default=9001
)
@property @property
def port_stun(self) -> int: def port_stun(self) -> int:
return CoreConfig.get_config_field( return CoreConfig.get_config_field(
self.__config, "pokken", "server", "port_stun", default=9002 self.__config, "pokken", "server", "port_stun", default=9001
) )
@property @property
def port_turn(self) -> int: def port_turn(self) -> int:
return CoreConfig.get_config_field( return CoreConfig.get_config_field(
self.__config, "pokken", "server", "port_turn", default=9003 self.__config, "pokken", "server", "port_turn", default=9002
) )
@property @property
def port_admission(self) -> int: def port_admission(self) -> int:
return CoreConfig.get_config_field( return CoreConfig.get_config_field(
self.__config, "pokken", "server", "port_admission", default=9004 self.__config, "pokken", "server", "port_admission", default=9003
) )
@property
def ssl_cert(self) -> str:
return CoreConfig.get_config_field(
self.__config, "pokken", "server", "ssl_cert", default="cert/pokken.crt"
)
@property
def ssl_key(self) -> str:
return CoreConfig.get_config_field(
self.__config, "pokken", "server", "ssl_key", default="cert/pokken.key"
)
class PokkenConfig(dict): class PokkenConfig(dict):
def __init__(self) -> None: def __init__(self) -> None:
self.server = PokkenServerConfig(self) self.server = PokkenServerConfig(self)

View File

@ -1,18 +1,20 @@
from typing import Tuple from typing import Tuple
from twisted.web.http import Request from twisted.web.http import Request
from twisted.web import resource, server from twisted.web import resource
from twisted.internet import reactor, endpoints import json, ast
from datetime import datetime
import yaml import yaml
import logging, coloredlogs import logging, coloredlogs
from logging.handlers import TimedRotatingFileHandler from logging.handlers import TimedRotatingFileHandler
from titles.pokken.proto import jackal_pb2 import inflection
from os import path from os import path
from google.protobuf.message import DecodeError from google.protobuf.message import DecodeError
from core.config import CoreConfig from core import CoreConfig, Utils
from titles.pokken.config import PokkenConfig from titles.pokken.config import PokkenConfig
from titles.pokken.base import PokkenBase from titles.pokken.base import PokkenBase
from titles.pokken.const import PokkenConstants from titles.pokken.const import PokkenConstants
from titles.pokken.proto import jackal_pb2
class PokkenServlet(resource.Resource): class PokkenServlet(resource.Resource):
@ -65,17 +67,10 @@ class PokkenServlet(resource.Resource):
if not game_cfg.server.enable: if not game_cfg.server.enable:
return (False, "", "") return (False, "", "")
if core_cfg.server.is_develop:
return (
True,
f"https://{game_cfg.server.hostname}:{game_cfg.server.port}/{game_code}/$v/",
f"{game_cfg.server.hostname}:{game_cfg.server.port}/",
)
return ( return (
True, True,
f"https://{game_cfg.server.hostname}/{game_code}/$v/", f"https://{game_cfg.server.hostname}:{game_cfg.server.port}/{game_code}/$v/",
f"{game_cfg.server.hostname}/", f"{game_cfg.server.hostname}/SDAK/$v/",
) )
@classmethod @classmethod
@ -90,46 +85,19 @@ class PokkenServlet(resource.Resource):
) )
if not game_cfg.server.enable: if not game_cfg.server.enable:
return (False, "", "") return (False, "")
return (True, "PKFN") return (True, "PKF2")
def setup(self): def setup(self) -> None:
""" # TODO: Setup stun, turn (UDP) and admission (WSS) servers
There's currently no point in having this server on because Twisted pass
won't play ball with both the fact that it's TLSv1.1, and because the
types of certs that pokken will accept are too flimsy for Twisted
so it will throw a fit. Currently leaving this here in case a bypass
is discovered in the future, but it's unlikly. For now, just use NGINX.
"""
if self.game_cfg.server.enable and self.core_cfg.server.is_develop:
key_exists = path.exists(self.game_cfg.server.ssl_key)
cert_exists = path.exists(self.game_cfg.server.ssl_cert)
if key_exists and cert_exists:
endpoints.serverFromString(
reactor,
f"ssl:{self.game_cfg.server.port}"
f":interface={self.core_cfg.server.listen_address}:privateKey={self.game_cfg.server.ssl_key}:"
f"certKey={self.game_cfg.server.ssl_cert}",
).listen(server.Site(self))
self.logger.info(
f"Pokken title server ready on port {self.game_cfg.server.port}"
)
else:
self.logger.error(
f"Could not find cert at {self.game_cfg.server.ssl_key} or key at {self.game_cfg.server.ssl_cert}, Pokken not running."
)
def render_POST( def render_POST(
self, request: Request, version: int = 0, endpoints: str = "" self, request: Request, version: int = 0, endpoints: str = ""
) -> bytes: ) -> bytes:
if endpoints == "": if endpoints == "matching":
endpoints = request.uri.decode() return self.handle_matching(request)
if endpoints.startswith("/matching"):
self.logger.info("Matching request")
content = request.content.getvalue() content = request.content.getvalue()
if content == b"": if content == b"":
@ -143,12 +111,50 @@ class PokkenServlet(resource.Resource):
self.logger.warn(f"{e} {content}") self.logger.warn(f"{e} {content}")
return b"" return b""
endpoint = jackal_pb2.MessageType(pokken_request.type).name.lower() endpoint = jackal_pb2.MessageType.DESCRIPTOR.values_by_number[
pokken_request.type
self.logger.info(f"{endpoint} request") ].name.lower()
handler = getattr(self.base, f"handle_{endpoint}", None) handler = getattr(self.base, f"handle_{endpoint}", None)
if handler is None: if handler is None:
self.logger.warn(f"No handler found for message type {endpoint}") self.logger.warn(f"No handler found for message type {endpoint}")
return self.base.handle_noop(pokken_request) return self.base.handle_noop(pokken_request)
return handler(pokken_request)
self.logger.info(f"{endpoint} request from {Utils.get_ip_addr(request)}")
self.logger.debug(pokken_request)
ret = handler(pokken_request)
self.logger.debug(f"Response: {ret}")
return ret
def handle_matching(self, request: Request) -> bytes:
content = request.content.getvalue()
client_ip = Utils.get_ip_addr(request)
if content is None or content == b"":
self.logger.info("Empty matching request")
return json.dumps(self.base.handle_matching_noop()).encode()
json_content = ast.literal_eval(content.decode().replace('null', 'None').replace('true', 'True').replace('false', 'False'))
self.logger.info(f"Matching {json_content['call']} request")
self.logger.debug(json_content)
handler = getattr(self.base, f"handle_matching_{inflection.underscore(json_content['call'])}", None)
if handler is None:
self.logger.warn(f"No handler found for message type {json_content['call']}")
return json.dumps(self.base.handle_matching_noop()).encode()
ret = handler(json_content, client_ip)
if ret is None:
ret = {}
if "result" not in ret:
ret["result"] = "true"
if "data" not in ret:
ret["data"] = {}
if "timestamp" not in ret:
ret["timestamp"] = int(datetime.now().timestamp() * 1000)
self.logger.debug(f"Response {ret}")
return json.dumps(ret).encode()

View File

@ -1,6 +1,7 @@
import yaml import yaml
import jinja2 import jinja2
from twisted.web.http import Request from twisted.web.http import Request
from os import path
from core.frontend import FE_Base from core.frontend import FE_Base
from core.config import CoreConfig from core.config import CoreConfig
@ -16,7 +17,10 @@ class WaccaFrontend(FE_Base):
super().__init__(cfg, environment) super().__init__(cfg, environment)
self.data = WaccaData(cfg) self.data = WaccaData(cfg)
self.game_cfg = WaccaConfig() self.game_cfg = WaccaConfig()
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/wacca.yaml"))) if path.exists(f"{cfg_dir}/{WaccaConstants.CONFIG_NAME}"):
self.game_cfg.update(
yaml.safe_load(open(f"{cfg_dir}/{WaccaConstants.CONFIG_NAME}"))
)
self.nav_name = "Wacca" self.nav_name = "Wacca"
def render_GET(self, request: Request) -> bytes: def render_GET(self, request: Request) -> bytes:

View File

@ -8,7 +8,7 @@ from twisted.web.http import Request
from typing import Dict, Tuple from typing import Dict, Tuple
from os import path from os import path
from core.config import CoreConfig from core import CoreConfig, Utils
from titles.wacca.config import WaccaConfig from titles.wacca.config import WaccaConfig
from titles.wacca.config import WaccaConfig from titles.wacca.config import WaccaConfig
from titles.wacca.const import WaccaConstants from titles.wacca.const import WaccaConstants
@ -89,25 +89,30 @@ class WaccaServlet:
request.responseHeaders.addRawHeader(b"X-Wacca-Hash", hash.hex().encode()) request.responseHeaders.addRawHeader(b"X-Wacca-Hash", hash.hex().encode())
return json.dumps(resp).encode() return json.dumps(resp).encode()
client_ip = Utils.get_ip_addr(request)
try: try:
req_json = json.loads(request.content.getvalue()) req_json = json.loads(request.content.getvalue())
version_full = Version(req_json["appVersion"]) version_full = Version(req_json["appVersion"])
except: except:
self.logger.error( self.logger.error(
f"Failed to parse request toi {request.uri} -> {request.content.getvalue()}" f"Failed to parse request to {url_path} -> {request.content.getvalue()}"
) )
resp = BaseResponse() resp = BaseResponse()
resp.status = 1 resp.status = 1
resp.message = "不正なリクエスト エラーです" resp.message = "不正なリクエスト エラーです"
return end(resp.make()) return end(resp.make())
url_split = url_path.split("/") if "/api/" in url_path:
start_req_idx = url_split.index("api") + 1 func_to_find = (
"handle_" + url_path.partition("api/")[2].replace("/", "_") + "_request"
)
func_to_find = "handle_" else:
for x in range(len(url_split) - start_req_idx): self.logger.error(f"Malformed url {url_path}")
func_to_find += f"{url_split[x + start_req_idx]}_" resp = BaseResponse()
func_to_find += "request" resp.status = 1
resp.message = "Bad URL"
return end(resp.make())
ver_search = int(version_full) ver_search = int(version_full)
@ -136,7 +141,7 @@ class WaccaServlet:
return end(resp.make()) return end(resp.make())
self.logger.info( self.logger.info(
f"v{req_json['appVersion']} {url_path} request from {request.getClientAddress().host} with chipId {req_json['chipId']}" f"v{req_json['appVersion']} {url_path} request from {client_ip} with chipId {req_json['chipId']}"
) )
self.logger.debug(req_json) self.logger.debug(req_json)