1package Net::RNDC; 2{ 3 $Net::RNDC::VERSION = '0.003'; 4} 5# ABSTRACT: Speak the BIND RNDC protocol 6 7use strict; 8use warnings; 9 10use Carp qw(croak); 11 12use Net::RNDC::Session; 13 14my $sock; 15 16BEGIN { 17 eval 'use IO::Socket::INET6;'; 18 19 if ($@) { 20 eval 'use IO::Socket::INET;'; 21 22 die $@ if $@; 23 24 $sock = 'IO::Socket::INET'; 25 } else { 26 $sock = 'IO::Socket::INET6'; 27 } 28} 29 30# Required for new() 31my @required_args = qw( 32); 33 34# Optional for new()/do() 35my @optional_args = qw( 36 key 37 host 38 port 39); 40 41sub new { 42 my ($class, %args) = @_; 43 44 my %obj = $class->_parse_args(%args); 45 46 return bless \%obj, $class; 47} 48 49sub _parse_args { 50 my ($class, %args) = @_; 51 52 for my $r (@required_args) { 53 unless ($args{$r}) { 54 croak("Required argument '$r' is missing"); 55 } 56 } 57 58 $args{port} ||= 953; 59 60 return map { 61 $_ => $args{$_} 62 } grep { $args{$_} } (@required_args, @optional_args); 63} 64 65sub _check_do_args { 66 my ($self, %args) = @_; 67 68 for my $r (qw(key host)) { 69 unless ($args{$r}) { 70 croak("Required argument '$r' is missing"); 71 } 72 } 73} 74 75sub do { 76 my ($self, $command, %override) = @_; 77 78 $self->{response} = $self->{error} = ''; 79 80 my $host = $self->{host}; 81 my $port = $self->{port}; 82 my $key = $self->{key}; 83 84 if (%override) { 85 my %args = $self->_parse_args( 86 host => $host, 87 port => $port, 88 key => $key, 89 %override, 90 ); 91 92 $host = $args{host}; 93 $port = $args{port}; 94 $key = $args{key}; 95 } 96 97 $self->_check_do_args( 98 host => $host, 99 port => $port, 100 key => $key, 101 ); 102 103 my $c = $sock->new( 104 PeerAddr => "$host:$port", 105 ); 106 107 unless ($c) { 108 $self->{error} = "Failed to create a socket: $@ ($!)"; 109 110 return 0; 111 } 112 113 # Net::RNDC::Session does all of the work 114 my $sess = Net::RNDC::Session->new( 115 key => $key, 116 command => $command, 117 is_client => 1, 118 119 want_write => sub { 120 my $s = shift; 121 122 $c->send(shift); 123 124 $s->next; 125 }, 126 127 want_read => sub { 128 my $s = shift; 129 130 my $buff; 131 132 $c->recv($buff, 4096); 133 134 $s->next($buff); 135 }, 136 137 want_finish => sub { 138 my $s = shift; 139 my $res = shift; 140 141 $self->{response} = $res; 142 }, 143 144 want_error => sub { 145 my $s = shift; 146 my $err = shift; 147 148 $self->{error} = $err; 149 } 150 ); 151 152 # Work! 153 $sess->start; 154 155 $c->close; 156 157 if ($self->response) { 158 return 1; 159 } else { 160 return 0; 161 } 162} 163 164sub response { 165 my ($self) = @_; 166 167 return $self->{response}; 168} 169 170sub error { 171 my ($self) = @_; 172 173 return $self->{error}; 174} 175 1761; 177__END__; 178 179=head1 NAME 180 181Net::RNDC - Speak the BIND Remote Name Daemon Control (RNDC) V1 protocol 182 183=head1 VERSION 184 185version 0.003 186 187=head1 SYNOPSIS 188 189Simple synchronous command/response: 190 191 use Net::RNDC; 192 193 my $rndc = Net::RNDC->new( 194 host => '127.0.0.1', 195 port => 953, # Defaults to 953 196 key => 'abcd', 197 ); 198 199 if (!$rndc->do('status')) { 200 die "RNDC failed: " . $rndc->error; 201 } 202 203 print $rndc->response; 204 205All arguments to new() are allowed in do: 206 207 my $rndc = Net::RNDC->new(); 208 209 my $key = 'abcd'; 210 211 for my $s (qw(127.0.0.1 127.0.0.2)) { 212 if (!$rndc->do('status', key => $key, host => $s)) { 213 my $err = $rndc->error; 214 } else { 215 my $resp = $rndc->response; 216 } 217 } 218 219=head1 DESCRIPTION 220 221This package provides a synchronous, easy to use interface to the RNDC V1 222protocol. For more mid-level control, see L<Net::RNDC::Session>, and for 223absolute control, L<Net::RNDC::Packet>. 224 225=head2 Constructor 226 227=head3 new 228 229 Net::RNDC->new(%args); 230 231Optional Arguments: 232 233=over 4 234 235=item * 236 237B<key> - The Base64 encoded HMAC-MD5 private key to use. 238 239=item * 240 241B<host> - The hostname/IP of the remote server to connect to. If 242L<IO::Socket::INET6> is installed, IPv6 support will be enabled. 243 244=item * 245 246B<port> - The port to connect to. Defaults to I<953>. 247 248=back 249 250=head2 Methods 251 252=head3 do 253 254 $rndc->do($command); 255 256 $rndc->do($commands, %args); 257 258Connects to the remote nameserver configured in L</new> or passed in to 259B<%args> and sends the specified command. 260 261Returns 1 on success, 0 on failure. 262 263Arguments: 264 265=over 4 266 267=item * 268 269B<$command> - The RNDC command to run. For example: C<status>. 270 271=back 272 273Optional Arguments - See L</new> above. 274 275=head3 error 276 277 $rndc->error; 278 279Returns the last string error from a call to L</do>, if any. Only set if 280L</do> returns 0. 281 282=head3 response 283 284 $rndc->response; 285 286Returns the last string response from a call to L</do>, if any. Only set if 287L</do> returns 1. 288 289=head1 SEE ALSO 290 291L<Net::RNDC::Session> - Manage the 4-packet RNDC session 292 293L<Net::RNDC::Packet> - Low level RNDC packet manipulation. 294 295=head1 AUTHOR 296 297Matthew Horsfall (alh) <WolfSage@gmail.com> 298 299=head1 LICENSE 300 301You may distribute this code under the same terms as Perl itself. 302 303=cut 304