mirror of
https://github.com/kuitoi/kuitoi-Server.git
synced 2025-08-17 16:25:36 +00:00
Start adding RCON part!
This commit is contained in:
parent
21dd23cb55
commit
719e705bab
@ -8,4 +8,5 @@ pydantic~=2.0.2
|
|||||||
click~=8.1.4
|
click~=8.1.4
|
||||||
lupa~=2.0
|
lupa~=2.0
|
||||||
toml~=0.10.2
|
toml~=0.10.2
|
||||||
colorama~=0.4.6
|
colorama~=0.4.6
|
||||||
|
cryptography~=41.0.2
|
@ -270,6 +270,9 @@ class Core:
|
|||||||
tasks = []
|
tasks = []
|
||||||
# self.udp.start,
|
# self.udp.start,
|
||||||
f_tasks = [self.tcp.start, self.udp._start, console.start, self.stop_me, self.heartbeat, self.check_alive]
|
f_tasks = [self.tcp.start, self.udp._start, console.start, self.stop_me, self.heartbeat, self.check_alive]
|
||||||
|
if config.RCON['enabled']:
|
||||||
|
rcon = console.rcon(config.RCON['password'], config.RCON['server_ip'], config.RCON['server_port'])
|
||||||
|
f_tasks.append(rcon.start)
|
||||||
for task in f_tasks:
|
for task in f_tasks:
|
||||||
tasks.append(asyncio.create_task(task()))
|
tasks.append(asyncio.create_task(task()))
|
||||||
t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
|
t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
import secrets
|
from typing import Dict
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self, auth=None, game=None, server=None, options=None, web=None):
|
Auth: Dict[str, object]
|
||||||
self.Auth = auth or {"key": None, "private": True}
|
Game: Dict[str, object]
|
||||||
self.Game = game or {"map": "gridmap_v2", "players": 8, "max_cars": 1}
|
Server: Dict[str, object]
|
||||||
self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!",
|
RCON: Dict[str, object]
|
||||||
"server_ip": "0.0.0.0", "server_port": 30814}
|
Options: Dict[str, object]
|
||||||
self.Options = options or {"language": "en", "encoding": "utf8", "speed_limit": 0, "use_queue": False,
|
WebAPI: Dict[str, object]
|
||||||
"debug": False}
|
enc: str | None
|
||||||
self.WebAPI = web or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 8433,
|
|
||||||
"secret_key": secrets.token_hex(16)}
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(Auth=%r, Game=%r, Server=%r)" % (self.__class__.__name__, self.Auth, self.Game, self.Server)
|
return "%s(Auth=%r, Game=%r, Server=%r)" % (self.__class__.__name__, self.Auth, self.Game, self.Server)
|
||||||
class config (Config): ...
|
class config (Config): ...
|
||||||
|
@ -12,15 +12,17 @@ import yaml
|
|||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self, auth=None, game=None, server=None, options=None, web=None):
|
def __init__(self, auth=None, game=None, server=None, rcon=None, options=None, web=None):
|
||||||
self.Auth = auth or {"key": None, "private": True}
|
self.Auth = auth or {"key": None, "private": True}
|
||||||
self.Game = game or {"map": "gridmap_v2", "players": 8, "max_cars": 1}
|
self.Game = game or {"map": "gridmap_v2", "players": 8, "max_cars": 1}
|
||||||
self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!",
|
self.Server = server or {"name": "KuiToi-Server", "description": "Welcome to KuiToi Server!",
|
||||||
"server_ip": "0.0.0.0", "server_port": 30814}
|
"server_ip": "0.0.0.0", "server_port": 30814}
|
||||||
|
self.RCON = rcon or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 10383,
|
||||||
|
"password": secrets.token_hex(16)}
|
||||||
self.Options = options or {"language": "en", "encoding": "utf-8", "speed_limit": 0, "use_queue": False,
|
self.Options = options or {"language": "en", "encoding": "utf-8", "speed_limit": 0, "use_queue": False,
|
||||||
"debug": False, "use_lua": False, "log_chat": True}
|
"debug": False, "use_lua": False, "log_chat": True}
|
||||||
self.WebAPI = web or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 8433,
|
self.WebAPI = web or {"enabled": False, "server_ip": "127.0.0.1", "server_port": 8433,
|
||||||
"secret_key": secrets.token_hex(16)}
|
"access_token": secrets.token_hex(16)}
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(Auth=%r, Game=%r, Server=%r)" % (self.__class__.__name__, self.Auth, self.Game, self.Server)
|
return "%s(Auth=%r, Game=%r, Server=%r)" % (self.__class__.__name__, self.Auth, self.Game, self.Server)
|
||||||
|
92
src/modules/ConsoleSystem/RCON.py
Normal file
92
src/modules/ConsoleSystem/RCON.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
from base64 import b64decode, b64encode
|
||||||
|
|
||||||
|
from cryptography.hazmat.primitives import padding
|
||||||
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
|
||||||
|
from core import get_logger
|
||||||
|
|
||||||
|
"""
|
||||||
|
shared key: SHA256 of "password"
|
||||||
|
<header>: "\x00\x00\x00\x00" (Byte order: Little Endian) - like you use
|
||||||
|
<iv>: A set of random bytes packed in base64 (New for each message)
|
||||||
|
-> To server
|
||||||
|
<- From server
|
||||||
|
|
||||||
|
Open TCP connection /
|
||||||
|
| -> "<iv>:hello" Without header, immediately with AES encryption (shared key)
|
||||||
|
| *Decrypt and some processes*
|
||||||
|
| Fail /
|
||||||
|
| | <- ":E:Bad key" | ":E:Error Message" Without header, without AES encryption
|
||||||
|
| | tcp.close() # End
|
||||||
|
| Success /
|
||||||
|
| | <- "<iv>:hello" with header, with AES encryption
|
||||||
|
| | (Next, everywhere with header, with AES encryption)
|
||||||
|
| -> "<iv>:<header>Cs:ver"
|
||||||
|
| <- "<iv>:<header>Os:KuiToi 0.4.3 | "<iv>:<header>Os:BeamMP 3.2.0"
|
||||||
|
| # Prints server and they version
|
||||||
|
| -> "<iv>:<header>Cs:commands"
|
||||||
|
| <- "<iv>:<header>Os:stop,help,plugins" | "<iv>:<header>Os:SKIP" For an autocomplete; "SKIP" For no autocomplete;
|
||||||
|
| *Ready to handle commands*
|
||||||
|
| -> "<iv>:<header>C:help"
|
||||||
|
| <- "<iv>:<header>O:stop: very cool stop\nhelp: Yayayayoy"
|
||||||
|
| -> "<iv>:<header>C:...."
|
||||||
|
| <- "<iv>:<header>O:...."
|
||||||
|
| -> "<iv>:<header>C:exit"
|
||||||
|
| tcp.close()
|
||||||
|
|
||||||
|
Codes:
|
||||||
|
* "hello" - Hello message
|
||||||
|
* "E:error_message" - Send RCON error
|
||||||
|
* "C:command" - Receive command
|
||||||
|
* "Cs:" - Receive system command
|
||||||
|
* "O:output" - Send command output
|
||||||
|
* "Os:" - Send system output
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class RCONSystem:
|
||||||
|
console = None
|
||||||
|
|
||||||
|
def __init__(self, key, host, port):
|
||||||
|
self.log = get_logger("RCON")
|
||||||
|
self.key = key
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
|
||||||
|
def encrypt(self, message, key):
|
||||||
|
self.log.debug(f"Encrypt message: {message}")
|
||||||
|
key = hashlib.sha256(key).digest()
|
||||||
|
iv = os.urandom(16)
|
||||||
|
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
|
||||||
|
encryptor = cipher.encryptor()
|
||||||
|
padder = padding.PKCS7(algorithms.AES.block_size).padder()
|
||||||
|
padded_data = padder.update(message.encode('utf-8')) + padder.finalize()
|
||||||
|
encrypted_data = encryptor.update(padded_data) + encryptor.finalize()
|
||||||
|
encoded_data = b64encode(encrypted_data)
|
||||||
|
encoded_iv = b64encode(iv)
|
||||||
|
return encoded_iv + b":" + encoded_data
|
||||||
|
|
||||||
|
def decrypt(self, ciphertext, key):
|
||||||
|
self.log.debug(f"Dencrypt message: {ciphertext}")
|
||||||
|
key = hashlib.sha256(key).digest()
|
||||||
|
encoded_iv, encoded_data = ciphertext.split(":")
|
||||||
|
iv = b64decode(encoded_iv)
|
||||||
|
encrypted_data = b64decode(encoded_data)
|
||||||
|
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
|
||||||
|
decryptor = cipher.decryptor()
|
||||||
|
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
|
||||||
|
decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize()
|
||||||
|
unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()
|
||||||
|
return unpadded_data.decode('utf-8')
|
||||||
|
|
||||||
|
async def handle_client(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def start(self):
|
||||||
|
self.log.info("TODO: RCON")
|
||||||
|
|
||||||
|
async def stop(self):
|
||||||
|
pass
|
@ -1,36 +1,34 @@
|
|||||||
class Console(object):
|
from logging import Logger
|
||||||
|
from typing import AnyStr
|
||||||
|
|
||||||
def __init__(self,
|
from core import get_logger
|
||||||
prompt_in: str = ">",
|
|
||||||
prompt_out: str = "]:",
|
|
||||||
not_found: str = "Command \"%s\" not found in alias.") -> None: ...
|
|
||||||
|
|
||||||
def __getitem__(self, item): ...
|
|
||||||
@property
|
|
||||||
def alias(self) -> dict: ...
|
|
||||||
def add(self, key: str, func: function) -> dict: ...
|
|
||||||
def log(self, s: str, r='\r') -> None: ...
|
|
||||||
def write(self, s: str, r='\r') -> None: ...
|
|
||||||
def __lshift__(self, s: AnyStr) -> None: ...
|
|
||||||
def logger_hook(self) -> None: ...
|
|
||||||
def builtins_hook(self) -> None: ...
|
|
||||||
async def start(self) -> None: ...
|
|
||||||
|
|
||||||
class console(object):
|
class RCONSystem:
|
||||||
|
console = None
|
||||||
|
|
||||||
|
def __init__(self, key, host, port):
|
||||||
|
self.log = get_logger("RCON")
|
||||||
|
self.key = key
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
|
||||||
|
async def start(self): ...
|
||||||
|
async def stop(self): ...
|
||||||
|
|
||||||
|
class console:
|
||||||
|
rcon: RCONSystem = RCONSystem
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def alias() -> dict: ...
|
def alias() -> dict: ...
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_command(key: str, func: function) -> dict: ...
|
def add_command(key: str, func, man: str = None, desc: str = None, custom_completer: dict = None) -> dict: ...
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def start() -> None: ...
|
async def start() -> None: ...
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def builtins_hook() -> None: ...
|
def builtins_hook() -> None: ...
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def logger_hook() -> None: ...
|
def logger_hook() -> None: ...
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def log(s: str) -> None: ...
|
def log(s: str) -> None: ...
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -18,6 +18,7 @@ from prompt_toolkit.output.win32 import NoConsoleScreenBufferError
|
|||||||
from prompt_toolkit.patch_stdout import patch_stdout
|
from prompt_toolkit.patch_stdout import patch_stdout
|
||||||
|
|
||||||
from core import get_logger
|
from core import get_logger
|
||||||
|
from modules.ConsoleSystem import RCON
|
||||||
|
|
||||||
|
|
||||||
class Console:
|
class Console:
|
||||||
@ -45,6 +46,9 @@ class Console:
|
|||||||
self.add_command("help", self.__create_help_message, i18n.man_message_help, i18n.help_message_help,
|
self.add_command("help", self.__create_help_message, i18n.man_message_help, i18n.help_message_help,
|
||||||
custom_completer={"help": {"--raw": None}})
|
custom_completer={"help": {"--raw": None}})
|
||||||
self.completer = NestedCompleter.from_nested_dict(self.__alias)
|
self.completer = NestedCompleter.from_nested_dict(self.__alias)
|
||||||
|
rcon = RCON
|
||||||
|
rcon.console = self
|
||||||
|
self.rcon = rcon
|
||||||
|
|
||||||
def __debug(self, *x):
|
def __debug(self, *x):
|
||||||
self.__logger.debug(f"{x}")
|
self.__logger.debug(f"{x}")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user