aboutsummaryrefslogtreecommitdiff
path: root/server.py
diff options
context:
space:
mode:
Diffstat (limited to 'server.py')
-rw-r--r--server.py144
1 files changed, 144 insertions, 0 deletions
diff --git a/server.py b/server.py
new file mode 100644
index 0000000..7edd081
--- /dev/null
+++ b/server.py
@@ -0,0 +1,144 @@
1import socket
2import struct
3from http.server import BaseHTTPRequestHandler, HTTPServer
4from json import load
5from os.path import expanduser
6from re import compile
7from traceback import print_exc
8
9import config
10
11
12def rcon_packet(id: int, type: int, body: str) -> bytes:
13 return (
14 struct.pack(
15 "<iii",
16 len(body) + 10,
17 id,
18 type,
19 )
20 + body.encode("utf-8")
21 + bytes((0, 0))
22 )
23
24
25def rcon_login() -> socket.socket:
26 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
27 s.connect(("127.0.0.1", config.rcon_port))
28 s.send(rcon_packet(0, 3, config.rcon_password))
29 return s
30
31
32def rcon(command: str) -> None:
33 rcon_socket.send(rcon_packet(0, 2, command))
34
35
36def pipe(command: str) -> None:
37 with open(expanduser(config.pipe_path), "w") as pipe:
38 pipe.write(f"{command}\n")
39
40
41class HttpHandler(BaseHTTPRequestHandler):
42 def disabled(self) -> None:
43 self.send_response(503)
44 self.send_header("Content-Type", "text/plain")
45 self.end_headers()
46 self.wfile.write("Endpoint disabled".encode("utf-8"))
47
48 def do_GET(self):
49 if "GET" not in config.enabled_endpoints:
50 self.disabled()
51 return
52
53 try:
54 with open(expanduser(config.whitelist_file_path)) as file:
55 status = 200
56 response = file.read()
57 except Exception:
58 status = 500
59 print_exc()
60
61 self.send_response(status)
62 self.send_header("Content-Type", "application/json")
63 self.end_headers()
64 self.wfile.write(response.encode("utf-8"))
65
66 def do_POST(self) -> None:
67 if "POST" not in config.enabled_endpoints:
68 self.disabled()
69 return
70
71 try:
72 length = int(self.headers.get("Content-Length", 0))
73 username = self.rfile.read(length).decode("utf-8")
74
75 if allowed_usernames_regex.match(username) is None:
76 status = 400
77 response = "Incorrect username"
78 elif username in whitelist:
79 status = 409
80 response = "Player is already whitelisted"
81 else:
82 status = 201
83 response = f"Added {username} to the whitelist"
84
85 whitelist.add(username)
86 console(f"whitelist add {username}")
87 except Exception:
88 status = 500
89 print_exc()
90
91 self.send_response(status)
92 self.send_header("Content-Type", "text/plain")
93 self.end_headers()
94 self.wfile.write(response.encode("utf-8"))
95
96 def do_DELETE(self) -> None:
97 if "DELETE" not in config.enabled_endpoints:
98 self.disabled()
99 return
100
101 try:
102 length = int(self.headers.get("Content-Length", 0))
103 username = self.rfile.read(length).decode("utf-8")
104
105 if allowed_usernames_regex.match(username) is None:
106 status = 400
107 response = "Incorrect username"
108 elif username not in whitelist:
109 status = 409
110 response = "Player is not whitelisted"
111 else:
112 status = 200
113 response = f"Removed {username} from the whitelist"
114
115 whitelist.remove(username)
116 console(f"whitelist remove {username}")
117 except Exception:
118 status = 500
119 print_exc()
120
121 self.send_response(status)
122 self.send_header("Content-Type", "text/plain")
123 self.end_headers()
124 self.wfile.write(response.encode("utf-8"))
125
126
127server = HTTPServer(("0.0.0.0", config.http_port), HttpHandler)
128allowed_usernames_regex = compile(r"^[a-zA-Z0-9_]+$")
129
130with open(expanduser(config.whitelist_file_path)) as file:
131 whitelist = {e["name"] for e in load(file)}
132
133if config.whitelist_access_type == "pipe":
134 console = pipe
135elif config.whitelist_access_type == "rcon":
136 rcon_socket = rcon_login()
137 console = rcon
138
139
140try:
141 server.serve_forever()
142except KeyboardInterrupt:
143 rcon_socket.close()
144 server.server_close()