<commit_before>package Zonemaster::Nameserver v1.0.0;

use 5.14.2;
use Moose;
use Moose::Util::TypeConstraints;

use Zonemaster::DNSName;
use Zonemaster;
use Zonemaster::Packet;
use Zonemaster::Nameserver::Cache;
use Zonemaster::Recursor;

use Net::LDNS;

use Net::IP::XS qw(:PROC);
use Time::HiRes qw[time];
use JSON::XS;
use MIME::Base64;
use Module::Find qw[useall];
use Carp;
use List::Util qw[max min sum];

use overload
  '""'  => \&string,
  'cmp' => \&compare;

subtype 'Zonemaster::Net::IP::XS', as 'Object', where { $_->isa( 'Net::IP::XS' ) };
coerce 'Zonemaster::Net::IP::XS', from 'Str', via { Net::IP::XS->new( $_ ) };

has 'name'    => ( is => 'ro', isa => 'Zonemaster::DNSName',     coerce => 1, required => 0 );
has 'address' => ( is => 'ro', isa => 'Zonemaster::Net::IP::XS', coerce => 1, required => 1 );

has 'dns'   => ( is => 'ro', isa => 'Net::LDNS',                     lazy_build => 1 );
has 'cache' => ( is => 'ro', isa => 'Zonemaster::Nameserver::Cache', lazy_build => 1 );
has 'times' => ( is => 'ro', isa => 'ArrayRef',                      default    => sub { [] } );

has 'fake_delegations' => ( is => 'ro', isa => 'HashRef', default => sub { {} } );
has 'fake_ds'          => ( is => 'ro', isa => 'HashRef', default => sub { {} } );

###
### Variables
###

our %object_cache;

###
### Build methods for attributes
###

around 'new' => sub {
    my $orig = shift;
    my $self = shift;

    my $obj  = $self->$orig( @_ );
    my $name = uc( '' . $obj->name );
    $name = '$$$NONAME' unless $name;

    if ( not exists $object_cache{$name}{ $obj->address->ip } ) {
        Zonemaster->logger->add( NS_CREATED => { name => $name, ip => $obj->address->ip } );
        $object_cache{$name}{ $obj->address->ip } = $obj;
    }

    return $object_cache{$name}{ $obj->address->ip };
};

sub _build_dns {
    my ( $self ) = @_;

    my $res = Net::LDNS->new( $self->address->ip );
    $res->recurse( 0 );

    my %defaults = %{ Zonemaster->config->resolver_defaults };
    foreach my $flag ( keys %defaults ) {
        $res->$flag( $defaults{$flag} );
    }

    return $res;
}

sub _build_cache {
    my ( $self ) = @_;

    Zonemaster::Nameserver::Cache->new( { address => $self->address } );
}

###
### Public Methods (and helpers)
###

sub query {
    my ( $self, $name, $type, $href ) = @_;
    $type //= 'A';

    if ( $self->address->version == 4 and not Zonemaster->config->ipv4_ok ) {
        Zonemaster->logger->add( IPV4_BLOCKED => { ns => $self->string } );
        return;
    }

    if ( $self->address->version == 6 and not Zonemaster->config->ipv6_ok ) {
        Zonemaster->logger->add( IPV6_BLOCKED => { ns => $self->string } );
        return;
    }

    Zonemaster->logger->add(
        'query',
        {
            name  => "$name",
            type  => $type,
            flags => $href,
            ip    => $self->address->short
        }
    );

    my %defaults = %{ Zonemaster->config->resolver_defaults };

    my $class     = $href->{class}     // 'IN';
    my $dnssec    = $href->{dnssec}    // $defaults{dnssec};
    my $usevc     = $href->{usevc}     // $defaults{usevc};
    my $recurse   = $href->{recurse}   // $defaults{recurse};
    my $edns_size = $href->{edns_size} // $defaults{edns_size};

    # Fake a DS answer
    if ( $type eq 'DS' and $class eq 'IN' and $self->fake_ds->{ lc( $name ) } ) {
        my $p = Net::LDNS::Packet->new( $name, $type, $class );
        $p->aa( 0 );
        $p->do( $dnssec );
        $p->rd( $recurse );
        foreach my $rr ( @{ $self->fake_ds->{ lc( $name ) } } ) {
            $p->unique_push( 'answer', $rr );
        }
        my $res = Zonemaster::Packet->new( { packet => $p } );
        Zonemaster->logger->add( FAKE_DS_RETURNED => { name => "$name", from => "$self" } );
        return $res;
    }

    # Fake a delegation
    foreach my $fname ( sort keys %{ $self->fake_delegations } ) {
        if ( $name =~ m/(\.|^)\Q$fname\E$/i ) {
            my $p = Net::LDNS::Packet->new( $name, $type, $class );

            if ( lc( $name ) eq lc( $fname ) and $type eq 'NS' ) {
                my $name = $self->fake_delegations->{$fname}{authority};
                my $addr = $self->fake_delegations->{$fname}{additional};
                $p->unique_push( 'answer',     $_ ) for @$name;
                $p->unique_push( 'additional', $_ ) for @$addr;
            }
            else {
                while ( my ( $section, $aref ) = each %{ $self->fake_delegations->{$fname} } ) {
                    $p->unique_push( $section, $_ ) for @$aref;
                }
            }<commit_msg>Faked DS response packets should have AA set.<commit_after>package Zonemaster::Nameserver v1.0.0;

use 5.14.2;
use Moose;
use Moose::Util::TypeConstraints;

use Zonemaster::DNSName;
use Zonemaster;
use Zonemaster::Packet;
use Zonemaster::Nameserver::Cache;
use Zonemaster::Recursor;

use Net::LDNS;

use Net::IP::XS qw(:PROC);
use Time::HiRes qw[time];
use JSON::XS;
use MIME::Base64;
use Module::Find qw[useall];
use Carp;
use List::Util qw[max min sum];

use overload
  '""'  => \&string,
  'cmp' => \&compare;

subtype 'Zonemaster::Net::IP::XS', as 'Object', where { $_->isa( 'Net::IP::XS' ) };
coerce 'Zonemaster::Net::IP::XS', from 'Str', via { Net::IP::XS->new( $_ ) };

has 'name'    => ( is => 'ro', isa => 'Zonemaster::DNSName',     coerce => 1, required => 0 );
has 'address' => ( is => 'ro', isa => 'Zonemaster::Net::IP::XS', coerce => 1, required => 1 );

has 'dns'   => ( is => 'ro', isa => 'Net::LDNS',                     lazy_build => 1 );
has 'cache' => ( is => 'ro', isa => 'Zonemaster::Nameserver::Cache', lazy_build => 1 );
has 'times' => ( is => 'ro', isa => 'ArrayRef',                      default    => sub { [] } );

has 'fake_delegations' => ( is => 'ro', isa => 'HashRef', default => sub { {} } );
has 'fake_ds'          => ( is => 'ro', isa => 'HashRef', default => sub { {} } );

###
### Variables
###

our %object_cache;

###
### Build methods for attributes
###

around 'new' => sub {
    my $orig = shift;
    my $self = shift;

    my $obj  = $self->$orig( @_ );
    my $name = uc( '' . $obj->name );
    $name = '$$$NONAME' unless $name;

    if ( not exists $object_cache{$name}{ $obj->address->ip } ) {
        Zonemaster->logger->add( NS_CREATED => { name => $name, ip => $obj->address->ip } );
        $object_cache{$name}{ $obj->address->ip } = $obj;
    }

    return $object_cache{$name}{ $obj->address->ip };
};

sub _build_dns {
    my ( $self ) = @_;

    my $res = Net::LDNS->new( $self->address->ip );
    $res->recurse( 0 );

    my %defaults = %{ Zonemaster->config->resolver_defaults };
    foreach my $flag ( keys %defaults ) {
        $res->$flag( $defaults{$flag} );
    }

    return $res;
}

sub _build_cache {
    my ( $self ) = @_;

    Zonemaster::Nameserver::Cache->new( { address => $self->address } );
}

###
### Public Methods (and helpers)
###

sub query {
    my ( $self, $name, $type, $href ) = @_;
    $type //= 'A';

    if ( $self->address->version == 4 and not Zonemaster->config->ipv4_ok ) {
        Zonemaster->logger->add( IPV4_BLOCKED => { ns => $self->string } );
        return;
    }

    if ( $self->address->version == 6 and not Zonemaster->config->ipv6_ok ) {
        Zonemaster->logger->add( IPV6_BLOCKED => { ns => $self->string } );
        return;
    }

    Zonemaster->logger->add(
        'query',
        {
            name  => "$name",
            type  => $type,
            flags => $href,
            ip    => $self->address->short
        }
    );

    my %defaults = %{ Zonemaster->config->resolver_defaults };

    my $class     = $href->{class}     // 'IN';
    my $dnssec    = $href->{dnssec}    // $defaults{dnssec};
    my $usevc     = $href->{usevc}     // $defaults{usevc};
    my $recurse   = $href->{recurse}   // $defaults{recurse};
    my $edns_size = $href->{edns_size} // $defaults{edns_size};

    # Fake a DS answer
    if ( $type eq 'DS' and $class eq 'IN' and $self->fake_ds->{ lc( $name ) } ) {
        my $p = Net::LDNS::Packet->new( $name, $type, $class );
        $p->aa( 1 );
        $p->do( $dnssec );
        $p->rd( $recurse );
        foreach my $rr ( @{ $self->fake_ds->{ lc( $name ) } } ) {
            $p->unique_push( 'answer', $rr );
        }
        my $res = Zonemaster::Packet->new( { packet => $p } );
        Zonemaster->logger->add( FAKE_DS_RETURNED => { name => "$name", from => "$self" } );
        return $res;
    }

    # Fake a delegation
    foreach my $fname ( sort keys %{ $self->fake_delegations } ) {
        if ( $name =~ m/(\.|^)\Q$fname\E$/i ) {
            my $p = Net::LDNS::Packet->new( $name, $type, $class );

            if ( lc( $name ) eq lc( $fname ) and $type eq 'NS' ) {
                my $name = $self->fake_delegations->{$fname}{authority};
                my $addr = $self->fake_delegations->{$fname}{additional};
                $p->unique_push( 'answer',     $_ ) for @$name;
                $p->unique_push( 'additional', $_ ) for @$addr;
            }
            else {
                while ( my ( $section, $aref ) = each %{ $self->fake_delegations->{$fname} } ) {
                    $p->unique_push( $section, $_ ) for @$aref;
                }
            }
{'fraction_non_alphanumeric': 0.21052060737527115, 'fraction_numerical': 0.004989154013015184, 'mean_word_length': 2.344577439245557, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 243, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6484105', 'n_tokens_mistral': 3437, 'n_tokens_neox': 3329, 'n_words': 733}