Permalink
Find file
f0b8581 Mar 25, 2015
@jodrell @spinza @seb-at-nzrs
executable file 170 lines (121 sloc) 3.92 KB
#!/usr/bin/perl
use Net::DNS;
use Getopt::Long;
use Pod::Usage;
use strict;
my ($zone, $server, $srv, $class, $port, $tsig_name, $tsig_key, $timeout, $source, $help);
GetOptions(
'zone=s' => \$zone,
'server:s' => \$server,
'srv:s' => \$srv,
'class:s' => \$class,
'port:s' => \$port,
'tsig-name:s' => \$tsig_name,
'tsig-key:s' => \$tsig_key,
'timeout:i' => \$timeout,
'source:s' => \$source,
'help' => \$help,
) or pod2usage(1);
pod2usage(1) if ($help);
if ($zone eq '') {
print "pnotify: --zone argument is missing or empty\n";
pod2usage(1);
}
my $resolver = Net::DNS::Resolver->new;
my @servers;
if ($srv ne '') {
my $answer = $resolver->query('_dns._udp.'.$srv, 'SRV');
if (!$answer) {
print "pnotify: no answer received when querying for _dns._udp.$srv/SRV\n";
exit(1);
} elsif ($answer->header->rcode ne 'NOERROR') {
print "pnotify: received ".$answer->header->rcode." when querying for _dns._udp.$srv/SRV\n";
exit(1);
} elsif (scalar($answer->answer) < 0) {
print "pnotify: received no answer records when querying for _dns._udp.$srv/SRV\n";
exit(1);
} else {
foreach my $rr ($answer->answer) {
push(@servers, $rr->target) if ($rr->type eq 'SRV');
}
}
} elsif ($server ne '') {
push(@servers, $server);
} else {
print "pnotify: --server or --srv arguments are missing or empty\n";
pod2usage(1);
}
my $packet = Net::DNS::Packet->new($zone, 'SOA', ($class || 'IN'));
$packet->header->opcode('NS_NOTIFY_OP');
$packet->header->aa(1);
$packet->header->rd(0);
$packet->header->rcode('NOERROR');
$packet->sign_tsig($tsig_name, $tsig_key) if ($tsig_name ne '' && $tsig_key ne '');
my $errors = 0;
foreach my $server (@servers) {
$resolver->nameserver($server);
$resolver->udp_timeout($timeout || 1);
$resolver->srcaddr($source) if ($source ne '');
$resolver->port($port // 53);
my $answer = $resolver->send($packet);
if ($answer) {
printf("%s: %s\n", $server, $answer->header->rcode);
$errors++ if ($answer->header->rcode ne 'NOERROR');
} else {
printf("%s: TIMEOUT\n", $server);
$errors++;
}
}
exit($errors);
__END__
=head1 NAME
pnotify - A simple, portable Perl script for sending DNS NOTIFY packets
with TSIG support.
=head1 SYNOPSIS
pnotfy [options]
Options:
--zone=ZONE The DNS zone
--class=CLASS The class (default IN)
--server=HOST Host to send the packet to, OR
--srv=DOMAIN FQDN to use to construct _dns._udp SRV query
to determine which servers to send the
NOTIFY to
--port=PORT Destination port to send the packet to (default 53)
--timeout=TIMEOUT Timeout in seconds (default 1)
--tsig-name=NAME Optional TSIG name
--tsig-key=KEY Optional TSIG KEY (only HMAC-MD5 is
supported)
--source=ADDR Use this source address (optional)
--help Show this help
=head1 SENDING MULTIPLE PACKETS
You can send a NOTIFY to a single host using the --server argument. If you
provide a value for the --server argumnet, then pnotify will perform a SRV
lookup and retrieve a list of servers, and send a packet to each.
For example: if you used C<--srv=example.com>, pnotify will perform a SRV
query for C<_dns._udp.example.com>. A NOTIFY packet will then be sent to
each host in the response to the SRV query.
=head1 OUTPUT
pnotify will print the rcode of the response from each server or "TIMEOUT"
to STDOUT.
=head1 EXIT CODE
The exit code will be equal to the number of errors observed.
=head1 RATIONALE
It is sometimes useful to be able to manually send a NOTIFY packet to a
DNS server. There are other tools to do this (eg nsd-notify(8)) but they
are not as portable as pnotify, which is a pure Perl script with only a
limited range of prerequisites.
=head1 REQUIREMENTS
=over
=item * L<Net::DNS>
=item * L<Getopt::Long>
=item * L<Pod::Usage>
=back
=head1 COPYRIGHT
Copyright 2011 CentralNic Ltd. This program is Free Software, you can
use it and/or modify it under the same terms as Perl itself.
=head1 SEE ALSO
=over
=item * L<Net::DNS>
=item * L<https://www.centralnic.com/>
=back
=cut