View | Details | Raw Unified | Return to bug 7529
Collapse All | Expand All

(-)lib/Mail/SpamAssassin/Plugin/RelayCountry.pm (-48 / +98 lines)
Lines 32-39 Link Here
32
32
33
=head1 REQUIREMENT
33
=head1 REQUIREMENT
34
34
35
This plugin requires the Geo::IP module from CPAN. For backward
35
This plugin requires the Geo::IP or IP::Country::Fast module from CPAN.
36
compatibility IP::Country::Fast is used if Geo::IP is not installed.
36
For backward compatibility IP::Country::Fast is used if Geo::IP is 
37
not installed.
37
38
38
=cut
39
=cut
39
40
Lines 49-100 Link Here
49
50
50
our @ISA = qw(Mail::SpamAssassin::Plugin);
51
our @ISA = qw(Mail::SpamAssassin::Plugin);
51
52
52
my ($db, $dbv6);
53
my $db;
53
my $ip_to_cc; # will hold a sub() for the lookup
54
my $dbv6;
54
my $db_info;  # will hold a sub() for database info
55
my $db_info;  # will hold database info
56
my $db_type;  # will hold database type
55
57
56
# Try to load Geo::IP first
57
eval {
58
  require Geo::IP;
59
  $db = Geo::IP->open_type(Geo::IP->GEOIP_COUNTRY_EDITION, Geo::IP->GEOIP_STANDARD);
60
  die "GeoIP.dat not found" unless $db;
61
  # IPv6 requires version Geo::IP 1.39+ with GeoIP C API 1.4.7+
62
  if (Geo::IP->VERSION >= 1.39 && Geo::IP->api eq 'CAPI') {
63
    $dbv6 = Geo::IP->open_type(Geo::IP->GEOIP_COUNTRY_EDITION_V6, Geo::IP->GEOIP_STANDARD);
64
    if (!$dbv6) {
65
      dbg("metadata: RelayCountry: IPv6 support not enabled, GeoIPv6.dat not found");
66
    }
67
  } else {
68
    dbg("metadata: RelayCountry: IPv6 support not enabled, versions Geo::IP 1.39, GeoIP C API 1.4.7 required");
69
  }
70
  $ip_to_cc = sub {
71
    if ($dbv6 && $_[0] =~ /:/) {
72
      return $dbv6->country_code_by_addr_v6($_[0]) || "XX";
73
    } else {
74
      return $db->country_code_by_addr($_[0]) || "XX";
75
    }
76
  };
77
  $db_info = sub { return "Geo::IP " . ($db->database_info || '?') };
78
  1;
79
} or do {
80
  my $eval_stat = $@ ne '' ? $@ : "errno=$!";  chomp $eval_stat;
81
  dbg("metadata: RelayCountry: failed to load 'Geo::IP', skipping: $eval_stat");
82
  # Try IP::Country::Fast as backup
83
  eval {
84
    require IP::Country::Fast;
85
    $db = IP::Country::Fast->new();
86
    $ip_to_cc = sub {
87
      return $db->inet_atocc($_[0]) || "XX";
88
    };
89
    $db_info = sub { return "IP::Country::Fast ".localtime($db->db_time()); };
90
    1;
91
  } or do {
92
    my $eval_stat = $@ ne '' ? $@ : "errno=$!";  chomp $eval_stat;
93
    dbg("metadata: RelayCountry: failed to load 'IP::Country::Fast', skipping: $eval_stat");
94
    return 1;
95
  };
96
};
97
98
# constructor: register the eval rule
58
# constructor: register the eval rule
99
sub new {
59
sub new {
100
  my $class = shift;
60
  my $class = shift;
Lines 104-115 Link Here
104
  $class = ref($class) || $class;
64
  $class = ref($class) || $class;
105
  my $self = $class->SUPER::new($mailsaobject);
65
  my $self = $class->SUPER::new($mailsaobject);
106
  bless ($self, $class);
66
  bless ($self, $class);
67
68
  $self->set_config($mailsaobject->{conf});
107
  return $self;
69
  return $self;
108
}
70
}
109
71
72
sub set_config {
73
  my ($self, $conf) = @_;
74
  my @cmds;
75
76
=head1 USER PREFERENCES
77
78
The following options can be used in both site-wide (C<local.cf>) and
79
user-specific (C<user_prefs>) configuration files to customize how
80
SpamAssassin handles incoming email messages.
81
82
=over 4
83
84
=item country_db_type STRING
85
86
This option tells SpamAssassin which type of Geo database to use.
87
Valid database types are GeoIP and Fast.
88
89
=back
90
91
=cut
92
93
  push (@cmds, {
94
    setting => 'country_db_type',
95
    default => "GeoIP",
96
    type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
97
    code => sub {
98
      my ($self, $key, $value, $line) = @_;
99
      if ( $value !~ /GeoIP|Fast/) {
100
        return $Mail::SpamAssassin::Conf::INVALID_VALUE;
101
      }
102
103
      $self->{country_db_type} = $value;
104
    }
105
  });
106
  
107
  $conf->{parser}->register_commands(\@cmds);
108
}
109
110
sub extract_metadata {
110
sub extract_metadata {
111
  my ($self, $opts) = @_;
111
  my ($self, $opts) = @_;
112
  my $geo;
113
  my $cc;
112
114
115
  my $conf_country_db_type = $self->{'main'}{'resolver'}{'conf'}->{country_db_type};
116
117
  if ( $conf_country_db_type eq "GeoIP") {
118
    eval {
119
      require Geo::IP;
120
      $db = Geo::IP->open_type(Geo::IP->GEOIP_COUNTRY_EDITION, Geo::IP->GEOIP_STANDARD);
121
      die "GeoIP.dat not found" unless $db;
122
      # IPv6 requires version Geo::IP 1.39+ with GeoIP C API 1.4.7+
123
      if (Geo::IP->VERSION >= 1.39 && Geo::IP->api eq 'CAPI') {
124
       $dbv6 = Geo::IP->open_type(Geo::IP->GEOIP_COUNTRY_EDITION_V6, Geo::IP->GEOIP_STANDARD);
125
       if (!$dbv6) {
126
         dbg("metadata: RelayCountry: IPv6 support not enabled, GeoIPv6.dat not found");
127
       }
128
       $db_info = sub { return "Geo::IP " . ($db->database_info || '?') };
129
      } else {
130
       dbg("metadata: RelayCountry: IPv6 support not enabled, versions Geo::IP 1.39, GeoIP C API 1.4.7 required");
131
      }
132
   } or do {
133
     # Fallback to IP::Country::Fast
134
     dbg("metadata: RelayCountry: GeoIP.dat not found, IP::Country::Fast enabled as fallback");
135
     $conf_country_db_type = "Fast";
136
   }
137
  }
138
  if( $conf_country_db_type eq "Fast") {
139
    my $eval_stat = $@ ne '' ? $@ : "errno=$!";  chomp $eval_stat;
140
    # Try IP::Country::Fast as backup
141
    eval {
142
      require IP::Country::Fast;
143
      $db = IP::Country::Fast->new();
144
      $db_info = sub { return "IP::Country::Fast ".localtime($db->db_time()); };
145
      1;
146
    } or do {
147
      my $eval_stat = $@ ne '' ? $@ : "errno=$!";  chomp $eval_stat;
148
      dbg("metadata: RelayCountry: failed to load 'IP::Country::Fast', skipping: $eval_stat");
149
      return 1;
150
    };
151
  };
152
113
  return 1 unless $db;
153
  return 1 unless $db;
114
154
115
  dbg("metadata: RelayCountry: Using database: ".$db_info->());
155
  dbg("metadata: RelayCountry: Using database: ".$db_info->());
Lines 117-126 Link Here
117
157
118
  my $countries = '';
158
  my $countries = '';
119
  my $IP_PRIVATE = IP_PRIVATE;
159
  my $IP_PRIVATE = IP_PRIVATE;
160
  my $IPV4_ADDRESS = IPV4_ADDRESS;
120
  foreach my $relay (@{$msg->{metadata}->{relays_untrusted}}) {
161
  foreach my $relay (@{$msg->{metadata}->{relays_untrusted}}) {
121
    my $ip = $relay->{ip};
162
    my $ip = $relay->{ip};
122
    # Private IPs will always be returned as '**'
163
    # Private IPs will always be returned as '**'
123
    my $cc = $ip =~ /^$IP_PRIVATE$/o ? '**' : $ip_to_cc->($ip);
164
    if ( $conf_country_db_type eq "GeoIP" ) {
165
	  if ( $ip !~ /^$IPV4_ADDRESS$/o ) {
166
	    $geo = $dbv6->country_code_by_addr_v6($ip) || "XX";
167
	  } else {
168
	    $geo = $db->country_code_by_addr($ip) || "XX";
169
	  }
170
    } elsif ( $conf_country_db_type eq "Fast" ) {
171
        $geo = $db->inet_atocc($ip) || "XX";
172
    }
173
    $cc = $ip =~ /^$IP_PRIVATE$/o ? '**' : $geo;
124
    $countries .= $cc." ";
174
    $countries .= $cc." ";
125
  }
175
  }
126
176

Return to bug 7529