Here's a patch I used on a qmail system I used to run which ran behind
a NAT load balancer.  It solves a problem qmail has when it doesn't
know all of the IP addresses that connect to it.  If you run qmail on
a server behind any kind of network address translator, if you have
any other machines that forward their SMTP port to your SMTP port, or
if you have machines which listen on SMTP and unconditionally forward
mail to your qmail server, take a look at this patch and see if it
prevents a potential problem.

Here's a brief README and the patch itself.  It doesn't have a
permanent home yet (I'm moving my Web page; if anybody has suggestions
for a good, inexpensive Web hosting service for personal pages that
doesn't have ads or stupid restrictions, please email me off-list),
but I'll post the URL when it finds one.

Comments are welcome.

-----ScottG.

This patch may be necessary in some configurations that involve network
address translation or port forwarding.  It prevents a problem caused
by an MX or other mail routing directive instructing qmail to connect to
itself without realizing it's connecting to itself.  When this happens,
it accepts the message, finds out where to deliver it to (itself), and
promptly reconnects to itself to deliver the message.  Eventually, when
it has done this 20 or 30 times, it will give up and bounce the message,
but not before sucking up all of your CPU while it's happening.

Normally, qmail can detect what IP addresses refer to itself by getting
a list of all network interfaces with IP addresses from the operating
system.  It uses this list to determine whether connecting to an address
will cause it to connect to itself, and avoid the situation (it calls
the perm_ambigmx() function, which prints the message:

   Sorry. Although I'm listed as a best-preference MX or A for that host,
   it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)

But in situations where the OS is not aware of all IP addresses that
connect back to itself, this detection fails, causing the CPU-sucking
phenomenon described above.  This can happen if there is a network
address translation device in front of the qmail server, such as a
load-balancer or a router which allows you to share one IP address among
several machines; if there is a port forwarder forwarding connections
from some other machine to the SMTP server on the qmail server; or in
configurations where a "dumb" mailserver is configured to use your qmail
server as a "smarthost", delivering all mail to it without inspection.

To solve this, other IP addresses which will ultimately connect back to
your machine can be added to the file "control/moreipme", one per line.
qmail will treat all addresses in this file exactly as if they were
local, and if it finds an MX record or other mail routing information
which would cause it to connect to any of these addresses, it will call
perm_ambigmx(), and print the above error message.

You can cd into your qmail directory (normally /var/qmail) and run the
program "ipmeprint" from the source directory to see what interfaces
qmail is detecting or finds in moreipme.

This patch also incorporates the "0.0.0.0" patch, which causes qmail 
to recognize the IP address 0.0.0.0 as a local address.  See:

    http://www.tir.com/~sgifford/qmail/qmail-0.0.0.0.README

for more information, and

    http://www.tir.com/~sgifford/qmail/qmail-0.0.0.0.patch

for a copy of the patch.


diff -ur qmail-1.03/Makefile qmail-1.03-sg2/Makefile
--- qmail-1.03/Makefile	Mon Jun 15 06:53:16 1998
+++ qmail-1.03-sg2/Makefile	Sun Sep 16 04:01:13 2001
@@ -787,9 +787,9 @@
 	./compile ipme.c
 
 ipmeprint: \
-load ipmeprint.o ipme.o ip.o ipalloc.o stralloc.a alloc.a substdio.a \
+load ipmeprint.o ipme.o ip.o ipalloc.o open.a getln.a stralloc.a alloc.a substdio.a \
 error.a str.a fs.a socket.lib
-	./load ipmeprint ipme.o ip.o ipalloc.o stralloc.a alloc.a \
+	./load ipmeprint ipme.o ip.o ipalloc.o open.a getln.a stralloc.a alloc.a \
 	substdio.a error.a str.a fs.a  `cat socket.lib`
 
 ipmeprint.o: \
diff -ur qmail-1.03/ipme.c qmail-1.03-sg2/ipme.c
--- qmail-1.03/ipme.c	Mon Jun 15 06:53:16 1998
+++ qmail-1.03-sg2/ipme.c	Sun Sep 16 04:07:09 2001
@@ -5,6 +5,7 @@
 #include <sys/socket.h>
 #include <net/if.h>
 #include <netinet/in.h>
+#include <unistd.h>
 #ifndef SIOCGIFCONF /* whatever works */
 #include <sys/sockio.h>
 #endif
@@ -14,6 +15,7 @@
 #include "ipalloc.h"
 #include "stralloc.h"
 #include "ipme.h"
+#include "substdio.h"
 
 static int ipmeok = 0;
 ipalloc ipme = {0};
@@ -40,12 +42,19 @@
   int len;
   int s;
   struct ip_mx ix;
+  int moreipme_fd;
  
   if (ipmeok) return 1;
   if (!ipalloc_readyplus(&ipme,0)) return 0;
   ipme.len = 0;
   ix.pref = 0;
  
+  /* 0.0.0.0 is a special address which always refers to
+   * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a.
+  */
+  byte_copy(&ix.ip,4,"\0\0\0\0");
+  if (!ipalloc_append(&ipme,&ix)) { return 0; }
+
   if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1;
  
   len = 256;
@@ -90,6 +99,34 @@
     x += len;
   }
   close(s);
+ 
+  /* Now see if there are any supplemental IPs */
+  if ( (moreipme_fd = open_read("control/moreipme")) != -1)  
+  {
+    char inbuf[1024];
+    substdio ss;
+    stralloc l = {0};
+    int match;
+
+    substdio_fdbuf(&ss, read, moreipme_fd, inbuf, sizeof(inbuf));
+    while (1)
+    {
+      if (getln(&ss, &l, &match, '\n') == -1)
+      {
+        break;
+      }
+      if (!match && !l.len)
+      {
+        break;
+      }
+      l.len--;
+      if (!stralloc_0(&l)) { close(moreipme_fd); return 0; }
+      if (!ip_scan(l.s, &ix.ip)) { continue; }
+      if (!ipalloc_append(&ipme,&ix)) { close(moreipme_fd); return 0; }
+    }
+  }
+  close(moreipme_fd);
+
   ipmeok = 1;
   return 1;
 }

Reply via email to