Le Ray
Le Ray

Reputation: 387

Perl Undefined Error on Geo IP lookup

I am using Geo::IP to perform location lookups on ip addresses. Everything works fine until I come across an ip address which is not in the geo ip lookup database and the program shuts abruptly giving this error

Can't call method "city" on an undefined value at script.pl line 16.

Current code looks like this

 $gi = Geo::IP->open("/usr/local/share/GeoIP/GeoLiteCity.dat", GEOIP_STANDARD);
 my $record = $gi->record_by_addr($key);
 my $city= $record->city;

Any suggestions on how I can by pass this? It works perfectly fine until it hits an ip address that isn't defined within that module.

Upvotes: 0

Views: 751

Answers (2)

i alarmed alien
i alarmed alien

Reputation: 9520

Looking at the Geo::IP source, if the IP address is not in the database, it returns undef. Therefore, to bypass the problem, you can do:

my $record = $gi->record_by_addr($key);
## check that $record is defined
if ($record) {
    my $city= $record->city;
    ...
}
else {
    # issue an error message if wanted
    warn "No record found for $key";
}

Relevant code from the Geo::IP source:

The function you're using is record_by_addr. From the source, record_by_addr is an alias for get_city_record_as_hash (see perlref for the syntax used to create an 'alias' for a function):

*record_by_addr = \&get_city_record_as_hash;

The code for get_city_record_as_hash is as follows:

#this function returns the city record as a hash ref
sub get_city_record_as_hash {
  my ( $gi, $host ) = @_;
  my %gir;
  @gir{qw/ country_code   country_code3   country_name   region     city 
           postal_code    latitude        longitude      dma_code   area_code 
           continent_code region_name     metro_code / } =
    $gi->get_city_record($host);

  return defined($gir{latitude}) ? bless( \%gir, 'Geo::IP::Record' ) : undef;
}

This code runs get_city_record using $host, the IP address you supplied, as the argument. If get_city_record finds a record, the data it returns populates the %gir hash. The last line of the sub uses the [ternary form of if-else] to evaluate whether getting the record was successful, and to return the appropriate result. It checks whether $gir{latitude} is defined, and if it is, it creates and returns a Geo::IP::Record object from it (which you can query with methods like city, etc.). If it isn't, it returns undef.

A simpler way to view the last line would be this:

# is $gir{latitude} defined?
if (defined ($gir{latitude})) {
    # yes: create a Geo::IP::Record object with the data in %gir
    # return that object
    return bless( \%gir, 'Geo::IP::Record' )
}
else {
    # no: return undefined.
    return undef;
}

Upvotes: 3

Sobrique
Sobrique

Reputation: 53478

I'd suggest that you need Data::Dumper here, to tell you what's going on with $record. I would guess that record_by_addr($key); is the root of your problems, and that because $key is in some way bad, $record is undefined.

This would thus be fixed:

use Data::Dumper;
print Dumper \$record;

I'm guessing $record will be undefined, and therefore:

next unless $record; 

will skip it.

Upvotes: 1

Related Questions