[net-dns-users] IXFR methods

Chris Buxton chris at buxtonfamily.us
Tue Dec 18 20:22:55 UTC 2012


Hello all,

As Doug said, it's very nice to have a list.

Has anyone else created an IXFR method for Net::DNS::Resolver? I have, and I think it's pretty good, but I think it could probably be improved.

Here's my code -- requires perl 5.10 or later:

# Hack IXFR capability into Net::DNS::Resolver
sub Net::DNS::Resolver::Base::ixfr($$$)
{
    my( $res, $zone, $serial ) = @_;
    local $_;

    # Send IXFR requests over TCP
    $res->usevc(1);

    # Construct the request. It contains an abbreviated SOA record in
    # the authority section, where the only fields that matter are name,
    # class, type, and old serial number.
    my $packet = new Net::DNS::Packet( $zone, 'IXFR' );
    my $soa = Net::DNS::RR->new(
        name    => $zone,
        type    => 'SOA',
        mname   => '',
        rname   => '',
        serial  => $serial,
        refresh => 0,
        retry   => 0,
        expire  => 0,
        minimum => 0
    );
    $packet->push( authority => $soa );

    # Send the request and store the result in an array reference.
    my $result = $res->send( $packet );

    # The method here is, we're going to accumulate records into arrays
    # of @$items, and then put those into @$hunks like [ $mode, $items
    # ]. We then push each $hunk onto @diff. $mode will switch from '+'
    # to '-' and back again for each new @$hunk. Therefore:
    #
    # @diff = (
    #   [ $mode, [ $rr, ... ] ],
    #   ...
    # )
    #
    # Each @$hunk ends with an SOA record. Every time we encounter an
    # SOA record, we start a new hunk right afterward (with the next
    # record) and switch the $mode (the operator for each hunk).
    #
    # If you look carefully, you'll see that the very first SOA record
    # is inserted into an empty @$items, but this @$items array doesn't
    # get attached to @diff before being replaced with a fresh array. So
    # we're processing it to set the $mode, but we're otherwise throwing
    # it away.
    #
    # The last hunk (started after the final SOA record of the IXFR) has
    # no records. Always. So we remove it before returning @diff.
    # Therefore, an IXFR that requests changes since the current serial
    # will return an empty array; an AXFR-style IXFR will contain
    # exactly one hunk. To summarize:
    #
    # @diff == 1 # no changes since the presented serial number
    # @diff == 2 # AXFR-style IXFR
    # @diff >  2 # Normal IXFR containing changes
    #
    # Algorithm to interpret an IXFR courtesy of Mark Andrews @ISC, via
    # a quick and dirty awk script he posted to BIND Users.

    # Get ready to parse the result.
    my( @diff, $hunk, $items );
    my $mode = '-';

    for my $rr ( $result->answer )
    {
        given( $rr->type )
        {
            when( 'SOA' )
            {
                push @$items, $rr;
                $mode = ( $mode eq '-' ? '+' : '-' );
                $hunk = [ $mode, [] ];
                $items = $hunk->[1];
                push @diff, $hunk;
            }
            when( [ qw( RRSIG NSEC NSEC3 NSEC3PARAM ) ] )
            {
                # Strip out DNSSEC records like this, as we can't really
                # do anything useful with them
            }
            when( 'TSIG' )
            {
                # TODO: Add some error checking here
                # For now, strip these out also
            }
            default
            {
                # Anything else gets added to @$items
                push @$items, $rr;
            }
        }
    }

    # Discard the empty hunk from the end and return everything else
    pop @diff;
    return @diff;
}

Chris



More information about the net-dns-users mailing list