File Coverage

blib/lib/Net/DNS/ZoneParse.pm
Criterion Covered Total %
statement 95 113 84.0
branch 39 74 52.7
condition 19 44 43.1
subroutine 14 15 93.3
pod 6 6 100.0
total 173 252 68.6


line stmt bran cond sub pod time code
1             package Net::DNS::ZoneParse;
2              
3 7     7   463319 use 5.008000;
  7         29  
  7         306  
4 7     7   751 use strict;
  7         12  
  7         241  
5 7     7   1308 use warnings;
  7         28  
  7         288  
6 7     7   37 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  7         16  
  7         1851  
7              
8 7     7   40 use Exporter;
  7         13  
  7         660  
9 7     7   10360 use Net::DNS;
  7         1225325  
  7         1267  
10 7     7   6792 use Net::DNS::ZoneParse::Zone;
  7         26  
  7         1555  
11              
12             @ISA = qw(Exporter);
13             $VERSION = 0.103;
14             @EXPORT = qw( );
15             %EXPORT_TAGS = (
16             parser => [ qw( parse writezone ) ],
17             );
18              
19             @EXPORT_OK = map { @{$_} } values %EXPORT_TAGS;
20              
21             =pod
22              
23             =head1 NAME
24              
25             Net::DNS::ZoneParse - Perl extension for Parsing and Writing BIND8/9
26             (and RFC1035) compatible zone-files.
27              
28             =head1 SYNOPSIS
29              
30             Plain interface
31              
32             use Net::DNS::ZoneParse qw(parse, writezone);
33              
34             my $rr = parse("db.example.com");
35             my $zonetext = writezone($rr);
36              
37             Object oriented interface - supporting cached parsing
38              
39             use Net::DNS::ZoneParse;
40              
41             my $parser = Net::DNS::ZoneParse->new;
42             my $zone = $parser->zone("example.com");
43             $zone->rr->[0]->newserial if($zone->rr->[0]->{type} eq "SOA");
44             $zone->save;
45              
46              
47             =head1 DESCRIPTION
48              
49             This module is yet another caching parser/generator for RFC1035 compatible
50             zone-files. It aims to have a fast interface for parsing and support all RRs
51             known to Net::DNS::RR.
52              
53             In some circumstances the parsing of an entry is too complicated for the
54             default _from_string-logic of the corresponding RR. For this cases,
55             N::D::ZoneParse extends the Interface of N::D::RR with the function
56             new_from_filestring. Per default this will call the responding new_from_string,
57             but may be implemented differently for a given RR.
58              
59             When dealing not just with one Zonefile, the Object-oriented Interface
60             even becomes more handy. It provides an interface loading only the zonefiles
61             needed and those only once. Each Zone is then represented by a
62             Net::DNS::ZoneParse::Zone object.
63              
64             =cut
65              
66             #####################
67             # private functions #
68             #####################
69              
70             # test if the given directive may be valid and set it up
71             sub _dns_test_set_origin {
72 3     3   7 my ($origin, $param) = @_;
73              
74 3 50       11 return unless $origin;
75 3 100       15 $origin .= "." unless (substr($origin, -1) eq ".");
76 3 50       15 $origin = substr($origin, 1) if(substr($origin, 0, 1) eq ".");
77 3         9 $param->{origin} = $origin;
78 3         23 $param->{name} = substr($origin, 0, -1);
79             }
80              
81             # read and validate the arguments
82             sub _parser_param {
83 4     4   16 my $self = { dummy => 1 };
84 4 100       43 $self = shift if(ref($_[0]) eq "Net::DNS::ZoneParse");
85 4 50       28 my $file = shift unless(ref($_[0]));
86 4 100       18 my $fh = shift if(ref($_[0]) eq "GLOB");
87 4         10 my $param = {};
88 4 100       18 $param = shift if(ref($_[0]) eq "HASH");
89 4         87 my $rrs = [];
90 4 50       24 $rrs = shift if(ref($_[0]) eq "ARRAY");
91 4 50       15 $rrs = \@_ if(ref($_[0]) =~ m/^Net::DNS::RR/);
92              
93 4 50       16 if($param->{origin}) {
94 0         0 _dns_test_set_origin($param->{origin}, $param);
95             } else {
96 4         19 $param->{origin} = "";
97 4         12 $param->{name} = "";
98             }
99              
100 4   50     88 $param->{ttl} = $param->{ttl} || $self->{$param->{name}}->{ttl} || 0;
101 4   100     64 $param->{file} = $file || $param->{file} ||
102             $self->{$param->{name}}->{filename} || "";
103 4   66     27 $param->{fh} = $fh || $param->{fh};
104 4 50       19 $param->{rrs} = $rrs if $rrs;
105 4 100 66     38 if($param->{file} and not $param->{fh}) {
106 1 50       45 open($param->{fh}, "<", $param->{file}) or return;
107 1         4 $param->{fileopen} = 1;
108             }
109 4 50       30 $param->{nocache} = $self->{conf}->{nocache} unless $param->{nocache};
110 4 50 50     54 $param->{parser} = $self->{conf}->{parser} || [ qw( Native ) ]
111             unless $param->{parser};
112 4 50       17 $param->{parser_args} = {} unless $param->{parser_args};
113 4         10 $param->{self} = $self;
114 4         17 return $param;
115             }
116              
117             =pod
118              
119             =head2 METHODS
120              
121             =head3 new
122              
123             $parser = Net::DNS::ZoneParse->new( [ $config ] )
124              
125             Creating a new Net::DNS::ZoneParse object with the given configuration
126             for file-autoloading. The following parameters are currently supported:
127              
128             =over
129              
130             =item path
131              
132             Path to the directory where all of the ZoneFiles can be found. The default is
133             the current working directory.
134              
135             =item prefix
136              
137             The prefix to the generate the filename out of the zonename.
138             The default is "db.". Thus using the default, loading "example.com" will
139             search for the file "db.example.com"
140              
141             =item suffix
142              
143             Similar to prefix, the default is the empty string.
144              
145             =item dontload
146              
147             If set to true, the zone() wont parsed the corresponding file automaticly
148              
149             =item nocache
150              
151             If set to true, loaded files won't be cached within this object automaticly.
152             In this case, extent has to be used.
153              
154             =item parser
155              
156             The parsers to use when reading files. These must be given within a
157             arrayreference. All of the given parsers must be found within
158             Net::DNS::ZoneParse::Parser. The default is [ "Native" ]. All
159             Parsers in the list will be used consecutively unless one has returned
160             contents of the file to read.
161              
162             =item parser_args
163              
164             A hashref with parser-names as keys. Some parser may allow further
165             options; these can be accessed using this argument.
166              
167             =item generator
168              
169             The generators to use when generating a new zonefile. The use is corresponding
170             to parsers. Generators must be found within Net::DNS::ZoneParse::Generator
171              
172             =back
173              
174             =cut
175              
176             sub new {
177 4     4 1 34969 my ($self, $config) = @_;
178 4 50       29 $config = {} unless(defined $config);
179 4   50     97 my %conf = (
      50        
180             path => $config->{path},
181             prefix => $config->{prefix} || "db.",
182             suffix => $config->{suffix} || "",
183             dontload => $config->{dontload},
184             nocache => $config->{nocache},
185             );
186 4         22 return bless({conf => \%conf});
187             }
188              
189             =head3 zone
190              
191             $zone = $parser->zone("example.com" [, $param])
192              
193             Returns the Net::DNS::ZoneParse::Zone object for the given domain. If there
194             where no corresponding zonefile found, an empty Object will be returned.
195             If the zone was loaded for the first time the corresponding file will be loaded,
196             otherwise the cached object will be returned.
197              
198             $param is a hash-reference which can be used to adjust the behaviour.
199             If a parameter is not given here, it's value used at the time of the objects
200             generation will be used. Supported parameters are:
201              
202             =over
203              
204             =item path
205              
206             =item filename
207              
208             This will take precedence above prefix/suffix
209              
210             =item prefix
211              
212             =item suffix
213              
214             =item dontload
215              
216             =back
217              
218             =cut
219              
220             sub zone {
221 1     1 1 2 my ($self, $zone, $prm) = @_;
222 1 50       11 $prm = {} unless $prm;
223 1 50       10 unless($self->{$zone}) {
224 1   33     48 $self->{$zone} = Net::DNS::ZoneParse::Zone->new($zone, {
      33        
      33        
225             path => $prm->{path} || $self->{conf}->{path},
226             filename => $prm->{filename} ||
227             ($prm->{prefix} || $self->{conf}->{prefix} || "" ).$zone.($prm->{suffix} || $self->{conf}->{suffix} || ""),
228             dontload => $prm->{dontload} ||
229             $self->{conf}->{dontload},
230             parent => $self,
231             });
232             }
233 1         4 return $self->{$zone};
234             }
235              
236             =pod
237              
238             =head3 extent
239              
240             $parser->extent("example.com", $rrs);
241              
242             Extent the cached entries for the given origin - "example.com" in the example
243             by the RRs given in $rrs. These might be an array or a reference to an array
244             of Net::DNS::RR-Objects.
245              
246             =cut
247              
248             sub extent {
249 1     1 1 7 my ($self, $zone, @rrs) = @_;
250 1 50       4 return unless $zone;
251 1         6 $zone = $self->zone($zone, { dontload => 1 });
252 1         6 $zone->add(@rrs);
253 1         2 return;
254             }
255              
256             =pod
257              
258             =head3 uncache
259              
260             $parser->uncache("example.com");
261              
262             Uncaching removes a zone from the zonecache. The exported instances of this zone
263             will stay alive, but the next call to zone() will generate a new object.
264              
265             =cut
266              
267             sub uncache {
268 0     0 1 0 my ($self, $zone) = @_;
269              
270 0 0       0 return unless $self->{$zone};
271 0         0 delete $self->{$zone};
272             }
273              
274             =pod
275              
276             =head2 EXPORT
277              
278             =head3 writezone
279              
280             $zonetext = writezone($rr);
281             # or
282             $zonetext = $parser->writezone($zone);
283              
284             $rr might be either an array of Net::DNS::RR object, or a reference to them.
285             If using the object-oriented interface, this can be used to by just using
286             the name of the zone to write. In that case, correct directives for $ORIGIN
287             and $TTL will be created, too.
288              
289             writezone will then return a string with the contents of a corresponding
290             zone-file.
291              
292             As last parameter, a additional Hash-ref can be used to tweak some of the
293             behavior. The following parameters are supported:
294              
295             =over
296              
297             =item origin
298              
299             This may contain the zone-name, unless used in the object-oriented interface
300              
301             =item ttl
302              
303             The default TTL to be used in the generated file.
304              
305             =item rr
306              
307             The Resource-records to write
308              
309             =item generator
310              
311             the same as in new
312              
313             =back
314              
315             =cut
316              
317             sub writezone {
318 2     2 1 3694 my $self = {};
319 2 100       11 $self = shift if(ref($_[0]) eq "Net::DNS::ZoneParse");
320 2 50       9 my $prm = (ref($_[-1]) eq "HASH")?pop(@_):{};
321 2         4 my %param;
322 2 100 50     23 $param{origin} = ((not ref($_[0]))?shift():($prm->{origin} || ""));
323 2   50     27 $param{ttl} = $prm->{ttl} || $self->{$param{origin}}->{ttl} || 0;
324 2 50 33     16 $param{rr} = ((ref($_[0]) eq "ARRAY")?shift():
    100          
325             (($#_>=0) ? \@_ :
326             $prm->{rr} || $self->{$param{origin}}->rr));
327 2   50     30 $param{generator} = $prm->{generator} || $self->{conf}->{generator} || [ qw( Native ) ];
328 2 50       7 return unless $param{rr};
329              
330 2         4 for(@{$param{generator}}) {
  2         5  
331 2         22 my $gen = $_;
332 2         5 my $mod = "Net::DNS::ZoneParse::Generator::$gen";
333 2         129 eval "require $mod";
334 2 50       8 next if $@;
335 2   50     17 $param{parser_arg} = $param{parser_args}->{$gen} || {};
336 2         12 my $ret = $mod->generate(\%param);
337 2 50       25 return $ret if $ret;
338             }
339 0         0 return undef;
340             }
341              
342             =head3 parse
343              
344             $rr = parse($file);
345             # or
346             $rr = $parser->parse($file [, $param [, $rrin]]);
347              
348             parse a specific zonefile and returns a reference to an array of
349             Net::DNS::RR objects.
350              
351             If the function-oriented interface is used and Net::DNS::ZoneFile::Fast is
352             installed, that parser is used instead.
353              
354             $file might be either a filename or a open filehandle to be read.
355              
356             $param is a HASH-ref, with the following entries. If given, those may change to
357             reflect the contents of the parsed file.
358              
359             =over
360              
361             =item origin
362              
363             The current domains name
364              
365             =item ttl
366              
367             The default TTL for all RRs
368              
369             =item fh
370              
371             An already opened filehandle, $file can be ommitted, if this is given.
372              
373             =item file
374              
375             The name of the file to read, $file can be ommitted, if this is given
376              
377             =item rrs
378              
379             Resource Records, which will be added to these found in the file
380              
381             =item nocache
382              
383             if given, the parsed file will not be cached in the object.
384              
385             =item parser
386              
387             =item parser_args
388              
389             Same as in the call of new().
390              
391             =back
392              
393             $rrin is a ARRAY-ref of Net::DNS::RR objects. If given, this list will be
394             extended.
395              
396             =cut
397              
398             sub parse {
399 4     4 1 32 my $param = _parser_param(@_);
400 4 50       16 return unless $param;
401 4 50       29 return unless $param->{fh};
402              
403 4         7 my $ret;
404 4         8 for(@{$param->{parser}}) {
  4         14  
405 4         7 my $parser = $_;
406 4         10 my $mod = "Net::DNS::ZoneParse::Parser::$_";
407 4         351 eval "require $mod";
408 4 50       95 next if $@;
409 4   50     49 $param->{parser_arg} = $param->{parser_args}->{$parser} || {};
410 4         48 $ret = $mod->parse($param);
411 0 0         next unless $ret;
412 0           last;
413             }
414 0 0         if($param->{fileopen}) {
415 0           close($param->{fh});
416 0           delete($param->{fh}); delete($param->{fileopen});
  0            
417             };
418 0 0 0       if((not $param->{self}->{dummy})
      0        
419             and $param->{name} and not $param->{nocache}) {
420 0           $param->{self}->extent($param->{name}, $ret);
421             }
422 0 0         delete $param->{self} if $param->{self}->{dummy};
423 0 0         unshift(@{$ret}, @{$param->{rrs}}) if $param->{rrs};
  0            
  0            
424 0           return $ret;
425             }
426              
427             =head1 SEE ALSO
428              
429             Net::DNS
430              
431             =head1 AUTHOR
432              
433             Benjamin Tietz Ebenjamin@micronet24.deE
434              
435             =head1 COPYRIGHT
436              
437             Copyright (C) 2010 by Benjamin Tietz
438              
439             This library is free software; you can redistribute it and/or modify
440             it under the same terms as Perl itself, either Perl version 5.10.0 or,
441             at your option, any later version of Perl 5 you may have available.
442              
443             =cut
444              
445             1;
446