Forkserver can accept a --listen-address switch to listen on a particular
address/interface instead of 0.0.0.0, but only one.  This expands the handling
of that switch so as to listen on any number of local addresses, using
IO::Select to manage them.  The default is a single socket on 0.0.0.0, as
usual.

Tested lightly; it works fine on my spam-only secondary MX, and isn't
perturbed by having interfaces dropped or similar surprises, but I haven't
subjected it to much stress yet.

And since I was one line away -- is there any remaining good reason why the
default port for forkserver is 2525?  This has the look of development or
testing leftovers.

-- 
Devin  \ aqua(at)devin.com, IRC:Requiem; http://www.devin.com
Carraway \ 1024D/E9ABFCD2: 13E7 199E DD1E 65F0 8905 2E43 5395 CA0D E9AB FCD2
Index: qpsmtpd-forkserver
===================================================================
--- qpsmtpd-forkserver  (revision 460)
+++ qpsmtpd-forkserver  (working copy)
@@ -10,6 +10,7 @@
 use Qpsmtpd::TcpServer;
 use Qpsmtpd::Constants;
 use IO::Socket;
+use IO::Select;
 use Socket;
 use Getopt::Long;
 use POSIX qw(:sys_wait_h :errno_h :signal_h);
@@ -19,7 +20,7 @@
 # Configuration
 my $MAXCONN   = 15;                            # max simultaneous connections
 my $PORT      = 2525;                          # port number
-my $LOCALADDR = '0.0.0.0';             # ip address to bind to
+my @LOCALADDR;                                  # ip address(es) to bind to
 my $USER      = 'smtpd';               # user to suid to
 my $MAXCONNIP = 5;              # max simultaneous connections from one IP
 my $PID_FILE   = '/var/run/qpsmtpd.pid';
@@ -27,7 +28,9 @@
 sub usage {
         print <<"EOT";
 usage: qpsmtpd-forkserver [ options ]
- -l, --listen-address addr : listen on a specific address; default 0.0.0.0
+ -l, --listen-address addr : listen on specific address(es); can be specified
+                             multiple times for multiple bindings.  Default is
+                            0.0.0.0 (all interfaces).
  -p, --port P              : listen on a specific port; default 2525
  -c, --limit-connections N : limit concurrent connections to N; default 15
  -u, --user U              : run as a particular user (default 'smtpd')
@@ -38,7 +41,7 @@
 }
 
 GetOptions('h|help' => \&usage,
-           'l|listen-address=s' => \$LOCALADDR,
+           'l|listen-address=s' => [EMAIL PROTECTED],
            'c|limit-connections=i' => \$MAXCONN,
            'm|max-from-ip=i' => \$MAXCONNIP,
            'p|port=i' => \$PORT,
@@ -48,7 +51,14 @@
 
 # detaint the commandline
 if ($PORT =~ /^(\d+)$/) { $PORT = $1 } else { &usage }
-if ($LOCALADDR =~ /^([\d\w\-.]+)$/) { $LOCALADDR = $1 } else { &usage }
[EMAIL PROTECTED] = ( '0.0.0.0' ) if [EMAIL PROTECTED];
+for (0..$#LOCALADDR) {
+  if ($LOCALADDR[$_] =~ /^([\d\w\-.]+)$/) {
+    $LOCALADDR[$_] = $1;
+  } else {
+    &usage;
+  }
+}
 if ($USER =~ /^([\w\-]+)$/) { $USER = $1 } else { &usage }
 if ($MAXCONN =~ /^(\d+)$/) { $MAXCONN = $1 } else { &usage }
 if ($PID_FILE =~ m#^(/[\w\d/\-.]+)$#) { $PID_FILE = $1 } else { &usage }
@@ -77,14 +87,19 @@
 $SIG{INT} = \&HUNTSMAN;
 $SIG{TERM} = \&HUNTSMAN;
 
-# establish SERVER socket, bind and listen.
-my $server = IO::Socket::INET->new(LocalPort => $PORT,
-                                  LocalAddr => $LOCALADDR,
-                                   Proto     => 'tcp',
-                                   Reuse     => 1,
-                                   Listen    => SOMAXCONN )
-  or die "Creating TCP socket $LOCALADDR:$PORT: $!\n";
+my $select = new IO::Select;
 
+# establish SERVER socket(s), bind and listen.
+for my $listen_addr (@LOCALADDR) {
+  my $server = IO::Socket::INET->new(LocalPort => $PORT,
+                                     LocalAddr => $listen_addr,
+                                     Proto     => 'tcp',
+                                     Reuse     => 1,
+                                     Listen    => SOMAXCONN )
+    or die "Creating TCP socket $listen_addr:$PORT: $!\n";
+  $select->add($server);
+}
+
 if (-e $PID_FILE) {
   open PID, "+<$PID_FILE"
     or die "open pid_file: $!\n";
@@ -139,9 +154,16 @@
     sleep(1) ;
     $running = scalar keys %childstatus;
   }
-    my $hisaddr = accept(my $client, $server);
-    if (!$hisaddr) {
-        # possible something condition...
+
+  my @ready = $select->can_read();
+  next if [EMAIL PROTECTED];
+  # accept one connection per loop
+  my $server = shift @ready;
+
+  my ($client, $hisaddr) = $server->accept;
+
+    if (!$client) {
+        ::log(LOGERROR,"Error accepting new connection: $!");
         next;
     }
     my ($port, $iaddr) = sockaddr_in($hisaddr);
@@ -181,7 +203,7 @@
     # all children should have different seeds, to prevent conflicts
     srand( time ^ ($$ + ($$ << 15)) );
 
-    close($server);
+    $_->close for $select->handles;
 
     $SIG{$_} = 'DEFAULT' for keys %SIG;
     $SIG{ALRM} = sub { 

Reply via email to