Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Bind zone file search

by ranceh (Novice)
on Sep 26, 2012 at 01:13 UTC ( [id://995662]=perlquestion: print w/replies, xml ) Need Help??

ranceh has asked for the wisdom of the Perl Monks concerning the following question:

I have a directory full of bind dns zone files with hundreds of zone stanzas like:

zone "zone.com" { type slave; file "path/to/db.zone"; masters { ip.add.ress.here }; };

There is some variation in the stanzas wrt the line spacing around the masters directive. Sometimes the ip address is on a line by itself under the open brace.

I have part of a perl script that has built an array of zone file names, and an array of zone names from these zone files.

Now I need to find the master ip for each zone in the zone array but I have no idea which zone file it came from.

I'm thinking nested foreach or while loops over the zone names and zone files arrays.

The end result should be a hash with the zones as key names and the master ip as the value.

My data is consistent in that there are only two variations of line spacing in all the zone files I have.

I specifically don't understand how to write the regex for this case given the variation in line spacing to place in the map function to return the ip address. I also don't have a feel for the most efficient way to loop through the zone names and files stopping when I find a match, because there will be only one match in the set of files.

I know from reading docs and googling that I need a multiline grep and that I need to unset $/ and read the whole zone file into a temp variable, but that's where I stopped understanding.

I would appreciate any hints, insights and pointers you may have.

Replies are listed 'Best First'.
Re: Bind zone file search
by Kenosis (Priest) on Sep 26, 2012 at 04:09 UTC

    Perhaps the following will offer some direction:

    use strict; use warnings; my $directory = './zones'; my %zones = getZoneHash($directory); print "$_ -> $zones{$_}\n" for keys %zones; sub getZoneHash { my ($dir) = @_; my %hash; local $/; for my $file ( grep -f, <"$dir/*"> ) { open my $fh, '<', $file or die $!; my $data = <$fh>; close $fh; $hash{$1} = $2 if $data =~ /zone\s+"([^"]+)".+masters\s+{\s*([^}\s]+)\s*}/s +; } Kg return %hash; }

    There are three files in the directory zones, and each contains only one of the following zone stanzas:

    zone "zone.com" { type slave; file "path/to/db.zone"; masters {ip.add.ress.here}; }; zone "cnn.com" { type slave; file "path/to/db.zone"; masters { cnn.add.ress.here }; }; zone "perlmonks.org" { type slave; file "path/to/db.zone"; masters { ip.perlmonks.ress.here }; };

    Output:

    cnn.com -> cnn.add.ress.here perlmonks.org -> ip.perlmonks.ress.here zone.com -> ip.add.ress.here

    The subroutine getZoneHash takes a directory parameter, and then opens, reads, and captures the zone info and masters ip address of the zone stanza in each file in that directory, building and returning a hash.

    The capturing regex:

    /zone\s+"([^"]+)".+masters\s+{\s*([^}\s]+)\s*}/s ^ ^ ^ ^ ^ ^ ^ ^ ^ | | | | | | | | | | | | | | | | | + - Treat string as sin +gle line | | | | | | | + - 0+ whitespaces | | | | | | + - Match anything that's not } o +r a whitespace | | | | | + - 0+ whitespaces | | | | + - 1+ whitespaces | | | + - End quotation | | + - Match anything that's not a " | + - Begin quotation + - 1+ whitespaces

    Hope this helps!

      This has real promise, Thanks much.

        You're welcome much!

      What version of perl did you use?

      This code does not generate any errors, but does not generate output.

        Hi, ranceh

        Am using v5.16, but there's nothing in the code that would prevent its execution by much earlier versions, so I suspect the issue is something else. Try the following change to the subroutine:

        use strict; use warnings; my $directory = './zones'; my %zones = getZoneHash($directory); print "$_ -> $zones{$_}\n" for keys %zones; sub getZoneHash { my ($dir) = @_; my %hash; my @files = grep -f, <"$dir/*"> or die "Problem getting filenames in $dir/: $!"; local $/; for my $file (@files) { open my $fh, '<', $file or die $!; my $data = <$fh>; close $fh; $hash{$1} = $2 if $data =~ /zone\s+"([^"]+)".+masters\s+{\s*([^}\s]+)\s*}/s +; } return %hash; }

        Now there's a check for file names being read from the desired directory. The script will die with a message if none were read.

        If you're still getting the same results, temporarily place the following just below the close $fh; line:

        print $data; exit;

        You should see a zone stanza that the regex would process. If not, the next step would be to figure out why the file contents are not being read into $data.

Re: Bind zone file search
by toolic (Bishop) on Sep 26, 2012 at 02:04 UTC

      I looked at the DNS::ZoneParse module before starting this project. Something like this would help as I will eventually be interested in the serial from the master and slave and checking that they match, but its use creates a different version of the same problem: Grepping all the zone files for zone names and db file locations.

      Thanks for the pointer.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://995662]
Approved by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (3)
As of 2024-03-28 17:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found