Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[svn:qpsmtpd] r740 - in trunk: . lib/Qpsmtpd plugins

1 view
Skip to first unread message

mser...@cvs.perl.org

unread,
May 17, 2007, 6:16:29 PM5/17/07
to svn-commi...@perl.org
Author: msergeant
Date: Thu May 17 15:16:27 2007
New Revision: 740

Modified:
trunk/lib/Qpsmtpd/Constants.pm
trunk/lib/Qpsmtpd/TcpServer.pm
trunk/plugins/check_relay
trunk/plugins/require_resolvable_fromhost
trunk/qpsmtpd-forkserver
trunk/qpsmtpd-prefork

Log:
IPv6 support from issue #7.


Modified: trunk/lib/Qpsmtpd/Constants.pm
==============================================================================
--- trunk/lib/Qpsmtpd/Constants.pm (original)
+++ trunk/lib/Qpsmtpd/Constants.pm Thu May 17 15:16:27 2007
@@ -29,24 +29,6 @@
YIELD => 911,
);

-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");

Modified: trunk/lib/Qpsmtpd/TcpServer.pm
==============================================================================
--- trunk/lib/Qpsmtpd/TcpServer.pm (original)
+++ trunk/lib/Qpsmtpd/TcpServer.pm Thu May 17 15:16:27 2007
@@ -8,6 +8,23 @@

use POSIX ();

+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;
+}
+
my $first_0;

sub start_connection {
@@ -104,4 +121,43 @@
exit;
}

+# local/remote port and ip address
+sub lrpip {
+ my ($server, $client, $hisaddr) = @_;
+
+ my ($port, $iaddr) = ($server->sockdomain == AF_INET) ? (sockaddr_in($hisaddr)) : (sockaddr_in6($hisaddr));
+ my $localsockaddr = getsockname($client);
+ 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 $nto_laddr = ($server->sockdomain == AF_INET) ? (inet_ntoa($laddr)) : (inet_ntop(AF_INET6(), $laddr));
+ $nto_iaddr =~ s/::ffff://;
+ $nto_laddr =~ s/::ffff://;
+
+ return ($port, $iaddr, $lport, $laddr, $nto_iaddr, $nto_laddr);
+}
+
+sub tcpenv {
+ my ($nto_laddr, $nto_iaddr, $no_rdns) = @_;
+
+ my $TCPLOCALIP = $nto_laddr;
+ my $TCPREMOTEIP = $nto_iaddr;
+
+ if ($no_rdns) {
+ return ($TCPLOCALIP, $TCPREMOTEIP, $TCPREMOTEIP ? "[$ENV{TCPREMOTEIP}]" : "[noip!]");
+ }
+ my $res = new Net::DNS::Resolver;
+ $res->tcp_timeout(3);
+ $res->udp_timeout(3);
+ my $query = $res->query($nto_iaddr);
+ my $TCPREMOTEHOST;
+ if($query) {
+ foreach my $rr ($query->answer) {
+ next unless $rr->type eq "PTR";
+ $TCPREMOTEHOST = $rr->ptrdname;
+ }
+ }
+ return ($TCPLOCALIP, $TCPREMOTEIP, $TCPREMOTEHOST || "Unknown");
+}
+
1;

Modified: trunk/plugins/check_relay
==============================================================================
--- trunk/plugins/check_relay (original)
+++ trunk/plugins/check_relay Thu May 17 15:16:27 2007
@@ -2,25 +2,70 @@
# $ENV{RELAYCLIENT} to see if relaying is allowed.
#

+use Net::IP qw(:PROC);
+
sub hook_connect {
my ($self, $transaction) = @_;
my $connection = $self->qp->connection;

# Check if this IP is allowed to relay
- my @relay_clients = $self->qp->config("relayclients");
- my $more_relay_clients = $self->qp->config("morerelayclients", "map");
- my %relay_clients = map { $_ => 1 } @relay_clients;
my $client_ip = $self->qp->connection->remote_ip;
- while ($client_ip) {
- if (exists($ENV{RELAYCLIENT}) or
- exists($relay_clients{$client_ip}) or
- exists($more_relay_clients->{$client_ip}))
- {
- $connection->relay_client(1);
- last;
+
+ # @crelay... for comparing, @srelay... for stripping
+ my (@crelay_clients, @srelay_clients);
+
+ my @relay_clients = $self->qp->config("relayclients");
+ for (@relay_clients) {
+ my ($range_ip, $range_prefix) = ip_splitprefix($_);
+ if($range_prefix){
+ # has a prefix, so due for comparing
+ push @crelay_clients, $_;
+ }
+ else {
+ # has no prefix, so due for splitting
+ push @srelay_clients, $_;
}
- $client_ip =~ s/(\d|\w|::)+(:|\.)?$//; # strip off another 8 bits
}

+ if (@crelay_clients){
+ my ($range_ip, $range_prefix, $rversion, $begin, $end, $bin_client_ip);
+ my $cversion = ip_get_version($client_ip);
+ for (@crelay_clients) {
+ # Get just the IP from the CIDR range, to get the IP version, so we can
+ # get the start and end of the range
+ ($range_ip, $range_prefix) = ip_splitprefix($_);
+ $rversion = ip_get_version($range_ip);
+ ($begin, $end) = ip_normalize($_, $rversion);
+
+ # expand the client address (zero pad it) before converting to binary
+ $bin_client_ip = ip_iptobin(ip_expand_address($client_ip, $cversion), $cversion);
+
+ if (ip_bincomp($bin_client_ip, 'gt', ip_iptobin($begin, $rversion))
+ && ip_bincomp($bin_client_ip, 'lt', ip_iptobin($end, $rversion)))
+ {
+ $connection->relay_client(1);
+ last;
+ }
+ }
+ }
+
+ # If relay_client is already set, no point checking again
+ if (@srelay_clients && !$connection->relay_client) {
+ my $more_relay_clients = $self->qp->config("morerelayclients", "map");
+ my %srelay_clients = map { $_ => 1 } @srelay_clients;
+ $client_ip =~ s/::/:/;
+ ($connection->relay_client(1) && undef($client_ip)) if $client_ip eq ":1";
+
+ while ($client_ip) {
+ if (exists($ENV{RELAYCLIENT}) or
+ exists($srelay_clients{$client_ip}) or
+ exists($more_relay_clients->{$client_ip}))
+ {
+ $connection->relay_client(1);
+ last;
+ }
+ $client_ip =~ s/(\d|\w)+(:|\.)?$//; # strip off another 8 bits
+ }
+ }
return (DECLINED);
}

Modified: trunk/plugins/require_resolvable_fromhost
==============================================================================
--- trunk/plugins/require_resolvable_fromhost (original)
+++ trunk/plugins/require_resolvable_fromhost Thu May 17 15:16:27 2007
@@ -1,9 +1,10 @@
use Qpsmtpd::DSN;
use Net::DNS qw(mx);
use Socket;
+use Net::IP qw(:PROC);

my %invalid = ();
-my $has_ipv6 = Qpsmtpd::Constants::has_ipv6;
+my $has_ipv6 = Qpsmtpd::TcpServer::has_ipv6;

sub hook_mail {
my ($self, $transaction, $sender, %param) = @_;
@@ -99,6 +100,9 @@
sub mx_valid {
my ($self, $name, $host) = @_;
my $res = new Net::DNS::Resolver;
+ # IP in MX
+ return is_valid($name) if ip_is_ipv4($name) or ip_is_ipv6($name);
+
my @mx_answers;
my $query = $res->search($name, 'A');
if ($query) {

Modified: trunk/qpsmtpd-forkserver
==============================================================================
--- trunk/qpsmtpd-forkserver (original)
+++ trunk/qpsmtpd-forkserver Thu May 17 15:16:27 2007
@@ -18,7 +18,7 @@
use strict;
$| = 1;

-my $has_ipv6 = Qpsmtpd::Constants::has_ipv6;
+my $has_ipv6 = Qpsmtpd::TcpServer::has_ipv6;

if ($has_ipv6) {
eval 'use Socket6';
@@ -40,8 +40,10 @@
print <<"EOT";
usage: qpsmtpd-forkserver [ options ]
-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).
+ multiple times for multiple bindings. IPv6
+ addresses must be inside square brackets [], and
+ don't need to be zero padded.
+ Default is [::] (if has_ipv6) or 0.0.0.0 (if not)
-p, --port P : listen on a specific port; default 2525; can be
specified multiple times for multiple bindings.
-c, --limit-connections N : limit concurrent connections to N; default 15
@@ -234,14 +236,8 @@
next;
}
IO::Handle::blocking($client, 1);
- my ($port, $iaddr) = ($server->sockdomain == AF_INET) ? (sockaddr_in($hisaddr)) : (sockaddr_in6($hisaddr));
- my $localsockaddr = getsockname($client);
- 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://;
+ # get local/remote hostname, port and ip address
+ my ($port, $iaddr, $lport, $laddr, $nto_iaddr, $nto_laddr) = Qpsmtpd::TcpServer::lrpip($server, $client, $hisaddr);

my ($rc, @msg) = $qpsmtpd->run_hooks("pre-connection",
remote_ip => $nto_iaddr,
@@ -293,28 +289,9 @@
::log(LOGINFO, "Connection Timed Out");
exit; };

- $ENV{TCPLOCALIP} = $nto_laddr;
- # my ($port, $iaddr) = sockaddr_in($hisaddr);
- $ENV{TCPREMOTEIP} = $nto_iaddr;
-
- if ($NORDNS) {
- $ENV{TCPREMOTEHOST} = $ENV{TCPREMOTEIP} ? "[$ENV{TCPREMOTEIP}]" : "[noip!]";
- }
- else {
- my $zero = $0;
- $0 = "$zero (gethostbyname $ENV{TCPREMOTEIP})";
-
- 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";
- }
-
- $0 = $zero;
- }
-
+ # set enviroment variables
+ ($ENV{TCPLOCALIP}, $ENV{TCPREMOTEIP}, $ENV{TCPREMOTEHOST}) = Qpsmtpd::TcpServer::tcpenv($nto_laddr, $nto_iaddr);
+
# don't do this!
#$0 = "qpsmtpd-forkserver: $ENV{TCPREMOTEIP} / $ENV{TCPREMOTEHOST}";

Modified: trunk/qpsmtpd-prefork
==============================================================================
--- trunk/qpsmtpd-prefork (original)
+++ trunk/qpsmtpd-prefork Thu May 17 15:16:27 2007
@@ -19,6 +19,12 @@
use Qpsmtpd::Constants;
use Getopt::Long;

+my $has_ipv6 = Qpsmtpd::TcpServer::has_ipv6;
+
+if ($has_ipv6) {
+ use Socket6;
+}
+
#use Time::HiRes qw(gettimeofday tv_interval);

# secure shell
@@ -47,7 +53,14 @@
my $pid_path = '/var/run/qpsmtpd/';
my $PID = $pid_path . "/qpsmtpd.pid";
my $d_port = 25;
-my $d_addr = "0.0.0.0";
+my $d_addr;
+if ($has_ipv6) {
+ $d_addr = "[::]";
+}
+else {
+ $d_addr = "0.0.0.0";
+}
+
my $debug = 0;
my $max_children = 15; # max number of child processes to spawn
my $idle_children = 5; # number of idle child processes to spawn
@@ -67,7 +80,7 @@
print <<"EOT";
Usage: qpsmtpd-prefork [ options ]
--quiet : Be quiet (even errors are suppressed)
---version : Show version information
+--version : Show version information
--debug : Enable debug output
--interface addr : Interface daemon should listen on (default: $d_addr)
--port int : TCP port daemon should listen on (default: $d_port)
@@ -134,15 +147,20 @@
if (!$uuid || !$ugid);
}

+ my @Socket_opts = (
+ LocalPort => $d_port,
+ LocalAddr => $d_addr,
+ Proto => 'tcp',
+ Listen => SOMAXCONN,
+ Reuse => 1,
+ );
# create new socket (used by clients to communicate with daemon)
- $d =
- new IO::Socket::INET(
- LocalPort => $d_port,
- LocalAddr => $d_addr,
- Proto => 'tcp',
- Listen => SOMAXCONN,
- Reuse => 1,
- );
+ if ($has_ipv6) {
+ $d = IO::Socket::INET6->new(@Socket_opts);
+ }
+ else {
+ $d = IO::Socket::INET(@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;
@@ -361,7 +379,7 @@
my $sigset = block_signal(SIGHUP);

# start a session if connection looks valid
- qpsmtpd_session($client, $qpsmtpd) if ($iinfo);
+ qpsmtpd_session($client, $iinfo, $qpsmtpd) if ($iinfo);

# close connection and cleanup
$client->shutdown(2);
@@ -512,15 +530,16 @@

# start qpmstpd session
# arg0: ref to socket object
-# arg1: ref to qpsmtpd instance
+# arg1: ref to socket object
+# arg2: ref to qpsmtpd instance
# ret0: void
sub qpsmtpd_session {
my $client = shift; #arg0
- my $qpsmtpd = shift; #arg1
+ my $iinfo = shift; #arg1
+ my $qpsmtpd = shift; #arg2

# get local/remote hostname, port and ip address
- my ($port, $iaddr) = sockaddr_in(getpeername($client)); #remote
- my ($lport, $laddr) = sockaddr_in(getsockname($client)); #local
+ my ($port, $iaddr, $lport, $laddr, $nto_iaddr, $nto_laddr) = Qpsmtpd::TcpServer::lrpip($d, $client, $iinfo);

# get current connected ip addresses (from shared memory)
my %children;
@@ -529,9 +548,9 @@
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 %children],
@@ -574,9 +593,7 @@
};

# set enviroment variables
- $ENV{TCPLOCALIP} = inet_ntoa($laddr);
- $ENV{TCPREMOTEIP} = inet_ntoa($iaddr);
- $ENV{TCPREMOTEHOST} = gethostbyaddr($iaddr, AF_INET) || "Unknown";
+ ($ENV{TCPLOCALIP}, $ENV{TCPREMOTEIP}, $ENV{TCPREMOTEHOST}) = Qpsmtpd::TcpServer::tcpenv($nto_laddr, $nto_iaddr);

# run qpmsptd functions
$SIG{__DIE__} = 'DEFAULT';

a...@develooper.com

unread,
May 17, 2007, 6:27:36 PM5/17/07
to svn-commi...@perl.org

On May 17, 2007, at 15:16, mser...@cvs.perl.org wrote:

+my $has_ipv6 = Qpsmtpd::TcpServer::has_ipv6;

Does that work with the Apache and the async server?

Also - I'm not entirely sure how it should work, but it seems like
there are some opportunities for putting some of the "deal with IP
addresses" stuff in as helper methods in Qpsmtpd::Plugin (or another
"helper base class").


- ask

--
http://develooper.com/ - http://askask.com/


Matt Sergeant

unread,
May 17, 2007, 6:30:45 PM5/17/07
to Ask Bjørn Hansen, svn-commi...@perl.org
On 17-May-07, at 6:27 PM, Ask Bjørn Hansen wrote:

>
> On May 17, 2007, at 15:16, mser...@cvs.perl.org wrote:
>
> +my $has_ipv6 = Qpsmtpd::TcpServer::has_ipv6;
>
> Does that work with the Apache and the async server?

Sort of :-)

It works in the sense that all has_ipv6() does is check the perl IPv6
libraries load. But -async (and I think Apache::*) has no support for
ipv6.

> Also - I'm not entirely sure how it should work, but it seems like
> there are some opportunities for putting some of the "deal with IP
> addresses" stuff in as helper methods in Qpsmtpd::Plugin (or
> another "helper base class").

No, I think the way it's done is specific to TcpServer, mostly.

Matt.


a...@develooper.com

unread,
May 17, 2007, 6:34:08 PM5/17/07
to Matt Sergeant, svn-commi...@perl.org

On May 17, 2007, at 15:30, Matt Sergeant wrote:

> On 17-May-07, at 6:27 PM, Ask Bjørn Hansen wrote:
>
>>
>> On May 17, 2007, at 15:16, mser...@cvs.perl.org wrote:
>>
>> +my $has_ipv6 = Qpsmtpd::TcpServer::has_ipv6;
>>
>> Does that work with the Apache and the async server?
>
> Sort of :-)
>
> It works in the sense that all has_ipv6() does is check the perl
> IPv6 libraries load. But -async (and I think Apache::*) has no
> support for ipv6.

Well, it makes the tests break. :-)

>> Also - I'm not entirely sure how it should work, but it seems like
>> there are some opportunities for putting some of the "deal with IP
>> addresses" stuff in as helper methods in Qpsmtpd::Plugin (or
>> another "helper base class").
>
> No, I think the way it's done is specific to TcpServer, mostly.

(Just to be clear) I was thinking the stuff that went into the
plugins -- it's nasty to have it be TcpServer specific (for one
thing) and it seems like some of it should be general enough to be in
the API somehow. (Not important now, but worth keeping in mind
before we add more IPv6 support).

0 new messages