#!/usr/bin/perl use DBI; use strict; use Net::IP qw(ip_expand_address); my $dsn = "DBI:mysql:database=powerdns:host=localhost"; my $dbh = DBI->connect( $dsn, '', '' ); my $sth = $dbh->prepare('SELECT * FROM records'); $sth->execute(); while ( my $ref = $sth->fetchrow_hashref() ) { # Things we care about: # name # type # prio # content -- may contain weight/port if SRV # -- SOA looks like ns1.occnetwork.net hostmaster@occnetwork.net 2010101902 86400 7200 604800 10800 # ttl if ( $ref->{'type'} eq "SRV" ) { $ref->{'content'} =~ s/(\d+) (\d+) (.*)/\3/; $ref->{'weight'} = $1; $ref->{'port'} = $2; } elsif ( $ref->{'type'} eq "SOA" ) { $ref->{'content'} =~ s/([^ ]+) ([^@]+)@([^ ]+) (\d+) (\d+) (\d+) (\d+) (\d+)/\1/; $ref->{'contact'} = $2 . '.' . $3; $ref->{'serial'} = $4; $ref->{'refresh'} = $5; $ref->{'retry'} = $6; $ref->{'expire'} = $7; $ref->{'min'} = $8; } #print 'got record ' . $ref->{'type'} . "\n"; print createRecord($ref) . "\n"; } # Logic partially from Anders' tinydns record builder # http://anders.com/projects/sysadmin/djbdnsRecordBuilder/ sub createRecord { my $href = shift; my %record = %$href; my $result; if ( $record{'type'} eq "SOA" ) { $result = 'Z' . escapeText( $record{'name'} ) . ':' . escapeText( $record{'content'} ) . ':' . escapeText( $record{'contact'} ) . ':' . $record{'serial'} . ':' . $record{'refresh'} . ':' . $record{'retry'} . ':' . $record{'expire'} . ':' . $record{'min'} . ':' . $record{'ttl'}; } elsif ( $record{'type'} eq "NS" ) { $result = '&' . escapeText( $record{'name'} ) . '::' . escapeText( $record{'content'} ) . ':' . $record{'ttl'}; } elsif ( $record{'type'} eq "A" ) { $result = '+' . escapeText( $record{'name'} ) . ':' . escapeText( $record{'content'} ) . ':' . $record{'ttl'}; } elsif ( $record{'type'} eq "CNAME" ) { $result = 'C' . escapeText( $record{'name'} ) . ':' . escapeText( $record{'content'} ) . ':' . $record{'ttl'}; } elsif ( $record{'type'} eq "MX" ) { $result = '@' . escapeText( $record{'name'} ) . '::' . escapeText( $record{'content'} ) . ':' . $record{'prio'} . ':' . $record{'ttl'}; } elsif ( $record{'type'} eq "PTR" ) { $result = '^' . escapeText( $record{'name'} ) . ':' . escapeText( $record{'content'} ) . ':' . $record{'ttl'}; } elsif ( $record{'type'} eq "TXT" ) { $result = "\'" . escapeText( $record{'name'} ) . ":" . escapeText( $record{'content'} ) . ":" . $record{'ttl'}; } elsif ( $record{'type'} eq "SPF" ) { $result = ":" . escapeText( $record{'name'} ) . ":16:" . characterCount( $record{'content'} ) . escapeText( $record{'content'} ) . ":" . $record{'ttl'}; } elsif ( $record{'type'} eq "SRV" ) { # :sip.tcp.example.com:33:\000\001\000\002\023\304\003pbx\007example\003com\000 if ( ( $record{'prio'} >= 0 && $record{'prio'} <= 65535 ) && ( $record{'weight'} >= 0 && $record{'weight'} <= 65535 ) && ( $record{'port'} >= 0 && $record{'port'} <= 65535 ) ) { my $target = ""; my @chunks = split /\./, $record{'content'}; foreach my $chunk (@chunks) { $target = $target . characterCount($chunk) . $chunk; } $result = ":" . escapeText( $record{'name'} ) . ":33:" . escapeNumber( $record{'prio'} ) . escapeNumber( $record{'weight'} ) . escapeNumber( $record{'port'} ) . $target . "\\000" . ":" . $record{'ttl'}; } else { print "priority, weight or port not within 0 - 65535\n"; } } elsif ( $record{'type'} eq "NAPTR" ) { # XXX - not used? # :comunip.com:35:\000\012\000\144\001u\007E2U+sip\036!^.*$!sip\072info@comunip.com.br!\000:300 # |-order-|-pref--|flag|-services-|---------------regexp---------------|re-| if ( ( $record{'order'} >= 0 && $record{'order'} <= 65535 ) && ( $record{'prefrence'} >= 0 && $record{'prefrence'} <= 65535 ) ) { $result = ":" . escapeText( $record{'domain'} ) . ":35:" . escapeNumber( $record{'order'} ) . escapeNumber( $record{'prefrence'} ) . characterCount( $record{'flag'} ) . $record{'flag'} . characterCount( $record{'services'} ) . escapeText( $record{'services'} ) . characterCount( $record{'regexp'} ) . escapeText( $record{'regexp'} ); if ( $record{'replacement'} ne "" ) { $result = $result . characterCount( $record{'replacement'} ) . escapeText( $record{'replacement'} ); } $result = $result . "\\000:" . $record{'ttl'}; } else { print "order or prefrence not within 0 - 65535\n"; } } elsif ( $record{'type'} eq "domainKeys" ) { # XXX - this is just a special TXT record... not used. # :joe._domainkey.anders.com:16:\341k=rsa; p=MIGfMA0GCSqGSIb3DQ ... E2hHCvoVwXqyZ/MbQIDAQAB # |lt| |typ| |-key----------------------------------------| if ( $record{'key'} ne "" ) { my $key = $record{'key'}; $key =~ s/\r//g; $key =~ s/\n//g; my $line = "k=" . $record{'encryptionType'} . "; p=" . $key; $result = ":" . escapeText( $record{'domain'} ) . ":16:" . characterCount($line) . escapeText($line) . ":" . $record{'ttl'}; } else { print "didn't get a valid key for the key field\n"; } } elsif ( $record{'type'} eq "AAAA" ) { # ffff:1234:5678:9abc:def0:1234:0:0 # :example.com:28:\377\377\022\064\126\170\232\274\336\360\022\064\000\000\000\000 $record{'content'} = ip_expand_address( $record{'content'}, 6 ); if ( $record{'content'} ne "" && $record{'name'} ne "" ) { my ( $a, $b, $c, $d, $e, $f, $g, $h ) = split /:/, $record{'content'}; if ( !defined $h ) { print "Didn't get a valid-looking IPv6 address: $record{content}\n"; } else { $a = escapeHex( sprintf "%04s", $a ); $b = escapeHex( sprintf "%04s", $b ); $c = escapeHex( sprintf "%04s", $c ); $d = escapeHex( sprintf "%04s", $d ); $e = escapeHex( sprintf "%04s", $e ); $f = escapeHex( sprintf "%04s", $f ); $g = escapeHex( sprintf "%04s", $g ); $h = escapeHex( sprintf "%04s", $h ); $result = ":" . escapeText( $record{'name'} ) . ":28:" . "$a$b$c$d$e$f$g$h" . ":" . $record{'ttl'}; } } else { print "didn't get a valid address or domain\n"; } } else { print "didn't get a valid record type\n"; } return $result; } sub escapeText { my $line = pop @_; my $out; my @chars = split //, $line; foreach my $char (@chars) { if ( $char =~ /[\r\n\t: \\\/]/ ) { $out = $out . sprintf "\\%.3lo", ord $char; } else { $out = $out . $char; } } return $out; } sub escapeNumber { my $number = pop @_; my $highNumber = 0; if ( $number - 256 >= 0 ) { $highNumber = int( $number / 256 ); $number = $number - ( $highNumber * 256 ); } my $out = sprintf "\\%.3lo", $highNumber; $out = $out . sprintf "\\%.3lo", $number; return $out; } sub escapeHex { # takes a 4 character hex value and converts it to two excaped numbers my $line = pop @_; my @chars = split //, $line; my $out = sprintf "\\%.3lo", hex "$chars[0]$chars[1]"; $out = $out . sprintf "\\%.3lo", hex "$chars[2]$chars[3]"; return $out; } sub characterCount { my $line = pop @_; my @chars = split //, $line; my $count = @chars; return sprintf( "\\%.3lo", $count ); }