--- /dev/null
+#!/usr/bin/perl
+#
+# Utility to query GeoLite2 database (.iv4/.iv6 files)
+# Copyright Philip Prindeville, 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 $asn (@ARGV) {
+ if ($asn !~ m/^\d+$/i) {
+ print STDERR "Invalid ASN '$asn'\n";
+ exit 1;
+ }
+
+ my $file = $target_dir . '/' . uc($asn) . '.iv4';
+
+ if (! -f $file) {
+ printf STDERR "Can't find data for ASN '$asn'\n";
+ exit 1;
+ }
+
+ my ($contents, $buffer, $bytes, $fh);
+
+ if ($ipv4) {
+ open($fh, '<', $file) || die "Couldn't open file for '$asn'\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 '$asn'\n";
+ exit 1;
+ } elsif ($bytes != 0) {
+ printf STDERR "Short read on file for '$asn'\n";
+ exit 1;
+ }
+ }
+
+ substr($file, -1) = '6';
+
+ if ($ipv6) {
+ open($fh, '<', $file) || die "Couldn't open file for '$asn'\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 '$asn'\n";
+ exit 1;
+ } elsif ($bytes != 0) {
+ printf STDERR "Short read on file for '$asn'\n";
+ exit 1;
+ }
+ }
+}
+
+exit 0;
--- /dev/null
+.TH xt_asn_query 1 "2020-04-30" "xtables-addons" "xtables-addons"
+.SH Name
+.PP
+xt_asn_query \(em dump an ASN database to stdout
+.SH Syntax
+.PP
+\fBxt_asn_query\fP [\fB\-D\fP
+\fIdatabase_dir\fP] [\fB-4\fP] [\fB-6\fP] \fIasn\fP [ \fIasn\fP ... ]
+.SH Description
+.PP
+xt_asn_query reads an ASN's IPv4 or IPv6 databases and dumps
+them to standard output as a sorted, non-overlapping list of ranges (which
+is how they are represented in the database), suitable for browsing or
+further processing.
+.PP Options
+.TP
+\fB\-D\fP \fIdatabase_dir\fP
+Specifies the directory into which the files have been put. Defaults to ".".
+.TP
+\fB-4\fP
+Specifies IPv4 data only.
+.TP
+\fB-6\fP
+Specifies IPv6 data only.
+.TP
+\fIasn\fP [ \fIasn\fP ... ]
+The ASN (Autonomous System Numbers) from global BGP peering in decimal notation.
+.SH Application
+.PP
+Shell command to dump the list of OVH's IPv4 address ranges from ASN 16276:
+.PP
+xt_asn_query \-D /usr/share/xt_asn \-6 16276
+.SH See also
+.PP
+xt_asn_build(1)