Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ASNLookup code and documentation #1257

Merged
merged 13 commits into from
Sep 17, 2024
Merged
111 changes: 51 additions & 60 deletions lib/Zonemaster/Engine/ASNLookup.pm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use warnings;
use version; our $VERSION = version->declare( "v1.0.11" );

use Zonemaster::Engine;
use Zonemaster::Engine::Util qw( name );
use Zonemaster::Engine::Nameserver;
use Zonemaster::Engine::Profile;

Expand All @@ -21,23 +22,9 @@ sub get_with_prefix {
my ( $class, $ip ) = @_;

if ( not @db_sources ) {
#
# Backward compatibility in case asnroots is still configured in profile
# but we prefer new model if present
#
my @roots;
if ( Zonemaster::Engine::Profile->effective->get( q{asnroots} ) ) {
@roots = map { Zonemaster::Engine->zone( $_ ) } @{ Zonemaster::Engine::Profile->effective->get( q{asnroots} ) };
}
if ( scalar @roots ) {
@db_sources = @roots;
$db_style = q{cymru};
}
else {
$db_style = Zonemaster::Engine::Profile->effective->get( q{asn_db.style} );
my %db_sources = %{ Zonemaster::Engine::Profile->effective->get( q{asn_db.sources} ) };
@db_sources = map { Zonemaster::Engine->zone( $_ ) } @{ $db_sources{ $db_style } };
}
$db_style = Zonemaster::Engine::Profile->effective->get( q{asn_db.style} );
my %db_sources = %{ Zonemaster::Engine::Profile->effective->get( q{asn_db.sources} ) };
@db_sources = map { name( $_ ) } @{ $db_sources{ $db_style } };
}

if ( not ref( $ip ) or not $ip->isa( 'Net::IP::XS' ) ) {
Expand All @@ -58,10 +45,10 @@ sub get_with_prefix {
}
else {
if ( not $db_style ) {
die "ASN database style is [UNDEFINED]";
die "ASN database style undefined";
}
else {
die "ASN database style value [$db_style] is illegal";
die "ASN database style value '$db_style' is illegal";
}
}
mattias-p marked this conversation as resolved.
Show resolved Hide resolved

Expand All @@ -75,60 +62,64 @@ sub _cymru_asn_lookup {
my $ip = shift;
my @asns = ();

my $reverse = $ip->reverse_ip;
my $db_source_nb = 0;
foreach my $db_source ( @db_sources ) {
my $domain = $db_source->name->string;
Zonemaster::Engine->logger->add( ASN_LOOKUP_SOURCE => { name => $db_source } );
my $reverse = $ip->reverse_ip;
my $domain = $db_source->string;
my $pair = {
'in-addr.arpa.' => "origin.$domain",
'ip6.arpa.' => "origin6.$domain",
};
$db_source_nb++;

foreach my $root ( keys %{$pair} ) {
if ( $reverse =~ s/$root/$pair->{$root}/ix ) {
my $p = $db_source->query_persistent( $reverse, 'TXT' );
my @rr;
my $p = Zonemaster::Engine->recurse( $reverse, 'TXT' );

if ( $p ) {
@rr = $p->get_records( 'TXT' );
}
if ( $p and ( $p->rcode eq q{NXDOMAIN} or ( $p->rcode eq q{NOERROR} and not scalar @rr ) ) ) {
return \@asns, undef, q{}, q{EMPTY_ASN_SET};
}
if ( not $p or $p->rcode ne q{NOERROR} ) {
if ( $db_source_nb == scalar @db_sources ) {
return \@asns, undef, q{}, q{ERROR_ASN_DATABASE};
}
else {
last;
if ( $p->rcode eq q{NOERROR} or $p->rcode eq q{NXDOMAIN} ) {
if ( $p->rcode eq q{NOERROR} ) {
my @rr = $p->get_records( 'TXT', 'answer' );

if ( @rr ) {
my $prefix_length = 0;
my @fields;
my $str;

foreach my $rr ( @rr ) {
my $_str = $rr->txtdata;
my @_fields = split( /[ ][|][ ]?/x, $_str );
my @_asns = split( /\s+/x, $_fields[0] );
my $_prefix_length = ($_fields[1] =~ m!^.*[/](.*)!x)[0];
if ( $_prefix_length > $prefix_length ) {
$str = $_str;
@asns = @_asns;
@fields = @_fields;
$prefix_length = $_prefix_length;
}
}

return \@asns, Net::IP::XS->new( $fields[1] ), $str, q{AS_FOUND};
}
else {
return \@asns, undef, q{}, q{EMPTY_ASN_SET};
}
}

if ( $p->rcode eq q{NXDOMAIN} ) {
if ( $p->get_records( 'SOA', 'authority' ) and scalar $p->get_records( 'SOA', 'authority' ) == 1 and ($p->get_records( 'SOA', 'authority' ))[0]->owner eq name( $db_source ) ) {
return \@asns, undef, q{}, q{EMPTY_ASN_SET};
}
}
}
}

my $prefix_length = 0;
my @fields;
my $str;
foreach my $rr ( @rr ) {
my $_str = $rr->txtdata;
my @_fields = split( /[ ][|][ ]?/x, $_str );
my @_asns = split( /\s+/x, $_fields[0] );
my $_prefix_length = ($_fields[1] =~ m!^.*[/](.*)!x)[0];
if ( $_prefix_length > $prefix_length ) {
$str = $_str;
@asns = @_asns;
@fields = @_fields;
$prefix_length = $_prefix_length;
}
}
if ( scalar @rr ) {
return \@asns, Net::IP::XS->new( $fields[1] ), $str, q{AS_FOUND};
}
else {
if ( $db_source_nb == scalar @db_sources ) {
return \@asns, undef, $str, q{ERROR_ASN_DATABASE};
}
else {
last;
}
if ( $db_source_nb == scalar @db_sources ) {
return \@asns, undef, q{}, q{ERROR_ASN_DATABASE};
}

last;
}
}
} ## end foreach my $db_source ( @db_sources )
Expand All @@ -142,7 +133,7 @@ sub _ripe_asn_lookup {
my $db_source_nb = 0;
foreach my $db_source ( @db_sources ) {
$db_source_nb++;
my $socket = IO::Socket::INET->new( PeerAddr => $db_source->name->string,
my $socket = IO::Socket::INET->new( PeerAddr => $db_source->string,
PeerPort => q{43},
Proto => q{tcp} );
unless ( $socket ) {
Expand Down
37 changes: 13 additions & 24 deletions lib/Zonemaster/Engine/Profile.pm
Original file line number Diff line number Diff line change
Expand Up @@ -107,20 +107,6 @@ my %profile_properties_details = (
q{no_network} => {
type => q{Bool}
},
q{asnroots} => {
type => q{ArrayRef},
test => sub {
foreach my $ndd ( @{$_[0]} ) {
die "Property asnroots has a NULL item\n" if not defined $ndd;
die "Property asnroots has a non scalar item\n" if not defined ref($ndd);
die "Property asnroots has an item too long\n" if length($ndd) > 255;
foreach my $label ( split /[.]/, $ndd ) {
die "Property asnroots has a non domain name item\n" if $label !~ /^[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?$/;
}
}
},
default => ["asnlookup.zonemaster.net"],
},
q{asn_db.style} => {
type => q{Str},
test => sub {
Expand Down Expand Up @@ -734,25 +720,28 @@ A boolean. If true, network traffic is forbidden. Default false.
Use when you want to be sure that any data is only taken from a preloaded
cache.

=head2 asnroots (DEPRECATED)
=head2 asn_db
mattias-p marked this conversation as resolved.
Show resolved Hide resolved

An arrayref of domain names. Default C<["asnlookup.zonemaster.net"]>.
A hash of hashes. The currently supported keys are C<"style"> and C<"sources">.

The domains will be assumed to be Cymru-style AS lookup zones.
Only the first name in the list will be used.
See more information in L<asn_db.style> and L<asn_db.sources>.

=head2 asn_db.style

A string that is either C<"Cymru"> or C<"RIPE">. Defines which method will
be used for AS lookup zones.
A string that is either C<"Cymru"> or C<"RIPE"> (case-insensitive).

Defines which service will be used for AS lookup zones.

Default C<"Cymru">.

=head2 asn_db.sources

An arrayref of domain names when asn_db.style is set to C<"Cymru"> or whois
servers when asn_db.style is set to C<"RIPE">. Only the first item
in the list will be used.
Default C<"asnlookup.zonemaster.net">.
A hash of arrayrefs of strings. The currently supported keys are C<"Cymru"> or C<"RIPE"> (case-insensitive).

For C<"Cymru">, the strings are domain names. For C<"RIPE">, they are WHOIS servers. Normally only the first
item in the list will be used, the rest are backups in case the previous ones didn't work.

Default C<{Cymru: [ "asnlookup.zonemaster.net", "asn.cymru.com" ], RIPE: [ "riswhois.ripe.net" ]}>.

=head2 cache (EXPERIMENTAL)

Expand Down
2 changes: 1 addition & 1 deletion share/profile.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"asn_db" : {
"style" : "Cymru",
"sources" : {
"Cymru" : [ "asnlookup.zonemaster.net" ],
"Cymru" : [ "asnlookup.zonemaster.net", "asn.cymru.com" ],
"RIPE" : [ "riswhois.ripe.net" ]
}
},
Expand Down
Loading
Loading