added GeoIP2 support, partial ASN support
This commit is contained in:
parent
9862cdc042
commit
c61dbb5734
@ -30,8 +30,9 @@ WriteMakefile(
|
||||
# 'DBIx::Simple' => 0, # log2sql
|
||||
# modules that cause Travis build tests to fail
|
||||
# 'Mail::SpamAssassin' => 0,
|
||||
# 'Geo::IP' => 0,
|
||||
# 'Math::Complex' => 0, # geodesic distance in Geo::IP
|
||||
# 'GeoIP2' => 2,
|
||||
# 'Geo::IP' => 1,
|
||||
# 'Math::Complex' => 0, # geodesic distance in geoip
|
||||
# 'Mail::SPF' => 0,
|
||||
},
|
||||
ABSTRACT => 'Flexible smtpd daemon written in Perl',
|
||||
|
@ -85,6 +85,8 @@ This plugin does not update the GeoIP databases. You may want to.
|
||||
|
||||
=head1 CHANGES
|
||||
|
||||
2014-06 - Matt Simerson - added GeoIP2 support
|
||||
|
||||
2012-06 - Matt Simerson - added GeoIP City support, continent, distance
|
||||
|
||||
2012-05 - Matt Simerson - added geoip_country_name note, added tests
|
||||
@ -126,8 +128,25 @@ sub register {
|
||||
|
||||
my $loaded = 0;
|
||||
|
||||
eval 'use GeoIP2';
|
||||
if ($@) {
|
||||
eval 'use GeoIP2::Database::Reader';
|
||||
if (!$@) {
|
||||
warn "using GeoIP2";
|
||||
$self->log(LOGINFO, "using GeoIP2");
|
||||
|
||||
eval {
|
||||
$self->{_geoip2_city} = GeoIP2::Database::Reader->new(
|
||||
file => $self->{_args}{db_dir} . '/GeoLite2-City.mmdb',
|
||||
);
|
||||
};
|
||||
eval {
|
||||
$self->{_geoip2_country} = GeoIP2::Database::Reader->new(
|
||||
file => $self->{_args}{db_dir} . '/GeoLite2-Country.mmdb',
|
||||
);
|
||||
};
|
||||
$self->register_hook('connect', 'geoip2_lookup');
|
||||
return;
|
||||
}
|
||||
|
||||
warn "could not load GeoIP2";
|
||||
$self->log(LOGERROR, "could not load GeoIP2");
|
||||
|
||||
@ -137,19 +156,47 @@ sub register {
|
||||
$self->log(LOGERROR, "could not load Geo::IP");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$self->open_geoip_db();
|
||||
|
||||
# Note that opening the GeoIP DB only in register has caused problems before:
|
||||
# https://github.com/smtpd/qpsmtpd/commit/29ea9516806e9a8ca6519fcf987dbd684793ebdd#plugins/ident/geoip
|
||||
# Opening the DB anew for every connection is horribly inefficient.
|
||||
# Instead, attempt to reopen upon connect if the DB connection fails.
|
||||
$self->open_geoip_db();
|
||||
|
||||
$self->init_my_country_code();
|
||||
|
||||
$self->register_hook('connect', 'geoip_lookup');
|
||||
}
|
||||
|
||||
sub geoip2_lookup {
|
||||
my $self = shift;
|
||||
|
||||
my $ip = $self->qp->connection->remote_ip;
|
||||
|
||||
if ($self->{_geoip2_city}) {
|
||||
my $city_rec = $self->{_geoip2_city}->city(ip => $ip);
|
||||
if ($city_rec) {
|
||||
$self->qp->connection->notes('geoip_country', $city_rec->country->iso_code());
|
||||
$self->qp->connection->notes('geoip_country_name', $city_rec->country->name());
|
||||
$self->qp->connection->notes('geoip_continent', $city_rec->continent->code());
|
||||
$self->qp->connection->notes('geoip_city', $city_rec->city->name());
|
||||
$self->qp->connection->notes('geoip_asn', $city_rec->traits->autonomous_system_number());
|
||||
return DECLINED;
|
||||
}
|
||||
}
|
||||
|
||||
if ($self->{_geoip2_country}) {
|
||||
my $country_rec = $self->{_geoip2_country}->country(ip => $ip);
|
||||
if ($country_rec) {
|
||||
$self->qp->connection->notes('geoip_country', $country_rec->country->iso_code());
|
||||
$self->qp->connection->notes('geoip_country_name', $country_rec->country->name());
|
||||
$self->qp->connection->notes('geoip_continent', $country_rec->continent->code());
|
||||
};
|
||||
}
|
||||
|
||||
return DECLINED;
|
||||
}
|
||||
|
||||
sub geoip_lookup {
|
||||
my $self = shift;
|
||||
|
||||
@ -200,10 +247,15 @@ sub open_geoip_db {
|
||||
# save the handles in different locations
|
||||
my $db_dir = $self->{_args}{db_dir};
|
||||
foreach my $db (qw/ GeoIPCity GeoLiteCity /) {
|
||||
if (-f "$db_dir/$db.dat") {
|
||||
next if !-f "$db_dir/$db.dat";
|
||||
$self->log(LOGDEBUG, "using db $db");
|
||||
$self->{_geoip_city} = Geo::IP->open("$db_dir/$db.dat");
|
||||
}
|
||||
|
||||
foreach my $db (qw/ GeoIPASNum GeoIPASNumv6 /) {
|
||||
next if !-f "$db_dir/$db.dat";
|
||||
$self->log(LOGDEBUG, "using db $db");
|
||||
$self->{$db} = Geo::IP->open("$db_dir/$db.dat");
|
||||
}
|
||||
|
||||
# can't think of a good reason to load country if city data is present
|
||||
@ -287,6 +339,26 @@ sub get_continent_gc {
|
||||
return $self->{_geoip_record}->continent_code();
|
||||
}
|
||||
|
||||
sub set_asn {
|
||||
my ($self) = @_;
|
||||
my $remote_ip = $self->qp->connection->remote_ip;
|
||||
return if ! $self->{GeoIPASNum};
|
||||
|
||||
my $asn = $self->{GeoIPASNum}
|
||||
? $self->get_asn_gc($remote_ip)
|
||||
: $self->{_geoip}->asn_by_addr($remote_ip);
|
||||
|
||||
$asn or return;
|
||||
$self->qp->connection->notes('geoip_asn', $asn);
|
||||
return $asn;
|
||||
}
|
||||
|
||||
sub get_asn_gc {
|
||||
my $self = shift;
|
||||
return if !$self->{GeoIPASNum};
|
||||
return $self->{GeoIPASNum}->asn_by_addr();
|
||||
}
|
||||
|
||||
sub set_city_gc {
|
||||
my $self = shift;
|
||||
return if !$self->{_geoip_record};
|
||||
|
@ -9,6 +9,13 @@ use Qpsmtpd::Constants;
|
||||
sub register_tests {
|
||||
my $self = shift;
|
||||
|
||||
eval 'use GeoIP2::Database::Reader';
|
||||
if ( !$@ ) {
|
||||
warn "using GeoIP2\n";
|
||||
$self->register_test('test_geoip2_lookup');
|
||||
return;
|
||||
}
|
||||
|
||||
eval 'use Geo::IP';
|
||||
if ( $@ ) {
|
||||
warn "could not load Geo::IP\n";
|
||||
@ -22,6 +29,19 @@ sub register_tests {
|
||||
$self->register_test('test_set_country_name');
|
||||
$self->register_test('test_set_continent');
|
||||
$self->register_test('test_set_distance');
|
||||
$self->register_test('test_set_isp');
|
||||
};
|
||||
|
||||
sub test_geoip2_lookup {
|
||||
my $self = shift;
|
||||
|
||||
$self->qp->connection->remote_ip('24.24.24.24');
|
||||
cmp_ok( $self->geoip2_lookup(), '==', DECLINED, "exit code");
|
||||
|
||||
cmp_ok( $self->connection->notes('geoip_country'), 'eq', 'US', "24.24.24.24 is in the US");
|
||||
cmp_ok( $self->connection->notes('geoip_country_name'), 'eq', 'United States', "24.24.24.24 is in the United States");
|
||||
cmp_ok( $self->connection->notes('geoip_continent'), 'eq', 'NA', "24.24.24.24 is in NA");
|
||||
cmp_ok( $self->connection->notes('geoip_city'), 'eq', 'Deer Park', "24.24.24.24 is in Deer Park");
|
||||
};
|
||||
|
||||
sub test_geoip_lookup {
|
||||
@ -30,7 +50,7 @@ sub test_geoip_lookup {
|
||||
$self->qp->connection->remote_ip('24.24.24.24');
|
||||
cmp_ok( $self->geoip_lookup(), '==', DECLINED, "exit code");
|
||||
|
||||
cmp_ok( $self->connection->notes('geoip_country'), 'eq', 'US', "note");
|
||||
cmp_ok( $self->connection->notes('geoip_country'), 'eq', 'US', "24.24.24.24 is in the US");
|
||||
};
|
||||
|
||||
sub test_geoip_load_db {
|
||||
@ -74,10 +94,10 @@ sub test_set_country_code {
|
||||
|
||||
$self->qp->connection->remote_ip('24.24.24.24');
|
||||
$cc = $self->set_country_code();
|
||||
cmp_ok( $cc, 'eq', 'US', "$cc");
|
||||
cmp_ok( $cc, 'eq', 'US', "set_country_code result is $cc");
|
||||
|
||||
my $note = $self->connection->notes('geoip_country');
|
||||
cmp_ok( $note, 'eq', 'US', "note has: $cc");
|
||||
cmp_ok( $note, 'eq', 'US', "set_country_code set note to $cc");
|
||||
};
|
||||
|
||||
sub test_set_country_name {
|
||||
@ -144,4 +164,3 @@ sub test_set_distance {
|
||||
ok( 1, "no distance data");
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user