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.diagnosticSeverityOverrides": {
"reportMissingTypeStubs": "none"
}
},
"cSpell.words": ["REUSEADDR", "varint"]
}

View File

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

View File

@@ -1,11 +1,15 @@
import json
import os
import socket
import logging
import threading
from typing import Literal
import uuid
from app import byte_utils
from dockerHandler import DockerHandler
from minecraftServerHandler import MinecraftServerHandler
from objects.minecraftServer import MinecraftServer
import byte_utils
class RequestHandler(threading.Thread):
@@ -95,8 +99,9 @@ class RequestHandler(threading.Thread):
if minecraft_server.is_starting() == True:
logging.info(
f'[RequestHandler:{self.port}] container {service_name} is already starting...')
self.forward_request_to_placeholder(
request, minecraft_server)
self.send_response(self.client_address)
# self.forward_request_to_placeholder(
# request, minecraft_server)
else:
logging.info(
f'[RequestHandler:{self.port}] starting container {service_name}')
@@ -105,43 +110,142 @@ class RequestHandler(threading.Thread):
elif b'\x01' in request:
logging.info(
f'[RequestHandler:{self.port}] detected 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)
elif request[0] == 0xFE:
logging.info(
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:
logging.info(
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:
logging.info(
f'[RequestHandler:{self.port}] no container mapped to port {self.port}')
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))
def send_response(self, addr):
client_socket = self.connection
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()
data = client_socket.recv(1024)
client_ip = addr[0]
fqdn = socket.getfqdn(client_ip)
if self.show_hostname and client_ip != fqdn:
client_ip = fqdn + "/" + client_ip
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 os
import json
import json
from typing import List, Dict
import struct
from typing import Dict
def docker_container_mapping() -> Dict[str, str]:
@@ -22,29 +21,48 @@ def docker_container_mapping() -> Dict[str, str]:
return port_map
# motd = {
# "1": "§4Maintenance!",
# "2": "§aCheck example.com for more information!"
# }
# version_text = "§4Maintenance"
# samples = ["§bexample.com", "", "§4Maintenance"]
# kick_message = ["§bSorry", "", "§aThis server is offline!"]
def read_varint(byte, i):
result = 0
bytes = 0
while True:
byte_in = byte[i]
i += 1
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:
json.dump(config, f, indent=4)
def read_utf(byte, i):
(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)