File Coverage

blib/lib/Net/DNS/Question.pm
Criterion Covered Total %
statement 102 102 100.0
branch 42 42 100.0
path n/a
condition 15 15 100.0
subroutine 23 23 100.0
pod 14 14 100.0
total 196 196 100.0


line stmt bran path cond sub pod time code
1               package Net::DNS::Question;
2                
3 95       95   95132 use strict;
  95           354  
  95           3099  
4 95       95   382 use warnings;
  95           265  
  95           6072  
5                
6               our $VERSION = (qw$Id: Question.pm 2002 2025-01-07 09:57:46Z willem $)[2];
7                
8                
9               =head1 NAME
10                
11               Net::DNS::Question - DNS question record
12                
13               =head1 SYNOPSIS
14                
15               use Net::DNS::Question;
16                
17               $question = Net::DNS::Question->new('example.com', 'AAAA', 'IN');
18                
19               =head1 DESCRIPTION
20                
21               A Net::DNS::Question object represents a record in the question
22               section of a DNS packet.
23                
24               =cut
25                
26                
27 95       95   793 use integer;
  95           144  
  95           422  
28 95       95   1698 use Carp;
  95           148  
  95           6361  
29                
30 95       95   901 use Net::DNS::Parameters qw(%classbyname %typebyname :class :type);
  95           145  
  95           12887  
31 95       95   1779 use Net::DNS::Domain;
  95           190  
  95           2635  
32 95       95   1827 use Net::DNS::DomainName;
  95           237  
  95           28842  
33                
34                
35               =head1 METHODS
36                
37               =head2 new
38                
39               $question = Net::DNS::Question->new('example.com', 'AAAA', 'IN');
40               $question = Net::DNS::Question->new('example.com', 'A', 'IN');
41               $question = Net::DNS::Question->new('example.com');
42                
43               $question = Net::DNS::Question->new('2001::DB8::dead:beef', 'PTR', 'IN');
44               $question = Net::DNS::Question->new('2001::DB8::dead:beef');
45                
46               Creates a question object from the domain, type, and class passed as
47               arguments. One or both type and class arguments may be omitted and
48               will assume the default values shown above.
49                
50               RFC4291 and RFC4632 IP address/prefix notation is supported for
51               queries in both in-addr.arpa and ip6.arpa namespaces.
52                
53               =cut
54                
55               sub new {
56 301       301 1 202143 my $self = bless {}, shift;
57 301           479 my $qname = shift;
58 301     100     830 my $qtype = shift || '';
59 301     100     867 my $qclass = shift || '';
60                
61               # tolerate (possibly unknown) type and class in zone file order
62 301 100         795 unless ( exists $classbyname{$qclass} ) {
63 238 100         483 ( $qtype, $qclass ) = ( $qclass, $qtype ) if exists $classbyname{$qtype};
64 238 100         497 ( $qtype, $qclass ) = ( $qclass, $qtype ) if $qtype =~ /CLASS/;
65               }
66 301 100         746 unless ( exists $typebyname{$qtype} ) {
67 142 100         278 ( $qtype, $qclass ) = ( $qclass, $qtype ) if exists $typebyname{$qclass};
68 142 100         238 ( $qtype, $qclass ) = ( $qclass, $qtype ) if $qclass =~ /TYPE/;
69               }
70                
71               # if argument is an IP address, do appropriate reverse lookup
72 301 100   100     2230 if ( defined $qname and $qname =~ m/:|\d$/ ) {
73 84 100         157 if ( my $reverse = _dns_addr($qname) ) {
74 82           118 $qname = $reverse;
75 82     100     217 $qtype ||= 'PTR';
76               }
77               }
78                
79 301           1135 $self->{qname} = Net::DNS::DomainName1035->new($qname);
80 299     100     1125 $self->{qtype} = typebyname( $qtype || 'A' );
81 299     100     1064 $self->{qclass} = classbyname( $qclass || 'IN' );
82                
83 299           885 return $self;
84               }
85                
86                
87               =head2 decode
88                
89               $question = Net::DNS::Question->decode(\$data, $offset);
90                
91               ($question, $offset) = Net::DNS::Question->decode(\$data, $offset);
92                
93               Decodes the question record at the specified location within a DNS
94               wire-format packet. The first argument is a reference to the buffer
95               containing the packet data. The second argument is the offset of
96               the start of the question record.
97                
98               Returns a Net::DNS::Question object and the offset of the next
99               location in the packet.
100                
101               An exception is raised if the object cannot be created
102               (e.g., corrupt or insufficient data).
103                
104               =cut
105                
106 95       95   598 use constant QFIXEDSZ => length pack 'n2', (0) x 2;
  95           196  
  95           122582  
107                
108               sub decode {
109 138       138 1 897 my ( $class, @argument ) = @_;
110 138           310 my ( $data, $offset ) = @argument;
111 138           298 my $self = bless {}, $class;
112                
113 138           1072 ( $self->{qname}, $offset ) = Net::DNS::DomainName1035->decode(@argument);
114                
115 125           294 my $next = $offset + QFIXEDSZ;
116 125 100         446 die 'corrupt wire-format data' if length $$data < $next;
117 120           379 @{$self}{qw(qtype qclass)} = unpack "\@$offset n2", $$data;
  120           400  
118                
119 120 100         502 return wantarray ? ( $self, $next ) : $self;
120               }
121                
122                
123               =head2 encode
124                
125               $data = $question->encode( $offset, $hash );
126                
127               Returns the Net::DNS::Question in binary format suitable for
128               inclusion in a DNS packet buffer.
129                
130               The optional arguments are the offset within the packet data where
131               the Net::DNS::Question is to be stored and a reference to a hash
132               table used to index compressed names within the packet.
133                
134               =cut
135                
136               sub encode {
137 208       208 1 500 my ( $self, @opaque ) = @_;
138 208           752 return pack 'a* n2', $self->{qname}->encode(@opaque), @{$self}{qw(qtype qclass)};
  208           2090  
139               }
140                
141                
142               =head2 string
143                
144               print "string = ", $question->string, "\n";
145                
146               Returns a string representation of the question record.
147                
148               =cut
149                
150               sub string {
151 85       85 1 586 my $self = shift;
152 85           182 return join "\t", $self->{qname}->string, $self->qclass, $self->qtype;
153               }
154                
155                
156               =head2 print
157                
158               $object->print;
159                
160               Prints the record to the standard output. Calls the string() method
161               to get the string representation.
162                
163               =cut
164                
165               sub print {
166 1       1 1 303 print &string, "\n";
167 1           4 return;
168               }
169                
170                
171               =head2 name
172                
173               $name = $question->name;
174                
175               Internationalised domain name corresponding to the qname attribute.
176                
177               Decoding non-ASCII domain names is computationally expensive and
178               undesirable for names which are likely to be used to construct
179               further queries.
180                
181               When required to communicate with humans, the 'proper' domain name
182               should be extracted from a query or reply packet.
183                
184               $query = Net::DNS::Packet->new( $example, 'SOA' );
185               $reply = $resolver->send($query) or die;
186               ($question) = $reply->question;
187               $name = $question->name;
188                
189               =cut
190                
191               sub name {
192 2       2 1 7 my ( $self, @argument ) = @_;
193 2           6 for (@argument) { croak 'immutable object: argument invalid' }
  1           134  
194 1           8 return $self->{qname}->xname;
195               }
196                
197                
198               =head2 qname, zname
199                
200               $qname = $question->qname;
201               $zname = $question->zname;
202                
203               Fully qualified domain name in the form required for a query
204               transmitted to a nameserver. In dynamic update packets, this
205               attribute is known as zname() and refers to the zone name.
206                
207               =cut
208                
209               sub qname {
210 127       127 1 704 my ( $self, @argument ) = @_;
211 127           234 for (@argument) { croak 'immutable object: argument invalid' }
  1           185  
212 126           364 return $self->{qname}->name;
213               }
214                
215 3       3 1 9 sub zname { return &qname; }
216                
217                
218               =head2 qtype, ztype, type
219                
220               $qtype = $question->type;
221               $qtype = $question->qtype;
222               $ztype = $question->ztype;
223                
224               Returns the question type attribute. In dynamic update packets,
225               this attribute is known as ztype() and refers to the zone type.
226                
227               =cut
228                
229               sub type {
230 95       95 1 110 my ( $self, @argument ) = @_;
231 95           109 for (@argument) { croak 'immutable object: argument invalid' }
  1           143  
232 94           151 return typebyval( $self->{qtype} );
233               }
234                
235 92       92 1 150 sub qtype { return &type; }
236 2       2 1 5 sub ztype { return &type; }
237                
238                
239               =head2 qclass, zclass, class
240                
241               $qclass = $question->class;
242               $qclass = $question->qclass;
243               $zclass = $question->zclass;
244                
245               Returns the question class attribute. In dynamic update packets,
246               this attribute is known as zclass() and refers to the zone class.
247                
248               =cut
249                
250               sub class {
251 185       185 1 241 my ( $self, @argument ) = @_;
252 185           237 for (@argument) { croak 'immutable object: argument invalid' }
  1           162  
253 184           358 return classbyval( $self->{qclass} );
254               }
255                
256 87       87 1 140 sub qclass { return &class; }
257 97       97 1 154 sub zclass { return &class; }
258                
259                
260               ########################################
261                
262               sub _dns_addr { ## Map IP address into reverse lookup namespace
263 85       85   115 local $_ = shift;
264                
265               # IP address must contain address characters only
266 85           135 s/[%].+$//; # discard RFC4007 scopeid
267 85 100         284 return unless m#^[a-fA-F0-9:./]+$#;
268                
269 84           274 my ( $address, $pfxlen ) = split m#/#;
270                
271               # map IPv4 address to in-addr.arpa space
272 84 100         311 if (m#^\d*[.\d]*\d(/\d+)?$#) {
273 22           38 my @parse = split /\./, $address;
274 22 100         43 $pfxlen = scalar(@parse) << 3 unless $pfxlen;
275 22 100         44 my $last = $pfxlen > 24 ? 3 : ( $pfxlen - 1 ) >> 3;
276 22           121 return join '.', reverse( ( @parse, (0) x 3 )[0 .. $last] ), 'in-addr.arpa.';
277               }
278                
279               # map IPv6 address to ip6.arpa space
280 62 100         279 return unless m#^[:\w]+:([.\w]*)(/\d+)?$#;
281 61     100     195 my $rhs = $1 || '0';
282 61 100         137 return _dns_addr($rhs) if m#^[:0]*:0*:[fF]{4}:[^:]+$#; # IPv4
283 60 100         113 $rhs = sprintf '%x%0.2x:%x%0.2x', map { $_ || 0 } split( /\./, $rhs, 4 ) if /\./;
  4 100         14  
284 60           317 $address =~ s/:[^:]*$/:0$rhs/;
285 60           242 my @parse = split /:/, ( reverse "0$address" ), 9;
286 60 100         96 my @xpand = map { /./ ? $_ : ('0') x ( 9 - @parse ) } @parse; # expand ::
  368           740  
287 60 100         111 $pfxlen = ( scalar(@xpand) << 4 ) unless $pfxlen; # implicit length if unspecified
288 60 100         100 my $len = $pfxlen > 124 ? 32 : ( $pfxlen + 3 ) >> 2;
289 60           90 my $hex = pack 'A4' x 8, map { $_ . '000' } ('0') x ( 8 - @xpand ), @xpand;
  480           708  
290 60           770 return join '.', split( //, substr( $hex, -$len ) ), 'ip6.arpa.';
291               }
292                
293                
294               1;
295               __END__