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

I need to deconstruct tinydns/djbdns SRV records to build zone files for Bind.
The records are created by our inhouse management system and stored in LDAP.


From LDAP I get:
\000\012\000\144\023\304\003pbx\007example\003com\000:3600
which is a mixture of octal and ascii and decimal. It is not a real string so split() et al will not be of use. (I think)

It breaks down as
\000\012=SRV priority where the first number is a multiple of 256 and the second is the remainder so \000\012 = (0*256) + 10 = 10
\000\144=SRV weight, as before \000\144 = (0*256) + 100 = 100
\023\304=SRV port, \023\304 = (19*256) + 196 = 5060
\003pbx\007example\003com\000 = SRV target, split on dots with the octal denoting how many chars in each part: pbx.example.com.
The final part after ":" is the TTL in decimal. 3600

Any advice on extracting the various fields from the LDAP reply would be greatly appreciated. Just converting the reply to ascii string would be a start.
I'm not a real coder as you can probably guess, so please bear that in mind.. many thanks.

Replies are listed 'Best First'.
Re: deconstructing tinydns(djbdns) SRV records (updated)
by haukex (Archbishop) on Aug 25, 2017 at 10:36 UTC

    Corion is right that if there is an appropriate module to do this for you, you should use that. <update> For example as VinsWorldcom demonstrated here</update>

    If it turns out you do need to do this yourself, here are two ideas based on unpack (templates explained in perlpacktut and pack) and regexes. The first is a little more robust in its parsing, the second a little shorter and simpler.

    my $data = "\000\012\000\144\023\304\003pbx\007example\003com\000:3600 +"; my ($pri,$weight,$port,$rest) = unpack "S>3A*", $data; my $target=''; $target .= (length($target)?'.':'').$2 while $rest=~/\G([\x01-\xFF])((??{".{".ord($1)."}"}))/saagc; my ($ttl) = $rest=~/\G\x00:(.+)\z/saagc or die "failed to parse"; # - OR - my ($pri,$weight,$port,$target,$ttl) = unpack "S>3Z*A*", $data; $target = join '.', unpack "(C/A)*", $target; $ttl=~s/\A://;
      Thanks haukex, your second suggestion works perfectly.
Re: deconstructing tinydns(djbdns) SRV records
by Corion (Patriarch) on Aug 25, 2017 at 09:56 UTC

    Could it be that this is just a raw SRV record as it would go over the wire?

    Have you tried converting it to its binary representation and then parsing it with (say) Net::DNS?

    my $data = "\000\012\000\144\023\304\003pbx\007example\003com\000:3600 +"; ( $rr, $next ) = decode Net::DNS::RR( \$data, $offset, @opaque );

      I believe that is the "raw" format as it would go over the wire, but it's only the SRV record part, not the whole packet or whole DNS layer. Your solution above requires at least the whole DNS layer (if using $offset = 0) - at least that's they way I interpret it and according to a quick test, seems likely:

      corrupt wire-format data at C:/Strawberry/perl/vendor/lib/Net/DNS/RR.p +m line 241.

      You can try the Net::Frame::Layer::DNS module and it's NFL::DNS::RR::SRV sub module - you'll need the Net::Frame and Net::Frame::Simple modules as well (they will auto-install if you do a 'cpan Net::Frame::Layer::DNS' install as they are listed as dependencies) and they have dependencies themselves - so it may get a bit bigger than you need, but this works:

      #!perl use strict; use warnings; use Net::Frame::Simple; my $data = "\000\012\000\144\023\304\003pbx\007example\003com\000"; my $info = Net::Frame::Simple->new( raw => $data, firstLayer => 'DNS::RR::SRV', ); print $info->print;
      Output:
      DNS::RR::SRV: priority:10 weight:100 port:5060 DNS::RR::SRV: target:pbx.example.com

      UPDATE: Note the ':3600' is *not* part of the of the SRV rdata. It is part of the DNS Answer section so the $data in the OP isn't exactly the raw wire format.

      Thanks for responding so quickly!

      I'm not very familiar with Net::DNS but I tried your suggestion:
      my $data = "\000\012\000\144\023\304\003pbx\007example\003com\000:3600 +"; $offset=0; ( $rr, $next ) = decode Net::DNS::RR( \$data, $offset); print "RR = $rr\n"; print "NEXT = $next\n"; exit;

      corrupt wire-format data at /usr/lib64/perl5/vendor_perl/Net/DNS/RR.pm line 242.

        So either the data is not a raw SRV record or we are handing it in the wrong way to Net::DNS.

        Maybe you can find out from the documentation what exactly is stored in your LDAP directory?

Re: deconstructing tinydns(djbdns) SRV records
by 0xdeadbad (Novice) on Aug 25, 2017 at 10:55 UTC
    Thanks for the help, folks. All done now.