diff --git a/Makefile b/Makefile index a26e688..346c974 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # $Id: Makefile,v 1.30 2003/01/20 14:33:25 jrief Exp $ VERSION=0.3.6 -RELEASE=2 +RELEASE=3 CC=gcc -O2 CCDEBUG=gcc -g CFLAGS=$(INC) -DVERSION='"$(VERSION)"' diff --git a/TODO b/TODO index a26501e..f5a4ffd 100644 --- a/TODO +++ b/TODO @@ -1,2 +1,7 @@ * Add support for configuring basedn, binddn, bindpw, and execute command via environment variables. (2005/12/07 bklang) + +* Add support for reading more config params out of /etc/ldap.conf (at least + DNS search base DN, possibly more) (2005/12/07 bklang) + +* Add possbility to search multiple LDAP hosts (2005/12/07 bklang) diff --git a/data2ldap.pl b/data2ldap.pl index 18cacb5..ae2abd4 100644 --- a/data2ldap.pl +++ b/data2ldap.pl @@ -1,36 +1,41 @@ #!/usr/bin/perl -# To use this script, your tinydns data file must define dns zones -# before any records associated with that zone. For instance, to define the -# alkaloid.net zone the first record found by this script must either be a -# "Z" or "." record so that the tree is created in the proper order. Not -# following this rule will result in LDAP errors when importing the resulting -# dataset. To correct such an error just find all zone definitions and import -# those first, then add all records. This script may be extended to do that -# automatically for you at some point in the future but until that day follow -# this simple procedure and you shouldn't have any problems. use strict; use warnings; my $file = $ARGV[0]; my $output = $ARGV[1]; +my $rejout; my $basedn = $ARGV[2]; +my %domains; # Keep track of which domains for which we have + # already written an SOA my $outfh; +my $rejfh; if (!defined($file)) { print STDERR "Must specify path to 'data' file to read\n"; exit 1; } -if (!defined($output) || $file eq '-') { +if (!defined($output) || $output eq '-') { $output = "/dev/stdout"; + $rejout = "/dev/null"; +} else { + $rejout = "$output.rej"; } open($outfh, ">$output") or die ("Unable to open $output for writing!"); +open($rejfh, ">$rejout") or die ("Unable to open $rejout for writing"); if (!defined($basedn)) { print STDERR "Must specify a base DN as the third argument\n"; exit 1; } + +# We run in two iterations. The first attempts to enumerate all zones +# for which we have records and create SOAs in LDAP. The reason for this is +# zones are used as a container for all records so they must be in place before +# we start to add any zone data. While it takes longer, this mechanism ensures +# the proper sequence. open(DATA, $file) or die ("Unable to open $file for reading\n"); LINE: while() { chomp; @@ -40,6 +45,13 @@ LINE: while() { next LINE; }; + /^-/ && do { + # Found a disabled A record + print STDERR "Ignoring disabled record: $_\n"; + print $rejfh "$_\n"; + next LINE; + }; + /^%/ && do { # Location definition: %code:1.2.3.4 my ($loc, $ip) = split /:/; @@ -49,7 +61,7 @@ LINE: while() { print $outfh "objectClass: top\n"; print $outfh "objectClass: dnsloccodes\n"; print $outfh "dnslocation: $loc\n"; - if (defined($ip)) { + if (defined($ip) && $ip) { print $outfh "dnsipaddr: $ip\n"; } else { print $outfh "dnsipaddr: :\n"; @@ -68,17 +80,17 @@ LINE: while() { print $outfh "objectClass: top\n"; print $outfh "objectClass: dnszone\n"; print $outfh "cn: $domain\n"; - print $outfh "dnszonename: v-office.biz\n"; - if (defined($master)) { print $outfh "dnszonemaster: $master\n"; } - if (defined($admin)) { print $outfh "dnsadminmailbox: $admin\n"; } - if (defined($serial)) { print $outfh "dnsserial: $serial\n"; } - if (defined($refresh)) { print $outfh "dnsrefresh: $refresh\n"; } - if (defined($retry)) { print $outfh "dnsretry: $retry\n"; } - if (defined($expire)) { print $outfh "dnsexpire: $expire\n"; } - if (defined($minimum)) { print $outfh "dnsminimum: $minimum\n"; } - if (defined($ttl)) { print $outfh "dnsttl: $ttl\n"; } - if (defined($timestamp)) { print $outfh "dnstimestamp: $timestamp\n"; } - if (defined($loc)) { print $outfh "dnslocation: $loc\n"; } + print $outfh "dnszonename: $domain\n"; + if ($master) { print $outfh "dnszonemaster: $master\n"; } + if ($admin) { print $outfh "dnsadminmailbox: $admin\n"; } + if ($serial) { print $outfh "dnsserial: $serial\n"; } + if ($refresh) { print $outfh "dnsrefresh: $refresh\n"; } + if ($retry) { print $outfh "dnsretry: $retry\n"; } + if ($expire) { print $outfh "dnsexpire: $expire\n"; } + if ($minimum) { print $outfh "dnsminimum: $minimum\n"; } + if ($ttl) { print $outfh "dnsttl: $ttl\n"; } + if ($timestamp) { print $outfh "dnstimestamp: $timestamp\n"; } + if ($loc) { print $outfh "dnslocation: $loc\n"; } print $outfh "\n"; }; # End SOA record @@ -87,16 +99,18 @@ LINE: while() { my ($fqdn, $ip, $x, $ttl, $timestamp, $loc) = split /:/; $fqdn =~ s/^\.//; - my $id = "$fqdn-$ip-$x-$ttl-$timestamp-$loc"; # To find the domain name, the fqdn must have two words of any # characters with one period somehere in the middle and an optional # trailing period (which is trimmed) just before the end of the line - $fqdn =~ /.\.*(.+\..+)\.*$/; -print STDERR "$1\n"; + $fqdn =~ /^\.*([A-Za-z0-9-]+\.[A-Za-z0-9-]+)\.*$/; if (!defined($1)) { die ("Unable to find domain name for $fqdn!\n"); } - my $domain = $1; + my $domain = getdomain($fqdn); + if (defined($domains{$domain})) { + # We've already generated an SOA for this domain + next LINE; + } print $outfh "dn: cn=$domain,$basedn\n"; print $outfh "objectClass: top\n"; print $outfh "objectClass: dnszone\n"; @@ -105,7 +119,68 @@ print STDERR "$1\n"; if (defined($ttl)) { print $outfh "dnsttl: $ttl\n"; } if (defined($timestamp)) { print $outfh "dnstimestamp: $timestamp\n"; } if (defined($loc)) { print $outfh "dnslocation: $loc\n"; } + print $outfh "\n"; + $domains{$domain} = 1; + next LINE; + }; + } # End for($_) block +} # End LINE while() +# Done with zone SOAs, being with resource records + +seek(DATA, 0, 0) or die ("Unable to seek $file for reading\n"); +LINE: while() { + chomp; + for ($_) { + /^\s*#/ && do { + # Found a comment + next LINE; + }; + + /^-/ && do { + # Found a disabled. User was warned above + next LINE; + }; + + /^\./ && do { + # Found NS + A + SOA (SOA handled above) + my ($fqdn, $ip, $x, $ttl, $timestamp, $loc) = split /:/; + $fqdn =~ s/^\.//; + if (!defined($ip)) { $ip = ""; } + if (!defined($x)) { $x = ""; } + if (!defined($ttl)) { $ttl = ""; } + if (!defined($timestamp)) { $timestamp = ""; } + if (!defined($loc)) { $loc = ""; } + my $id = "NSA-$fqdn-$ip-$x-$ttl-$timestamp-$loc"; + my $domain = getdomain($fqdn); + + print $outfh "dn: cn=$id,cn=$domain,$basedn\n"; + print $outfh "objectClass: top\n"; + print $outfh "objectClass: dnszone\n"; + print $outfh "objectClass: dnsrrset\n"; + print $outfh "cn: $id\n"; + print $outfh "dnstype: a\n"; + print $outfh "dnsdomainname: $fqdn.\n"; + if ($x) { print $outfh "dnscname: $x\n"; } + if ($ip) { print $outfh "dnsipaddr: $ip\n"; } + if ($ttl) { print $outfh "dnsttl: $ttl\n"; } + if ($timestamp) { print $outfh "dnstimestamp: $timestamp\n"; } + if ($loc) { print $outfh "dnsloc: $loc\n"; } + print $outfh "\n"; + next LINE; + }; + + /^&/ && do { + # Found NS + my ($fqdn, $ip, $x, $ttl, $timestamp, $loc) = split /:/; + $fqdn =~ s/^&//; + if (!defined($ip)) { $ip = ""; } + if (!defined($x)) { $x = ""; } + if (!defined($ttl)) { $ttl = ""; } + if (!defined($timestamp)) { $timestamp = ""; } + if (!defined($loc)) { $loc = ""; } + my $id = "NS-$fqdn-$ip-$x-$ttl-$timestamp-$loc"; + my $domain = getdomain($fqdn); print $outfh "dn: cn=$id,cn=$domain,$basedn\n"; print $outfh "objectClass: top\n"; @@ -113,14 +188,190 @@ print STDERR "$1\n"; print $outfh "objectClass: dnsrrset\n"; print $outfh "cn: $id\n"; print $outfh "dnstype: ns\n"; - if (index($x, /\./) > -1) { - print $outfh "dnsdomainname: $x.\n"; - } else { - print $outfh "dnsdomainname: $x.ns.$fqdn.\n"; - } - if (defined($ip)) { print $outfh "dnscipaddr: $ip\n"; } + print $outfh "dnsdomainname: $fqdn.\n"; + if ($ip) { print $outfh "dnsipaddr: $ip\n"; } + if ($x) { print $outfh "dnscname: $x\n"; } + if ($ttl) { print $outfh "dnsttl: $ttl\n"; } + if ($timestamp) { print $outfh "dnstimestamp: $timestamp\n"; } + if ($loc) { print $outfh "dnsloc: $loc\n"; } print $outfh "\n"; next LINE; }; + + /^=/ && do { + # Found an A + PTR + my ($fqdn, $ip, $ttl, $timestamp, $loc) = split /:/; + $fqdn =~ s/^=//; + if (!defined($ip)) { $ip = ""; } + if (!defined($ttl)) { $ttl = ""; } + if (!defined($timestamp)) { $timestamp = ""; } + if (!defined($loc)) { $loc = ""; } + my $id = "APTR-$fqdn-$ip-$ttl-$timestamp-$loc"; + my $domain = getdomain($fqdn); + + print $outfh "dn: cn=$id,cn=$domain,$basedn\n"; + print $outfh "objectClass: top\n"; + print $outfh "objectClass: dnszone\n"; + print $outfh "objectClass: dnsrrset\n"; + print $outfh "cn: $id\n"; + print $outfh "dnstype: a\n"; + print $outfh "dnsdomainname: $fqdn.\n"; + if ($ip) { print $outfh "dnscipaddr: $ip\n"; } + if ($ttl) { print $outfh "dnsttl: $ttl\n"; } + if ($timestamp) { print $outfh "dnstimestamp: $timestamp\n"; } + if ($loc) { print $outfh "dnsloc: $loc\n"; } + print $outfh "\n"; + next LINE; + }; + + /^\+/ && do { + # Found an A + my ($fqdn, $ip, $ttl, $timestamp, $loc) = split /:/; + $fqdn =~ s/^\+//; + if (!defined($ip)) { $ip = ""; } + if (!defined($ttl)) { $ttl = ""; } + if (!defined($timestamp)) { $timestamp = ""; } + if (!defined($loc)) { $loc = ""; } + my $id = "A-$fqdn-$ip-$ttl-$timestamp-$loc"; + my $domain = getdomain($fqdn); + + print $outfh "dn: cn=$id,cn=$domain,$basedn\n"; + print $outfh "objectClass: top\n"; + print $outfh "objectClass: dnszone\n"; + print $outfh "objectClass: dnsrrset\n"; + print $outfh "cn: $id\n"; + print $outfh "dnstype: a\n"; + print $outfh "dnsdomainname: $fqdn.\n"; + if ($ip) { print $outfh "dnsipaddr: $ip\n"; } + if ($ttl) { print $outfh "dnsttl: $ttl\n"; } + if ($timestamp) { print $outfh "dnstimestamp: $timestamp\n"; } + if ($loc) { print $outfh "dnsloc: $loc\n"; } + print $outfh "\n"; + next LINE; + }; + + /^@/ && do { + # Found an MX + my ($fqdn, $ip, $x, $dist, $ttl, $timestamp, $loc) = split /:/; + $fqdn =~ s/^@//; + if (!defined($ip)) { $ip = ""; } + if (!defined($x)) { $x = ""; } + if (!defined($dist)) { $dist = ""; } + if (!defined($ttl)) { $ttl = ""; } + if (!defined($timestamp)) { $timestamp = ""; } + if (!defined($loc)) { $loc = ""; } + my $id = "MX-$fqdn-$ip-$x-$dist-$ttl-$timestamp-$loc"; + my $domain = getdomain($fqdn); + + print $outfh "dn: cn=$id,cn=$domain,$basedn\n"; + print $outfh "objectClass: top\n"; + print $outfh "objectClass: dnszone\n"; + print $outfh "objectClass: dnsrrset\n"; + print $outfh "cn: $id\n"; + print $outfh "dnstype: mx\n"; + print $outfh "dnsdomainname: $fqdn.\n"; + if ($ip) { print $outfh "dnsipaddr: $ip\n" }; + if ($x) { print $outfh "dnscname: $x\n"; } + if ($dist) { print $outfh "dnspreference: $dist\n"; } + if ($ttl) { print $outfh "dnsttl: $ttl\n"; } + if ($timestamp) { print $outfh "dnstimestamp: $timestamp\n"; } + if ($loc) { print $outfh "dnsloc: $loc\n"; } + print $outfh "\n"; + next LINE; + }; + + /^'/ && do { + # Currently unsupported + print STDERR "Ignoring unsupported TXT record: $_\n"; + print $rejfh "$_\n"; + next LINE; + # Found an MX + my ($fqdn, $s, $ttl, $timestamp, $loc) = split /:/; + $fqdn =~ s/^'//; + if (!defined($ttl)) { $ttl = ""; } + if (!defined($timestamp)) { $timestamp = ""; } + if (!defined($loc)) { $loc = ""; } + my $id = "TXT-$fqdn-$ttl-$timestamp-$loc"; + my $domain = getdomain($fqdn); + + print $outfh "dn: cn=$id,cn=$domain,$basedn\n"; + print $outfh "objectClass: top\n"; + print $outfh "objectClass: dnszone\n"; + print $outfh "objectClass: dnsrrset\n"; + print $outfh "cn: $id\n"; + print $outfh "dnstype: txt\n"; + print $outfh "dnsdomainname: $fqdn.\n"; + # FIXME Add TXT support to ldap2dns + # print $outfh "dnstxt: $s\n"; + if ($ttl) { print $outfh "dnsttl: $ttl\n"; } + if ($timestamp) { print $outfh "dnstimestamp: $timestamp\n"; } + if ($loc) { print $outfh "dnsloc: $loc\n"; } + print $outfh "\n"; + next LINE; + }; + + /^\^/ && do { + # Found an PTR + my ($fqdn, $ptr, $ttl, $timestamp, $loc) = split /:/; + $fqdn =~ s/^\^//; + if (!defined($ttl)) { $ttl = ""; } + if (!defined($timestamp)) { $timestamp = ""; } + if (!defined($loc)) { $loc = ""; } + my $id = "$fqdn-$ptr-$ttl-$timestamp-$loc"; + my $domain = getdomain($fqdn); + + print $outfh "dn: cn=$id,cn=$domain,$basedn\n"; + print $outfh "objectClass: top\n"; + print $outfh "objectClass: dnszone\n"; + print $outfh "objectClass: dnsrrset\n"; + print $outfh "cn: $id\n"; + print $outfh "dnstype: ptr\n"; + print $outfh "dnscname: $fqdn.\n"; + print $outfh "dnsipaddr: $ptr\n"; + if ($ttl) { print $outfh "dnsttl: $ttl\n"; } + if ($timestamp) { print $outfh "dnstimestamp: $timestamp\n"; } + if ($loc) { print $outfh "dnsloc: $loc\n"; } + print $outfh "\n"; + next LINE; + }; + + /^C/ && do { + # Found a CNAME + my ($fqdn, $p, $ttl, $timestamp, $loc) = split /:/; + $fqdn =~ s/^C//; + if (!defined($ttl)) { $ttl = ""; } + if (!defined($timestamp)) { $timestamp = ""; } + if (!defined($loc)) { $loc = ""; } + my $id = "CNAME-$fqdn-$p-$ttl-$timestamp-$loc"; + my $domain = getdomain($fqdn); + + print $outfh "dn: cn=$id,cn=$domain,$basedn\n"; + print $outfh "objectClass: top\n"; + print $outfh "objectClass: dnszone\n"; + print $outfh "objectClass: dnsrrset\n"; + print $outfh "cn: $id\n"; + print $outfh "dnstype: cname\n"; + print $outfh "dnsdomainname: $fqdn.\n"; + print $outfh "dnscname: $p.\n"; + if ($ttl) { print $outfh "dnsttl: $ttl\n"; } + if ($timestamp) { print $outfh "dnstimestamp: $timestamp\n"; } + if ($loc) { print $outfh "dnsloc: $loc\n"; } + print $outfh "\n"; + next LINE; + }; + + /^:/ && do { + # Found unsupported "unknown record" + print STDERR "Ignoring \"unknown record\": $_\n"; + print $rejfh "$_\n"; + next LINE; + } } # End for($_) block } # End LINE while() + +sub getdomain +{ + my $fqdn = shift(@_); + $fqdn =~ /\.*([A-Za-z0-9\-]+\.[A-Za-z0-9\-]+)\.*$/; + return $1; +} diff --git a/ldap2dns.c b/ldap2dns.c index 1634195..1102d29 100644 --- a/ldap2dns.c +++ b/ldap2dns.c @@ -1,8 +1,9 @@ /* * Create data from an LDAP directory service to be used for tinydns - * $Id: ldap2dns.c,v 1.33 2003/01/20 14:33:25 jrief Exp $ - * Copyright 2000 by Jacob Rief - * License: GPL version 2 or later. See http://www.fsf.org for details + * $Id: ldap2dns.c,v 1.36 2005/12/07 19:03:11 bklang Exp $ + * Copyright 2000-2005 by Jacob Rief + * Copyright 2005 by Ben Klang + * License: GPL version 2. See http://www.fsf.org for details */ #include @@ -34,7 +35,8 @@ static int main_argc; static void print_version(void) { printf("ldap2dns, version %s\n", VERSION); - printf(" Copyright 2000 by Jacob Rief \n\n"); + printf(" Copyright 2000-2005 by Jacob Rief \n\n"); + printf(" Copyright 2000 by Ben Klang \n"); } diff --git a/ldap2tinydns-conf b/ldap2tinydns-conf index b2fd758..132a7d7 100755 --- a/ldap2tinydns-conf +++ b/ldap2tinydns-conf @@ -21,7 +21,7 @@ touch $LDAP2DNSDIR/log/status cat << EOF_run > $LDAP2DNSDIR/run #!/bin/sh exec 2>&1 -exec setuidgid $LDAP2DNSUSER envdir ./env softlimit -d250000 /usr/bin/ldap2dns -e "cd $TINYDNSDIR && tinydns-data" +exec setuidgid $LDAP2DNSUSER envdir ./env softlimit -d250000 /usr/bin/ldap2dns -e "cd \$TINYDNSDIR && tinydns-data" EOF_run chmod +t $LDAP2DNSDIR