#!/usr/bin/perl # # Utility to query GeoLite2 database (.iv4/.iv6 files) # Copyright Philip Prindeville, 2018, 2023 # use Getopt::Long; use Socket qw(AF_INET AF_INET6 inet_ntop); use warnings; use strict; sub AF_INET_SIZE() { 4 } sub AF_INET6_SIZE() { 16 } my $target_dir = "."; my $ipv4 = 0; my $ipv6 = 0; &Getopt::Long::Configure(qw(bundling)); &GetOptions( "D=s" => \$target_dir, "4" => \$ipv4, "6" => \$ipv6, ); if (!-d $target_dir) { print STDERR "Target directory $target_dir does not exit.\n"; exit 1; } # if neither specified, assume both if (! $ipv4 && ! $ipv6) { $ipv4 = $ipv6 = 1; } foreach my $cc (@ARGV) { if ($cc !~ m/^([a-z]{2}|a[12]|o1)$/i) { print STDERR "Invalid country code '$cc'\n"; exit 1; } my $file = $target_dir . '/' . uc($cc) . '.iv4'; if (! -f $file) { printf STDERR "Can't find data for country '$cc'\n"; exit 1; } my ($contents, $buffer, $bytes, $fh); if ($ipv4) { open($fh, '<', $file) || die "Couldn't open file for '$cc'\n"; binmode($fh); while (($bytes = read($fh, $buffer, AF_INET_SIZE * 2)) == AF_INET_SIZE * 2) { my ($start, $end) = unpack('a4a4', $buffer); $start = inet_ntop(AF_INET, $start); $end = inet_ntop(AF_INET, $end); print $start, '-', $end, "\n"; } close($fh); if (! defined $bytes) { printf STDERR "Error reading file for '$cc'\n"; exit 1; } elsif ($bytes != 0) { printf STDERR "Short read on file for '$cc'\n"; exit 1; } } substr($file, -1) = '6'; if ($ipv6) { open($fh, '<', $file) || die "Couldn't open file for '$cc'\n"; binmode($fh); while (($bytes = read($fh, $buffer, AF_INET6_SIZE * 2)) == AF_INET6_SIZE * 2) { my ($start, $end) = unpack('a16a16', $buffer); $start = inet_ntop(AF_INET6, $start); $end = inet_ntop(AF_INET6, $end); print $start, '-', $end, "\n"; } close($fh); if (! defined $bytes) { printf STDERR "Error reading file for '$cc'\n"; exit 1; } elsif ($bytes != 0) { printf STDERR "Short read on file for '$cc'\n"; exit 1; } } } exit 0;