Files
mailsystem/files/srs/srs-reverse-policy.py
T

114 lines
2.8 KiB
Python
Executable File

#!/usr/bin/env python3
import socket
import sys
SRS_DOMAIN = "srs.warenform.de"
SOCKET_HOST = "127.0.0.1"
SOCKET_PORT = 10003
TIMEOUT = 5
MAX_RESPONSE = 4096
def read_netstring(sock):
length_bytes = bytearray()
while True:
char = sock.recv(1)
if not char:
raise ValueError("Incomplete netstring length")
if char == b":":
break
if char < b"0" or char > b"9" or len(length_bytes) >= 8:
raise ValueError("Invalid netstring length")
length_bytes.extend(char)
if not length_bytes:
raise ValueError("Empty netstring length")
length = int(length_bytes)
if length > MAX_RESPONSE:
raise ValueError("Netstring response too large")
data = bytearray()
while len(data) < length + 1:
chunk = sock.recv(length + 1 - len(data))
if not chunk:
raise ValueError("Incomplete netstring response")
data.extend(chunk)
if data[-1:] != b",":
raise ValueError("Invalid netstring terminator")
return bytes(data[:-1]).decode("utf-8")
def verify_srs(address):
request = f"reverse {address}".encode("utf-8")
packet = str(len(request)).encode("ascii") + b":" + request + b","
with socket.create_connection(
(SOCKET_HOST, SOCKET_PORT), timeout=TIMEOUT
) as sock:
sock.settimeout(TIMEOUT)
sock.sendall(packet)
response = read_netstring(sock)
if response.startswith("OK "):
original = response[3:]
if not original or "@" not in original:
raise ValueError("Invalid reverse result")
return "OK"
if response.startswith("NOTFOUND "):
return "REJECT 550 5.1.1 Invalid SRS address"
raise ValueError(f"Unexpected socketmap response: {response}")
def check_recipient(recipient):
if "@" not in recipient:
return "DUNNO"
localpart, domain = recipient.rsplit("@", 1)
if domain.lower() != SRS_DOMAIN:
return "DUNNO"
if not localpart.upper().startswith(("SRS0=", "SRS1=")):
return "REJECT 550 5.1.1 Invalid SRS address"
try:
return verify_srs(recipient)
except (OSError, UnicodeError, ValueError) as exc:
print(f"SRS verification error: {exc}", file=sys.stderr)
return "DEFER 451 4.3.0 SRS verification unavailable"
def main():
while True:
attributes = {}
for line in sys.stdin:
line = line.rstrip("\r\n")
if not line:
break
if "=" in line:
key, value = line.split("=", 1)
attributes[key] = value
else:
return
recipient = attributes.get("recipient", "")
action = check_recipient(recipient)
print(f"action={action}\n", flush=True)
if __name__ == "__main__":
main()