You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by gb...@apache.org on 2018/06/17 09:41:02 UTC

svn commit: r1833660 - in /spamassassin: branches/3.4/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm trunk/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm

Author: gbechis
Date: Sun Jun 17 09:41:02 2018
New Revision: 1833660

URL: http://svn.apache.org/viewvc?rev=1833660&view=rev
Log:
partial fix for bz 7529
starting from 04/01/2018, Geolite legacy databases has been
discontinued and they will be no more updates.
Add a "country_db_type" option that will let the user choose
between GeoIP and IP::Country::Fast databases.
By default GeoIP is enabled and there is still a fallback
on IP::Country::Fast as in previuos implementation.

IP::Country::Fast has no ipv6 support, so a better api
should be adopted sooner or later.

Modified:
    spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm
    spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm

Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm?rev=1833660&r1=1833659&r2=1833660&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm Sun Jun 17 09:41:02 2018
@@ -32,8 +32,9 @@ header markup.
 
 =head1 REQUIREMENT
 
-This plugin requires the Geo::IP module from CPAN. For backward
-compatibility IP::Country::Fast is used if Geo::IP is not installed.
+This plugin requires the Geo::IP or IP::Country::Fast module from CPAN.
+For backward compatibility IP::Country::Fast is used if Geo::IP is 
+not installed.
 
 =cut
 
@@ -49,51 +50,10 @@ use re 'taint';
 
 our @ISA = qw(Mail::SpamAssassin::Plugin);
 
-my ($db, $dbv6);
-my $ip_to_cc; # will hold a sub() for the lookup
-my $db_info;  # will hold a sub() for database info
-
-# Try to load Geo::IP first
-eval {
-  require Geo::IP;
-  $db = Geo::IP->open_type(Geo::IP->GEOIP_COUNTRY_EDITION, Geo::IP->GEOIP_STANDARD);
-  die "GeoIP.dat not found" unless $db;
-  # IPv6 requires version Geo::IP 1.39+ with GeoIP C API 1.4.7+
-  if (Geo::IP->VERSION >= 1.39 && Geo::IP->api eq 'CAPI') {
-    $dbv6 = Geo::IP->open_type(Geo::IP->GEOIP_COUNTRY_EDITION_V6, Geo::IP->GEOIP_STANDARD);
-    if (!$dbv6) {
-      dbg("metadata: RelayCountry: IPv6 support not enabled, GeoIPv6.dat not found");
-    }
-  } else {
-    dbg("metadata: RelayCountry: IPv6 support not enabled, versions Geo::IP 1.39, GeoIP C API 1.4.7 required");
-  }
-  $ip_to_cc = sub {
-    if ($dbv6 && $_[0] =~ /:/) {
-      return $dbv6->country_code_by_addr_v6($_[0]) || "XX";
-    } else {
-      return $db->country_code_by_addr($_[0]) || "XX";
-    }
-  };
-  $db_info = sub { return "Geo::IP " . ($db->database_info || '?') };
-  1;
-} or do {
-  my $eval_stat = $@ ne '' ? $@ : "errno=$!";  chomp $eval_stat;
-  dbg("metadata: RelayCountry: failed to load 'Geo::IP', skipping: $eval_stat");
-  # Try IP::Country::Fast as backup
-  eval {
-    require IP::Country::Fast;
-    $db = IP::Country::Fast->new();
-    $ip_to_cc = sub {
-      return $db->inet_atocc($_[0]) || "XX";
-    };
-    $db_info = sub { return "IP::Country::Fast ".localtime($db->db_time()); };
-    1;
-  } or do {
-    my $eval_stat = $@ ne '' ? $@ : "errno=$!";  chomp $eval_stat;
-    dbg("metadata: RelayCountry: failed to load 'IP::Country::Fast', skipping: $eval_stat");
-    return 1;
-  };
-};
+my $db;
+my $dbv6;
+my $db_info;  # will hold database info
+my $db_type;  # will hold database type
 
 # constructor: register the eval rule
 sub new {
@@ -104,11 +64,91 @@ sub new {
   $class = ref($class) || $class;
   my $self = $class->SUPER::new($mailsaobject);
   bless ($self, $class);
+
+  $self->set_config($mailsaobject->{conf});
   return $self;
 }
 
+sub set_config {
+  my ($self, $conf) = @_;
+  my @cmds;
+
+=head1 USER PREFERENCES
+
+The following options can be used in both site-wide (C<local.cf>) and
+user-specific (C<user_prefs>) configuration files to customize how
+SpamAssassin handles incoming email messages.
+
+=over 4
+
+=item country_db_type STRING
+
+This option tells SpamAssassin which type of Geo database to use.
+Valid database types are GeoIP and Fast.
+
+=back
+
+=cut
+
+  push (@cmds, {
+    setting => 'country_db_type',
+    default => "GeoIP",
+    type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
+    code => sub {
+      my ($self, $key, $value, $line) = @_;
+      if ( $value !~ /GeoIP|Fast/) {
+        return $Mail::SpamAssassin::Conf::INVALID_VALUE;
+      }
+
+      $self->{country_db_type} = $value;
+    }
+  });
+  
+  $conf->{parser}->register_commands(\@cmds);
+}
+
 sub extract_metadata {
   my ($self, $opts) = @_;
+  my $geo;
+  my $cc;
+
+  my $conf_country_db_type = $self->{'main'}{'resolver'}{'conf'}->{country_db_type};
+
+  if ( $conf_country_db_type eq "GeoIP") {
+    eval {
+      require Geo::IP;
+      $db = Geo::IP->open_type(Geo::IP->GEOIP_COUNTRY_EDITION, Geo::IP->GEOIP_STANDARD);
+      die "GeoIP.dat not found" unless $db;
+      # IPv6 requires version Geo::IP 1.39+ with GeoIP C API 1.4.7+
+      if (Geo::IP->VERSION >= 1.39 && Geo::IP->api eq 'CAPI') {
+       $dbv6 = Geo::IP->open_type(Geo::IP->GEOIP_COUNTRY_EDITION_V6, Geo::IP->GEOIP_STANDARD);
+       if (!$dbv6) {
+         dbg("metadata: RelayCountry: IPv6 support not enabled, GeoIPv6.dat not found");
+       }
+       $db_info = sub { return "Geo::IP " . ($db->database_info || '?') };
+      } else {
+       dbg("metadata: RelayCountry: IPv6 support not enabled, versions Geo::IP 1.39, GeoIP C API 1.4.7 required");
+      }
+   } or do {
+     # Fallback to IP::Country::Fast
+     dbg("metadata: RelayCountry: GeoIP.dat not found, IP::Country::Fast enabled as fallback");
+     $conf_country_db_type = "Fast";
+   }
+  }
+  if( $conf_country_db_type eq "Fast") {
+    my $eval_stat = $@ ne '' ? $@ : "errno=$!";  chomp $eval_stat;
+    # Try IP::Country::Fast as backup
+    eval {
+      require IP::Country::Fast;
+      $db = IP::Country::Fast->new();
+      $db_info = sub { return "IP::Country::Fast ".localtime($db->db_time()); };
+      1;
+    } or do {
+      my $eval_stat = $@ ne '' ? $@ : "errno=$!";  chomp $eval_stat;
+      dbg("metadata: RelayCountry: failed to load 'IP::Country::Fast', skipping: $eval_stat");
+      return 1;
+    };
+  };
 
   return 1 unless $db;
 
@@ -117,10 +157,20 @@ sub extract_metadata {
 
   my $countries = '';
   my $IP_PRIVATE = IP_PRIVATE;
+  my $IPV4_ADDRESS = IPV4_ADDRESS;
   foreach my $relay (@{$msg->{metadata}->{relays_untrusted}}) {
     my $ip = $relay->{ip};
     # Private IPs will always be returned as '**'
-    my $cc = $ip =~ /^$IP_PRIVATE$/o ? '**' : $ip_to_cc->($ip);
+    if ( $conf_country_db_type eq "GeoIP" ) {
+	  if ( $ip !~ /^$IPV4_ADDRESS$/o ) {
+	    $geo = $dbv6->country_code_by_addr_v6($ip) || "XX";
+	  } else {
+	    $geo = $db->country_code_by_addr($ip) || "XX";
+	  }
+    } elsif ( $conf_country_db_type eq "Fast" ) {
+        $geo = $db->inet_atocc($ip) || "XX";
+    }
+    $cc = $ip =~ /^$IP_PRIVATE$/o ? '**' : $geo;
     $countries .= $cc." ";
   }
 

Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm?rev=1833660&r1=1833659&r2=1833660&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm Sun Jun 17 09:41:02 2018
@@ -32,8 +32,9 @@ header markup.
 
 =head1 REQUIREMENT
 
-This plugin requires the Geo::IP module from CPAN. For backward
-compatibility IP::Country::Fast is used if Geo::IP is not installed.
+This plugin requires the Geo::IP or IP::Country::Fast module from CPAN.
+For backward compatibility IP::Country::Fast is used if Geo::IP is 
+not installed.
 
 =cut
 
@@ -49,51 +50,10 @@ use re 'taint';
 
 our @ISA = qw(Mail::SpamAssassin::Plugin);
 
-my ($db, $dbv6);
-my $ip_to_cc; # will hold a sub() for the lookup
-my $db_info;  # will hold a sub() for database info
-
-# Try to load Geo::IP first
-eval {
-  require Geo::IP;
-  $db = Geo::IP->open_type(Geo::IP->GEOIP_COUNTRY_EDITION, Geo::IP->GEOIP_STANDARD);
-  die "GeoIP.dat not found" unless $db;
-  # IPv6 requires version Geo::IP 1.39+ with GeoIP C API 1.4.7+
-  if (Geo::IP->VERSION >= 1.39 && Geo::IP->api eq 'CAPI') {
-    $dbv6 = Geo::IP->open_type(Geo::IP->GEOIP_COUNTRY_EDITION_V6, Geo::IP->GEOIP_STANDARD);
-    if (!$dbv6) {
-      dbg("metadata: RelayCountry: IPv6 support not enabled, GeoIPv6.dat not found");
-    }
-  } else {
-    dbg("metadata: RelayCountry: IPv6 support not enabled, versions Geo::IP 1.39, GeoIP C API 1.4.7 required");
-  }
-  $ip_to_cc = sub {
-    if ($dbv6 && $_[0] =~ /:/) {
-      return $dbv6->country_code_by_addr_v6($_[0]) || "XX";
-    } else {
-      return $db->country_code_by_addr($_[0]) || "XX";
-    }
-  };
-  $db_info = sub { return "Geo::IP " . ($db->database_info || '?') };
-  1;
-} or do {
-  my $eval_stat = $@ ne '' ? $@ : "errno=$!";  chomp $eval_stat;
-  dbg("metadata: RelayCountry: failed to load 'Geo::IP', skipping: $eval_stat");
-  # Try IP::Country::Fast as backup
-  eval {
-    require IP::Country::Fast;
-    $db = IP::Country::Fast->new();
-    $ip_to_cc = sub {
-      return $db->inet_atocc($_[0]) || "XX";
-    };
-    $db_info = sub { return "IP::Country::Fast ".localtime($db->db_time()); };
-    1;
-  } or do {
-    my $eval_stat = $@ ne '' ? $@ : "errno=$!";  chomp $eval_stat;
-    dbg("metadata: RelayCountry: failed to load 'IP::Country::Fast', skipping: $eval_stat");
-    return 1;
-  };
-};
+my $db;
+my $dbv6;
+my $db_info;  # will hold database info
+my $db_type;  # will hold database type
 
 # constructor: register the eval rule
 sub new {
@@ -104,11 +64,91 @@ sub new {
   $class = ref($class) || $class;
   my $self = $class->SUPER::new($mailsaobject);
   bless ($self, $class);
+
+  $self->set_config($mailsaobject->{conf});
   return $self;
 }
 
+sub set_config {
+  my ($self, $conf) = @_;
+  my @cmds;
+
+=head1 USER PREFERENCES
+
+The following options can be used in both site-wide (C<local.cf>) and
+user-specific (C<user_prefs>) configuration files to customize how
+SpamAssassin handles incoming email messages.
+
+=over 4
+
+=item country_db_type STRING
+
+This option tells SpamAssassin which type of Geo database to use.
+Valid database types are GeoIP and Fast.
+
+=back
+
+=cut
+
+  push (@cmds, {
+    setting => 'country_db_type',
+    default => "GeoIP",
+    type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
+    code => sub {
+      my ($self, $key, $value, $line) = @_;
+      if ( $value !~ /GeoIP|Fast/) {
+        return $Mail::SpamAssassin::Conf::INVALID_VALUE;
+      }
+
+      $self->{country_db_type} = $value;
+    }
+  });
+  
+  $conf->{parser}->register_commands(\@cmds);
+}
+
 sub extract_metadata {
   my ($self, $opts) = @_;
+  my $geo;
+  my $cc;
+
+  my $conf_country_db_type = $self->{'main'}{'resolver'}{'conf'}->{country_db_type};
+
+  if ( $conf_country_db_type eq "GeoIP") {
+    eval {
+      require Geo::IP;
+      $db = Geo::IP->open_type(Geo::IP->GEOIP_COUNTRY_EDITION, Geo::IP->GEOIP_STANDARD);
+      die "GeoIP.dat not found" unless $db;
+      # IPv6 requires version Geo::IP 1.39+ with GeoIP C API 1.4.7+
+      if (Geo::IP->VERSION >= 1.39 && Geo::IP->api eq 'CAPI') {
+       $dbv6 = Geo::IP->open_type(Geo::IP->GEOIP_COUNTRY_EDITION_V6, Geo::IP->GEOIP_STANDARD);
+       if (!$dbv6) {
+         dbg("metadata: RelayCountry: IPv6 support not enabled, GeoIPv6.dat not found");
+       }
+       $db_info = sub { return "Geo::IP " . ($db->database_info || '?') };
+      } else {
+       dbg("metadata: RelayCountry: IPv6 support not enabled, versions Geo::IP 1.39, GeoIP C API 1.4.7 required");
+      }
+   } or do {
+     # Fallback to IP::Country::Fast
+     dbg("metadata: RelayCountry: GeoIP.dat not found, IP::Country::Fast enabled as fallback");
+     $conf_country_db_type = "Fast";
+   }
+  }
+  if( $conf_country_db_type eq "Fast") {
+    my $eval_stat = $@ ne '' ? $@ : "errno=$!";  chomp $eval_stat;
+    # Try IP::Country::Fast as backup
+    eval {
+      require IP::Country::Fast;
+      $db = IP::Country::Fast->new();
+      $db_info = sub { return "IP::Country::Fast ".localtime($db->db_time()); };
+      1;
+    } or do {
+      my $eval_stat = $@ ne '' ? $@ : "errno=$!";  chomp $eval_stat;
+      dbg("metadata: RelayCountry: failed to load 'IP::Country::Fast', skipping: $eval_stat");
+      return 1;
+    };
+  };
 
   return 1 unless $db;
 
@@ -117,10 +157,20 @@ sub extract_metadata {
 
   my $countries = '';
   my $IP_PRIVATE = IP_PRIVATE;
+  my $IPV4_ADDRESS = IPV4_ADDRESS;
   foreach my $relay (@{$msg->{metadata}->{relays_untrusted}}) {
     my $ip = $relay->{ip};
     # Private IPs will always be returned as '**'
-    my $cc = $ip =~ /^$IP_PRIVATE$/o ? '**' : $ip_to_cc->($ip);
+    if ( $conf_country_db_type eq "GeoIP" ) {
+	  if ( $ip !~ /^$IPV4_ADDRESS$/o ) {
+	    $geo = $dbv6->country_code_by_addr_v6($ip) || "XX";
+	  } else {
+	    $geo = $db->country_code_by_addr($ip) || "XX";
+	  }
+    } elsif ( $conf_country_db_type eq "Fast" ) {
+        $geo = $db->inet_atocc($ip) || "XX";
+    }
+    $cc = $ip =~ /^$IP_PRIVATE$/o ? '**' : $geo;
     $countries .= $cc." ";
   }