Archive discontinued selective SRS implementation
This commit is contained in:
+113
@@ -0,0 +1,113 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user