118 lines
3.8 KiB
Python
118 lines
3.8 KiB
Python
import socket
|
|
import struct
|
|
|
|
from .base import BaseClient
|
|
from ..shared import glob
|
|
|
|
import logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class QueryClient(BaseClient):
|
|
def __init__(self, server: "__ServerInstance__", ip: str, port: int):
|
|
super().__init__(server, ip, port)
|
|
logger.debug("Client resolved to QueryClient")
|
|
|
|
self.handlers = {
|
|
b"i": self.query_i,
|
|
b"r": self.query_r,
|
|
b"c": self.query_c,
|
|
b"d": self.query_d,
|
|
b"p": self.query_p,
|
|
b"x": self.query_x
|
|
}
|
|
|
|
async def on_packet(self, packet: bytes):
|
|
logger.debug("on_packet(%s)" % packet)
|
|
|
|
if len(packet) <= 10: # Invalid
|
|
return
|
|
|
|
if packet[10:11] in self.handlers:
|
|
packet = await self.handlers[packet[10:11]](packet)
|
|
|
|
if len(packet): # Send packet back if not 0
|
|
await self.send(packet)
|
|
|
|
async def query_i(self, packet: bytes) -> bytes:
|
|
len_hostname = len(self.server.config.hostname)
|
|
len_mode = len(self.server.config.mode)
|
|
len_language = len(self.server.config.language)
|
|
|
|
return packet + struct.pack(b"<?HHI%dsI%dsI%ds" % (len_hostname, len_mode, len_language),
|
|
len(self.server.config.password) != 0,
|
|
|
|
len(await self.server.get_online_players()),
|
|
self.server.config.max_players,
|
|
|
|
len_hostname,
|
|
self.server.config.hostname.encode(),
|
|
|
|
len_mode,
|
|
self.server.config.mode.encode(),
|
|
|
|
len_language,
|
|
self.server.config.language.encode()
|
|
)
|
|
|
|
async def query_r(self, packet: bytes) -> bytes:
|
|
data = []
|
|
|
|
rules = await self.server.get_rules()
|
|
data.append(len(rules))
|
|
|
|
for k, v in rules.items():
|
|
data += [
|
|
len(k), k,
|
|
len(v), v
|
|
]
|
|
|
|
return packet + struct.pack(b"<H" + (b"B%dsB%ds" * data[0]) % tuple(len(y) for x in rules.items() for y in x), *data)
|
|
|
|
async def query_c(self, packet: bytes) -> bytes:
|
|
data = []
|
|
|
|
scores = await self.server.get_players_scores()
|
|
data.append(len(scores))
|
|
|
|
for k, v in scores.items():
|
|
data += [
|
|
len(k), k,
|
|
v
|
|
]
|
|
|
|
return packet + struct.pack(b"<H" + (b"B%dsI" * data[0]) % tuple(len(x) for x in scores.keys()), *data)
|
|
|
|
async def query_d(self, packet: bytes) -> bytes:
|
|
data = []
|
|
|
|
players = await self.server.get_online_players()
|
|
data.append(len(players))
|
|
|
|
for p in players:
|
|
data += [
|
|
p["id"],
|
|
len(p["nick"]), p["nick"],
|
|
p["score"],
|
|
p["ping"]
|
|
]
|
|
|
|
return packet + struct.pack(b"<H" + (b"BB%dsII" * data[0]) % tuple(len(p["nick"]) for p in players), *data)
|
|
|
|
async def query_p(self, packet: bytes) -> bytes:
|
|
return packet
|
|
|
|
async def query_x(self, packet: bytes) -> bytes:
|
|
len_pswd, = struct.unpack_from(b"<H", packet, 11)
|
|
pswd, len_cmd = struct.unpack_from(b"<%dsH" % len_pswd, packet, 13)
|
|
cmd = struct.unpack_from(b"<%ds" % len_cmd, packet, 15 + len_pswd)
|
|
|
|
if len(self.server.config.rcon_password) == 0:
|
|
msg = b"Remote Console is not enabled on this server."
|
|
elif self.server.config.rcon_password.encode() != pswd:
|
|
msg = b"Invalid RCON password."
|
|
logger.warning("BAD RCON ATTEMPT BY: %s:%d" % (self.ip, self.port))
|
|
else:
|
|
# TODO: Add rcon client to command stdouts
|
|
return b"" # No response as all is ok
|
|
|
|
return packet[:11] + struct.pack(b"<H%ds" % len(msg), len(msg), msg) |