You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by mm...@apache.org on 2011/09/16 18:44:01 UTC

svn commit: r1171657 - in /spamassassin/trunk: sa-update.raw spamd/spamd.raw

Author: mmartinec
Date: Fri Sep 16 16:44:01 2011
New Revision: 1171657

URL: http://svn.apache.org/viewvc?rev=1171657&view=rev
Log:
Bug 6654: Make sa-update and its infrastructure usable over IPv6
- let sa-update check whether it has inet and inet6 capabilities;
- add command line options -4 and -6 to be able to overrule a
  default (which is to use whatever is available or both);
  these options are modelled after their nameseke options
  in telnet, ssh, curl, ...
- for consistency add an alias option -4 to spamd too
  (alias for --ipv4);
- as the LWP module does not understand IPv6, trick it by
  hotpatching IO::Socket::INET6 over IO::Socket::INET,
  as sugested by the Net::INET6Glue::INET_is_INET6 module;
- btw, slightly generalize a do_txt_query into do_dns_query,
  we may decide to do additional tests on host addresses
  in URLs to avoid some ugly warnings from LWP;
- btw, do not treat user data as perl booleans;
- improved debugging messages a bit


Modified:
    spamassassin/trunk/sa-update.raw
    spamassassin/trunk/spamd/spamd.raw

Modified: spamassassin/trunk/sa-update.raw
URL: http://svn.apache.org/viewvc/spamassassin/trunk/sa-update.raw?rev=1171657&r1=1171656&r2=1171657&view=diff
==============================================================================
--- spamassassin/trunk/sa-update.raw (original)
+++ spamassassin/trunk/sa-update.raw Fri Sep 16 16:44:01 2011
@@ -128,6 +128,20 @@ my @channels = ( 'updates.spamassassin.o
 
 my $IGNORE_MIRBY_OLDER_THAN = (24 * 60 * 60 * 7);       # 1 week
 
+my $have_inet4 = eval {
+  require IO::Socket::INET;
+  my $sock = IO::Socket::INET->new(LocalAddr => '0.0.0.0', Proto => 'udp');
+  $sock->close or die "error closing inet socket: $!"  if $sock;
+  $sock ? 1 : undef;
+};
+
+my $have_inet6 = eval {
+  require IO::Socket::INET6;
+  my $sock = IO::Socket::INET6->new(LocalAddr => '::', Proto => 'udp');
+  $sock->close or die "error closing inet6 socket: $!"  if $sock;
+  $sock ? 1 : undef;
+};
+
 ##############################################################################
 
 use constant MIRBY_DOWNLOADED => -1;
@@ -162,6 +176,9 @@ GetOptions(
   'updatedir=s'				=> \$opt{'updatedir'},
   'gpg!'				=> \$GPG_ENABLED,
 
+  '4'                                   => sub { $opt{'force_pf'} = 'inet' },
+  '6'                                   => sub { $opt{'force_pf'} = 'inet6' },
+
   # backward compatibility
   'usegpg'				=> \$GPG_ENABLED,
 
@@ -175,6 +192,15 @@ if ( defined $opt{'version'} ) {        
   exit(0);
 }
 
+if ( $opt{'force_pf'} && $opt{'force_pf'} eq 'inet' && !$have_inet4 ) {
+  warn "Option -4 specified but support for the ".
+       "INET protocol family is not available.\n";
+}
+if ( $opt{'force_pf'} && $opt{'force_pf'} eq 'inet6' && !$have_inet6 ) {
+  warn "Option -6 specified but support for the ".
+       "INET6 protocol family is not available.\n";
+}
+
 # Figure out what version of SpamAssassin we're using, and also figure out the
 # reverse of it for the DNS query.  Handle x.yyyzzz as well as x.yz.
 my $SAVersion = $Mail::SpamAssassin::VERSION;
@@ -274,7 +300,7 @@ if (defined $opt{'gpgkeyfile'}) {
     $key =~ s/#.*$//;   # remove comments
     $key =~ s/^\s+//;   # remove leading whitespace
     $key =~ s/\s+$//;   # remove tailing whitespace
-    next unless ($key); # skip empty lines
+    next if $key eq ''; # skip empty lines
 
     unless (is_valid_gpg_key_id($key)) {
       dbg("gpg: invalid key id $key");
@@ -349,7 +375,7 @@ if (defined $opt{'channelfile'}) {
     $chan =~ s/#.*$//;   # remove comments
     $chan =~ s/^\s+//;   # remove leading whitespace
     $chan =~ s/\s+$//;   # remove tailing whitespace
-    next unless ($chan); # skip empty lines
+    next if $chan eq ''; # skip empty lines
 
     $chan = lc $chan;
     dbg("channel: adding $chan");
@@ -380,11 +406,20 @@ if ($opt{'install'}) {
 
 } else {
   $res = Net::DNS::Resolver->new();
-
+  $res->force_v4(1)  if $have_inet4 &&
+                        $opt{'force_pf'} && $opt{'force_pf'} eq 'inet';
   $ua = LWP::UserAgent->new();
   $ua->agent("sa-update/$VERSION/$SAVersion");
   $ua->timeout(60);      # a good long timeout; 10 is too short for Coral!
   $ua->env_proxy;
+
+  if ($opt{'force_pf'}) {
+    if ($have_inet4 && $opt{'force_pf'} eq 'inet') {
+      $ua->local_address('0.0.0.0');
+    } elsif ($have_inet6 && $opt{'force_pf'} eq 'inet6') {
+      $ua->local_address('::');
+    }
+  }
 }
 
 # Generate a temporary file to put channel content in for later use ...
@@ -453,7 +488,7 @@ foreach my $channel (@channels) {
     # Setup the channel version DNS query
     my $DNSQ = "$RevSAVersion.$channel";
 
-    my $dnsV = join(' ', do_txt_query($DNSQ));
+    my $dnsV = join(' ', do_dns_query($DNSQ));
     local($1);
     if (defined $dnsV && $dnsV =~ /^(\d+)/) {
       $newV = $1 if (!defined $newV || $1 > $newV);
@@ -463,7 +498,7 @@ foreach my $channel (@channels) {
     # Not getting a response isn't a failure, there may just not be any updates
     # for this SA version yet.
     if (!defined $newV) {
-      my @mirs = do_txt_query("mirrors.$channel");
+      my @mirs = do_dns_query("mirrors.$channel");
       if (defined shift @mirs) {
         dbg("channel: no updates available, skipping channel");
       } else {
@@ -534,8 +569,29 @@ foreach my $channel (@channels) {
   my $SHA1;
   my $GPG;
   if (!$instfile) {
+
+    dbg("channel: protocol family available: %s%s",
+        join(',', $have_inet4 ? 'inet'  : (),
+                  $have_inet6 ? 'inet6' : ()),
+        $opt{'force_pf'} ? '; force '.$opt{'force_pf'} : '' );
+
+    if ($have_inet6 && (!$opt{'force_pf'} || $opt{'force_pf'} eq 'inet6')) {
+      # LWP module has no support yet for IPv6.
+      # Use hotpatching, copying IO::Socket::INET6 to IO::Socket::INET.
+      # 'Borrowed' from Net::INET6Glue::INET_is_INET6 :
+      $INC{'IO/Socket/INET.pm'} = $INC{'IO/Socket/INET6.pm'};
+      no strict 'refs';
+      no warnings 'redefine';
+      for ( keys %{IO::Socket::INET6::} ) {
+        ref(my $v = $IO::Socket::INET6::{$_}) and next;
+        *{ 'IO::Socket::INET::'.$_ } =
+          \&{ 'IO::Socket::INET6::'.$_ } if *{$v}{CODE};
+      }
+    }
+
     # Read in the MIRRORED.BY file if it exists
     if (open(MIRBY, $mirby_path)) {
+      dbg("channel: reading MIRRORED.BY file $mirby_path");
       $mirby_time = (stat $mirby_path)[9];
 
       if ($opt{'refreshmirrors'}) {
@@ -555,7 +611,7 @@ foreach my $channel (@channels) {
     if (!defined $mirby) {
       # We don't currently have the list of mirrors, so go grab it.
       dbg("channel: no MIRRORED.BY file available");
-      my @mirrors = do_txt_query("mirrors.$channel");
+      my @mirrors = do_dns_query("mirrors.$channel");
       unless (@mirrors) {
         warn "error: no mirror data available for channel $channel\n";
         channel_failed("channel: MIRRORED.BY file location was not in DNS");
@@ -580,7 +636,7 @@ foreach my $channel (@channels) {
     }
 
     # Read in the list of mirrors
-    dbg("channel: reading MIRRORED.BY file");
+    dbg("channel: parsing MIRRORED.BY file");
     my %mirrors;
     my @mirrors = split(/^/, $mirby);
     while(my $mirror = shift @mirrors) {
@@ -589,7 +645,7 @@ foreach my $channel (@channels) {
       $mirror =~ s/#.*$//;   # remove comments
       $mirror =~ s/^\s+//;   # remove leading whitespace
       $mirror =~ s/\s+$//;   # remove tailing whitespace
-      next unless ($mirror); # skip empty lines
+      next if $mirror eq ''; # skip empty lines
 
       # We only support HTTP right now
       if ($mirror !~ m@^http://@i) {
@@ -597,10 +653,9 @@ foreach my $channel (@channels) {
         next;
       }
 
-      my @data;
-
       dbg("channel: found mirror $mirror");
 
+      my @data;
       ($mirror,@data) = split(/\s+/, $mirror);
       $mirror =~ s@/+$@@; # http://example.com/updates/ -> .../updates
       $mirrors{$mirror}->{weight} = 1;
@@ -610,7 +665,7 @@ foreach my $channel (@channels) {
       }
     }
 
-    unless (keys %mirrors) {
+    unless (%mirrors) {
       warn "error: no mirrors available for channel $channel\n";
       channel_failed("channel: no mirrors available");
       next;
@@ -1160,28 +1215,28 @@ failed:
 
 ##############################################################################
 
-# Do a generic TXT query
-sub do_txt_query {
-  my($query) = shift;
+# Do a generic DNS query
+sub do_dns_query {
+  my($query, $rr_type) = @_;
+  $rr_type = 'TXT'  if !defined $rr_type;
 
-  my $RR = $res->query($query, 'TXT');
+  my $RR = $res->query($query, $rr_type);
   my @result;
 
   if ($RR) {
     foreach my $rr ($RR->answer) {
       next if !$rr;  # no answer records, only rcode
-      next if $rr->type ne 'TXT';  # only interested in TXT fields
+      next if $rr->type ne $rr_type;
       my $text = $rr->rdatastr;
       local($1);
-      $text =~ /^"(.*)"$/;
-      push @result, $1;
+      push(@result,$1) if $text =~ /^"(.*)"$/;
     }
-    printf("DNS TXT query: %s -> %s\n", $query, join(", ",@result))
+    printf("DNS %s query: %s -> %s\n", $rr_type, $query, join(", ",@result))
       if $opt{'verbose'} && $opt{'verbose'} > 1;
   }
   else {
     dbg("dns: query failed: $query => " . $res->errorstring);
-    printf("DNS TXT query %s failed: %s\n", $query, $res->errorstring)
+    printf("DNS %s query %s failed: %s\n", $rr_type, $query, $res->errorstring)
       if $opt{'verbose'} && $opt{'verbose'} > 1;
   }
 
@@ -1229,6 +1284,10 @@ sub http_get {
     if ($ims && $response->status_line =~ /^3/) {
       return;
     }
+    if ($response->status_line =~ /^[45]/) {
+      # client error or server error, makes no sense retrying
+      return;
+    }
 
     # include the text in the debug output; it's useful in some cases,
     # e.g. proxies that require authentication, diagnosing fascist
@@ -1494,6 +1553,8 @@ Options:
                           For more verbosity specify multiple times
   -V, --version           Print version
   -h, --help              Print usage message
+  -4                      Force using the inet protocol (IPv4), not inet6
+  -6                      Force using the inet6 protocol (IPv6), not inet
 
 =head1 DESCRIPTION
 

Modified: spamassassin/trunk/spamd/spamd.raw
URL: http://svn.apache.org/viewvc/spamassassin/trunk/spamd/spamd.raw?rev=1171657&r1=1171656&r2=1171657&view=diff
==============================================================================
--- spamassassin/trunk/spamd/spamd.raw (original)
+++ spamassassin/trunk/spamd/spamd.raw Fri Sep 16 16:44:01 2011
@@ -227,7 +227,7 @@ GetOptions(
   'helper-home-dir|H:s'      => \$opt{'home_dir_for_helpers'},
   'help|h'                   => \$opt{'help'},
   'ident-timeout=f'          => \$opt{'ident-timeout'},
-  'ipv4only|ipv4-only|ipv4'  => \$opt{'force_ipv4'},
+  '4|ipv4only|ipv4-only|ipv4'=> \$opt{'force_ipv4'},
   'ldap-config!'             => \$opt{'ldap-config'},
   'listen-ip|ip-address|i:s' => \$opt{'listen-ip'},
   'local!'                   => \$opt{'local'},
@@ -2849,7 +2849,7 @@ Options:
  -d, --daemonize                   Daemonize
  -h, --help                        Print usage message
  -i [ipaddr], --listen-ip=ipaddr   Listen on the IP ipaddr
- --ipv4only, --ipv4-only, --ipv4   Disable attempted use of ipv6 for DNS
+ -4, --ipv4only, --ipv4-only, --ipv4  Disable attempted use of ipv6 for DNS
  -p port, --port=port              Listen on specified port
  -m num, --max-children=num        Allow maximum num children
  --min-children=num                Allow minimum num children
@@ -3235,7 +3235,7 @@ please see the documentation at:
 
 	C<http://wiki.apache.org/spamassassin/DebugChannels>
 
-=item B< --ipv4only>, B<--ipv4-only>, B<--ipv4>
+=item B<-4>, B<--ipv4only>, B<--ipv4-only>, B<--ipv4>
 
 Do not use IPv6 for DNS tests. Use if the existing tests
 for IPv6 availability produce incorrect results or crashes.