minetest-matrix_bridge/proxy.py

177 lines
4.3 KiB
Python

import http.client, re, socket, sys, time, _thread, threading, os
RECBUF = 1024
def getargv(arg, default="", n=1, args=sys.argv):
if arg in args and len(args) > args.index(arg)+n:
return args[args.index(arg)+n]
else:
return default
p_clen = re.compile("\r?\ncontent-length: *(\d+)\r?\n?", re.IGNORECASE)
def handle(client):
# Get request
paquet = b""
header = b""
content = b""
content_len = 0
resp = {}
lf = 0
while True:
raw = client.recv(RECBUF)
if raw:
paquet += raw
if lf >= 0:
for c in raw:
if c == 10:# LF
lf += 1
elif c != 13:# CR
lf = 0
if lf > 1:
parts = paquet.split(b"\r\n\r\n")
header = parts[0]
content = parts[1]
try:
content_len = int(p_clen.search(header.decode()).group(1))
except (AttributeError, ValueError):
content_len = 0
break
if lf > 1:
break
else:
break
while len(content) < content_len:
raw = client.recv(RECBUF)
paquet += raw
content += raw
httpreq = paquet.split(b" ", 2)
if len(httpreq) != 3:
client.close()
return
method = httpreq[0]
url = httpreq[1]
rest = httpreq[2]
if b"/send/m.room.message/" in url:
method = b"PUT"
matrix_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if matrix_https:
matrix_sock = ssl_ctx.wrap_socket(matrix_sock, server_hostname=matrix_addr)
matrix_sock.settimeout(60)
try:
matrix_sock.connect((matrix_addr, matrix_port))
except socket.timeout:
sys.stderr.write("timeout connecting to matrix")
return
matrix_sock.settimeout(None)
matrix_sock.sendall(b" ".join([method, url, rest]))
paquet = b""
header = b""
content = b""
content_len = 0
resp = {}
lf = 0
while True:
raw = matrix_sock.recv(RECBUF)
if raw:
paquet += raw
if lf >= 0:
for c in raw:
if c == 10:# LF
lf += 1
elif c != 13:# CR
lf = 0
if lf > 1:
parts = paquet.split(b"\r\n\r\n")
header = parts[0]
content = parts[1]
try:
content_len = int(p_clen.search(header.decode()).group(1))
except AttributeError:
content_len = 0
break
if lf > 1:
break
else:
break
while len(content) < content_len:
raw = matrix_sock.recv(RECBUF)
paquet += raw
content += raw
client.sendall(paquet)
client.close()
def run_watchdog(service_name, timeout):
while True:
if time.time() > last_request + timeout:
os.system("systemctl restart {}".format(service_name))
last_request = time.time()
time.sleep(60)
if __name__ == "__main__":
if "--help" in sys.argv or "-h" in sys.argv:
print("""MineTest Matrix Bridge mod proxy
GNU AGPLv3, CopyLeft 2022 Pascal Engélibert
This program is useful for <5.4.0 MineTest servers using the mod matrix_bridge.
For security reasons, please listen to a local address (default value is OK) and use a firewall if needed to prevent public access.
Options: (with defaults)
-a 127.0.0.1 Listen address
-p 18448 Listen port
-A matrix.txmn.tk Matrix address (without protocol and port)
-P 8448 Matrix port
-s Use HTTP instead of HTTPS (default: no)
-w <service-name> Enable watchdog
Restarts the given systemd service when no request from
the minetest server after a given time. Needs root.
--wtime 600 Watchdog timeout (seconds)
""")
exit()
listen_addr = getargv("-a", "127.0.0.1")
listen_port = int(getargv("-p", 18448))
matrix_addr = getargv("-A", "matrix.txmn.tk")
matrix_port = int(getargv("-P", 8448))
matrix_https = not "-s" in sys.argv
watchdog = getargv("-w", None)
watchdog_timeout = int(getargv("--wtime", 600))
last_request = time.time()
if watchdog:
t = threading.Thread(target=run_watchdog, args=(watchdog, watchdog_timeout,))
t.start()
if matrix_https:
import ssl
ssl_ctx = ssl.create_default_context()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(60)
sock.bind((listen_addr, listen_port))
sock.listen(1)
try:
while True:
try:
client, addr = sock.accept()
except socket.timeout:
last_request = time.time()
continue
last_request = time.time()
_thread.start_new_thread(handle, (client,))
sock.close()
except KeyboardInterrupt:
sock.shutdown(socket.SHUT_WR)