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

I am working on a script that compares a local zonefile to the results of gethostby*() and writes to an error file if the two do not match. I have all of the SOA, A, CNAME records completed. The MX records are giving me trouble in doing the compare though.

Here is the code for the MX part

#!/usr/bin/env perl -w use Net::DNS; use DNS::ZoneParse; use Socket; $res = Net::DNS::Resolver->new(); my $ZONEPATH = $ARGV[0] or die "You Must specify the /full/path/to/z +onefiles/domain.com\n"; my @fields = split(/\//,$ARGV[0]); $DOMAIN = $fields[-1]; my $data_file = "/tmp/$DOMAIN.err"; open FILE, "+>$data_file" or die "can't open $data_file $!"; my $zonefile = DNS::ZoneParse->new($ZONEPATH); my $mx_records = $zonefile->mx(); # DNS query MX records my @mx = mx($res, $DOMAIN); my $DNS_mx_records_size = scalar @mx; my $mx_records_size = scalar @$mx_records; my $mx_priority = $mx_record->{priority}; my $mx_hostname = $mx_record->{host}; if ($DNS_mx_records_size ne $mx_records_size) { print FILE "$DOMAIN:MX:Count:$mx_records_size//Count:$DNS_mx_r +ecords_size\n"; } foreach $mx_record (@$mx_records) { print FILE $DOMAIN, ":MX:", $mx_record->{priority}, ":", $mx_record- +>{host},"//\n"; } foreach my $record (@mx) { print FILE "DNS:MX:", $record->preference, ":", $record->exchang +e, "\n"; } close (FILE);

Here is an example of the A rr comparison:

my $a_records = $zonefile->a(); # A records foreach my $record (@$a_records) { my $a_name = $record->{name}; $a_name =~ s/$/.$DOMAIN/g; $a_name =~ s/\@.//g; my $a_host = $record->{host}; my $a_name_packed_ip = gethostbyname("$a_name.$DOMAIN"); if (defined $a_name_packed_ip) { $a_name_ip_address = inet_ntoa($a_name_packed_ip); } my $a_host_packed_ip = gethostbyname("$a_host"); if (defined $a_host_packed_ip) { $a_host_ip_address = inet_ntoa($a_host_packed_ip); } if ($a_name_ip_address ne $a_host_ip_address) { print FILE "$DOMAIN:A:$a_name:$a_name_ip_address//DNS: +A:$a_host_ip_address\n"; } }

I am not having any luck comparing the MX records from the zonefile to the MX records returned in the lookup. Ive banged my head against this one for a while, so Now I turn to the brethren perl monks of the world for advice

I think it may have something to do with the fact that I have an array in mx_records and a hash in mx.
What I cant get my noodle around is that if this zonefile is a MAJOR change I may see 1 MX rr in the query and have 4 or 5 in the zonefile.

ANY Help is appreciated
Darrin

OOPS... I guess I should post an example of the output:

SOMEDOMAIN.com:SOA:Serial:200912070901//DNS:SOA:Serial107082511 SOMEDOMAIN.com:SOA:Primary:ns.SOMEZONE.com.//DNS:SOA:Primary:ns.SOMEBI +GMX.com SOMEDOMAIN.com:SOA:Refresh:3600//DNS:SOA:Refresh:10800 SOMEDOMAIN.com:SOA:TTL:3600//DNS:SOA:TTL:7200 SOMEDOMAIN.com:SOA:eMail:postmaster.SOMEOTHMAIL.com.//DNS:SOA:eMail:na +mehost.SOMEBIGMX.com SOMEDOMAIN.com:SOA:Expire:3600000//DNS:SOA:Expire:604800 SOMEDOMAIN.com:A:SOMEDOMAIN.com:111.111.111.11//DNS:A:222.222.22.222 SOMEDOMAIN.com:A:ftp.SOMEDOMAIN.com:111.111.111.12//DNS:A:222.222.22.2 +22 SOMEDOMAIN.com:A:mail.SOMEDOMAIN.com:111.111.111.25//DNS:A:11.22.123.2 +34 SOMEDOMAIN.com:A:pop.SOMEDOMAIN.com:111.111.111.35//DNS:A:11.22.123.23 +4 SOMEDOMAIN.com:A:smtp.SOMEDOMAIN.com:111.111.111.35//DNS:A:11.22.123.2 +34 SOMEDOMAIN.com:A:webmail.SOMEDOMAIN.com:111.111.111.45//DNS:A:11.22.12 +3.233 SOMEDOMAIN.com:A:www.SOMEDOMAIN.com:111.111.111.11//DNS:A:222.222.22.2 +22 SOMEDOMAIN.com:A:personalmail.SOMEDOMAIN.com:111.111.111.55//DNS:A:11. +22.123.234 SOMEDOMAIN.com:CNAME:imap:111.111.111.11//DNS:CNAME:imap.IMAPMAILSVCS. +net.:222.22.232.22 SOMEDOMAIN.com:CNAME:intermanager:111.111.111.65//DNS:CNAME:intermanag +er.SOMEOTHMAIL.com.:222.22.232.22 SOMEDOMAIN.com:CNAME:selfcare:111.111.111.75//DNS:CNAME:selfcare.SOMEO +THMAIL.com.:222.22.232.22 SOMEDOMAIN.com:MX:Count:4//Count:1 SOMEDOMAIN.com:MX:10:mx1.MXHOSTING.com.// SOMEDOMAIN.com:MX:100:mx2.MXHOSTING.com.// SOMEDOMAIN.com:MX:110:mx3.MXHOSTING.com.// SOMEDOMAIN.com:MX:120:mx4.MXHOSTING.com.// DNS:MX:10:inbound.SOMEDOMAIN.com.SOMEMAIL.net

Replies are listed 'Best First'.
Re: NET::DNS and Perl Comparison (hash ne array);
by zwon (Abbot) on Dec 24, 2009 at 20:57 UTC

    Have you checked that

    dig somedomain.com mx
    returns all 4 MX records?

      Yes. I think that you missed the intent here


      I work in an environment where I have to update numerous zone files and propagate the DNS changes to various DNS servers quite often


      My intent was during the interim transfer period where I have updated the zone file on the nameserver I would be able to parse the zone file, break the RRs into constituent components and compare those to an actual lookup on the nameserver once the named has been restarted (and the ttl lowered to 5 min and propagated)


      so it is quite the point to have differing returned variables. Where my trouble lie is that the code I posted returns the MX portion in such a way that I am not sure how to compare the datum.

        Just incase anyone was interested, I solved my delima (with a LOT of digging around CPAN and perlmonks) here is the complete script::

        #!/usr/bin/env perl ############################################################ # # # This script will parse a zonefile and perfom a Lookup on # # the host's entries to verify nameserver propagation. # # # # # # # # v2.2 - Syntax cleanup # # # ############################################################ use Net::DNS; use DNS::ZoneParse; use Socket; my $res = Net::DNS::Resolver->new; my $ZONEPATH = $ARGV[0] or die "You Must specify the /full/path/to/zon +efiles/domain.com\n"; my @fields = split(/\//,$ARGV[0]); my $DOMAIN = $fields[-1]; my $data_file = "/tmp/$DOMAIN.err"; open FILE, "+>$data_file" or die "can't open $data_file $!"; my $zonefile = DNS::ZoneParse->new($ZONEPATH); my $mx_records = $zonefile->mx(); my $a_records = $zonefile->a(); my $soa_record = $zonefile->soa(); my $cname_records = $zonefile->cname(); my $ns_records = $zonefile->ns(); # -- ZoneParse Variables -- my $SOA_SERIAL=($soa_record->{serial}); my $SOA_PRIMARY=($soa_record->{primary}); my $SOA_REFRESH=($soa_record->{refresh}); my $SOA_RETRY=($soa_record->{retry}); my $SOA_TTL=($soa_record->{ttl}); my $SOA_MINTTL=($soa_record->{minimumTTL}); my $SOA_EMAIL=($soa_record->{email}); my $SOA_EXPIRE=($soa_record->{expire}); # DNS query SOA my $soa_query = $res->query($DOMAIN, "SOA"); if ($soa_query) { my $lookup_primary = ($soa_query->answer)[0]->mname; my $lookup_eMail = ($soa_query->answer)[0]->rname; my $lookup_serial = ($soa_query->answer)[0]->serial; my $lookup_refresh = ($soa_query->answer)[0]->refresh; my $lookup_retry = ($soa_query->answer)[0]->retry; my $lookup_TTL = ($soa_query->answer)[0]->ttl; my $lookup_minimum = ($soa_query->answer)[0]->minimum; my $lookup_expire = ($soa_query->answer)[0]->expire; if ($SOA_SERIAL ne $lookup_serial){ print FILE "$DOMAIN:SOA:Serial:$SOA_SERIAL//DNS:SOA:Serial:$lo +okup_serial\n"; } if ($SOA_PRIMARY ne $lookup_primary){ print FILE "$DOMAIN:SOA:Primary:$SOA_PRIMARY//DNS:SOA:Primary: +$lookup_primary\n"; } if ($SOA_REFRESH ne $lookup_refresh){ print FILE "$DOMAIN:SOA:Refresh:$SOA_REFRESH//DNS:SOA:Refresh: +$lookup_refresh\n"; } if ($SOA_RETRY ne $lookup_retry){ print FILE "$DOMAIN:SOA:Retry:$SOA_RETRY//DNS:SOA:Retry:$looku +p_retry\n"; } if ($SOA_TTL ne $lookup_TTL){ print FILE "$DOMAIN:SOA:TTL:$SOA_TTL//DNS:SOA:TTL:$lookup_TTL\ +n"; } if ($SOA_MINTTL ne $lookup_minimum){ print FILE "$DOMAIN:SOA:MinTTL:$SOA_MINTTL//DNS:SOA:MinTTL:$lo +okup_TTL\n"; } if ($SOA_EMAIL ne $lookup_eMail){ print FILE "$DOMAIN:SOA:eMail:$SOA_EMAIL//DNS:SOA:eMail:$looku +p_eMail\n"; } if ($SOA_EXPIRE ne $lookup_expire){ print FILE "$DOMAIN:SOA:Expire:$SOA_EXPIRE//DNS:SOA:Expire:$lo +okup_expire\n"; } } else { print FILE "query failed: ", $res->errorstring, "\n"; } # A records foreach my $record (@$a_records) { my $a_name = $record->{name}; $a_name =~ s/$/.$DOMAIN/g; $a_name =~ s/\@.//g; my $a_host = $record->{host}; my $a_name_packed_ip = gethostbyname("$a_name.$DOMAIN"); if (defined $a_name_packed_ip) { $a_name_ip_address = inet_ntoa($a_name_packed_ip); } my $a_host_packed_ip = gethostbyname("$a_host"); if (defined $a_host_packed_ip) { $a_host_ip_address = inet_ntoa($a_host_packed_ip); } if ($a_name_ip_address ne $a_host_ip_address) { print FILE "$DOMAIN:A:$a_name:$a_name_ip_address//DNS:A:$a_hos +t_ip_address\n"; } } # CNAME records handler foreach my $record (@$cname_records) { my $cname_name = $record->{name}; my $cname_host = $record->{host}; my $cname_name_packed_ip = gethostbyname("$cname_name.$DOMAIN"); if (defined $cname_name_packed_ip) { $cname_name_ip_address = inet_ntoa($cname_name_packed_ip); } my $cname_host_packed_ip = gethostbyname("$cname_host"); if (defined $cname_host_packed_ip) { $cname_host_ip_address = inet_ntoa($cname_host_packed_ip); } if ($cname_name_ip_address ne $cname_host_ip_address) { print FILE "$DOMAIN:CNAME:$cname_name:$cname_name_ip_address//DNS: +CNAME:$cname_host:$cname_host_ip_address\n"; } } # DNS query MX records my @mx = mx($res, $DOMAIN); my $DNS_mx_records_size = scalar @mx; my $mx_records_size = scalar @$mx_records; if ($DNS_mx_records_size ne $mx_records_size) { print FILE "$DOMAIN:MX:Count:$mx_records_size//DNS:MX:Count:$DNS_m +x_records_size\n"; foreach my $mx_record(@$mx_records) { print FILE $DOMAIN, ":MX:", $mx_record->{priority}, ":", $mx_r +ecord->{host},"//\n"; } foreach my $record (@mx) { print FILE "DNS:MX:", $record->preference, ":", $record->excha +nge, "\n"; } } else { foreach $mx_record(@$mx_records){ foreach $record (@mx) { my $mx_priority = $mx_record->{priority}; my $mx_host = $mx_record->{host}; my $mx_preference = $record->preference; my $mx_exchange = $record->exchange; if ($mx_priority = $mx_preference and $mx_host ne $mx_ +exchange) { print FILE"Records Do Not Match: ", $DOMAIN, ":MX: +", $mx_record->{priority}, ":", $mx_record->{host}, "//", "DNS:MX:", +$record->preference, ":", $record->exchange,"\n"; } } } } close(FILE);