in lib/Mail/SpamAssassin/GeoDB.pm [416:627]
sub load_geoip {
my ($self, $geodb_opts) = @_;
my ($db, $dbapi, $ok);
my ($gic_wanted, $gic_have, $gip_wanted, $gip_have);
my ($flags, $fix_stderr, $can_ipv6);
# Warn about fatal errors if this module was specifically requested
my $errwarn = ($geodb_opts->{module}||'') eq 'geoip';
eval {
require Geo::IP;
# need GeoIP C library 1.6.3 and GeoIP perl API 1.4.4 or later to avoid messages leaking - Bug 7153
$gip_wanted = version->parse('v1.4.4');
$gip_have = version->parse(Geo::IP->VERSION);
$gic_wanted = version->parse('v1.6.3');
eval { $gic_have = version->parse(Geo::IP->lib_version()); }; # might not have lib_version()
$gic_have = 'none' if !defined $gic_have;
dbg("geodb: GeoIP: versions: Geo::IP $gip_have, C library $gic_have");
$flags = 0;
$fix_stderr = 0;
if (ref($gic_have) eq 'version') {
# this code burps an ugly message if it fails, but that's redirected elsewhere
eval '$flags = Geo::IP::GEOIP_SILENCE' if $gip_wanted >= $gip_have;
$fix_stderr = $flags && $gic_wanted >= $gic_have;
}
$can_ipv6 = Geo::IP->VERSION >= 1.39 && Geo::IP->api eq 'CAPI';
1;
} or do {
my $err = $@;
$err =~ s/ at .*//s;
$err = "geodb: Geo::IP module load failed: $err";
$errwarn ? warn("$err\n") : dbg($err);
return (undef, undef);
};
my %path;
foreach my $dbtype (@geoip_types) {
# skip country if city already loaded
next if $dbtype eq 'country' && $db->{city};
# skip asn if isp already loaded
next if $dbtype eq 'asn' && $db->{isp};
# skip if not needed
next if $geodb_opts->{wanted} && !$geodb_opts->{wanted}->{$dbtype};
# only autosearch if no absolute path given
if (!defined $geodb_opts->{dbs}->{$dbtype}) {
# Try some default locations
PATHS_GEOIP: foreach my $p (@{$geodb_opts->{search_path}}) {
foreach my $f (@{$geoip_default_files{$dbtype}}) {
if (-f "$p/$f") {
$path{$dbtype} = "$p/$f";
dbg("geodb: GeoIP: search found $dbtype $p/$f");
if ($can_ipv6 && $f =~ s/\.(dat)$/v6.$1/i) {
if (-f "$p/$f") {
$path{$dbtype."_v6"} = "$p/$f";
dbg("geodb: GeoIP: search found $dbtype $p/$f");
}
}
last PATHS_GEOIP;
}
}
}
} else {
if (!-f $geodb_opts->{dbs}->{$dbtype}) {
dbg("geodb: GeoIP: $dbtype database requested, but not found: ".
$geodb_opts->{dbs}->{$dbtype});
next;
}
$path{$dbtype} = $geodb_opts->{dbs}->{$dbtype};
}
}
if (!$can_ipv6) {
dbg("geodb: GeoIP: IPv6 support not enabled, versions Geo::IP 1.39, GeoIP C API 1.4.7 required");
}
if ($fix_stderr) {
open(OLDERR, ">&STDERR");
open(STDERR, ">/dev/null");
}
foreach my $dbtype (@geoip_types) {
next unless defined $path{$dbtype};
eval {
$db->{$dbtype} = Geo::IP->open($path{$dbtype}, Geo::IP->GEOIP_STANDARD | $flags);
if ($can_ipv6 && defined $path{$dbtype."_v6"}) {
$db->{$dbtype."_v6"} = Geo::IP->open($path{$dbtype."_v6"}, Geo::IP->GEOIP_STANDARD | $flags);
}
};
if ($@ || !$db->{$dbtype}) {
my $err = $@;
$err =~ s/ at .*//s;
dbg("geodb: GeoIP: database $path{$dbtype} load failed: $err");
} else {
dbg("geodb: GeoIP: loaded $dbtype from $path{$dbtype}");
$ok = 1;
}
}
if ($fix_stderr) {
open(STDERR, ">&OLDERR");
close(OLDERR);
}
if (!$ok) {
warn("geodb: GeoIP requested, but no databases could be loaded\n") if $errwarn;
return (undef, undef)
}
# dbinfo_DBTYPE()
$db->{city} and $dbapi->{dbinfo_city} = sub {
return "Geo::IP IPv4 city: " . ($_[0]->{db}->{city}->database_info || '?')." / IPv6: ".
($_[0]->{db}->{city_v6} ? $_[0]->{db}->{city_v6}->database_info || '?' : 'no')
};
$db->{country} and $dbapi->{dbinfo_country} = sub {
return "Geo::IP IPv4 country: " . ($_[0]->{db}->{country}->database_info || '?')." / IPv6: ".
($_[0]->{db}->{country_v6} ? $_[0]->{db}->{country_v6}->database_info || '?' : 'no')
};
$db->{isp} and $dbapi->{dbinfo_isp} = sub {
return "Geo::IP IPv4 isp: " . ($_[0]->{db}->{isp}->database_info || '?')." / IPv6: ".
($_[0]->{db}->{isp_v6} ? $_[0]->{db}->{isp_v6}->database_info || '?' : 'no')
};
$db->{asn} and $dbapi->{dbinfo_asn} = sub {
return "Geo::IP IPv4 asn: " . ($_[0]->{db}->{asn}->database_info || '?')." / IPv6: ".
($_[0]->{db}->{asn_v6} ? $_[0]->{db}->{asn_v6}->database_info || '?' : 'no')
};
# city()
$db->{city} and $dbapi->{city} = sub {
my $res = {};
my $city;
if ($_[1] =~ IS_IPV4_ADDRESS) {
$city = $_[0]->{db}->{city}->record_by_addr($_[1]);
} elsif ($_[0]->{db}->{city_v6}) {
$city = $_[0]->{db}->{city_v6}->record_by_addr_v6($_[1]);
}
if (!defined $city) {
dbg("geodb: GeoIP city query failed for $_[1]");
return $res;
}
$res->{city_name} = $city->city;
$res->{country} = $city->country_code;
$res->{country_name} = $city->country_name;
$res->{continent} = $city->continent_code;
return $res;
};
$dbapi->{city_v6} = $dbapi->{city} if $db->{city_v6};
# country()
$db->{country} and $dbapi->{country} = sub {
my $res = {};
my $country;
eval {
if ($_[1] =~ IS_IPV4_ADDRESS) {
$country = $_[0]->{db}->{country}->country_code_by_addr($_[1]);
} elsif ($_[0]->{db}->{country_v6}) {
$country = $_[0]->{db}->{country_v6}->country_code_by_addr_v6($_[1]);
}
1;
};
if (!defined $country) {
dbg("geodb: GeoIP country query failed for $_[1]");
return $res;
};
$res->{country} = $country || 'XX';
$res->{continent} = $country_to_continent{$country} || 'XX';
return $res;
};
$dbapi->{country_v6} = $dbapi->{country} if $db->{country_v6};
# isp()
$db->{isp} and $dbapi->{isp} = sub {
my $res = {};
my $isp;
eval {
if ($_[1] =~ IS_IPV4_ADDRESS) {
$isp = $_[0]->{db}->{isp}->isp_by_addr($_[1]);
} else {
# TODO?
return $res;
}
1;
};
if (!defined $isp) {
dbg("geodb: GeoIP isp query failed for $_[1]");
return $res;
};
$res->{isp} = $isp;
return $res;
};
# asn()
$db->{asn} and $dbapi->{asn} = sub {
my $res = {};
my $asn;
eval {
if ($_[1] =~ IS_IPV4_ADDRESS) {
$asn = $_[0]->{db}->{asn}->isp_by_addr($_[1]);
} else {
# TODO?
return $res;
}
1;
};
if (!defined $asn || $asn !~ /^((?:AS)?\d+)(?:\s+(.+))?/) {
dbg("geodb: GeoIP asn query failed for $_[1]");
return $res;
};
$res->{asn} = $1;
$res->{asn_organization} = $2 if defined $2;
return $res;
};
return ($db, $dbapi);
}