Hi

On my servers, I noticed increasing number of failed deliveries with "417 Temporary delivery error" and "Error connecting to remote address". I did a little investigation and long story short, XMail doesn't handle address family fallback, when connection using preferred one does not succeed. It simply tries only one address per MX and if it doesn't work, it considers that MX dead and moves to another one.

Now imagine what happens when you use -M7 parameter (use IPV6 records if present, or IPV4 records otherwise, for host name lookups), target system has both IPv4 and IPv6 records set for all MXes (soon to be standard for most servers, well perhaps not so soon, but it's getting more and more common) and IPv6 is broken on either side or anywhere between. XMail tries connecting using only IPv6 for a while, until it finally gives up and returns the message as undeliverable. Which is wrong, because if it tried IPv4, it would deliver it just fine.

Relatively safe workaround for now, assuming IPv6 as a new thing is going to break more often than IPv4, is to use -M5 instead (Use IPV4 records if present, or IPV6 records otherwise, for host name lookups). But it means that IPv6 won't get used at all, except for few rare IPv6-only MXes. Also the problem does not really go away, if it happens that IPv6 works while IPv4 does not, it will be back.

Attached is patch with "works for me" solution, i.e. not tested by anyone else nor even necessarily correct. It makes XMail try to connect to all addresses of MX before moving to next one. Apart from possible unintentional errors, it deliberately ignores -M5 and -M7 parameters and uses AF_UNSPEC for getaddrinfo() and all results when one of them is set. It respects -M4 and -M6 if someone really wants to use only one address family.

IMHO -M5 and -M7 are wrong, at least on Windows, where getaddrinfo() with AF_UNSPEC returns addresses in best order automatically and manual override should not be needed. I think Linux either does that too or at least has means to influence it using /etc/gai.conf. So even if -M5 and -M7 should stay as useful for someone, adding new -M8 for AF_UNSPEC order would be good idea.

PSYNC has the same problem. And I guess CtrlClnt connecting to server probably too, but it's far from critical.

--
--- xmail-1.27-clean\SMTPUtils.cpp      2010-02-26 04:33:44.000000000 +0100
+++ xmail-1.27-af-fix\SMTPUtils.cpp     2013-02-10 16:03:44.882282400 +0100
@@ -1212,30 +1212,71 @@
 
        SYS_INET_ADDR SvrAddr;
 
-       if (MscGetServerAddress(szAddress, SvrAddr, iPortNo) < 0)
+       int iError;
+       struct addrinfo *pCRes, *pRes;
+       struct addrinfo AHints;
+       bool bConnected = false;
+       SYS_SOCKET SockFD;
+
+       ZeroData(AHints);
+       switch (iAddrFamily) {
+       case AF_INET:
+               AHints.ai_family = AF_INET;
+               break;
+       case AF_INET6:
+               AHints.ai_family = AF_INET6;
+               break;
+       default:
+               AHints.ai_family = AF_UNSPEC;
+       }
+
+       if ((iError = getaddrinfo(szAddress, NULL, &AHints, &pRes)) != 0) {
                return INVALID_SMTPCH_HANDLE;
+       }
+       for (pCRes = pRes, iError = ERR_BAD_SERVER_ADDR; pCRes != NULL; pCRes = 
pCRes->ai_next) {
+               if (pCRes->ai_addr->sa_family != AF_INET && 
pCRes->ai_addr->sa_family != AF_INET6)
+                       continue;
 
-       SYS_SOCKET SockFD = SysCreateSocket(SysGetAddrFamily(SvrAddr), 
SOCK_STREAM, 0);
+               if (pCRes != NULL && sizeof(SvrAddr.Addr) >= pCRes->ai_addrlen) 
{
+                       ZeroData(SvrAddr);
+                       SvrAddr.iSize = pCRes->ai_addrlen;
+                       memcpy(SvrAddr.Addr, pCRes->ai_addr, pCRes->ai_addrlen);
+               } else
+                       continue;
 
-       if (SockFD == SYS_INVALID_SOCKET)
-               return INVALID_SMTPCH_HANDLE;
+               if (SysSetAddrPort(SvrAddr, iPortNo) < 0)
+                       continue;
 
-       /*
-        * Are we requested to bind to a specific interface to talk to this 
server?
-        */
-       if (pGw->pszIFace != NULL) {
-               SYS_INET_ADDR BndAddr;
+               SockFD = SysCreateSocket(SysGetAddrFamily(SvrAddr), 
SOCK_STREAM, 0);
 
-               if (MscGetServerAddress(pGw->pszIFace, BndAddr, 0) < 0 ||
-                   SysBindSocket(SockFD, &BndAddr) < 0) {
+               if (SockFD == SYS_INVALID_SOCKET)
+                       continue;
+
+               /*
+                * Are we requested to bind to a specific interface to talk to 
this server?
+                */
+               if (pGw->pszIFace != NULL) {
+                       SYS_INET_ADDR BndAddr;
+
+                       if (MscGetServerAddress(pGw->pszIFace, BndAddr, 0) < 0 
||
+                           SysBindSocket(SockFD, &BndAddr) < 0) {
+                               SysCloseSocket(SockFD);
+                               continue;
+                       }
+               }
+               if (SysConnect(SockFD, &SvrAddr, STD_SMTP_TIMEOUT) < 0) {
                        SysCloseSocket(SockFD);
-                       return INVALID_SMTPCH_HANDLE;
+                       continue;
+               } else {
+                       bConnected = true;
+                       break;
                }
        }
-       if (SysConnect(SockFD, &SvrAddr, STD_SMTP_TIMEOUT) < 0) {
-               SysCloseSocket(SockFD);
+
+       freeaddrinfo(pRes);
+
+       if (!bConnected)
                return INVALID_SMTPCH_HANDLE;
-       }
 
        /* Check if We need to supply an HELO host */
        char szHeloHost[MAX_HOST_NAME] = "";
_______________________________________________
xmail mailing list
xmail@xmailserver.org
http://xmailserver.org/mailman/listinfo/xmail

Reply via email to