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 |
|