[PATCH] prefork: add multi-address support
Allows qpsmtpd-prefork to listen on multiple address/port combinations simultaneously, based on the corresponding implementation in forkserver. Signed-off-by: Robert <rspier@pobox.com>
This commit is contained in:
parent
6b81c68666
commit
19a0f5ded1
108
qpsmtpd-prefork
108
qpsmtpd-prefork
@ -18,9 +18,9 @@ BEGIN {
|
|||||||
|
|
||||||
# includes
|
# includes
|
||||||
use IO::Socket;
|
use IO::Socket;
|
||||||
|
use IO::Select;
|
||||||
use POSIX;
|
use POSIX;
|
||||||
use IPC::Shareable(':all');
|
use IPC::Shareable(':all');
|
||||||
use lib 'lib';
|
|
||||||
use Qpsmtpd::TcpServer::Prefork;
|
use Qpsmtpd::TcpServer::Prefork;
|
||||||
use Qpsmtpd::Constants;
|
use Qpsmtpd::Constants;
|
||||||
use Getopt::Long;
|
use Getopt::Long;
|
||||||
@ -62,18 +62,12 @@ my $chld_pool;
|
|||||||
my $chld_busy;
|
my $chld_busy;
|
||||||
my @children_term; # terminated children, their death pending processing
|
my @children_term; # terminated children, their death pending processing
|
||||||
# by the main loop
|
# by the main loop
|
||||||
my $d; # socket
|
my $select = new IO::Select; # socket(s)
|
||||||
|
|
||||||
# default settings
|
# default settings
|
||||||
my $pid_file;
|
my $pid_file;
|
||||||
my $d_port = 25;
|
my $d_port = 25;
|
||||||
my $d_addr;
|
my @d_addr; # default applied after getopt call
|
||||||
if ($has_ipv6) {
|
|
||||||
$d_addr = "[::]";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$d_addr = "0.0.0.0";
|
|
||||||
}
|
|
||||||
|
|
||||||
my $debug = 0;
|
my $debug = 0;
|
||||||
my $max_children = 15; # max number of child processes to spawn
|
my $max_children = 15; # max number of child processes to spawn
|
||||||
@ -97,8 +91,10 @@ Usage: qpsmtpd-prefork [ options ]
|
|||||||
--quiet : Be quiet (even errors are suppressed)
|
--quiet : Be quiet (even errors are suppressed)
|
||||||
--version : Show version information
|
--version : Show version information
|
||||||
--debug : Enable debug output
|
--debug : Enable debug output
|
||||||
--listen-address addr: Listen for connections on the address 'addr' (default: $d_addr);
|
--listen-address addr: Listen for connections on the address 'addr' (either
|
||||||
synonymous with --interface
|
an IP address or ip:port pair). Listens on all
|
||||||
|
interfaces by default; may be specified multiple
|
||||||
|
times.
|
||||||
--port int : TCP port daemon should listen on (default: $d_port)
|
--port int : TCP port daemon should listen on (default: $d_port)
|
||||||
--max-from-ip int : Limit number of connections from single IP (default: $maxconnip, 0 to disable)
|
--max-from-ip int : Limit number of connections from single IP (default: $maxconnip, 0 to disable)
|
||||||
--children int : Max number of children that can be spawned (default: $max_children)
|
--children int : Max number of children that can be spawned (default: $max_children)
|
||||||
@ -118,7 +114,7 @@ GetOptions(
|
|||||||
'quiet' => \$quiet,
|
'quiet' => \$quiet,
|
||||||
'version' => sub { print "Qpsmtpd Daemon - version $VERSION\n"; exit 0; },
|
'version' => sub { print "Qpsmtpd Daemon - version $VERSION\n"; exit 0; },
|
||||||
'debug' => \$debug,
|
'debug' => \$debug,
|
||||||
'interface|listen-address=s' => \$d_addr,
|
'interface|listen-address=s' => \@d_addr,
|
||||||
'port=i' => \$d_port,
|
'port=i' => \$d_port,
|
||||||
'max-from-ip=i' => \$maxconnip,
|
'max-from-ip=i' => \$maxconnip,
|
||||||
'children=i' => \$max_children,
|
'children=i' => \$max_children,
|
||||||
@ -131,8 +127,20 @@ GetOptions(
|
|||||||
'help' => \&usage,
|
'help' => \&usage,
|
||||||
) || &usage;
|
) || &usage;
|
||||||
|
|
||||||
if ($user =~ /^([\w\-]+)$/) { $user = $1 } else { &usage }
|
if ($user && $user =~ /^([\w\-]+)$/) { $user = $1 } else { &usage }
|
||||||
if ($d_addr =~ /^(\[.*\]|[\w\-.]+)$/) { $d_addr = $1 } else { &usage }
|
|
||||||
|
if (@d_addr) {
|
||||||
|
for my $i (0..$#d_addr) {
|
||||||
|
if ($d_addr[$i] =~ /^(\[.*\]|[\d\w\-.]+)(?::(\d+))?$/) {
|
||||||
|
$d_addr[$i] = { 'addr' => $1, 'port' => $2 || $d_port };
|
||||||
|
} else {
|
||||||
|
print STDERR "Malformed listen address '$d_addr[$i]'\n";
|
||||||
|
&usage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@d_addr = ( 'addr' => $has_ipv6 ? "[::]" : "0.0.0.0" );
|
||||||
|
}
|
||||||
|
|
||||||
# set max from ip to max number of children if option is set to disabled
|
# set max from ip to max number of children if option is set to disabled
|
||||||
$maxconnip = $max_children if ($maxconnip == 0);
|
$maxconnip = $max_children if ($maxconnip == 0);
|
||||||
@ -186,26 +194,32 @@ sub run {
|
|||||||
endgrent;
|
endgrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
my @Socket_opts = (
|
for my $addr (@d_addr) {
|
||||||
LocalPort => $d_port,
|
my @Socket_opts = (
|
||||||
LocalAddr => $d_addr,
|
LocalPort => $addr->{port},
|
||||||
Proto => 'tcp',
|
LocalAddr => $addr->{addr},
|
||||||
Listen => SOMAXCONN,
|
Proto => 'tcp',
|
||||||
Reuse => 1,
|
Listen => SOMAXCONN,
|
||||||
);
|
Reuse => 1,
|
||||||
# create new socket (used by clients to communicate with daemon)
|
);
|
||||||
if ($has_ipv6) {
|
# create new socket (used by clients to communicate with daemon)
|
||||||
$d = IO::Socket::INET6->new(@Socket_opts);
|
my $s;
|
||||||
|
if ($has_ipv6) {
|
||||||
|
$s = IO::Socket::INET6->new(@Socket_opts);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$s = IO::Socket::INET->new(@Socket_opts);
|
||||||
|
}
|
||||||
|
die "FATAL: Failed to open socket on $addr->{addr}:$addr->{port} ($@)"
|
||||||
|
. "\nIt may be necessary to wait 20 secs before starting daemon"
|
||||||
|
. " again."
|
||||||
|
unless $s;
|
||||||
|
$select->add($s);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
$d = IO::Socket::INET->new(@Socket_opts);
|
|
||||||
}
|
|
||||||
die "FATAL: Failed to start daemon.\nReason: $!\n(It may be nessesary to "
|
|
||||||
. "wait 20 secs before starting daemon again)\n"
|
|
||||||
unless $d;
|
|
||||||
|
|
||||||
info("qpsmtpd-prefork daemon, version: $VERSION, staring on host: " .
|
info("qpsmtpd-prefork daemon, version: $VERSION, staring on host: "
|
||||||
"$d_addr, port: $d_port (user: $user [$<])");
|
. join(', ', map { "$_->{addr}:$_->{port}"} @d_addr)
|
||||||
|
. " (user: $user [$<])");
|
||||||
|
|
||||||
# reset priority
|
# reset priority
|
||||||
my $old_nice = getpriority(0, 0);
|
my $old_nice = getpriority(0, 0);
|
||||||
@ -244,9 +258,8 @@ sub run {
|
|||||||
# a notice, before the sleep below
|
# a notice, before the sleep below
|
||||||
info("shutting down");
|
info("shutting down");
|
||||||
|
|
||||||
# close socket
|
# close socket(s)
|
||||||
$d->close();
|
$_->close for $select->handles;
|
||||||
sleep 2;
|
|
||||||
|
|
||||||
# send signal to process group
|
# send signal to process group
|
||||||
kill -$sig_num{$sig} => $$;
|
kill -$sig_num{$sig} => $$;
|
||||||
@ -435,11 +448,14 @@ sub new_child {
|
|||||||
# continue to accept connections until "old age" is reached
|
# continue to accept connections until "old age" is reached
|
||||||
for (my $i = 0 ; $i < $child_lifetime ; $i++) {
|
for (my $i = 0 ; $i < $child_lifetime ; $i++) {
|
||||||
# accept a connection
|
# accept a connection
|
||||||
if ( $pretty ) {
|
if ( $pretty ) {
|
||||||
$ENV{PROCESS} = $0 if not defined $ENV{PROCESS}; # 1st time only
|
$ENV{PROCESS} = $0 if not defined $ENV{PROCESS}; # 1st time only
|
||||||
$0 = 'qpsmtpd child'; # set pretty child name in process listing
|
$0 = 'qpsmtpd child'; # set pretty child name in process listing
|
||||||
}
|
}
|
||||||
my ($client, $iinfo) = $d->accept()
|
my @ready = $select->can_read();
|
||||||
|
next unless @ready;
|
||||||
|
my $socket = $ready[0];
|
||||||
|
my ($client, $iinfo) = $socket->accept()
|
||||||
or die
|
or die
|
||||||
"failed to create new object - $!"; # wait here until client connects
|
"failed to create new object - $!"; # wait here until client connects
|
||||||
info("connect from: " . $client->peerhost . ":" . $client->peerport);
|
info("connect from: " . $client->peerhost . ":" . $client->peerport);
|
||||||
@ -464,7 +480,7 @@ sub new_child {
|
|||||||
my $sigset = block_signal(SIGHUP);
|
my $sigset = block_signal(SIGHUP);
|
||||||
|
|
||||||
# start a session if connection looks valid
|
# start a session if connection looks valid
|
||||||
qpsmtpd_session($client, $iinfo, $qpsmtpd) if ($iinfo);
|
qpsmtpd_session($socket, $client, $iinfo, $qpsmtpd) if ($iinfo);
|
||||||
|
|
||||||
# close connection and cleanup
|
# close connection and cleanup
|
||||||
$client->shutdown(2);
|
$client->shutdown(2);
|
||||||
@ -639,12 +655,14 @@ sub info {
|
|||||||
# arg2: ref to qpsmtpd instance
|
# arg2: ref to qpsmtpd instance
|
||||||
# ret0: void
|
# ret0: void
|
||||||
sub qpsmtpd_session {
|
sub qpsmtpd_session {
|
||||||
my $client = shift; #arg0
|
my $socket = shift; #arg0
|
||||||
my $iinfo = shift; #arg1
|
my $client = shift; #arg1
|
||||||
my $qpsmtpd = shift; #arg2
|
my $iinfo = shift; #arg2
|
||||||
|
my $qpsmtpd = shift; #arg3
|
||||||
|
|
||||||
# get local/remote hostname, port and ip address
|
# get local/remote hostname, port and ip address
|
||||||
my ($port, $iaddr, $lport, $laddr, $nto_iaddr, $nto_laddr) = Qpsmtpd::TcpServer::lrpip($d, $client, $iinfo);
|
my ($port, $iaddr, $lport, $laddr, $nto_iaddr, $nto_laddr) =
|
||||||
|
Qpsmtpd::TcpServer::lrpip($socket, $client, $iinfo);
|
||||||
|
|
||||||
# get current connected ip addresses (from shared memory)
|
# get current connected ip addresses (from shared memory)
|
||||||
my %children;
|
my %children;
|
||||||
|
Loading…
Reference in New Issue
Block a user