113 lines
3.0 KiB
Python
Executable File
113 lines
3.0 KiB
Python
Executable File
#!/usr/bin/python3
|
|
|
|
import socket
|
|
import socketserver
|
|
|
|
LISTEN_HOST = "127.0.0.1"
|
|
LISTEN_PORT = 10004
|
|
SRS_HOST = "127.0.0.1"
|
|
SRS_PORT = 10003
|
|
SRS_DOMAIN = "srs.warenform.de"
|
|
|
|
MAX_REQUEST = 8192
|
|
TIMEOUT = 3
|
|
|
|
|
|
def read_netstring(sock):
|
|
length = bytearray()
|
|
|
|
while True:
|
|
c = sock.recv(1)
|
|
if not c:
|
|
raise EOFError()
|
|
if c == b":":
|
|
break
|
|
if not c.isdigit() or len(length) >= 8:
|
|
raise ValueError("Invalid netstring length")
|
|
length.extend(c)
|
|
|
|
if not length:
|
|
raise ValueError("Empty netstring length")
|
|
|
|
size = int(length)
|
|
if size > MAX_REQUEST:
|
|
raise ValueError("Netstring too large")
|
|
|
|
data = bytearray()
|
|
while len(data) < size:
|
|
chunk = sock.recv(size - len(data))
|
|
if not chunk:
|
|
raise EOFError()
|
|
data.extend(chunk)
|
|
|
|
if sock.recv(1) != b",":
|
|
raise ValueError("Invalid netstring terminator")
|
|
|
|
return bytes(data)
|
|
|
|
|
|
def netstring(data):
|
|
return str(len(data)).encode() + b":" + data + b","
|
|
|
|
|
|
class Handler(socketserver.BaseRequestHandler):
|
|
def handle(self):
|
|
self.request.settimeout(TIMEOUT)
|
|
|
|
while True:
|
|
try:
|
|
request = read_netstring(self.request)
|
|
except (EOFError, OSError):
|
|
return
|
|
except ValueError:
|
|
self.request.sendall(netstring(b"PERM Invalid request"))
|
|
return
|
|
|
|
try:
|
|
operation, address = request.decode("utf-8").split(" ", 1)
|
|
except (UnicodeError, ValueError):
|
|
self.request.sendall(netstring(b"PERM Invalid request"))
|
|
continue
|
|
|
|
if operation != "reverse":
|
|
self.request.sendall(netstring(b"NOTFOUND "))
|
|
continue
|
|
|
|
if "@" not in address:
|
|
self.request.sendall(netstring(b"NOTFOUND "))
|
|
continue
|
|
|
|
domain = address.rsplit("@", 1)[1].lower()
|
|
|
|
if domain != SRS_DOMAIN:
|
|
self.request.sendall(netstring(b"NOTFOUND "))
|
|
continue
|
|
|
|
try:
|
|
with socket.create_connection(
|
|
(SRS_HOST, SRS_PORT), timeout=TIMEOUT
|
|
) as upstream:
|
|
upstream.settimeout(TIMEOUT)
|
|
upstream.sendall(netstring(request))
|
|
response = read_netstring(upstream)
|
|
|
|
if response.startswith((b"OK ", b"NOTFOUND ", b"TEMP ", b"PERM ")):
|
|
result = response
|
|
else:
|
|
result = b"TEMP Invalid upstream response"
|
|
|
|
except (OSError, EOFError, ValueError):
|
|
result = b"TEMP SRS backend unavailable"
|
|
|
|
self.request.sendall(netstring(result))
|
|
|
|
|
|
class Server(socketserver.ThreadingTCPServer):
|
|
allow_reuse_address = True
|
|
daemon_threads = True
|
|
|
|
|
|
if __name__ == "__main__":
|
|
with Server((LISTEN_HOST, LISTEN_PORT), Handler) as server:
|
|
server.serve_forever()
|