Update code: Fix linting issues and optimize code

This commit is contained in:
Janis
2024-01-04 18:54:39 +01:00
parent c9af5893e1
commit aaa6de38eb
5 changed files with 357 additions and 73 deletions

View File

@@ -3,5 +3,6 @@
"python.analysis.autoImportCompletions": true, "python.analysis.autoImportCompletions": true,
"python.analysis.diagnosticSeverityOverrides": { "python.analysis.diagnosticSeverityOverrides": {
"reportMissingTypeStubs": "none" "reportMissingTypeStubs": "none"
} },
"cSpell.words": ["REUSEADDR", "varint"]
} }

View File

@@ -4,25 +4,23 @@ from nginxHandler import NginxHandler
from minecraftServerHandler import MinecraftServerHandler from minecraftServerHandler import MinecraftServerHandler
from requestHandler import RequestHandler from requestHandler import RequestHandler
import logging import logging
from FakeMCServer.fake_mc_server import FakeMCServer
import threading
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
def init_placeholder_servers(): # def init_placeholder_servers():
sleeping = FakeMCServer(port=20000, motd={ # sleeping = FakeMCServer(port=20000, motd={
"1": "sleeping!", "2": "§aCheck example.com for more information!"}) # "1": "sleeping!", "2": "§aCheck example.com for more information!"})
starting = FakeMCServer(port=20001, motd={ # starting = FakeMCServer(port=20001, motd={
"1": "starting!", "2": "§aCheck example.com for more information!"}) # "1": "starting!", "2": "§aCheck example.com for more information!"})
# Create threads for each server initialization # # Create threads for each server initialization
sleeping_thread = threading.Thread(target=sleeping.start_server) # sleeping_thread = threading.Thread(target=sleeping.start_server)
starting_thread = threading.Thread(target=starting.start_server) # starting_thread = threading.Thread(target=starting.start_server)
# Start the threads # # Start the threads
sleeping_thread.start() # sleeping_thread.start()
starting_thread.start() # starting_thread.start()
def initialize_docker_handler() -> DockerHandler: def initialize_docker_handler() -> DockerHandler:
@@ -64,9 +62,9 @@ def initialize_request_handlers(docker_handler, minecraft_server_handler):
def main() -> None: def main() -> None:
try: try:
logging.info('[INIT] initializing placeholder servers...') # logging.info('[INIT] initializing placeholder servers...')
init_placeholder_servers() # init_placeholder_servers()
logging.info('[INIT] placeholder servers initialized') # logging.info('[INIT] placeholder servers initialized')
docker_handler = initialize_docker_handler() docker_handler = initialize_docker_handler()
nginx_handler = initialize_nginx_handler() nginx_handler = initialize_nginx_handler()

View File

@@ -1,11 +1,15 @@
import json
import os import os
import socket import socket
import logging import logging
import threading import threading
from typing import Literal from typing import Literal
import uuid
from app import byte_utils
from dockerHandler import DockerHandler from dockerHandler import DockerHandler
from minecraftServerHandler import MinecraftServerHandler from minecraftServerHandler import MinecraftServerHandler
from objects.minecraftServer import MinecraftServer from objects.minecraftServer import MinecraftServer
import byte_utils
class RequestHandler(threading.Thread): class RequestHandler(threading.Thread):
@@ -95,8 +99,9 @@ class RequestHandler(threading.Thread):
if minecraft_server.is_starting() == True: if minecraft_server.is_starting() == True:
logging.info( logging.info(
f'[RequestHandler:{self.port}] container {service_name} is already starting...') f'[RequestHandler:{self.port}] container {service_name} is already starting...')
self.forward_request_to_placeholder( self.send_response(self.client_address)
request, minecraft_server) # self.forward_request_to_placeholder(
# request, minecraft_server)
else: else:
logging.info( logging.info(
f'[RequestHandler:{self.port}] starting container {service_name}') f'[RequestHandler:{self.port}] starting container {service_name}')
@@ -105,43 +110,142 @@ class RequestHandler(threading.Thread):
elif b'\x01' in request: elif b'\x01' in request:
logging.info( logging.info(
f'[RequestHandler:{self.port}] detected ping request for {service_name}') f'[RequestHandler:{self.port}] detected ping request for {service_name}')
self.forward_request_to_placeholder( self.send_response(self.client_address)
request, minecraft_server)
# self.forward_request_to_placeholder(
# request, minecraft_server)
elif request[0] == 0xFE: elif request[0] == 0xFE:
logging.info( logging.info(
f'[RequestHandler:{self.port}] detected legacy ping request for {service_name}') f'[RequestHandler:{self.port}] detected legacy ping request for {service_name}')
self.forward_request_to_placeholder(request, minecraft_server) self.send_response(self.client_address)
# self.forward_request_to_placeholder(request, minecraft_server)
else: else:
logging.info( logging.info(
f'[RequestHandler:{self.port}] detected unknown request for {service_name}') f'[RequestHandler:{self.port}] detected unknown request for {service_name}')
self.forward_request_to_placeholder(request, minecraft_server) self.send_response(self.client_address)
# self.forward_request_to_placeholder(request, minecraft_server)
else: else:
logging.info( logging.info(
f'[RequestHandler:{self.port}] no container mapped to port {self.port}') f'[RequestHandler:{self.port}] no container mapped to port {self.port}')
def forward_request_to_placeholder(self, request, minecraft_server: MinecraftServer) -> None: def send_response(self, addr):
logging.info( client_socket = self.connection
'[RequestHandler:{self.port}] forwarding request to placeholder server')
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
ip = "127.0.0.1"
logging.info(
f'[RequestHandler:{self.port}] placeholder server ip: {ip}')
try:
if minecraft_server.is_starting() == True:
logging.info(
'[RequestHandler:{self.port}] container is starting. Using placeholder port 20001')
server_socket.connect((ip, 20001))
else:
logging.info(
'[RequestHandler:{self.port}] container is not starting. Using placeholder port 20000')
server_socket.connect((ip, 20000))
server_socket.sendall(request) data = client_socket.recv(1024)
response = server_socket.recv(1024) client_ip = addr[0]
self.connection.sendall(response)
except Exception as e: fqdn = socket.getfqdn(client_ip)
logging.info( if self.show_hostname and client_ip != fqdn:
f'[RequestHandler:{self.port}] error while handling request on port {self.port}: {e}') client_ip = fqdn + "/" + client_ip
self.restart()
try:
(length, i) = byte_utils.read_varint(data, 0)
(packetID, i) = byte_utils.read_varint(data, i)
if packetID == 0:
(version, i) = byte_utils.read_varint(data, i)
(ip, i) = byte_utils.read_utf(data, i)
ip = ip.replace('\x00', '').replace("\r", "\\r").replace(
"\t", "\\t").replace("\n", "\\n")
is_using_fml = False
if ip.endswith("FML"):
is_using_fml = True
ip = ip[:-3]
(port, i) = byte_utils.read_ushort(data, i)
(state, i) = byte_utils.read_varint(data, i)
if state == 1:
self.logger.info(("[%s:%s] Received client " + ("(using ForgeModLoader) " if is_using_fml else "") +
"ping packet (%s:%s).") % (client_ip, addr[1], ip, port))
motd = {}
motd["version"] = {}
motd["version"]["name"] = "testing"
motd["version"]["protocol"] = 2
motd["players"] = {}
motd["players"]["max"] = 0
motd["players"]["online"] = 0
motd["players"]["sample"] = []
for sample in self.samples:
motd["players"]["sample"].append(
{"name": sample, "id": str(uuid.uuid4())})
motd["description"] = {"text": {
"1": "§4Maintensadfance!", "2": "§aCheck example.caom for more information!"}}
if self.server_icon and len(self.server_icon) > 0:
motd["favicon"] = self.server_icon
self.write_response(client_socket, json.dumps(motd))
elif state == 2:
name = ""
if len(data) != i:
(some_int, i) = byte_utils.read_varint(data, i)
(some_int, i) = byte_utils.read_varint(data, i)
(name, i) = byte_utils.read_utf(data, i)
self.logger.info(
("[%s:%s] " + (name + " t" if len(name) > 0 else "T") + "ries to connect to the server " +
("(using ForgeModLoader) " if is_using_fml else "") + "(%s:%s).")
% (client_ip, addr[1], ip, port))
self.write_response(client_socket, json.dumps(
{"text": ["§bSorry", "", "§aThis servzzzer is offline!"]}))
else:
self.logger.info(
"[%s:%d] Tried to request a login/ping with an unknown state: %d" % (client_ip, addr[1], state))
elif packetID == 1:
(long, i) = byte_utils.read_long(data, i)
response = bytearray()
byte_utils.write_varint(response, 9)
byte_utils.write_varint(response, 1)
bytearray.append(long)
client_socket.sendall(bytearray)
self.logger.info(
"[%s:%d] Responded with pong packet." % (client_ip, addr[1]))
else:
self.logger.warning("[%s:%d] Sent an unexpected packet: %d" % (
client_ip, addr[1], packetID))
except (TypeError, IndexError):
self.logger.warning(
"[%s:%s] Received invalid data (%s)" % (client_ip, addr[1], data))
return
def write_response(self, client_socket, response):
response_array = bytearray()
byte_utils.write_varint(response_array, 0)
byte_utils.write_utf(response_array, response)
length = bytearray()
byte_utils.write_varint(length, len(response_array))
client_socket.sendall(length)
client_socket.sendall(response_array)
# def forward_request_to_placeholder(self, request, minecraft_server: MinecraftServer) -> None:
# logging.info(
# '[RequestHandler:{self.port}] forwarding request to placeholder server')
# with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
# ip = "127.0.0.1"
# logging.info(
# f'[RequestHandler:{self.port}] placeholder server ip: {ip}')
# try:
# if minecraft_server.is_starting() == True:
# logging.info(
# '[RequestHandler:{self.port}] container is starting. Using placeholder port 20001')
# server_socket.connect((ip, 20001))
# else:
# logging.info(
# '[RequestHandler:{self.port}] container is not starting. Using placeholder port 20000')
# server_socket.connect((ip, 20000))
# server_socket.sendall(request)
# response = server_socket.recv(1024)
# self.connection.sendall(response)
# except Exception as e:
# logging.info(
# f'[RequestHandler:{self.port}] error while handling request on port {self.port}: {e}')
# self.restart()

163
app/socket_server.py Normal file
View File

@@ -0,0 +1,163 @@
import json
import socket
import uuid
import logging
from threading import Thread
import utils as utils
class SocketServer:
def __init__(self, port: int, ip: str = "0.0.0.0", motd: dict = {"1": "§4Maintenance!", "2": "§aCheck example.com for more information!"},
version_text: str = "§4Maintenance", kick_message: list = ["§bSorry", "", "§aThis server is offline!"],
server_icon: str = "server_icon.png", samples: list = ["§bexample.com", "", "§4Maintenance"], show_hostname_if_available: bool = True,
player_max: int = 0, player_online: int = 0, protocol: int = 2):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.ip = ip
self.port = port
self.motd = motd
self.version_text = version_text
self.kick_message = kick_message
self.samples = samples
self.server_icon = server_icon
self.show_hostname = show_hostname_if_available
self.player_max = player_max
self.player_online = player_online
self.protocol = protocol
def is_server_list_ping(self, request, service_name):
if request[0] == 0xFE:
logging.info(
f'[RequestHandler:{self.port}] detected legacy ping request for {service_name}')
self.send_response(self.client_address)
elif b'\x01' in request:
logging.info(
f'[RequestHandler:{self.port}] detected ping request for {service_name}')
self.send_response(self.client_address)
else:
logging.info(
f'[RequestHandler:{self.port}] detected unknown request for {service_name}')
self.send_response(self.client_address)
def is_join_attempt(self, request, service_name, minecraft_server):
if request[0] == 0x10 or request[0] == 0x15 or request[0] == 0x1b:
if b'\x02' in request:
logging.info(
f'[RequestHandler:{self.port}] detected join/login request for {service_name}')
if minecraft_server.is_starting() == True:
logging.info(
f'[RequestHandler:{self.port}] container {service_name} is already starting...')
self.send_response(self.client_address)
else:
logging.info(
f'[RequestHandler:{self.port}] starting container {service_name}')
self.minecraft_server_handler.start_server(service_name)
def on_new_client(self, client_socket, addr):
data = client_socket.recv(1024)
client_ip = addr[0]
if self.is_server_list_ping(data):
fqdn = socket.getfqdn(client_ip)
if self.show_hostname and client_ip != fqdn:
client_ip = fqdn + "/" + client_ip
try:
(length, i) = utils.read_varint(data, 0)
(packetID, i) = utils.read_varint(data, i)
if packetID == 0:
(version, i) = utils.read_varint(data, i)
(ip, i) = utils.read_utf(data, i)
ip = ip.replace('\x00', '').replace("\r", "\\r").replace(
"\t", "\\t").replace("\n", "\\n")
is_using_fml = False
if ip.endswith("FML"):
is_using_fml = True
ip = ip[:-3]
(port, i) = utils.read_ushort(data, i)
(state, i) = utils.read_varint(data, i)
if state == 1:
logging.info(("[%s:%s] Received client " + ("(using ForgeModLoader) " if is_using_fml else "") +
"ping packet (%s:%s).") % (client_ip, addr[1], ip, port))
motd = {}
motd["version"] = {}
motd["version"]["name"] = self.version_text
motd["version"]["protocol"] = self.protocol
motd["players"] = {}
motd["players"]["max"] = self.player_max
motd["players"]["online"] = self.player_online
motd["players"]["sample"] = []
for sample in ["§bexamplaaaaaaaae.com", "", "§4Maintaaaaaaaaaaaaaaenance"]:
motd["players"]["sample"].append(
{"name": sample, "id": str(uuid.uuid4())})
motd["description"] = {"text": self.motd}
if self.server_icon and len(self.server_icon) > 0:
motd["favicon"] = self.server_icon
self.write_response(client_socket, json.dumps(motd))
elif state == 2:
name = ""
if len(data) != i:
(some_int, i) = utils.read_varint(data, i)
(some_int, i) = utils.read_varint(data, i)
(name, i) = utils.read_utf(data, i)
logging.info(
("[%s:%s] " + (name + " t" if len(name) > 0 else "T") + "ries to connect to the server " +
("(using ForgeModLoader) " if is_using_fml else "") + "(%s:%s).")
% (client_ip, addr[1], ip, port))
self.write_response(client_socket, json.dumps(
{"text": "kicked"}))
else:
logging.info(
"[%s:%d] Tried to request a login/ping with an unknown state: %d" % (client_ip, addr[1], state))
elif packetID == 1:
(long, i) = utils.read_long(data, i)
response = bytearray()
utils.write_varint(response, 9)
utils.write_varint(response, 1)
bytearray.append(long)
client_socket.sendall(bytearray)
logging.info(
"[%s:%d] Responded with pong packet." % (client_ip, addr[1]))
else:
logging.warning("[%s:%d] Sent an unexpected packet: %d" % (
client_ip, addr[1], packetID))
except (TypeError, IndexError):
logging.warning(
"[%s:%s] Received invalid data (%s)" % (client_ip, addr[1], data))
return
def write_response(self, client_socket, response):
response_array = bytearray()
utils.write_varint(response_array, 0)
utils.write_utf(response_array, response)
length = bytearray()
utils.write_varint(length, len(response_array))
client_socket.sendall(length)
client_socket.sendall(response_array)
def start(self):
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind((self.ip, self.port))
self.sock.settimeout(5)
self.sock.listen(30)
logging.info("Server started on %s:%s! Waiting for incoming connections..." % (
self.ip, self.port))
while 1:
try:
(client, address) = self.sock.accept()
except socket.timeout:
continue # timeouts may occur but shouldn't worry uns server-side
Thread(target=self.on_new_client, daemon=True,
args=(client, address,)).start()
def close(self):
self.sock.close()

View File

@@ -1,8 +1,7 @@
import logging import logging
import os import os
import json import struct
import json from typing import Dict
from typing import List, Dict
def docker_container_mapping() -> Dict[str, str]: def docker_container_mapping() -> Dict[str, str]:
@@ -22,29 +21,48 @@ def docker_container_mapping() -> Dict[str, str]:
return port_map return port_map
# motd = { def read_varint(byte, i):
# "1": "§4Maintenance!", result = 0
# "2": "§aCheck example.com for more information!" bytes = 0
# } while True:
# version_text = "§4Maintenance" byte_in = byte[i]
# samples = ["§bexample.com", "", "§4Maintenance"] i += 1
# kick_message = ["§bSorry", "", "§aThis server is offline!"] result |= (byte_in & 0x7F) << (bytes * 7)
if bytes > 32:
raise IOError("Packet is too long!")
if (byte_in & 0x80) != 0x80:
return result, i
def generate_placeholder_server_config_file(path: str, ip: str, port: int, motd: Dict[str, str], version_text: str, samples: List[str], kick_message: List[str]) -> None:
config = {
"ip": ip,
"kick_message": kick_message,
"motd": motd,
"player_max": 0,
"player_online": 0,
"port": port,
"protocol": 2,
"samples": samples,
"server_icon": "server_icon.png",
"show_hostname_if_available": True,
"show_ip_if_hostname_available": True,
"version_text": version_text
}
with open(path, 'w') as f: def read_utf(byte, i):
json.dump(config, f, indent=4) (length, i) = read_varint(byte, i)
ip = byte[i:(i + length)].decode('utf-8')
i += length
return ip, i
def read_ushort(byte, i):
new_i = i + 2
return struct.unpack(">H", byte[i:new_i])[0], new_i
def read_long(byte, i):
new_i = i + 8
return struct.unpack(">q", byte[i:new_i]), new_i
def write_varint(byte, value):
while True:
part = value & 0x7F
value >>= 7
if value != 0:
part |= 0x80
byte.append(part)
if value == 0:
break
def write_utf(byte, value):
write_varint(byte, len(value))
for b in value.encode():
byte.append(b)