From 56f7543fdf0395a0c3e8f9d4873a22f5a7b3fe9f Mon Sep 17 00:00:00 2001 From: Ben Klang Date: Fri, 2 Dec 2005 04:17:15 +0000 Subject: [PATCH] Importing version 0.2.2 git-svn-id: https://svn.alkaloid.net/gpl/ldap2dns/trunk@2 06cd67b6-e706-0410-b29e-9de616bca6e9 --- CHANGELOG | 49 +++++ Makefile | 18 +- README.html | 185 +++++++++-------- Specfile | 11 +- TODO | 3 + dns.schema | 12 +- index.html | 3 +- ldap2dns.c | 434 +++++++++++++++------------------------ ldap2tinydns-conf | 10 +- zoneedit.pl | 509 ++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 856 insertions(+), 378 deletions(-) create mode 100644 CHANGELOG create mode 100644 TODO create mode 100644 zoneedit.pl diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..82dcca3 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,49 @@ +Version 0.2.3 ++ Check for next availabe server in /etc/ldap.conf + if first one is unavialable. + +Version 0.2.2 +- Tested with djbdns-1.05 + +- Removed compleatly the possibility to create a binary data.cdb file. + Reasons: + * It just takes a few milliseconds to create a data.cdb file with + tinydns-data. + * Its much safer to have an ASCII data file handy just in case + something goes wrong. + * I am too lazy to adopt ldap2dns for each new version of djbdns. + * ldap2dns does not have to be linked statically against any other + package. + +- Now the output option takes parameters data and/or db instead + of numbers. + +Version 0.2.1 +- Additional attribute in DNSrrset: DNScipaddr + Canonical IP address, which when used instead + of DNSipaddr automatically resolves reverse. + +- Using Environement Variables LDAP2DNS_UPDATE and + LDAP2DNS_OUTPUT for default values used by ldap2dns. + +- If started as daemon, does not exit if connection to + LDAP server fails but tries to reconnect after a timeout. + +- An external program can be called if ldap2dns detects a + modification in the database. + +Version 0.2.0 +- New schema, unfortunately not compatible with old one, but + now its unambigous. + +- Mapping for reverse lookup works fine. + +- ldap2dns now can be started by daemontools. + +- Fixed a bug for DNSrrset's with type=TXT + +- Much better naming scheme for dn's when using + import.pl + +- RPM support + diff --git a/Makefile b/Makefile index 056db05..888b4f3 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,12 @@ -# ldap2dns Makefile -VERSION=0.2.0 -RELEASE=1 -WITHTINYDNS=-DWITH_TINYDNS +# $Id: Makefile,v 1.22 2001/02/16 09:51:23 jrief Exp $ +VERSION=0.2.2 +RELEASE=2 CC=gcc -O2 -DJBDNSDIR=../djbdns-1.02 -INC=-I$(DJBDNSDIR) CFLAGS=$(INC) $(WITHTINYDNS) -DVERSION='"$(VERSION)"' OBJS=ldap2dns.o LIBS=-lldap -llber -lresolv LD=gcc LDFLAGS= -ALIBS=$(DJBDNSDIR)/dns.a $(DJBDNSDIR)/env.a $(DJBDNSDIR)/libtai.a \ - $(DJBDNSDIR)/cdb.a $(DJBDNSDIR)/alloc.a $(DJBDNSDIR)/buffer.a \ - $(DJBDNSDIR)/unix.a $(DJBDNSDIR)/byte.a INSTALL_PREFIX= PREFIXDIR=$(INSTALL_PREFIX)/usr LDAPCONFDIR=$(INSTALL_PREFIX)/etc/openldap @@ -21,8 +15,8 @@ SPECFILE=ldap2dns.spec all: ldap2dns -ldap2dns: $(OBJS) $(LIBS) $(ALIBS) - $(LD) $(LDFLAGS) -o $@ $(OBJS) $(ALIBS) $(LIBS) +ldap2dns: $(OBJS) $(LIBS) + $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) ln -f ldap2dns ldap2dnsd ldap2dns.o: ldap2dns.c @@ -38,7 +32,7 @@ install: all install -o root -g root -m 644 dns.oc.conf $(LDAPCONFDIR)/ clean: - rm -f $(OBJS) ldap2dns ldap2dnsd data* $(SPECFILE) + rm -f $(OBJS) ldap2dns ldap2dnsd data* *.db $(SPECFILE) tar: clean cd ..; \ diff --git a/README.html b/README.html index da7807e..02edb68 100644 --- a/README.html +++ b/README.html @@ -1,16 +1,17 @@

LDAP to DNS gateway

-ldap2dns is a program to create DNS records directly from a LDAP directory. -It can and should be be used to replace the secondary name-server by a second -primary one.
-ldap2dns helps to reduce all kind of administration overhead. +ldap2dns is a program to create DNS (Domain Name Service) records directly +from a LDAP directory. It can and should be be used to replace the secondary +name-server by a second primary one.
+ldap2dns reduces all kind of administration overhead: No more flat file editing, no more zone file editing. After having installed ldap2dns, the administrator only has to access the LDAP directory.
-If he desires he can add access control for each zone, create a webbased GUI +Optionally she can add access control for each zone, create a GUI and add all other kind of zone and resource record information without interfering with the DNS server.
-ldap2dns is designed to write binary data.cdb files used by tinydns, but -also may be used to write .db-files used by named.
+ldap2dns is designed to write ASCII data files used by tinydns +from the djbdns package, but also may be used to write .db-files used +by named as found in the BIND package.

1. Introduction

@@ -22,7 +23,7 @@ DNS, information must be stored redundantly on two or more hosts. The classical data replication through zone transfer is unreliable, insecure and difficult to administer.
To solve this problem some proprietary attempts have been proposed to -store DNS information in relational databases. The nature of DNS however +store DNS information in relational databases. The nature of DNS, however, is hierarchical and such should the database be. Using a relational database to store DNS information is undesirable, because it becomes difficult to store free form information. Within a hierachical data scheme, the @@ -35,28 +36,28 @@ and generates a file suitable for name-servers.
Actually the most widely spread name-servers named and tinydns are -supported. ldap2dns has been specially designed to work with +supported. ldap2dns specially has been designed to work with tinydns and is the favored name server daemon for the author of this program. -ldap2dns can also generate files suitable for named, but this feature -is not well supported. +ldap2dns can also generate files suitable for named version 8, +but this feature is not well supported. There is a RFC for a format description how to store DNS information in LDAP. -This paper however is a draft RFC and expired in February 1999. The scheme this RFC -describes, looks as if it has been designed to be used only by 'named'. This scheme +This paper a draft RFC which expired in February 1999, looks as if it has been +specially designed to be used by named. This scheme does not have strict attribute-value-pair mapping, making it difficult to be used by -user interfaces. It also lacks of an implementation (I have never heard of any).
-Since tinydns is going another descriptive way, I implemented a similar object-scheme -more suitable for tinydns. Two objectclasses have been defined. DNSzone stores -all the information to define a DNS zone, such as the SOA (Start Of Authority), serial -numbers etc. DNSrrset is used to store the information for a single resource record, -such as the domain name, IP-addresses, class and type.
+user interfaces. It also lacks of an implementation (or I have never heard of any).
+Since tinydns is going another descriptive way. Therefore I implemented a similar +object-scheme more suitable for tinydns. Two object-classes have been defined. +DNSzone stores all the information to define a DNS zone, such as the SOA +(Start Of Authority), serial numbers etc. DNSrrset is used to store the information +for a single resource record, such as the domain name, IP-addresses, class and type.
Here are the tables:

DNSzone

This object-class represents a DNS zone. It is the container for all the resource records -within a zone. Zones can be primary or secondary, if used in conjunction with -tinydns zones are always primary. Secondary zones don't make sense anyway! +within a zone. Zones can be primary or secondary. If used in conjunction with +tinydns zones are always primary. Secondary zones don't make sense anyway! In addition to being a container, the zone object has attributes related to the management of the zone. These include the zone's SOA information. Each zone-object can have none to many children of class DNSrrset.
@@ -79,26 +80,26 @@ can have none to many children of class DNSrrset.
DNStimestamptimestampoptional, only used with tinydns

@@ -229,10 +230,12 @@ If You are a tinydns user, run ldap2dns in /services/tinydns/root.
If You are an openldap user, the command line switches are the same as for ldapsearch or ldapadd.

-$ ldap2dns -D "binddn" [ -w passwd ] -b "searchbase" -o 1
+$ ldap2dns -D "binddn" [ -w passwd ] -b "searchbase" -o data -e "cd /var/tinydns/root && /usr/bin/tinydns-data"
 
-This generates a data.cdb file which is automatically updated by tinydns. The password -is required if You restrict read queries to authenticated users only. Now test with +This generates a data file which is converted into a data.cdb by tinydns-data as +soon as ldap2dns detects a modification in the LDAP directory. +The password is required if You restrict read queries to authenticated users only. +Test with
 $ dnsq any corp.local ipaddr
 
@@ -241,10 +244,11 @@ Replace ipaddr with whatever You configured tinydns to listen to. If You are a BIND user, run ldap2dns in /var/named with
-$ ldap2dns -D "binddn" -w passwd -b "searchbase" -o 4
+$ ldap2dns -D "binddn" -w passwd -b "searchbase" -o db -e "kill -HUP `cat /var/run/named-pid`"
 
-Do not forget to add You primary definition to Your named.boot file and -do not forget to restart named with +Do not forget to add You primary definition to Your named.boot file. +Your named should be restarted automatically as soon as ldap2dns detects a modification +in the LDAP directory. If bind is not restarted, do so with
 # kill -HUP PID
 
@@ -253,15 +257,16 @@ Now run $ nslookup - localhost > ns1.corp.local -Note that nslookup only works with tinydns if Your nameserver resolves its IP-address +Note that nslookup only works with tinydns if Your nameserver resolves its IP-address backwards.

4. Running ldap2dnsd

-ldap2dnsd is a hard link onto ldap2dns. If invoked the program +ldap2dnsd is a hard link onto ldap2dns. If invoked, the program starts as backgound-daemon and contineously checks for modifications in the LDAP directory. -If the the daemon sees a modification in the DNSserial numbers it updates the data.cdb -file. This check is done about once a minute.
+If the the daemon sees a modification in the DNSserial numbers it updates the data +or .db files, depending what kind of output was configured. This check is done about once +a minute.
The command-line options for ldap2dnsd are the same as for ldap2dns. Use the -u option to modify the update intervall. You may also use -u on ldap2dns to start as a foreground daemon. This is useful if You want to run ldap2dns from @@ -270,7 +275,7 @@ in /service/tinydns and link /service/ldap2dns onto /service/tinydns/ldap2dns.
 # ln -s /service/tinydns/ldap2dns /service/ldap2dns
 
-After a few seconds daemontools starts ldap2dns which itself generates data.cdb +After a few seconds daemontools starts ldap2dns which itself generates data files whenever a modification is commited into the LDAP directory.

ldap2dns and ldap2dnsd recognize the following options: @@ -278,22 +283,30 @@ files whenever a modification is commited into the LDAP directory. -D binddn specify the distinguished name to bind to the LDAP directory -w bindpasswd use bindpasswd as the password for simple authentication -b searchbase use searchbase as the starting point for the search instead of the default --o 1|2|4 output format number or any binary or-ed combination. Defaults to 1 - 1: generate a binary file named 'data.cdb' to be used directly by tinydns - 2: generate a text file named 'data' to be parsed by tinydns-data - 4: for each zone generate a file named '.db' to be used by named --L[filename] print output in LDIF format for reimport, defaults to stdout if filename is omitted --h host specify the hostname of LDAP directory, defaults to localhost --p port portnumber to connect to LDAP directory, defaults to 389 +-o Generate a "data" file to be processed by tinydns-data. +-o db For each zone generate a ".db" file to be used by named. +-L[filename] print output in LDIF format for reimport. If no filename is specified output goes to stdout. +-h host specify the hostname of LDAP directory. Default is localhost. +-p port portnumber to connect to LDAP directory. Defaults is 389 -v run in verbose mode -vv even more verbose -V print version and exit --u numsecs update DNS data every numsecs. If started as ldap2dnsd this defaults to 59. +-u numsecs update DNS data every numsecs. +ldap2dns and ldap2dnsd recognizes the following environement variables:
+TINYDNSDIR: Specifies the directory where ldap2dns writes its data file.
+LDAP2DNS_UPDATE: Specifies the update intervall as the -u command line option would.
+LDAP2DNS_OUTPUT: Specifies the default output, as the -o command line option would. +

+ldap2dns and ldap2dnsd use the following parameters from /etc/ldap.conf if not +specified on the command line: +BASE: The LDAP search base.
+HOST: The LDAP server.
+PORT: The LDAP port.

5. Importing DNS data from Your named

-A perl-script 'import.pl' is contained in this package. Edit the first +A perl-script import.pl is contained in this package. Edit the first lines of the script to conform to Your configuration. If You have installed the Perl packages Net::LDAP and Net::DNS skip the following lines, otherwise do @@ -303,7 +316,7 @@ skip the following lines, otherwise do > install Net::DNS > install Net::LDAP -Now check that Your nameserver allows zone transfers to Your host and run the import script: +Now check that Your nameserver allows zone transfers to your host and run the import script:
 $ echo 'primary mydomain.org ' | ./import.pl
 
@@ -314,19 +327,17 @@ for a single domain or to populate Your LDAP directory.

-

5. To Do

+

6. To Do

-

6. Copyright

+

7. Copyright and disclaimer

This program is licensed under the GPL version 2 or at Your choice any later version.
-It was written in autumn 2000 by -Jacob Rief -in the hope it may be useful. +It is maintained by Jacob Rief. +If you run ldap2dns on a productive nameserver, please mail me +and tell me on what OS and with which nameserver you do so. diff --git a/Specfile b/Specfile index f66e78f..ebdc9bd 100644 --- a/Specfile +++ b/Specfile @@ -24,15 +24,18 @@ ldap2dns is designed to write binary data.cdb files used by tinydns, but also ma used to write .db-files used by named. %prep -%setup -a1 -q +#%setup -a1 -q +%setup %build -make -C %{djbdns} -make DJBDNSDIR="%{djbdns}" VERSION=%{version} RPM_OPT_FLAGS="$RPM_OPT_FLAGS" +#make -C %{djbdns} +#make DJBDNSDIR="%{djbdns}" VERSION=%{version} RPM_OPT_FLAGS="$RPM_OPT_FLAGS" +make VERSION=%{version} RPM_OPT_FLAGS="$RPM_OPT_FLAGS" %install [ -n "%{buildroot}" -a "%{buildroot}" != / ] && rm -rf %{buildroot} -make DJBDNSDIR="%{djbdns}" INSTALL_PREFIX=$RPM_BUILD_ROOT install +#make DJBDNSDIR="%{djbdns}" INSTALL_PREFIX=$RPM_BUILD_ROOT install +make INSTALL_PREFIX=$RPM_BUILD_ROOT install %clean [ -n "%{buildroot}" -a "%{buildroot}" != / ] && rm -rf %{buildroot} diff --git a/TODO b/TODO new file mode 100644 index 0000000..9709902 --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +- Use env-variable LDAP2DNS_UPDATE for update intervalls +- If connection to ldapserver fialed retry later + diff --git a/dns.schema b/dns.schema index 383a205..ac6e3c6 100644 --- a/dns.schema +++ b/dns.schema @@ -1,6 +1,6 @@ # schema for DNS data # include this file into Your slapd.conf for openldap-2.0.x -# $Id: dns.schema,v 1.3 2000/12/01 14:48:25 jrief Exp $ +# $Id: dns.schema,v 1.4 2001/02/16 09:51:23 jrief Exp $ attributetype ( 1.2.840.113556.1.17.1 NAME 'DNSzonename' @@ -98,14 +98,14 @@ attributetype ( 1.2.840.113556.1.17.20 objectclass ( 1.2.840.113556.1.17.21 NAME 'DNSzone' MUST ( objectclass $ cn ) - MAY ( zonedomainname $ serial $ refresh $ retry $ expire $ minimum - $ adminmailbox $ zonemaster $ zonetype $ zoneclass $ rrcount - $ ttl $ timestamp ) ) + MAY ( DNSdomainname $ DNSserial $ DNSrefresh $ DNSretry $ DNSexpire $ DNSminimum + $ DNSadminmailbox $ DNSzonemaster $ DNSzonetype $ DNSzoneclass $ DNSrrcount + $ DNSttl $ DNStimestamp ) ) objectclass ( 1.2.840.113556.1.17.22 NAME 'DNSrrset' SUP DNSzone MUST ( objectclass $ cn ) - MAY ( dnsdomainname $ aliasedobjectname $ rr $ macaddress $ zoneclass - $ zonetype $ ipaddr $ cname $ preference $ ttl $ timestamp ) ) + MAY ( DNSdomainname $ DNSaliasedobjectname $ DNSrr $ DNSmacaddress $ DNSclass + $ DNStype $ DNSipaddr $ DNScname $ DNSpreference $ DNSttl $ DNStimestamp ) ) diff --git a/index.html b/index.html index efea686..f1b05af 100644 --- a/index.html +++ b/index.html @@ -23,7 +23,7 @@ >

- +

Download

@@ -33,6 +33,7 @@ +
0.1.3ldap2dns2000-Sep-28
0.1.4ldap2dns2000-Oct-04
0.2.0ldap2dnsldap2dnsldap2dns2000-Dec-14
0.2.2ldap2dnsldap2dnsldap2dns2001-Feb-16
diff --git a/ldap2dns.c b/ldap2dns.c index d633569..ffb9a5e 100644 --- a/ldap2dns.c +++ b/ldap2dns.c @@ -1,6 +1,6 @@ /* * Create data from an LDAP directory service to be used for tinydns - * $Id: ldap2dns.c,v 1.20 2000/12/12 09:48:07 jrief Exp $ + * $Id: ldap2dns.c,v 1.26 2001/02/27 10:08:31 jrief Exp $ * Copyright 2000 by Jacob Rief * License: GPL version 2 or later. See http://www.fsf.org for details */ @@ -15,44 +15,17 @@ #define UPDATE_INTERVALL 59 #define LDAP_CONF "/etc/openldap/ldap.conf" - -#if defined WITH_TINYDNS -# include "uint16.h" -# include "uint32.h" -# include "str.h" -# include "byte.h" -# include "fmt.h" -# include "ip4.h" -# include "exit.h" -# include "readwrite.h" -# include "buffer.h" -# include "strerr.h" -# include "getln.h" -# include "cdb_make.h" -# include "stralloc.h" -# include "open.h" -# include "dns.h" - -int fdcdb; -struct cdb_make cdb; -buffer b; -char bspace[1024]; -static stralloc key; -static stralloc result; -static char* dottemp1; -static char* dottemp2; -static char tinydns_datafile[256]; -static char tinydns_tempfile[256]; - -#endif +#define OUTPUT_DATA 1 +#define OUTPUT_DB 2 static char tinydns_textfile[256]; +static char tinydns_texttemp[256]; static LDAP* ldap_con; -static FILE* bindfile; +static FILE* namedmaster; +static FILE* namedzone; static FILE* tinyfile; static FILE* ldifout; static time_t time_now; -static int autoreverse; static char* const* main_argv; static int main_argc; @@ -75,14 +48,15 @@ static struct { char domainname[64]; char zonemaster[64]; + char class[16]; char adminmailbox[64]; unsigned long serial; unsigned long refresh; unsigned long retry; unsigned long expire; unsigned long minimum; + char timestamp[20]; int ttl; - char timestamp[16]; } zone; struct resourcerecord @@ -92,9 +66,10 @@ struct resourcerecord char class[16]; char type[16]; char ipaddr[256][32]; + char cipaddr[32]; char cname[64]; + char timestamp[20]; int ttl; - char timestamp[16]; int preference; #if defined DRAFT_RFC char rr[1024]; @@ -116,6 +91,7 @@ static struct unsigned int output; int verbose; char ldifname[128]; + char exec_command[128]; } options; @@ -129,96 +105,46 @@ static void die_exit(const char* message) } -#if defined WITH_TINYDNS - -static void rr_add(char *buf, unsigned int len) -{ - if (!stralloc_catb(&result, buf, len)) die_exit(0); -} - -static void rr_addname(char *d) -{ - rr_add(d,dns_domain_length(d)); -} - -static void rr_start(char type[2], unsigned long ttl, char ttd[8]) -{ - char buf[4]; - if (!stralloc_copyb(&result, type,2)) die_exit(0); - rr_add("=",1); - uint32_pack_big(buf, ttl); - rr_add(buf,4); - rr_add(ttd,8); -} - -static void rr_finish(char *owner) -{ - if (byte_equal(owner,2,"\1*")) { - owner += 2; - result.s[2] = '*'; - } - if (!stralloc_copyb(&key, owner, dns_domain_length(owner))) die_exit(0); - case_lowerb(key.s, key.len); - if (cdb_make_add(&cdb, key.s, key.len, result.s, result.len) == -1) - die_exit("Unable to create 'data.tmp'"); -} - - -#endif - static void set_datadir(void) { char* ev = getenv("TINYDNSDIR"); int len; -#if defined WITH_TINYDNS - tinydns_datafile[0] = 0; - tinydns_tempfile[0] = 0; -#endif - tinydns_textfile[0] = 0; + tinydns_textfile[0] = '\0'; + tinydns_texttemp[0] = '\0'; if (ev && (len = strlen(ev))<240) { -#if defined WITH_TINYDNS - strncpy(tinydns_datafile, ev, 240); - strncpy(tinydns_tempfile, ev, 240); -#endif strncpy(tinydns_textfile, ev, 240); + strncpy(tinydns_texttemp, ev, 240); if (ev[len-1]!='/') { -#if defined WITH_TINYDNS - tinydns_datafile[len] = '/'; - tinydns_tempfile[len] = '/'; -#endif tinydns_textfile[len] = '/'; + tinydns_texttemp[len] = '/'; } } -#if defined WITH_TINYDNS - strcat(tinydns_datafile, "data.cdb"); - strcat(tinydns_tempfile, "data.tmp"); -#endif strcat(tinydns_textfile, "data"); + strcat(tinydns_texttemp, "data.temp"); } static void print_usage(void) { print_version(); - printf("usage: ldap2dns[d] [-D binddn] [-b searchbase] [-o 0|1|2|4] [-h host] [-p port] [-w password] [-L[filename]] [-u numsecs] [-v[v]] [-V]\n\n"); + printf("usage: ldap2dns[d] [-D binddn] [-b searchbase] [-o data|db] [-h host] [-p port] [-w password] [-L[filename]] [-u numsecs] [-v[v]] [-V]\n\n"); printf("ldap2dns connects to an LDAP server reads the DNS information stored in objectclasses\n" "\t\tDNSzone and DNSrrset and writes a file to be used by tinydns or named.\n" "\t\tldap2dnsd starts as background-job and continouesly updates DNS information.\n"); printf("options:\n"); printf(" -D binddn\tUse the distinguished name binddn to bind to the LDAP directory\n"); printf(" -w bindpasswd\tUse bindpasswd as the password for simple authentication\n"); - printf(" -b use searchbase as the starting point for the search instead of the default\n"); - printf(" -o 1|2|4\toutput format number or any binary or-ed combination. Defaults to 1\n"); - printf("\t1: generate a binary file named 'data.cdb' to be used directly by tinydns\n"); - printf("\t2: generate a text file named 'data' to be parsed by tinydns-data\n"); - printf("\t4: for each zone generate a file named '.db' to be used by named\n"); - printf(" -L[filename] print output in LDIF format for reimport\n"); - printf(" -h host\thostname of LDAP server, defaults to localhost\n"); - printf(" -p port\tportnumber to connect to LDAP server, defaults to %d\n", LDAP_PORT); + printf(" -b Use searchbase as the starting point for the search instead of the default\n"); + printf(" -o data\tGenerate a \"data\" file to be processed by tinydns-data\n"); + printf(" -o db\tFor each zone generate a \".db\" file to be used by named\n"); + printf(" -L[filename] Print output in LDIF format for reimport\n"); + printf(" -h host\tHostname of LDAP server, defaults to localhost\n"); + printf(" -p port\tPortnumber to connect to LDAP server, defaults to %d\n", LDAP_PORT); printf(" -u numsecs\tUpdate DNS data after numsecs. Defaults to %d if started as daemon.\n\t\t" "Important notice: data.cdb is rewritten only after DNSserial in DNSzone is increased.\n", UPDATE_INTERVALL); + printf(" -e \"exec-cmd\" This command is executed after ldap2dns regenerated its data files\n"); printf(" -v\t\trun in verbose mode\n"); printf(" -vv\t\teven more verbose\n"); printf(" -V\t\tprint version and exit\n\n"); @@ -229,8 +155,9 @@ static int parse_options() extern char* optarg; extern int optind, opterr, optopt; char buf[256], value[128]; - int c; + int len; FILE* ldap_conf; + char* ev; strcpy(options.searchbase, ""); strcpy(options.hostname, "localhost"); @@ -239,33 +166,46 @@ static int parse_options() while(fgets(buf, 256, ldap_conf)!=0) { if (sscanf(buf, "BASE %128s", value)==1) strcpy(options.searchbase, value); - if (sscanf(buf, "HOST %128s:%d", value, &c)==2) { + if (sscanf(buf, "HOST %128s:%d", value, &len)==2) { strcpy(options.hostname, value); - options.port = c; + options.port = len; } else if (sscanf(buf, "HOST %128s", value)==1) strcpy(options.hostname, value); - if (sscanf(buf, "PORT %d", &c)==1) - options.port = c; + if (sscanf(buf, "PORT %d", &len)==1) + options.port = len; } fclose(ldap_conf); } strcpy(options.binddn, ""); - options.output = 1; + len = strlen(main_argv[0]); + if (strcmp(main_argv[0]+len-9, "ldap2dnsd")==0) { + options.is_daemon = 1; + options.update_iv = UPDATE_INTERVALL; + } else { + options.is_daemon = 0; + options.update_iv = 0; + } + ev = getenv("LDAP2DNS_UPDATE"); + if (ev && sscanf(ev, "%d", &len)==1 && len>0) + options.update_iv = len; + options.output = 0; + ev = getenv("LDAP2DNS_OUTPUT"); + if (ev) { + if (strcmp(ev, "data")==0) + options.output = OUTPUT_DATA; + else if (strcmp(ev, "db")==0) + options.output = OUTPUT_DB; + } options.verbose = 0; options.ldifname[0] = '\0'; - c = strlen(main_argv[0]); - if (strcmp(main_argv[0]+c-9, "ldap2dnsd")==0) - options.is_daemon = 1; - else - options.is_daemon = 0; - options.update_iv = 59; strcpy(options.password, ""); - while ( (c = getopt(main_argc, main_argv, "b:D:h:o:p:u:Vw:v::L::"))>0 ) { + strcpy(options.exec_command, ""); + while ( (len = getopt(main_argc, main_argv, "b:D:e:h:o:p:u:Vw:v::L::"))>0 ) { if (optarg && strlen(optarg)>127) { fprintf(stderr, "argument %s too long\n", optarg); continue; } - switch (c) { + switch (len) { case 'b': strcpy(options.searchbase, optarg); break; @@ -273,7 +213,6 @@ static int parse_options() if (sscanf(optarg, "%d", &options.update_iv)!=1) options.update_iv = UPDATE_INTERVALL; if (options.update_iv<=0) options.update_iv = 1; - if (options.is_daemon==0) options.is_daemon = 2; /* foreground daemon */ break; case 'D': strcpy(options.binddn, optarg); @@ -288,8 +227,10 @@ static int parse_options() strcpy(options.ldifname, optarg); break; case 'o': - if (sscanf(optarg, "%d", &options.output)!=1) - options.output = 0; + if (strcmp(optarg, "data")==0) + options.output |= OUTPUT_DATA; + else if (strcmp(optarg, "db")==0) + options.output |= OUTPUT_DB; break; case 'p': if (sscanf(optarg, "%d", &options.port)!=1) @@ -307,11 +248,16 @@ static int parse_options() case 'w': strcpy(options.password, optarg); break; + case 'e': + strcpy(options.exec_command, optarg); + break; default: print_usage(); exit(1); } } + if (options.is_daemon==0 && options.update_iv>0) + options.is_daemon = 2; /* foreground daemon */ } @@ -341,86 +287,74 @@ static int expand_reverse(char target[64], const char* source) } -static void write_rr(struct resourcerecord* rr, int ipdx) +static void write_rr(struct resourcerecord* rr, int ipdx, int znix) { char ip[4]; char buf[4]; if (strcasecmp(rr->class, "IN")) return; - -#if defined WITH_TINYDNS - if (options.output&1) { - int dnsdn_len = strlen(rr->dnsdomainname); - int cname_len = strlen(rr->cname); - if (!dns_domain_fromdot(&dottemp1, rr->dnsdomainname, dnsdn_len)) die_exit(0); - if (!dns_domain_fromdot(&dottemp2, rr->cname, cname_len)) die_exit(0); - } -#endif - if (strcasecmp(rr->type, "NS")==0) { - if (tinyfile) - fprintf(tinyfile, "&%s:%s:%s:%d:%s\n", rr->dnsdomainname, (ipdx>=0 ? rr->ipaddr[ipdx] : ""), rr->cname, rr->ttl, rr->timestamp); - if (bindfile) { - fprintf(bindfile, "%s.\tIN NS\t%s.\n", rr->dnsdomainname, rr->cname); - if (ipdx>=0) - fprintf(bindfile, "%s.\tIN A\t%s\n", rr->cname, rr->ipaddr[ipdx]); - } -#if defined WITH_TINYDNS - if (options.output&1) { - rr_start(DNS_T_NS, rr->ttl, rr->timestamp); - rr_addname(dottemp2); - rr_finish(dottemp1); - if (ipdx>=0 && ip4_scan(rr->ipaddr[ipdx], ip)) { - rr_start(DNS_T_A, rr->ttl, rr->timestamp); - rr_add(ip, 4); - rr_finish(dottemp2); + if (tinyfile) { + if (znix==0) { + if (ipdx<=0 && rr->cipaddr[0]) { + fprintf(tinyfile, "&%s::%s:%d:%s\n", rr->dnsdomainname, rr->cname, rr->ttl, rr->timestamp); + if (rr->cname[0]) + fprintf(tinyfile, "=%s:%s:%d:%s\n", rr->cname, rr->cipaddr, rr->ttl, rr->timestamp); + if (ipdx==0) + fprintf(tinyfile, "+%s:%s:%d:%s\n", rr->cname, rr->ipaddr[0], rr->ttl, rr->timestamp); + } else if (ipdx<0) + fprintf(tinyfile, "&%s::%s:%d:%s\n", rr->dnsdomainname, rr->cname, rr->ttl, rr->timestamp); + else if (ipdx==0) + fprintf(tinyfile, "&%s:%s:%s:%d:%s\n", rr->dnsdomainname, rr->ipaddr[0], rr->cname, rr->ttl, rr->timestamp); + else if (ipdx>0 && rr->cname[0]) + fprintf(tinyfile, "+%s:%s:%d:%s\n", rr->cname, rr->ipaddr[ipdx], rr->ttl, rr->timestamp); + } else if (ipdx<=0) { + fprintf(tinyfile, "&%s::%s:%d:%s\n", rr->dnsdomainname, rr->cname, rr->ttl, rr->timestamp); } } -#endif + if (namedzone) { + fprintf(namedzone, "%s.\tIN NS\t%s.\n", rr->dnsdomainname, rr->cname); + if (ipdx>=0) + fprintf(namedzone, "%s.\tIN A\t%s\n", rr->cname, rr->ipaddr[ipdx]); + } } else if (strcasecmp(rr->type, "MX")==0) { - if (tinyfile) - fprintf(tinyfile, "@%s:%s:%s:%d:%d:%s\n", rr->dnsdomainname, (ipdx>=0 ? rr->ipaddr[ipdx] : ""), rr->cname, rr->preference, rr->ttl, rr->timestamp); - if (bindfile) { - fprintf(bindfile, "%s.\tIN MX\t%d %s.\n", rr->dnsdomainname, rr->preference, rr->cname); + if (tinyfile) { + if (znix==0) { + if (ipdx<=0 && rr->cipaddr[0]) { + fprintf(tinyfile, "@%s::%s:%d:%d:%s\n", rr->dnsdomainname, rr->cname, rr->preference, rr->ttl, rr->timestamp); + if (rr->cname[0]) + fprintf(tinyfile, "=%s:%s:%d:%s\n", rr->cname, rr->cipaddr, rr->ttl, rr->timestamp); + if (ipdx==0) + fprintf(tinyfile, "+%s:%s:%d:%s\n", rr->cname, rr->ipaddr[0], rr->ttl, rr->timestamp); + } else if (ipdx<0) + fprintf(tinyfile, "@%s::%s:%d:%d:%s\n", rr->dnsdomainname, rr->cname, rr->preference, rr->ttl, rr->timestamp); + else if (ipdx==0) + fprintf(tinyfile, "@%s:%s:%s:%d:%d:%s\n", rr->dnsdomainname, rr->ipaddr[0], rr->cname, rr->preference, rr->ttl, rr->timestamp); + else if (ipdx>0 && rr->cname[0]) + fprintf(tinyfile, "+%s:%s:%d:%s\n", rr->cname, rr->ipaddr[ipdx], rr->ttl, rr->timestamp); + } else if (ipdx<=0) { + fprintf(tinyfile, "@%s::%s:%d:%d:%s\n", rr->dnsdomainname, rr->cname, rr->preference, rr->ttl, rr->timestamp); + } + } + if (namedzone) { + fprintf(namedzone, "%s.\tIN MX\t%d %s.\n", rr->dnsdomainname, rr->preference, rr->cname); if (ipdx>=0) - fprintf(bindfile, "%s.\tIN A\t%s\n", rr->cname, rr->ipaddr[ipdx]); + fprintf(namedzone, "%s.\tIN A\t%s\n", rr->cname, rr->ipaddr[ipdx]); } -#if defined WITH_TINYDNS - if (options.output&1) { - rr_start(DNS_T_MX, rr->ttl, rr->timestamp); - uint16_pack_big(buf, rr->preference); - rr_add(buf, 2); - rr_addname(dottemp2); - rr_finish(dottemp1); - if (ipdx>=0 && ip4_scan(rr->ipaddr[ipdx], ip)) { - rr_start(DNS_T_A, rr->ttl, rr->timestamp); - rr_add(ip, 4); - rr_finish(dottemp2); - } - } -#endif } else if ( strcasecmp(rr->type, "A")==0) { - if (tinyfile) - fprintf(tinyfile, "%s%s:%s:%d:%s\n", (autoreverse ? "=" : "+"), rr->dnsdomainname, (ipdx>=0 ? rr->ipaddr[ipdx] : ""), rr->ttl, rr->timestamp); - if (bindfile && ipdx>=0) - fprintf(bindfile, "%s.\tIN A\t%s\n", rr->dnsdomainname, rr->ipaddr[ipdx]); -#if defined WITH_TINYDNS - if (options.output&1) { - char dptr[DNS_NAME4_DOMAIN]; - if (ipdx>=0 && ip4_scan(rr->ipaddr[ipdx], ip)) { - rr_start(DNS_T_A, rr->ttl, rr->timestamp); - rr_add(ip, 4); - rr_finish(dottemp1); - } - if (autoreverse) { - dns_name4_domain(dptr, ip); - rr_start(DNS_T_PTR, rr->ttl, rr->timestamp); - rr_addname(dottemp1); - rr_finish(dptr); - } + if (tinyfile) { + if (ipdx<=0 && rr->cipaddr[0]) + fprintf(tinyfile, "%s%s:%s:%d:%s\n", (znix==0 ? "=" : "+"), rr->dnsdomainname, rr->cipaddr, rr->ttl, rr->timestamp); + if (ipdx>=0) + fprintf(tinyfile, "+%s:%s:%d:%s\n", rr->dnsdomainname, rr->ipaddr[ipdx], rr->ttl, rr->timestamp); + } + if (namedzone) { + if (ipdx<=0 && rr->cipaddr[0]) + fprintf(namedzone, "%s.\tIN A\t%s\n", rr->dnsdomainname, rr->cipaddr); + if (ipdx>=0) + fprintf(namedzone, "%s.\tIN A\t%s\n", rr->dnsdomainname, rr->ipaddr[ipdx]); } -#endif } else if (strcasecmp(rr->type, "PTR")==0) { int ip[4] = {0, 0, 0, 0}; char buf[64]; @@ -436,41 +370,18 @@ static void write_rr(struct resourcerecord* rr, int ipdx) } if (tinyfile) fprintf(tinyfile, "^%s:%s:%d:%s\n", buf, rr->cname, rr->ttl, rr->timestamp); - if (bindfile) - fprintf(bindfile, "%s.\tIN PTR\t%s.\n", buf, rr->cname); -#if defined WITH_TINYDNS - if (options.output&1) { - int dnsdn_len = strlen(buf); - if (!dns_domain_fromdot(&dottemp1, buf, dnsdn_len)) die_exit(0); - rr_start(DNS_T_PTR, rr->ttl, rr->timestamp); - rr_addname(dottemp2); - rr_finish(dottemp1); - } -#endif + if (namedzone) + fprintf(namedzone, "%s.\tIN PTR\t%s.\n", buf, rr->cname); } else if (strcasecmp(rr->type, "CNAME")==0) { if (tinyfile) fprintf(tinyfile, "C%s:%s:%d:%s\n", rr->dnsdomainname, rr->cname, rr->ttl, rr->timestamp); - if (bindfile) - fprintf(bindfile, "%s.\tIN CNAME\t%s.\n", rr->dnsdomainname, rr->cname); -#if defined WITH_TINYDNS - if (options.output&1) { - rr_start(DNS_T_CNAME, rr->ttl, rr->timestamp); - rr_addname(dottemp2); - rr_finish(dottemp1); - } -#endif + if (namedzone) + fprintf(namedzone, "%s.\tIN CNAME\t%s.\n", rr->dnsdomainname, rr->cname); } else if (strcasecmp(rr->type, "TXT")==0) { if (tinyfile) fprintf(tinyfile, "'%s:%s:%d:%s\n", rr->dnsdomainname, rr->cname, rr->ttl, rr->timestamp); - if (bindfile) - fprintf(bindfile, "%s.\tIN TXT\t%s.\n", rr->dnsdomainname, rr->cname); -#if defined WITH_TINYDNS - if (options.output&1) { - rr_start(DNS_T_TXT, rr->ttl, rr->timestamp); - rr_addname(dottemp2); - rr_finish(dottemp1); - } -#endif + if (namedzone) + fprintf(namedzone, "%s.\tIN TXT\t%s.\n", rr->dnsdomainname, rr->cname); } } @@ -516,7 +427,7 @@ static void parse_rr(struct resourcerecord* rr) #endif -static void read_resourcerecords(char* dn) +static void read_resourcerecords(char* dn, int znix) { LDAPMessage* res = NULL; LDAPMessage* m; @@ -535,9 +446,10 @@ static void read_resourcerecords(char* dn) fprintf(ldifout, "dn: %s\n", dn); rr.cn[0] = '\0'; strncpy(rr.dnsdomainname, zone.domainname, 64); - strcpy(rr.class, "IN"); + strncpy(rr.class, "IN", 3); rr.type[0] = '\0'; rr.cname[0] = '\0'; + rr.cipaddr[0] = '\0'; rr.ttl = time_now; rr.timestamp[0] = '\0'; rr.preference = 10; @@ -583,6 +495,12 @@ static void read_resourcerecords(char* dn) if (options.ldifname[0]) fprintf(ldifout, "%s: %s\n", attr, rr.ipaddr[ipaddresses]); } + } else if (strcasecmp(attr, "DNScipaddr")==0) { + int ip[4]; + if (sscanf(bvals[0]->bv_val, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3])==4) + sprintf(rr.cipaddr, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + if (options.ldifname[0]) + fprintf(ldifout, "%s: %s\n", attr, rr.cipaddr); } else if (strcasecmp(attr, "DNScname")==0) { if (!expand_domainname(rr.cname, bvals[0]->bv_val, bvals[0]->bv_len)) rr.cname[0] = '\0'; @@ -628,7 +546,7 @@ static void read_resourcerecords(char* dn) #endif do { ipaddresses--; - write_rr(&rr, ipaddresses); + write_rr(&rr, ipaddresses, znix); } while (ipaddresses>0); #if defined DRAFT_RFC if (rr.aliasedobjectname[0]) @@ -654,34 +572,20 @@ static void write_zone(void) zone.zonemaster, zone.adminmailbox, zone.serial, zone.refresh, zone.retry, zone.expire, zone.minimum, zone.ttl, zone.timestamp); } - - if (bindfile) { - fprintf(bindfile, "; Automatically generated by ldap2dns - DO NOT EDIT!\n"); - fprintf(bindfile, "%s. IN SOA %s. %s. ", zone.domainname, zone.zonemaster, zone.adminmailbox); - fprintf(bindfile, "(\n\t%d\t; Serial\n\t%d\t; Refresh\n\t%d\t; Retry\n\t%d\t; Expire\n\t%d )\t; Minimum\n", zone.serial, zone.refresh, zone.retry, zone.expire, zone.minimum); + if (namedmaster) { + fprintf(namedmaster, "zone \"%s\" %s {\n\ttype master;\n\tfile \"%s.db\";\n};\n", + zone.domainname, zone.class, zone.domainname); } - -#if defined WITH_TINYDNS - if (options.output&1) { - byte_zero(zone.timestamp, 8); - len = strlen(zone.domainname); - if (!dns_domain_fromdot(&dottemp1, zone.domainname, len)) die_exit(0); - uint32_pack_big(soa, zone.serial); - uint32_pack_big(soa+4, zone.refresh); - uint32_pack_big(soa+8, zone.retry); - uint32_pack_big(soa+12, zone.expire); - uint32_pack_big(soa+16, zone.minimum); - rr_start(DNS_T_SOA, zone.ttl, zone.timestamp); + if (namedzone) { + fprintf(namedzone, "# Automatically generated by ldap2dns - DO NOT EDIT!\n"); + fprintf(namedzone, "$TTL %d\n", (zone.ttl>0) ? zone.ttl : 3600); + fprintf(namedzone, "%s. IN SOA ", zone.domainname); len = strlen(zone.zonemaster); - if (!dns_domain_fromdot(&dottemp2, zone.zonemaster, len)) die_exit(0); - rr_addname(dottemp2); + fprintf(namedzone, (zone.zonemaster[len-1]=='.') ? "%s " : "%s. ", zone.zonemaster); len = strlen(zone.adminmailbox); - if (!dns_domain_fromdot(&dottemp2, zone.adminmailbox, len)) die_exit(0); - rr_addname(dottemp2); - rr_add(soa, 20); - rr_finish(dottemp1); + fprintf(namedzone, (zone.adminmailbox[len-1]=='.') ? "%s " : "%s. ", zone.adminmailbox); + fprintf(namedzone, "(\n\t%d\t; Serial\n\t%d\t; Refresh\n\t%d\t; Retry\n\t%d\t; Expire\n\t%d )\t; Minimum\n", zone.serial, zone.refresh, zone.retry, zone.expire, zone.minimum); } -#endif if (options.ldifname[0]) fprintf(ldifout, "\n"); } @@ -725,6 +629,8 @@ static void read_dnszones(void) if (tinyfile) fprintf(tinyfile, "# Automatically generated by ldap2dns - DO NOT EDIT!\n"); + if (namedmaster) + fprintf(namedmaster, "# Automatically generated by ldap2dns - DO NOT EDIT!\n"); if ( (ldaperr = ldap_search_s(ldap_con, options.searchbase[0] ? options.searchbase : NULL, LDAP_SCOPE_SUBTREE, "objectclass=DNSzone", NULL, 0, &res))!=LDAP_SUCCESS ) die_ldap(ldaperr); for (m = ldap_first_entry(ldap_con, res); m; m = ldap_next_entry(ldap_con, m)) { @@ -735,6 +641,7 @@ static void read_dnszones(void) char zdn[256][64]; char ldif0; + strncpy(zone.class, "IN", 3); zone.serial = time_now; zone.refresh = 10800; zone.retry = 3600; @@ -812,16 +719,16 @@ static void read_dnszones(void) options.ldifname[0] = '\0'; if (options.verbose&1) printf("zonename: %s\n", zone.domainname); - if (options.output&4) { - char bindfilename[128]; - sprintf(bindfilename, "%s.db", zone.domainname); - if ( !(bindfile = fopen(bindfilename, "w")) ) + if (options.output&OUTPUT_DB) { + char namedzonename[128]; + sprintf(namedzonename, "%s.db", zone.domainname); + if ( !(namedzone = fopen(namedzonename, "w")) ) die_exit("Unable to open db-file for writing"); } write_zone(); - read_resourcerecords(dn); - if (bindfile) - fclose(bindfile); + read_resourcerecords(dn, i); + if (namedzone) + fclose(namedzone); if (options.verbose&2) printf("\n"); if (options.ldifname[0]) @@ -852,11 +759,12 @@ int main(int argc, char** argv) set_datadir(); for (;;) { int ldaperr; - if ( !(ldap_con = ldap_init(options.hostname, options.port)) ) - die_exit("Unable to initialize connection to LDAP server"); - ldaperr = ldap_simple_bind_s(ldap_con, options.binddn, options.password); + ldap_con = ldap_init(options.hostname, options.port); + ldaperr = ldap_con && ldap_simple_bind_s(ldap_con, options.binddn, options.password); if (ldaperr!=LDAP_SUCCESS) { fprintf(stderr, "Warning - Could not connect to LDAP server %s:%d as '%s'\n", options.hostname, options.port, options.binddn); + if (options.is_daemon==0) + break; sleep(options.update_iv); continue; } @@ -872,13 +780,6 @@ int main(int argc, char** argv) goto skip; } } -#if defined WITH_TINYDNS - if (options.output&1) { - fdcdb = open_trunc(tinydns_tempfile); - if (fdcdb == -1) die_exit("Unable to create 'data.tmp'"); - if (cdb_make_start(&cdb, fdcdb) == -1) die_exit("Unable to create 'data.tmp'"); - } -#endif if (options.ldifname[0]) { if (options.ldifname[0]=='-') ldifout = stdout; @@ -888,21 +789,24 @@ int main(int argc, char** argv) die_exit("Unable to open LDIF-file for writing"); } time(&time_now); - if ( options.output&2 && !(tinyfile = fopen(tinydns_textfile, "w")) ) - die_exit("Unable to open file 'data' for writing"); + if ( options.output&OUTPUT_DATA && !(tinyfile = fopen(tinydns_texttemp, "w")) ) + die_exit("Unable to open file 'data.temp' for writing"); + if ( options.output&OUTPUT_DB && !(namedmaster = fopen("named.zones", "w")) ) + die_exit("Unable to open file 'named.zones' for writing"); read_dnszones(); - if (tinyfile) + if (namedmaster) + fclose(namedmaster); + if (tinyfile) { fclose(tinyfile); + if (soa_numzones==0 || soa_checksum==0) + break; + if (rename(tinydns_texttemp, tinydns_textfile)==-1) + die_exit("Unable to move 'data.temp' to 'data'"); + } if (options.ldifname[0] && ldifout) fclose(ldifout); -#if defined WITH_TINYDNS - if (options.output&1) { - if (cdb_make_finish(&cdb)==-1 || fsync(fdcdb)==-1 || close(fdcdb)==-1) - die_exit("Unable to create 'data.tmp'"); - if (rename(tinydns_tempfile, tinydns_datafile)==-1) - die_exit("Unable to move 'data.tmp' to 'data.cdb'"); - } -#endif + if (options.exec_command[0]) + system(options.exec_command); skip: if ( (ldaperr = ldap_unbind_s(ldap_con))!=LDAP_SUCCESS ) die_ldap(ldaperr); diff --git a/ldap2tinydns-conf b/ldap2tinydns-conf index bdebb0c..8c1f042 100755 --- a/ldap2tinydns-conf +++ b/ldap2tinydns-conf @@ -1,9 +1,13 @@ #!/bin/sh mkdir ldap2tinydns mkdir ldap2tinydns/env -echo "#!/bin/sh" > ldap2tinydns/run -echo "exec 2>&1" >> ldap2tinydns/run -echo "exec envdir ./env softlimit -d250000 /usr/bin/ldap2dns -u 59" >> ldap2tinydns/run +cat << EOF_run > ldap2tinydns/run +#!/bin/sh +exec 2>&1 +exec envdir ./env softlimit -d250000 /usr/bin/ldap2dns -e "cd /var/tinydns/root && /usr/bin/tinydns-data" +EOF_run chmod 755 ldap2tinydns/run echo "/var/tinydns/root" > ldap2tinydns/env/TINYDNSDIR +echo "30" > ldap2tinydns/env/LDAP2DNS_UPDATE +echo "DATA" > ldap2tinydns/env/LDAP2DNS_OUTPUT diff --git a/zoneedit.pl b/zoneedit.pl new file mode 100644 index 0000000..88e66e3 --- /dev/null +++ b/zoneedit.pl @@ -0,0 +1,509 @@ +#!/usr/sbin/perl +use CGI qw(:standard); +use Net::LDAP; +use strict; +use vars qw($LDAPHOST $BASEDN $BINDBASE $BINDUID $ANONBINDDN $ZONEEDIT $DEFAULT_MAIN @our_nameserver @zoneinfo @setinfo); +my $LDAPHOST = "ldap0.server"; +my $BASEDN = "ou=dns,o=tiscover"; +my $BINDBASE = "ou=people,o=tiscover"; +my $BINDUID = "uid"; +my $ANONBINDDN = "ou=dns,o=tiscover"; +my $ZONEEDIT = "zoneedit.pl"; +my $DEFAULT_MAIN = "index.html"; +my $LOGFILE = "/opt/httpd/logs/zoneedit.log"; +my @our_nameserver = ( "ns1.tis.co.at", "ns2.tis.co.at" ); +my @zoneinfo = qw( DNSzonename DNSserial DNSclass DNStype DNSexpire DNSretry DNSminimum DNSzonemaster DNSrefresh DNSadminmailbox DNSttl ); +my @setinfo = qw( DNSdomainname DNStype DNSclass DNScname DNSipaddr DNSttl ); + + +################################################################################ + +eval { + main(); +}; +if ($@) { + errconfirm($@); +} + + +sub main +{ + my $request = Apache->request(); + my $query = new CGI; + my $call = $query->param('call'); + if (defined $call) { + my $ldap = Net::LDAP->new($LDAPHOST) or die "can't make new LDAP object: $@"; + my $user = $request->connection->user(); + my $binddn = $BINDUID."=".$user.",$BINDBASE"; + my ($ret, $password) = $request->get_basic_auth_pw(); + my $mesg = $ldap->bind($binddn, password => $password); + die "Unable to bind to LDAP server.
Reason: ".$mesg->error if ($mesg->code); + my $selet = $query->param('selet') if $query->param('selet'); + if ($call eq "dnslist") { + dns_list($query, $ldap, $selet); + } elsif ($call eq "newzone") { + new_zone($query, $selet); + } elsif ($call eq "addzone") { + my $zonedn = add_zone($query, $ldap); + log_action($user, "add_zone", $zonedn); + $query->delete_all(); + print $query->header, $query->start_html(-title=> 'Edit DNS Zone', + -target=> 'main', + -author=> 'jacob.rief@tiscover.com', + -BGCOLOR=> 'WHITE'), + "

"; + edit_zone($query, $ldap, $zonedn, $selet); + print $query->end_html; + } elsif ($call eq "editzone") { + my $zonedn = $query->param('zonedn'); + if (defined $query->param('modifyzone')) { + modify_zone($query, $ldap, $zonedn); + log_action($user, "modify_zone_soa", $zonedn); + } elsif (defined $query->param('addrrset')) { + add_rrset($query, $ldap, $zonedn); + log_action($user, "add_rrset", $zonedn); + } elsif (defined $query->param('modifyrrset')) { + my $setdn = $query->param('setdn'); + modify_rrset($query, $ldap, $zonedn, $setdn); + log_action($user, "modify_rrset", $setdn); + } elsif (defined $query->param('deleterrset')) { + my $setdn = $query->param('setdn'); + delete_rrset($query, $ldap, $zonedn, $setdn); + log_action($user, "delete_rrset", $setdn); + } + $query->delete_all(); + print $query->header, $query->start_html(-title=> 'Edit DNS Zone', + -target=> 'main', + -author=> 'jacob.rief@tiscover.com', + -BGCOLOR=> 'WHITE'), + "

"; + print_whois($ldap, $zonedn) if ($request->method eq "GET"); + edit_zone($query, $ldap, $zonedn, $selet); + print $query->end_html; + } elsif ($call eq "deletezone") { + my $zonedn = $query->param('zonedn'); + delete_zone($query, $ldap, $zonedn); + log_action($user, "delete_zone", $zonedn); + } + $ldap->unbind(); + } else { + # print frame + print $query->header, + "", + " ", + " ", + ""; + } +} + + +sub errconfirm +{ + my $errmsg = shift; + my $request = Apache->request(); + $request->note_basic_auth_failure(); + my $query = new CGI; + print $query->header, $query->start_html(-title=> 'DNS Zone Admin', + -target=> 'main', + -author=> 'jacob.rief@tiscover.com', + -BGCOLOR=> 'WHITE'), + "

", + $query->h2("An error occured"), + "Message: $errmsg
\n", + $query->end_html; + $request->child_terminate(); +} + + +sub log_action +{ + my ($user, $action, $dn) = @_; + my ($sec,$min,$hour,$mday,$mon,$year) = localtime(); + my @month = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); + my ($m, $y) = ($month[$mon], $year+1900); + open(FILE, ">>$LOGFILE"); + print FILE "[$mday/$m/$y:$hour:$min:$sec] $user $action $dn\n"; + close(FILE); +} + + +sub list_attrs +{ + my $attr = shift; + my (@list, $key, $value); + while (($key, $value) = each %$attr) { + push(@list, $key => $value); + } + return \@list; +} + + +sub dns_list +{ + my ($query, $ldap, $selet) = @_; + my @letters = qw( 0 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ); + print $query->header, $query->start_html(-title=> 'Zone-Selector', + -target=> 'menu', + -author=> 'jacob.rief@tiscover.com', + -BGCOLOR=> 'WHITE'); + my ($dnslookup, $resolver); + if ($selet =~ /\~/) { + $dnslookup = 1; + use Net::DNS; + $resolver = new Net::DNS::Resolver; + } else { + $dnslookup = 0; + } + print "\n", + "\n"; + foreach my $let (@letters) { + if ($selet =~ /$let/) { + my $newselet = $selet; + $newselet =~ s/$let//; + print "\n"; + } else { + my $newselet = $selet.$let; + print "\n"; + next; + } + my $mesg = $ldap->search(base => $BASEDN, filter => "(&(objectclass=DNSzone)(DNSzonename=$let*))"); + my @entries = $mesg->entries; + my ($zonename, %dn_entry, @unsorted); + foreach my $entry (@entries) { + $zonename = $entry->get_value('DNSzonename'); + push @unsorted, $zonename; + $dn_entry{$zonename} = $entry->dn(); + } + @entries = sort @unsorted; + foreach $zonename (@entries) { + my $zonedn = $dn_entry{$zonename}; + if ($dnslookup) { + my $query = $resolver->search($zonename, "NS"); + my @ns; + if ($query) { + foreach my $rr ($query->answer) { + next unless $rr->type eq "NS"; + push @ns, $rr->nsdname; + } + } + if (lc($ns[0]) eq lc($our_nameserver[0]) || lc($ns[1]) eq lc($our_nameserver[1]) + || lc($ns[0]) eq lc($our_nameserver[1]) || lc($ns[1]) eq lc($our_nameserver[0]) ) { + print "\n"; + } + } + print "\n"; + } else { + print "&selet=$selet~\" TARGET=\"menu\">With DNS-lookup\n"; + } + print "
Add New Zone
- $let
+ $let
"; + } elsif (defined $ns[0] || defined $ns[1]) { + print "
"; + } else { + print "
"; + } + } else { + print "
"; + } + print "$zonename
Without DNS-lookup
\n", $query->end_html; +} + + +sub print_zone_soa +{ + my $zonedata = shift; + print "Serial: ", + textfield(-name=>'DNSserial', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSserial'}), + "", "Refresh: ", + textfield(-name=>'DNSrefresh', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSrefresh'}), + "\n", + + "Retry: ", + textfield(-name=>'DNSretry', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSretry'}), + "", "Expire: ", + textfield(-name=>'DNSexpire', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSexpire'}), + "\n", + + "Minimum: ", + textfield(-name=>'DNSminimum', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSminimum'}), + "", "Adminmailbox: ", + textfield(-name=>'DNSadminmailbox', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSadminmailbox'}), + "\n", + + "Zonemaster: ", + textfield(-name=>'DNSzonemaster', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSzonemaster'}), + "", "Time to live: ", + textfield(-name=>'DNSttl', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSttl'}), + "\n"; +} + + +sub new_zone +{ + my ($query, $selet) = @_; + my %default_zonedata = ( + "DNSzonename" => "", + "DNSserial" => "", + "DNSclass" => "IN", + "DNStype" => "SOA", + "DNSexpire" => "259200", + "DNSretry" => "3600", + "DNSminimum" => "86400", + "DNSzonemaster" => "ns1.tis.co.at.", + "DNSrefresh" => "10800", + "DNSadminmailbox" => "domreg.tis.co.at.", + "DNSttl" => "3600", + ); + my ($sec,$min,$hour,$mday,$mon,$year) = localtime(); + $default_zonedata{"DNSserial"} = sprintf "%04d%02d%02d01", $year+1900, $mon+1, $mday; + my $onsubmit = "{ parent.frames.menu.location='$ZONEEDIT?call=dnslist&selet=$selet'; }"; + $query->param(call=>'addzone'); + print $query->header, $query->start_html(-title=> 'Add DNS Zone', + -target=> 'main', + -author=> 'jacob.rief@tiscover.com', + -BGCOLOR=> 'WHITE'), + "

", + $query->h2('Add DNS zone'), + $query->start_multipart_form(-method=>'POST', -action=>"$ZONEEDIT", -target=>'main', -onSubmit=>$onsubmit), + $query->hidden('call'), $query->hidden('selet'), + "\n", + "\n"; + print_zone_soa(\%default_zonedata); + print "
New Zonename: ", + $query->textfield(-name=>'DNSzonename', -size=>40, -maxlength=>64), + "
", + $query->submit(-name=>" Submit "), + "", + $query->reset(), + "
\n", + $query->end_form(), + $query->end_html; +} + + +sub add_zone +{ + my ($query, $ldap) = @_; + my %zonedata; + foreach my $za (@zoneinfo) { + $zonedata{$za} = $query->param($za) if defined $query->param($za); + } + my ($zonename, $zonedn) = ($zonedata{'DNSzonename'}, "cn=$zonedata{'DNSzonename'},$BASEDN"); + my $attrs = list_attrs(\%zonedata); + push(@$attrs, "objectclass", "DNSzone", "cn", "$zonename"); + my $mesg = $ldap->add(dn=>$zonedn, attr=>$attrs); + die "Failed to add zone: $zonename
Reason: ".$mesg->error if ($mesg->code); + my @attr = ( "cn", "NS1:", "objectclass", "DNSrrset", "dnstype", "NS", "dnsclass", "IN", + "dnsttl", "3600", "dnscname", $our_nameserver[0]."." ); + my $dnch = "cn=NS1:,$zonedn"; + die "Failed to add $dnch " if (($mesg = $ldap->add(dn=>$dnch, attr=>\@attr))->code); + + @attr = ( "cn", "NS2:", "objectclass", "DNSrrset", "dnstype", "NS", "dnsclass", "IN", + "dnsttl", "3600", "dnscname", $our_nameserver[1]."." ); + $dnch = "cn=NS2:,$zonedn"; + die "Failed to add $dnch " if (($mesg = $ldap->add(dn=>$dnch, attr=>\@attr))->code); + + @attr = ( "cn", "A:www", "objectclass", "DNSrrset", "dnstype", "A", "dnsclass", "IN", + "dnsdomainname", "www", "dnsttl", "3600", "dnsipaddr", "195.96.23.204" ); + $dnch = "cn=A:www,$zonedn"; + die "Failed to add $dnch
Reason: ".$mesg->error if (($mesg = $ldap->add(dn=>$dnch, attr=>\@attr))->code); + return $zonedn; +} + + +sub modify_zone +{ + my ($query, $ldap, $zonedn) = @_; + my %zonedata; + foreach my $za (@zoneinfo) { + $zonedata{$za} = $query->param($za) if defined $query->param($za); + } + my @zonename; + my $zn = ($ldap->search(base=>$zonedn, scope=>'base', filter=>"(objectclass=DNSzone)")->entry(0))->get_value('DNSzonename'); + push @zonename, $zn; + for (my $zc = 0; defined $query->param("DNSzonename$zc"); $zc++) { + $zn = $query->param("DNSzonename$zc"); + push @zonename, $zn if (length($zn)>3); + } + my $mesg = $ldap->modify($zonedn, delete => [ 'DNSzonename' ]); + $mesg = $ldap->modify($zonedn, replace => \%zonedata) unless ($mesg->code); + $mesg = $ldap->modify($zonedn, add => [ 'DNSzonename' => \@zonename ] ) unless ($mesg->code); + die "Unable to modify zone: $zonedn
Reason: ".$mesg->error if ($mesg->code); +} + + +sub delete_zone +{ + my ($query, $ldap, $zonedn) = @_; + my $zonedn = $query->param('zonedn'); + my $mesg = $ldap->search(base=>$zonedn, filter => "(objectclass=DNSrrset)"); + my @entries = $mesg->entries; + foreach my $entry (@entries) { + $mesg = $ldap->delete($entry->dn()); + last if ($mesg->code); + } + $mesg = $ldap->delete($zonedn) unless ($mesg->code); + die "Unable to delete zone $zonedn.
Reason: ".$mesg->error if ($mesg->code); + dnslist($query, $ldap); +} + + +sub edit_zone +{ + my ($query, $ldap, $zonedn, $selet) = @_; + my @zonename = ($ldap->search(base=>$zonedn, scope=>'base', filter=>"(objectclass=DNSzone)")->entry(0))->get_value('DNSzonename'); + my $zonemaster = shift @zonename; + $query->param(call=>'editzone'); + $query->param(zonedn=>$zonedn); + $query->param(selet=>$selet); + + # Table for SOA + print $query->h2("Edit DNS zone $zonemaster"); + print $query->start_multipart_form(-method=>'POST', -action=>"$ZONEEDIT", -target=>'main'), + $query->hidden('call'), $query->hidden('zonedn'), $query->hidden('selet'), + "\n"; + my $zc = 0; + my $entry = $ldap->search(base=>$zonedn, scope=>'base', filter=>"(objectclass=DNSzone)")->entry(0); + my %zonedata; + foreach my $za (@zoneinfo) { + $zonedata{$za} = $entry->get_value($za); + } + print_zone_soa(\%zonedata); + print "\n"; + foreach my $zn (@zonename) { + print "\n"; + $zc++; + } + print "
Additional Zonename: ", + $query->textfield(-name=>"DNSzonename$zc", -default=>$zn, -size=>40, -maxlength=>64), + "
Add additional Zonename: ", + $query->textfield(-name=>"DNSzonename$zc", -size=>40, -maxlength=>64), + "
\n"; + print "\n", $query->end_form(), + "
", + $query->submit(-name=>"modifyzone", -value=>" Modify Zone "), + ""; + my $onclick = "if(confirm('Do you really want to remove zone \"$zonemaster\" and all its resource records?'))" + ."{ parent.frames.menu.location='$ZONEEDIT?call=deletezone&zonedn=$zonedn&selet=$selet'; parent.frames.main.location='$DEFAULT_MAIN'; }"; + print $query->submit(-name=>"deletezone", -value=>" Delete Zone ", -onClick=>$onclick), + "", $query->start_multipart_form(-method=>'POST', -action=>"$ZONEEDIT", -target=>'main'), + $query->hidden('call'), $query->hidden('zonedn'), $query->hidden('selet'), + $query->submit(-name=>"resetform", -value=>" Reset Form "), + $query->end_form(), "
\n"; + + # Tables for RRsets + my $mesg = $ldap->search(base=>$zonedn, filter => "(objectclass=DNSrrset)"); + my @entries = $mesg->entries; + print "\n\n", + "\n"; + foreach $entry (@entries) { + my $setdn = $entry->dn(); + my $domainname = $entry->get_value('DNSdomainname'); + $domainname = "." if (!defined $domainname || length($domainname)<1); + my $ipaddr = $entry->get_value('DNSipaddr'); + my $cname = $entry->get_value('DNScname'); + my $type = $entry->get_value('DNStype'); + my $ttl = $entry->get_value('DNSttl'); + $query->param(setdn => $setdn); + print "", $query->start_multipart_form(-method=>'POST', -action=>"$ZONEEDIT", -target=>'main'), $query->hidden('call'), + $query->hidden('selet'), $query->hidden('zonedn'), $query->hidden('setdn'), + "", + "", + "", + "", + "", + "", + $query->end_form(), "\n"; + } + print "\n", $query->start_multipart_form(-method=>'POST', -action=>"$ZONEEDIT", -target=>'main'), $query->hidden('call'), + $query->hidden('selet'), $query->hidden('zonedn'), + "", + "", + "", + "", + "", + "", + $query->end_form(); + print "
Name $#entriesTypeIPaddrCNAMETTL
$domainname$type", $query->textfield(-name=>'DNSipaddr', -default=>$ipaddr, -size=>16, -maxlength=>16), "", $query->textfield(-name=>'DNScname', -default=>$cname, -size=>16, -maxlength=>64), "", $query->textfield(-name=>'DNSttl', -default=>$ttl, -size=>6, -maxlength=>6), "", $query->submit(-name=>"modifyrrset", -value=>" Modify "), + $query->submit(-name=>"deleterrset", -value=>" Delete "), "
", textfield(-name=>'DNSdomainname', -size=>8, -maxlength=>32), "", $query->popup_menu(-name=>'DNStype', -values=>['CNAME','A','MX','NS','PTR','TXT'], -default=>"A"), "", textfield(-name=>'DNSipaddr', -size=>16, -maxlength=>16), "", textfield(-name=>'DNScname', -size=>16, -maxlength=>64), "", textfield(-name=>'DNSttl', -default=>"3600", -size=>6, -maxlength=>6), "", $query->submit(-name=>"addrrset", -value=>" Add "), "
\n"; +} + + +sub add_rrset +{ + my ($query, $ldap, $zonedn) = @_; + my ($domainname, $type, @setattrs) = ($query->param('DNSdomainname'), $query->param('DNStype')); + foreach my $za (@setinfo) { + next unless (defined $query->param($za)); + push (@setattrs, $za, $query->param($za)); + } + my $chdn = "$type:$domainname"; + push (@setattrs, "objectclass", "DNSrrset", "cn", "$chdn"); + $chdn = "cn=$chdn,$zonedn"; + my $mesg = $ldap->add($chdn, attr => \@setattrs); + die "Unable to add rrset: $chdn ".$mesg->error if ($mesg->code); + my $newserial = $ldap->search(base=>$zonedn, scope=>'base', filter => "(objectclass=DNSzone)")->entry(0)->get_value('DNSserial')+1; + $mesg = $ldap->modify($zonedn, replace => { 'DNSserial', $newserial }); + die "Unable to modify serial number for: $zonedn ".$mesg->error if ($mesg->code); +} + + +sub modify_rrset +{ + my ($query, $ldap, $zonedn, $setdn) = @_; + my %setattrs; + foreach my $za (@setinfo) { + next unless (defined $query->param($za)); + $setattrs{$za} = $query->param($za); + } + my $mesg = $ldap->modify($setdn, replace => \%setattrs); + die "Unable to modify rrset: $setdn".$mesg->error if ($mesg->code); + my $newserial = $ldap->search(base=>$zonedn, scope=>'base', filter => "(objectclass=DNSzone)")->entry(0)->get_value('DNSserial')+1; + $mesg = $ldap->modify($zonedn, replace => { 'DNSserial', $newserial }); + die "Unable to modify serial number for: $zonedn ".$mesg->error if ($mesg->code); +} + + +sub delete_rrset +{ + my ($query, $ldap, $zonedn, $setdn) = @_; + my $mesg = $ldap->delete($setdn); + die "Unable to modify rrset: $setdn".$mesg->error if ($mesg->code); + my $newserial = $ldap->search(base=>$zonedn, scope=>'base', filter => "(objectclass=DNSzone)")->entry(0)->get_value('DNSserial')+1; + $mesg = $ldap->modify($zonedn, replace => { 'DNSserial', $newserial }); + die "Unable to modify serial number for: $zonedn ".$mesg->error if ($mesg->code); +} + + +sub print_whois +{ + my ($ldap, $zonedn) = @_; + my ($zonename, $whois) = ($ldap->search(base=>$zonedn, scope=>'base', filter=>"(objectclass=DNSzone)")->entry(0))->get_value('DNSzonename'); + use Net::Whois; + unless ($whois = new Net::Whois::Domain $zonename) { + print "

Unable to contact Whois-server

"; + return; + }; + unless ($whois->ok) { + print "

No Whois-record found for zone $zonename trying with "; + # try with parent zone + if ($zonename =~ /[^.]+\.(.*)/) { + $zonename = $1; + } + print "$zonename

\n"; + $whois = new Net::Whois::Domain($zonename); + return unless ($whois->ok); + } + print "

Whois record for zone $zonename

\n"; + print "Domain: ", $whois->domain, "
\n"; + print "Name: ", $whois->name, "
\n"; + print "Tag: ", $whois->tag, "
\n"; + print "Address:\n", map { " $_
\n" } $whois->address; + print "Country: ", $whois->country, "
\n"; + print "Name Servers:
\n", map { " $$_[0] ($$_[1])
\n" } @{$whois->servers}; + print "Record created:", $whois->record_created, "
\n"; + print "Record updated:", $whois->record_updated, "
\n" ; +} +