From eace9bd85257ebb0e9d2d9f61cff0cb25f1dae28 Mon Sep 17 00:00:00 2001
From: ale <ale@incal.net>
Date: Wed, 19 Mar 2025 11:10:54 +0000
Subject: [PATCH] Resolve the SMTP hostname on each connection

Combined with randomization of multiple results, this allows Mailman
to run simple round-robin balancing of the available SMTP servers.
---
 Mailman/Handlers/SMTPDirect.py | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/Mailman/Handlers/SMTPDirect.py b/Mailman/Handlers/SMTPDirect.py
index 2e5963ca..7a9e8315 100644
--- a/Mailman/Handlers/SMTPDirect.py
+++ b/Mailman/Handlers/SMTPDirect.py
@@ -28,6 +28,7 @@ for a threaded implementation.
 
 import copy
 import time
+import random
 import socket
 import smtplib
 from base64 import b64encode
@@ -55,13 +56,29 @@ except NameError:
 
 
 
+# Resolve host/port, randomizing multiple IPs.
+def _resolve(host, port):
+    results = [x[4] for x in socket.getaddrinfo(
+        host, port,
+        family=socket.AF_INET,
+        type=socket.SOCK_STREAM,
+        proto=socket.IPPROTO_TCP)]
+    return random.choice(results)
+
+# SMTP class that resolves the target randomly on each connection.
+class SMTPRR(smtplib.SMTP):
+
+    def _get_socket(self, host, port, timeout):
+        host, port = _resolve(host, port)
+        return socket.create_connection((host, port), timeout)
+
 # Manage a connection to the SMTP server
 class Connection:
     def __init__(self):
         self.__conn = None
 
     def __connect(self):
-        self.__conn = smtplib.SMTP(
+        self.__conn = SMTPRR(
             local_hostname=mm_cfg.SMTP_HELO_HOST)
         self.__conn.set_debuglevel(mm_cfg.SMTPLIB_DEBUG_LEVEL)
         self.__conn.connect(mm_cfg.SMTPHOST, mm_cfg.SMTPPORT)
-- 
GitLab