develooper Front page | perl.cvs.qpsmtpd | Postings from May 2007

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

Thread Next
From:
msergeant
Date:
May 17, 2007 15:16
Subject:
[svn:qpsmtpd] r740 - in trunk: . lib/Qpsmtpd plugins
Message ID:
20070517221629.6F7D3CB9B8@x12.develooper.com
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';

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About