App-geoip

 view release on metacpan or  search on metacpan

lib/App/geoip.pm  view on Meta::CPAN

$opt_v >= 7 and _dump ("Configuration", \%conf);

if (defined $opt_J) {
    if ($opt_J) {
	$conf{json_pretty}++;
	$conf{json}++;
	}
    else {
	$conf{json_pretty} = 0;
	}
    }
$conf{json} and $opt_J = $conf{json_pretty};

if (@ARGV == 0 and my $eh = $ENV{GEOIP_HOST}) {
    $eh =~ s{[\s\r\n]+\z}{};
    # No IPv6 support yet
    if ($eh =~ m{^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$} and
	$1 > 0 && $1 < 256 && $2 < 256 && $3 < 256 && $4 < 256) {
	# Simplistic IPv4
	push @ARGV => $eh;
	}
    elsif ($eh =~ m/^\w[-.\w]{0,252}\z/) { # Skip invalid clipboard content
	# https://en.wikipedia.org/wiki/Hostname#Syntax
	for (split m/\./ => $eh) {
	    m/^\w[-\w]{0,62}$/ or die "$eh is not a valid hostname or IPv4\n";
	    }
	push @ARGV => $eh;
	}
    }

my $dbh = do {
    my $dsn = $conf{dsn} =~ s{^b=(?=\w+:)}{}ir; # catch -DB=.. instead of --DB=
    my $help = $dsn =~ m/^dbi:(\w+):/i
	? "Did you forget to install DBD::$1?"
	: "Maybe the matching DBD for $dsn is not installed";
    eval {
	my %seen;
	my $fail = sub {
	    my $e = DBI->errstr or return;
	    !$seen{$e}++ and warn "$e\n";
	    };
	local $SIG{__WARN__} = $fail;
	local $SIG{__DIE__}  = $fail;
	DBI->connect ($conf{dsn}, undef, undef, {
	    AutoCommit		=> 0,
	    RaiseError		=> 1,
	    PrintError		=> 1,
	    ShowErrorStatement	=> 1,
	    });
	} or die "Cannot continue without a working database\n$help\n";
    };

sub _dump {
    my ($label, $ref) = @_;
    print STDERR $use_data_peek
	? Data::Peek::DDumper ({ $label => $ref })
	: Data::Dumper->Dump ([$ref], [$label]);
    } # _dump

# Based on GeoIP2 CSV databases
#  City:   http://geolite.maxmind.com/download/geoip/database/GeoLite2-City-CSV.zip
#  Country http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country-CSV.zip
#  ASN     http://geolite.maxmind.com/download/geoip/database/GeoLite2-ASN-CSV.zip

my $idx_type = $conf{dsn} =~ m/:Pg/     ? "using btree" : "";
my $truncate = $conf{dsn} =~ m/:SQLite/ ? "delete from" : "truncate table";

unless (grep m/\b country \b/ix => $dbh->tables (undef, undef, undef, undef)) {
    say "Create table stamps";
    $dbh->do (qq; create table stamps (
	name		text		not null	primary key,
	stamp		bigint);
	);
    say "Create table continent";
    $dbh->do (qq; create table continent (
	id		char (4)	not null	primary key,
	name		text);
	);
    say "Create table country";
    $dbh->do (qq; create table country (
	id		bigint		not null	primary key,
	name		text		not null,
	iso		text,
	continent	char (4),
	eu		smallint);
	);
    say "Create table ipv4"; # Country based
    $dbh->do (qq; create table ipv4 (
	cidr		cidr		not null	primary key,
	id		bigint,
	ip_from		text		not null,
	ip_to		text		not null,
	ip_from_n	bigint		not null,
	ip_to_n		bigint		not null,
	reg_country_id	bigint,
	rep_country_id	bigint,
	anon_proxy	smallint,
	satellite	smallint);
	);
    $dbh->do (qq; create index i_ipv4_ip on ipv4 $idx_type (ip_from_n, ip_to_n););
    say "Create table provider";
    $dbh->do (qq; create table provider (
	cidr		cidr		not null	primary key,
	id		bigint,
	name		text,
	ip_from		text,
	ip_to		text,
	ip_from_n	bigint,
	ip_to_n		bigint);
	);
    $dbh->do (qq; create index i_provider_ip on provider $idx_type (ip_from_n, ip_to_n););
    say "Create table city";
    $dbh->do (qq; create table city (
	id		bigint		not null	primary key,
	name		text,
	country_id	bigint,
	metro_code	text,
	tz		text,
	eu		smallint);
	);
    say "Create table ipc4"; # City based
    $dbh->do (qq; create table ipc4 (
	cidr		cidr		not null	primary key,

lib/App/geoip.pm  view on Meta::CPAN

default value       : False

The default output for the information is plain text. With this option,
the output will be in JSON format. The default is not prettified.

=item json-pretty

command line option : C<-J> or C<--json-pretty>

default value       : False

If set from the command-line, this implies the C<--json> option.

With this option, JSON output is done I<pretty> (indented).

=item local-location

command line option : C<-l lat/lon> or C<--local=lat/lon>

default value       : Undefined

Sets the local location coordinates for use with distances.

When running the tool from a different location than where the IP access is
to be analyzed for or when the network connection will not report a location
that would make sense (like working from a cloud or running over one or more
VPN connections), one can set the location of the base in decimal notation.
(degree-minute-second-notation is not yet supported).

This is also useful when there is no outbound connection possible or when you
do not move location and you want to restrict network requests.

The notation is decimal (with a C<.>, no localization support) where latitude
and longitude are separated by a C</> or a C<,>, like C<-l 12.345678/-9.876543>
or C<--local=12,3456,45,6789>.

=item maxmind-account

command line option : none

default value       : Undefined

Currently not (yet) used. Documentation only.

=item license-id

command line option : none

default value       : Undefined

Currently not (yet) used. Documentation only.

=item license-key

command line option : none

default value       : Undefined

As downloads are only allowed/possible using a valid MaxMind account, you need
to provide a valid license key in your configuration file. If you do not have
an account, you can sign up L<here|https://www.maxmind.com/en/geolite2/signup>.

=back

=head1 DATABASE

Currently PostgreSQL and SQLite have been tested, but others may (or may not)
work just as well. YMMV. Note that the database need to know the C<CIDR>
field type and is able to put a primary key on it.

MariaDB and MySQL are not supported, as they do not support the concept of
CIDR type fields.

The advantage of PostgreSQL over SQLite is that you can use it with multiple
users at the same time, and that you can share the database with other hosts
on the same network behind a firewall.

The advantage of SQLite over PostgreSQL is that it is a single file that you
can copy or move to your liking. This file will be somewhere around 500 Mb.

=head1 EXAMPLES

=head2 Configuration

 $ cat ~/.config/geoip
 use_distance    : True
 json-pretty     : yes

=head2 Basic use

 $ geoip --short 1.2.3.4

=head2 For automation

 $ geoip --json --no-json-pretty 1.2.3.4

 $ env GEOIP_HOST=1.2.3.4 geoip

=head2 Full report

 $ geoip --dist --whois 1.2.3.4

=head2 Selecting CIDR's for countries

=head3 List all CIDR's for Vatican City

 $ geoip --country=Vatican > vatican-city.cidr

=head3 Statistics

If you enable verbosity, the selected statistics will be presented at the
end of the CIDR-list: number of CIDR's, number of enclosed IP's, name of
the country and the continent. As the country name is just a perl regex,
you can select all countries with C<.>, or all countries that start with
a C<V>:

 $ geoip --country=^V -v >/dev/null
 Selected CIDR's
 # CIDR       # IP Country               Continent
 ------ ---------- --------------------- ---------------
     21      18176 Vanuatu               Oceania
    321      13056 Vatican City          Europe
    272    6798500 Venezuela             South America
    612   16014080 Vietnam               Asia

=head1 TODO

=over 2

=item IPv6

The ZIP files also contain IPv6 information, but it is not (yet) converted
to the database, nor supported in analysis.

=item Modularization

Split up the different parts of the script to modules: fetch, extract,
check, database, external tools, reporting.

=item CPAN

Turn this into something like App::geoip, complete with Makefile.PL

=back

=head1 SEE ALSO

L<DBI>, L<Net::CIDR>, L<Math::Trig>, L<LWP::Simple>, L<Archive::ZIP>,
L<Text::CSV_XS>, L<JSON::PP>, L<GIS::Distance>, L<Net::Whois::IP>,
L<HTML::TreeBuilder>, L<Data::Dumper>, L<Data::Peek>, L<Socket>

L<Geo::Coder::HostIP>, L<Geo::IP>, L<Geo::IP2Location>, L<Geo::IP2Proxy>,
L<Geo::IP6>, L<Geo::IPfree>, L<Geo::IP::RU::IpGeoBase>, L<IP::Country>,
L<IP::Country::DB_File>, L<IP::Country::DNSBL>, L<IP::Info>, L<IP::Location>,
L<IP::QQWry>, L<IP::World>, L<Metabrik::Lookup::Iplocation>, L<Pcore::GeoIP>

L<IP::Geolocation::MMDB>

Check L<CPAN|https://metacpan.org/search?q=geoip> for more.

=head1 THANKS

Thanks to cavac for the inspiration

=head1 AUTHOR

H.Merijn Brand F<E<lt>hmbrand@cpan.orgE<gt>>, aka Tux.

=head1 COPYRIGHT AND LICENSE

The GeoLite2 end-user license agreement, which incorporates components of the
Creative Commons Attribution-ShareAlike 4.0 International License 1) can be found
L<here|https://www.maxmind.com/en/geolite2/eula> 2). The attribution requirement
may be met by including the following in all advertising and documentation
mentioning features of or use of this database.

This tool uses, but does not include, the GeoLite2 data created by MaxMind,
available from [http://www.maxmind.com](http://www.maxmind.com).

 Copyright (C) 2018-2026 H.Merijn Brand.  All rights reserved.

This library is free software;  you can redistribute and/or modify it under
the same terms as Perl itself.
See L<here|https://opensource.org/licenses/Artistic-2.0> 3).

 1) https://creativecommons.org/licenses/by-sa/4.0/
 2) https://www.maxmind.com/en/geolite2/eula
 3) https://opensource.org/licenses/Artistic-2.0

=for elvis
:ex:se gw=75|color guide #ff0000:

=cut



( run in 1.840 second using v1.01-cache-2.11-cpan-98e64b0badf )