r4175@embla: ask | 2006-08-28 01:17:10 +0200

Experimental IPv6 support (forkserver only). (Mike Williams)


git-svn-id: https://svn.perl.org/qpsmtpd/branches/0.3x@657 958fd67b-6ff1-0310-b445-bb7760255be9
This commit is contained in:
Ask Bjørn Hansen 2006-08-27 23:17:33 +00:00
parent 52f38f9459
commit 6ef0bf27c7
5 changed files with 99 additions and 23 deletions

View File

@ -1,5 +1,7 @@
0.33
Experimental IPv6 support (forkserver only). (Mike Williams)
Support "module" plugins ("My::Plugin" in the config/plugins file)
Enhance the spamassassin plugin to support connecting to a remote

View File

@ -27,6 +27,24 @@ my %return_codes = (
DONE => 910,
);
my $has_ipv6;
if (
eval {require Socket6;} &&
# INET6 prior to 2.01 will not work; sorry.
eval {require IO::Socket::INET6; IO::Socket::INET6->VERSION("2.00");}
) {
import Socket6;
$has_ipv6=1;
}
else {
$has_ipv6=0;
}
sub has_ipv6 {
return $has_ipv6;
}
use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter);
@EXPORT = (keys(%return_codes), keys(%log_levels), "return_code", "log_level");

View File

@ -19,7 +19,7 @@ sub hook_connect {
$connection->relay_client(1);
last;
}
$client_ip =~ s/\d+\.?$//; # strip off another 8 bits
$client_ip =~ s/(\d|\w|::)+(:|\.)?$//; # strip off another 8 bits
}
return (DECLINED);

View File

@ -3,6 +3,7 @@ use Net::DNS qw(mx);
use Socket;
my %invalid = ();
my $has_ipv6 = Qpsmtpd::Constants::has_ipv6;
sub hook_mail {
my ($self, $transaction, $sender, %param) = @_;
@ -38,6 +39,7 @@ sub hook_mail {
sub check_dns {
my ($self, $host) = @_;
my @host_answers;
# for stuff where we can't even parse a hostname out of the address
return 0 unless $host;
@ -53,15 +55,24 @@ sub check_dns {
}
my $query = $res->search($host);
if ($query) {
foreach my $rr ($query->answer) {
if ($rr->type eq "A") {
return is_valid($rr->address);
}
elsif ($rr->type eq "MX") {
return mx_valid($self, $rr->exchange, $host);
foreach my $rrA ($query->answer) {
push(@host_answers, $rrA);
}
}
if ($has_ipv6) {
my $query = $res->search($host, 'AAAA');
if ($query) {
foreach my $rrAAAA ($query->answer) {
push(@host_answers, $rrAAAA);
}
}
}
if (@host_answers) {
foreach my $rr (@host_answers) {
return is_valid($rr->address) if $rr->type eq "A" or $rr->type eq "AAAA";
return mx_valid($self, $rr->exchange, $host) if $rr->type eq "MX";
}
}
else {
$self->log(LOGWARN, "$$ query for $host failed: ", $res->errorstring)
unless $res->errorstring eq "NXDOMAIN";
@ -88,10 +99,24 @@ sub is_valid {
sub mx_valid {
my ($self, $name, $host) = @_;
my $res = new Net::DNS::Resolver;
my $query = $res->search($name);
my @mx_answers;
my $query = $res->search($name, 'A');
if ($query) {
foreach my $rr ($query->answer) {
next unless $rr->type eq "A";
foreach my $rrA ($query->answer) {
push(@mx_answers, $rrA);
}
}
if ($has_ipv6) {
my $query = $res->search($name, 'AAAA');
if ($query) {
foreach my $rrAAAA ($query->answer) {
push(@mx_answers, $rrAAAA);
}
}
}
if (@mx_answers) {
foreach my $rr (@mx_answers) {
next unless $rr->type eq "A" or $rr->type eq "AAAA";
return is_valid($rr->address);
}
}

View File

@ -17,6 +17,12 @@ use POSIX qw(:sys_wait_h :errno_h :signal_h);
use strict;
$| = 1;
my $has_ipv6 = Qpsmtpd::Constants::has_ipv6;
if ($has_ipv6) {
use Socket6;
}
# Configuration
my $MAXCONN = 15; # max simultaneous connections
my @PORT; # port number(s)
@ -54,12 +60,17 @@ GetOptions('h|help' => \&usage,
) || &usage;
# detaint the commandline
@LOCALADDR = ( '0.0.0.0' ) if !@LOCALADDR;
if ($has_ipv6) {
@LOCALADDR = ( '[::]' ) if !@LOCALADDR;
}
else {
@LOCALADDR = ( '0.0.0.0' ) if !@LOCALADDR;
}
@PORT = ( 2525 ) if !@PORT;
my @LISTENADDR;
for (0..$#LOCALADDR) {
if ($LOCALADDR[$_] =~ /^([\d\w\-.]+)(?::(\d+))?$/) {
if ($LOCALADDR[$_] =~ /^(\[.*\]|[\d\w\-.]+)(?::(\d+))?$/) {
if ( defined $2 ) {
push @LISTENADDR, { 'addr' => $1, 'port' => $2 };
} else {
@ -106,16 +117,24 @@ $SIG{INT} = \&HUNTSMAN;
$SIG{TERM} = \&HUNTSMAN;
my $select = new IO::Select;
my $server;
# establish SERVER socket(s), bind and listen.
for my $listen_addr (@LISTENADDR) {
my $server = IO::Socket::INET->new(LocalPort => $listen_addr->{'port'},
my @Socket_opts = (LocalPort => $listen_addr->{'port'},
LocalAddr => $listen_addr->{'addr'},
Proto => 'tcp',
Reuse => 1,
Blocking => 0,
Listen => SOMAXCONN )
or die "Creating TCP socket $listen_addr->{'addr'}:$listen_addr->{'port'}: $!\n";
Listen => SOMAXCONN);
if ($has_ipv6) {
$server = IO::Socket::INET6->new(@Socket_opts)
or die "Creating TCP socket $listen_addr->{'addr'}:$listen_addr->{'port'}: $!\n";
}
else {
$server = IO::Socket::INET->new(@Socket_opts)
or die "Creating TCP socket $listen_addr->{'addr'}:$listen_addr->{'port'}: $!\n";
}
IO::Handle::blocking($server, 0);
$select->add($server);
}
@ -208,14 +227,19 @@ while (1) {
next;
}
IO::Handle::blocking($client, 1);
my ($port, $iaddr) = sockaddr_in($hisaddr);
my ($port, $iaddr) = ($server->sockdomain == AF_INET) ? (sockaddr_in($hisaddr)) : (sockaddr_in6($hisaddr));
my $localsockaddr = getsockname($client);
my ($lport, $laddr) = sockaddr_in($localsockaddr);
my ($lport, $laddr) = ($server->sockdomain == AF_INET) ? (sockaddr_in($localsockaddr)) : (sockaddr_in6($localsockaddr));
my $nto_iaddr = ($server->sockdomain == AF_INET) ? (inet_ntoa($iaddr)) : (inet_ntop(AF_INET6, $iaddr));
my $ton_iaddr = ($server->sockdomain == AF_INET) ? (inet_aton($iaddr)) : (inet_pton(AF_INET6, $iaddr));
my $nto_laddr = ($server->sockdomain == AF_INET) ? (inet_ntoa($laddr)) : (inet_ntop(AF_INET6, $laddr));
$nto_iaddr =~ s/::ffff://;
$nto_laddr =~ s/::ffff://;
my ($rc, @msg) = $qpsmtpd->run_hooks("pre-connection",
remote_ip => inet_ntoa($iaddr),
remote_ip => $nto_iaddr,
remote_port => $port,
local_ip => inet_ntoa($laddr),
local_ip => $nto_laddr,
local_port => $lport,
max_conn_ip => $MAXCONNIP,
child_addrs => [values %childstatus],
@ -259,11 +283,18 @@ while (1) {
::log(LOGINFO, "Connection Timed Out");
exit; };
$ENV{TCPLOCALIP} = inet_ntoa($laddr);
$ENV{TCPLOCALIP} = $nto_laddr;
# my ($port, $iaddr) = sockaddr_in($hisaddr);
$ENV{TCPREMOTEIP} = inet_ntoa($iaddr);
$ENV{TCPREMOTEHOST} = gethostbyaddr($iaddr, AF_INET) || "Unknown";
$ENV{TCPREMOTEIP} = $nto_iaddr;
if ($server->sockdomain == AF_INET) {
$ENV{TCPREMOTEHOST} = gethostbyaddr($iaddr, AF_INET) || "Unknown";
}
else {
my ($family, $socktype, $proto, $saddr, $canonname, @res) = getaddrinfo($iaddr, $port, AF_UNSPEC);
$ENV{TCPREMOTEHOST} = $canonname || "Unknown";
}
# don't do this!
#$0 = "qpsmtpd-forkserver: $ENV{TCPREMOTEIP} / $ENV{TCPREMOTEHOST}";