You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by jm...@apache.org on 2005/08/19 01:44:58 UTC

svn commit: r233398 - in /spamassassin/trunk: INSTALL lib/Mail/SpamAssassin/DnsResolver.pm lib/Mail/SpamAssassin/Util/DependencyInfo.pm

Author: jm
Date: Thu Aug 18 16:44:56 2005
New Revision: 233398

URL: http://svn.apache.org/viewcvs?rev=233398&view=rev
Log:
bug 4412: DNS resolver fails with an IPv6 resolver address in /etc/resolv.conf; add IPv6 support code

Modified:
    spamassassin/trunk/INSTALL
    spamassassin/trunk/lib/Mail/SpamAssassin/DnsResolver.pm
    spamassassin/trunk/lib/Mail/SpamAssassin/Util/DependencyInfo.pm

Modified: spamassassin/trunk/INSTALL
URL: http://svn.apache.org/viewcvs/spamassassin/trunk/INSTALL?rev=233398&r1=233397&r2=233398&view=diff
==============================================================================
--- spamassassin/trunk/INSTALL (original)
+++ spamassassin/trunk/INSTALL Thu Aug 18 16:44:56 2005
@@ -278,6 +278,13 @@
     to install this module.
 
 
+  - IO::Socket::INET6 (from CPAN)
+
+    This is required if the first nameserver listed in your IP
+    configuration or /etc/resolv.conf file is available only via an IPv6
+    address.
+
+
   - IO::Socket::SSL (from CPAN)
 
     If you wish to use SSL encryption to communicate between spamc and

Modified: spamassassin/trunk/lib/Mail/SpamAssassin/DnsResolver.pm
URL: http://svn.apache.org/viewcvs/spamassassin/trunk/lib/Mail/SpamAssassin/DnsResolver.pm?rev=233398&r1=233397&r2=233398&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/DnsResolver.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/DnsResolver.pm Thu Aug 18 16:44:56 2005
@@ -42,6 +42,9 @@
 use Mail::SpamAssassin::Logger;
 
 use IO::Socket::INET;
+use Errno qw(EINVAL EADDRINUSE);
+
+use constant HAS_SOCKET_INET6 => eval { require IO::Socket::INET6 };
 
 our @ISA = qw();
 
@@ -144,21 +147,58 @@
 
   $self->{sock}->close() if $self->{sock};
   my $sock;
+  my $errno;
+
+  # IO::Socket::INET6 may choose wrong LocalAddr if family is unspecified,
+  # causing EINVAL failure when automatically assigned local IP address
+  # and remote address do not belong to the same address family:
+  use Mail::SpamAssassin::Constants qw(:ip);
+  my $ip64 = IP_ADDRESS;
+  my $ip4 = IPV4_ADDRESS;
+  my $ns = $self->{res}->{nameservers}[0];
+
+  # note: if the use of "AF_INET6" as a bare word here causes trouble
+  # on your platform, just comment the line and replace with
+  # "my $family = AF_INET;" instead
+  my $family = (HAS_SOCKET_INET6 && $ns=~/^${ip64}$/o && $ns!~/^${ip4}$/o)
+                 ? AF_INET6 : AF_INET;
+
+  dbg("dns: name server: $ns, family: $family");
+
   # find next available unprivileged port (1024 - 65535)
   # starting at a random value to spread out use of ports
   my $port_offset = int(rand(64511));  # 65535 - 1024
-  for (my $i = 0; (!$sock && ($i<64511)); $i++) {
+  for (my $i = 0; $i<64511; $i++) {
     my $lport = 1024 + (($port_offset + $i) % 64511);
-    $sock = IO::Socket::INET->new (
-                                   Proto => 'udp',
-                                   LocalPort => $lport,
-                                   Type => SOCK_DGRAM,
-                                   );
+
+    my %args = (
+        PeerAddr => $ns,
+        PeerPort => $self->{res}->{port},
+        Proto => 'udp',
+        LocalPort => $lport,
+        Type => SOCK_DGRAM,
+        Domain => $family,
+    );
+
+    if (HAS_SOCKET_INET6) {
+      $sock = IO::Socket::INET6->new(%args);
+    } else {
+      $sock = IO::Socket::INET->new(%args);
+    }
+    $errno = $!;
+    if (defined $sock) {  # ok, got it
+      last;
+    } elsif ($! == EADDRINUSE) {  # in use, let's try another source port
+      dbg("dns: UDP port $lport already in use, trying another port");
+    } else {
+      # did we fail due to the attempted use of an IPv6 nameserver?
+      $self->_ipv6_ns_warning()  if !HAS_SOCKET_INET6 && $errno==EINVAL;
+      die "Error creating a DNS resolver socket: $errno";
+    }
   }
+  defined $sock or die "Can't create a DNS resolver socket: $errno";
 
   $self->{sock} = $sock;
-  $self->{dest} = sockaddr_in($self->{res}->{port},
-            inet_aton($self->{res}->{nameservers}[0]));
 
   $self->{sock_as_vec} = $self->fhs_to_vec($self->{sock});
 }
@@ -269,9 +309,8 @@
   my $pkt = $self->new_dns_packet($host, $type, $class);
 
   my $data = $pkt->data;
-  my $dest = $self->{dest};
   $self->connect_sock() if !$self->{sock};
-  if (!$self->{sock}->send ($pkt->data, 0, $self->{dest})) {
+  if (!$self->{sock}->send ($pkt->data, 0)) {
     warn "dns: sendto() failed: $@";
     return;
   }
@@ -425,6 +464,26 @@
   # and a new socket, so we don't have 5 spamds sharing the same
   # socket
   $self->connect_sock();
+}
+
+sub _ipv6_ns_warning {
+  my ($self) = @_;
+
+  # warn about the attempted use of an IPv6 nameserver without
+  # IO::Socket::INET6 installed (bug 4412)
+  my $firstns = $self->{res}->{nameservers}[0];
+
+  use Mail::SpamAssassin::Constants qw(:ip);
+  my $ip64 = IP_ADDRESS;
+  my $ip4 = IPV4_ADDRESS;
+
+  # was the nameserver in IPv6 format?
+  if ($firstns =~ /^${ip64}$/o && $firstns !~ /^${ip4}$/o) {
+    my $addr = inet_aton($firstns);
+    if (!defined $addr) {
+      die "IO::Socket::INET6 module is required to use IPv6 nameservers such as '$firstns': $@\n";
+    }
+  }
 }
 
 1;

Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Util/DependencyInfo.pm
URL: http://svn.apache.org/viewcvs/spamassassin/trunk/lib/Mail/SpamAssassin/Util/DependencyInfo.pm?rev=233398&r1=233397&r2=233398&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Util/DependencyInfo.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Util/DependencyInfo.pm Thu Aug 18 16:44:56 2005
@@ -109,6 +109,13 @@
   to install this module.',
 },
 {
+  module => 'IO::Socket::INET6',
+  version => '0.00',
+  desc => 'This is required if the first nameserver listed in your IP
+  configuration or /etc/resolv.conf file is available only via
+  an IPv6 address.',
+},
+{
   module => 'IO::Socket::SSL',
   version => '0.00',
   desc => 'If you wish to use SSL encryption to communicate between spamc and