Files
mailsystem/files/srs/srs-reverse-map.py

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()