qpsmtpd/t/plugin_tests/ident/geoip
Jared Johnson f8e220e3b5 Handle missing GeoIP data gracefully
Previously, the GeoIP plugin would crash on certain missing GeoIP data.
Now it will continue to operate on whatever data it can get at.

Note that it already warns when any data it's looking for is missing.
2015-01-14 15:28:14 -06:00

241 lines
7.1 KiB
Perl

#!perl -w
use strict;
use warnings;
use lib 'lib';
use Qpsmtpd::Constants;
sub register_tests {
my $self = shift;
eval 'use GeoIP2::Database::Reader';
if ( !$@ ) {
$self->register_test('test_geoip2_lookup');
}
eval 'use Geo::IP';
if ( !$@ ) {
$self->register_test('test_geoip_lookup');
$self->register_test('test_geoip_load_db');
$self->register_test('test_geoip_init_cc');
$self->register_test('test_set_country_code');
$self->register_test('test_set_country_name');
$self->register_test('test_set_continent');
$self->register_test('test_set_distance');
$self->register_test('test_set_asn');
$self->register_test('test_add_headers');
}
}
sub test_geoip2_lookup {
my $self = shift;
$self->qp->connection->remote_ip('24.24.24.24');
cmp_ok( $self->geoip2_lookup(), '==', DECLINED, "exit code DECLINED");
if (!$self->load_geoip2()) {
warn "failed to load GeoIP2\n";
}
cmp_ok( $self->connection->notes('geoip_country'), 'eq', 'US', "24.24.24.24 is in country US");
cmp_ok( $self->connection->notes('geoip_country_name'), 'eq', 'United States', "24.24.24.24 is in country United States");
cmp_ok( $self->connection->notes('geoip_continent'), 'eq', 'NA', "24.24.24.24 is in continent NA");
cmp_ok( $self->connection->notes('geoip_city'), 'eq', 'Deer Park', "24.24.24.24 is in city of Deer Park");
}
sub test_add_headers {
my ( $self ) = @_;
my @notes = qw( geoip_country geoip_continent geoip_city geoip_asn );
$self->connection->notes( $_ => "test $_" ) for @notes;
my $header = $self->transaction->header( Mail::Header->new );
my @tags = (qw( X-GeoIP-Country X-GeoIP-Continent X-GeoIP-City X-GeoIP-ASN ));
$header->add( $_ => 'DELETETHIS' ) for @tags;
$self->add_headers($self->transaction);
is( $self->all_headers('X-GeoIP-Country'), 'test geoip_country',
'X-GeoIP-Country header added' );
is( $self->all_headers('X-GeoIP-Continent'), 'test geoip_continent',
'X-GeoIP-Continent header added' );
is( $self->all_headers('X-GeoIP-City'), 'test geoip_city',
'X-GeoIP-City header added' );
is( $self->all_headers('X-GeoIP-ASN'), 'test geoip_asn',
'X-GeoIP-ASN header added' );
}
sub all_headers {
# Return all instances of a given message header
my ( $self, $tag ) = @_;
return join " | ", map { chomp $_; $_ } $self->transaction->header->get($tag);
}
sub test_geoip_lookup {
my $self = shift;
$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', "24.24.24.24 is in the US");
}
sub test_geoip_load_db {
my $self = shift;
$self->open_geoip_db();
if ( $self->{_geoip_city} ) {
ok( ref $self->{_geoip_city}, "loaded GeoIP city db" );
}
else {
ok( "no GeoIP city db" );
}
if ( $self->{_geoip} ) {
ok( ref $self->{_geoip}, "loaded GeoIP db" );
}
else {
ok( "no GeoIP db" );
}
}
sub test_geoip_init_cc {
my $self = shift;
$self->{_my_country_code} = undef;
ok( ! $self->{_my_country_code}, "undefined");
my $test_ip = '208.175.177.10';
$self->{_args}{distance} = $test_ip;
$self->init_my_country_code( $test_ip );
cmp_ok( $self->{_my_country_code}, 'eq', 'US', "country set and matches");
}
sub test_set_country_code {
my $self = shift;
$self->qp->connection->remote_ip('');
my $cc = $self->set_country_code();
ok( ! $cc, "undef");
$self->qp->connection->remote_ip('24.24.24.24');
$self->clear_geoip_data;
$cc = $self->set_country_code();
ok( ! $cc, "set_country_code() returns nothing for no geoip data");
$self->restore_geoip_data;
$cc = $self->set_country_code();
cmp_ok( $cc, 'eq', 'US', "set_country_code result is $cc");
my $note = $self->connection->notes('geoip_country');
cmp_ok( $note, 'eq', 'US', "set_country_code set note to $cc");
}
sub test_set_country_name {
my $self = shift;
$self->{_geoip_record} = undef;
$self->qp->connection->remote_ip('');
$self->set_country_code();
my $cn = $self->set_country_name();
ok( ! $cn, "undef") or warn "$cn\n";
$self->qp->connection->remote_ip('24.24.24.24');
$self->clear_geoip_data;
$self->set_country_code();
$cn = $self->set_country_name();
ok( ! $cn, "set_country_name() returns nothing for no geoip data");
$self->restore_geoip_data;
$self->set_country_code();
$cn = $self->set_country_name();
cmp_ok( $cn, 'eq', 'United States', "$cn");
my $note = $self->connection->notes('geoip_country_name');
cmp_ok( $note, 'eq', 'United States', "note has: $cn");
}
sub test_set_continent {
my $self = shift;
$self->{_geoip_record} = undef;
$self->qp->connection->remote_ip('');
$self->set_country_code();
my $cn = $self->set_continent();
ok( ! $cn, "undef") or warn "$cn\n";
$self->qp->connection->remote_ip('24.24.24.24');
$self->clear_geoip_data;
$self->set_country_code();
$cn = $self->set_continent('US');
ok( ! $cn, 'set_continent() returns nothing for no geoip data');
$self->restore_geoip_data;
$self->set_country_code();
$cn = $self->set_continent() || '';
my $note = $self->connection->notes('geoip_continent');
if ( $cn ) {
cmp_ok( $cn, 'eq', 'NA', "$cn");
cmp_ok( $note, 'eq', 'NA', "note has: $cn");
}
else {
ok(1, "no continent data" );
ok(1, "no continent data" );
}
}
sub test_set_distance {
my $self = shift;
$self->{_geoip_record} = undef;
$self->qp->connection->remote_ip('');
$self->set_country_code();
my $cn = $self->set_distance_gc();
ok( ! $cn, "undef") or warn "$cn\n";
$self->qp->connection->remote_ip('24.24.24.24');
$self->set_country_code();
$cn = $self->set_distance_gc();
if ( $cn ) {
ok( $cn, "$cn km");
my $note = $self->connection->notes('geoip_distance');
ok( $note, "note has: $cn");
}
else {
ok( 1, "no distance data");
ok( 1, "no distance data");
}
}
sub test_set_asn {
my $self = shift;
return if !$self->{GeoIPASNum};
$self->qp->connection->remote_ip('');
$self->set_asn();
my $asn = $self->set_asn();
ok( ! $asn, "undef") or warn "$asn\n";
$self->qp->connection->remote_ip('24.24.24.24');
$self->clear_geoip_data;
$asn = $self->set_asn();
ok( ! $asn, 'set_asn() returns nothing for no ASN data' );
$self->restore_geoip_data;
$asn = $self->set_asn();
ok( $self->connection->notes('geoip_asn') =~ /^11351/, "note has: $asn");
$self->qp->connection->remote_ip('66.128.51.163');
$asn = $self->set_asn();
ok( $self->connection->notes('geoip_asn') =~ /^7819/, "note has: $asn");
}
my $geoip_data_bak;
my @geoip_keys = qw( _geoip _geoip_city GeoIPASNum );
sub clear_geoip_data {
my ( $self ) = @_;
$geoip_data_bak->{$_} = delete $self->{$_} for @geoip_keys;
}
sub restore_geoip_data {
my ( $self ) = @_;
$self->{$_} = delete $geoip_data_bak->{$_} for @geoip_keys;
}