Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Parsing Zonefiles

by Elijah_A (Novice)
on Aug 05, 2004 at 05:55 UTC ( [id://380193]=perlquestion: print w/replies, xml ) Need Help??

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

Anyone here worked on dns zonefiles? I need some advice... I have some scripts that poll our dns frontend database for updated/new/deleted dns entries. They grab 'em just fine and dump them all together in a temporary file.
I need to be able to update the main zonefile using the temporary file. But everytime the script polls and dump more dns updates to temp, there may be a problem of adding (updated)duplicate entries to the main zonefile.
The old entries in the main zonefile must be updated, but how do I do that? I tried a variety of modules (DNS::Zone::File) but I couldn't get them to work right.

Here's a sample entry from the temp file:
; The Foreign Service Institute ; Created by GovPh: Thu Aug 5 07:51:07 2004 fsi IN NS tayxxxn.ino.com.ph IN NS monumxxo.ino.com.ph IN NS tayxxxn.ino.com.ph IN NS monumxxo.ino.com.ph IN NS tayxxxn.ino.com.ph IN NS monumxxo.ino.com.ph

There are hundreds of these entries in one zonefile, I simply need to find specific entries from the main zonefile to be updated. I can't just keep appending to it, duplicates might sneek in.

Elijah

Update: I've decided to use Net::DNS::ZoneFile(I'd have some use for the temp file with this) and hash as one of the monks suggested. But I'm having some trouble printing the objects using Net::DNS::ZoneFile.

Replies are listed 'Best First'.
Re: Parsing Zonefiles
by greenFox (Vicar) on Aug 05, 2004 at 06:35 UTC

    Unless you have a good reason not to I would be considering generating the zone file directly from your database. It is a technique described in Perl for System Administration and saves parsing the zone file at all.

    --
    Do not seek to follow in the footsteps of the wise. Seek what they sought. -Basho

      Or even better, if you are using bind 9.1 or newer then you can use use LDAP as the storage backend and do away with the zonefiles altogether - you can read about this stuff here.

      /J\

      Hmmm... now that you mention it... it kinda makes me wonder why I did that in the first place O_o. Anyway, got an online version of that book or the part about zone files?

        Looks like the sample chapter is "Log Files" so you are out of luck, you will have to buy a copy.

        --
        Do not seek to follow in the footsteps of the wise. Seek what they sought. -Basho

Re: Parsing Zonefiles
by bgreenlee (Friar) on Aug 05, 2004 at 06:44 UTC

    I don't have a lot of DNS experience, but any time you're trying to remove duplicates, a little voice should whisper "hash" in your ear. If you can parse the file so that each unique entry becomes the key of a hash, the hash will remove the duplicates for you.

    Brad

      I think I remember now how those hash thingies work, kinda like a dictionary... but how can that remove the duplicates?? sorry I didn't get that part, I'm still new to perl...
        Hashes remove duplicates because hashes have keys - you can't have 2 keys that are the same. If you try to add another key=value pair to the hash where the key already exists, the new key=value will just overwrite the old one.
Re: Parsing Zonefiles
by fokat (Deacon) on Aug 05, 2004 at 17:36 UTC

    Hi there:

    I don't know if you did look at it, but Net::DNS::ZoneFile will parse most BIND zone files, returning a list of Net::DNS::RR objects as the result.

    You can then act on the objects themselves, remove them from the list or add new ones. Finally, you can simply print them to a new file. The pseudo-code would look like:

    use Net::DNS; use Net::DNS::ZoneFile; my $rrs = Net::DNS::ZoneFile->read("/var/named/named.local", { # You might need to set the # $ORIGIN explicitly here if # your zone file does not make # this explicit. See perldoc } ); # Print the RRs that were fetched print $_->string, "\n" for @$rrs; # Add a sample RR push @$rrs, new Net::DNS::RR "3 7200 IN PTR always-listed.your.domain. +"; # Update the serial :) # You can also "replace" the current serial, with the current date, # as commonly recommended. However, if you're looking into this, you # probably have better change control on your zones already. foreach (@$rrs) { next unless $_->type eq 'SOA' and $_->class eq 'IN'; $_->serial($_->serial + 1); } # Print the resulting RR set (this could be your new zone by printing +to # a file) print $_->string, "\n" for @$rrs;

    Feel free to ask if you need further help with this code...

    Best regards

    -lem, but some call me fokat

      Ah!! now I remember why I used a temporary file for the main zone file, It's for the DNS::Zone::File; ... didn't work out though.
      Hmmm... Net::DNS::ZoneFile might just be what I'm looking for... I tried executing it till the print part, but didn't get any results.
Re: Parsing Zonefiles
by tomhukins (Curate) on Aug 05, 2004 at 17:00 UTC
    If you're generating the zone files automatically, and you know they'll render in a certain format, you could use Template::Extract. This would work especially well if you used Template Toolkit to generate the files.
Re: Parsing Zonefiles
by Elijah_A (Novice) on Aug 06, 2004 at 06:24 UTC
    I got only one result out of this one:
    "3. 7200 IN PTR gov.ph."
    #!/usr/bin/perl use strict; use warnings; use Net::DNS; use lib "/home/postgres/elijah/govph/tests/Net"; use Net::DNS::ZoneFile; my $rrset = Net::DNS::ZoneFile->read("/home/postgres/elijah/govph/etc/ +namedb/tem p.gov.ph"); print $_->string, "\n" for @$rrset; push @$rrset, new Net::DNS::RR "3 7200 IN PTR gov.ph."; foreach (@$rrset) { next unless $_->type eq 'SOA' and $_->class eq 'IN'; $_->serial($_->serial + 1); } print $_->string, "\n" for @$rrset;
    What am I doing wrong?? I should be getting something like this right?:
    ; Sta. Magdalena, Sorsogon ; Created by GovPh: Fri Aug 6 13:02:54 2004 stamagdalena IN NS ncc.xx.gov.ph IN NS ncc.xx.gov.ph

      Hi Elijah_A:

      I believe the problem might be your source file. Are you sure it is a real BIND zone? Your example does not have a SOA record, which might be ok for your scenario.

      On my side, the code below (slight modification of yours)...

      #!/usr/bin/perl use strict; use warnings; use Net::DNS; use Net::DNS::ZoneFile; my $rrset = Net::DNS::ZoneFile->readfh(*DATA); print "Just read:\n"; print $_->string, "\n" for @$rrset; push @$rrset, new Net::DNS::RR "3 7200 IN PTR gov.ph."; foreach (@$rrset) { next unless $_->type eq 'SOA' and $_->class eq 'IN'; $_->serial($_->serial + 1); } print "After adding one to the SOA's serial:\n"; print $_->string, "\n" for @$rrset; __DATA__ ; Sta. Magdalena, Sorsogon ; Created by GovPh: Fri Aug  6 13:02:54 2004 stamagdalena IN NS ncc.xx.gov.ph IN NS ncc.xx.gov.ph

      Produces the following output...

      Just read:
      stamagdalena.   0       IN      NS      ncc.xx.gov.ph.
      stamagdalena.   0       IN      NS      ncc.xx.gov.ph.
      After adding one to the SOA's serial:
      stamagdalena.   0       IN      NS      ncc.xx.gov.ph.
      stamagdalena.   0       IN      NS      ncc.xx.gov.ph.
      3.      7200    IN      PTR     gov.ph.
      

      I would check the source file for extraneous characters. For the record, this is what I'm running on my side:

      $ perl -MNet::DNS::ZoneFile -e 'print $Net::DNS::ZoneFile::VERSION, "\n"'
      1.10
      $ perl -v | egrep darwin
      This is perl, v5.8.1 built for darwin
      

      Best regards

      -lem, but some call me fokat

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (3)
As of 2024-04-16 22:09 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found