mirror of
				https://github.com/bklang/ldap2dns.git
				synced 2025-10-30 07:33:14 -04:00 
			
		
		
		
	
		
			
	
	
		
			744 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			744 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /* Patch for tinydns to pass DNS-query to LDAP in favour of a cdb lookup.
 | ||
|  |  * $Id: askldap.c,v 1.8 2002/08/12 16:41:25 jrief Exp $ | ||
|  |  * Copyright 2002 <jacob.rief@tiscover.com>  | ||
|  |  */  | ||
|  | 
 | ||
|  | #include <lber.h>
 | ||
|  | #include <ldap.h>
 | ||
|  | #include <stdlib.h>
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <ctype.h>
 | ||
|  | #include <string.h>
 | ||
|  | #include <assert.h>
 | ||
|  | #include <unistd.h>
 | ||
|  | #include <time.h>
 | ||
|  | #include <sys/time.h>
 | ||
|  | #include <setjmp.h>
 | ||
|  | #include "alloc.h"
 | ||
|  | #include "byte.h"
 | ||
|  | #include "response.h"
 | ||
|  | #include "askldap.h"
 | ||
|  | #include "dns.h"
 | ||
|  | 
 | ||
|  | static LDAP* ldap_con; | ||
|  | static sigjmp_buf stack_context; | ||
|  | 
 | ||
|  | static struct { | ||
|  | 	char ldaphosts[256]; | ||
|  | 	const char* basedn; | ||
|  | 	char binddn[256]; | ||
|  | 	char bindpwd[16]; | ||
|  | 	struct timeval timeout;	 | ||
|  | 	int verbose; | ||
|  | 	int initialized; | ||
|  | } options; | ||
|  | 
 | ||
|  | struct zonerecord { | ||
|  | 	char zonedn[256]; | ||
|  | 	char zonename[64]; | ||
|  | 	char class[16]; | ||
|  | 	char type[16]; | ||
|  | 	char adminmailbox[64]; | ||
|  | 	char zonemaster[64]; | ||
|  | 	unsigned long serial, refresh, retry, expire, minimum; | ||
|  | 	int ttl; | ||
|  | 	int timestamp; | ||
|  | }; | ||
|  | 
 | ||
|  | struct resourcerecord { | ||
|  | 	char qualifieddomainname[256]; | ||
|  | 	char class[16]; | ||
|  | 	char type[16]; | ||
|  | 	char ipaddr[8][4]; | ||
|  | 	int numipaddrs; | ||
|  | 	char cname[256]; | ||
|  | 	unsigned int preference; | ||
|  | 	int ttl; | ||
|  | 	int timestamp; | ||
|  | 	int additionalinfo; | ||
|  | 	struct resourcerecord* next; | ||
|  | }; | ||
|  | 
 | ||
|  | enum { ASKLDAP_RETRY = 1, ASKLDAP_RETURN = 2, ASKLDAP_RECONNECT = 3 }; | ||
|  | 	 | ||
|  | static | ||
|  | void assert_ldap(int err) | ||
|  | { | ||
|  | 	static int retries; | ||
|  | 	switch (err) { | ||
|  | 	    case LDAP_SUCCESS: | ||
|  | 		return; | ||
|  | 	    case LDAP_TIMELIMIT_EXCEEDED: | ||
|  | 		fprintf(stderr, "Warning: %s\n", ldap_err2string(err)); | ||
|  | 		retries++; | ||
|  | 		if (retries<3) | ||
|  | 			siglongjmp(stack_context, ASKLDAP_RETRY); | ||
|  | 		retries = 0; | ||
|  | 		siglongjmp(stack_context, ASKLDAP_RETURN); | ||
|  | 	    case LDAP_TIMEOUT: | ||
|  | 	    case LDAP_NO_SUCH_OBJECT: | ||
|  | 		fprintf(stderr, "Warning: %s\n", ldap_err2string(err)); | ||
|  | 		siglongjmp(stack_context, ASKLDAP_RETURN); | ||
|  | 	    case LDAP_BUSY: | ||
|  | 	    case LDAP_UNAVAILABLE: | ||
|  | 	    case LDAP_UNWILLING_TO_PERFORM: | ||
|  | 	    case LDAP_SERVER_DOWN: | ||
|  | 		fprintf(stderr, "Warning: %s\n", ldap_err2string(err)); | ||
|  | 		siglongjmp(stack_context, ASKLDAP_RECONNECT); | ||
|  | 	    default: | ||
|  | 		fprintf(stderr, "Fatal error: %s\n", ldap_err2string(err)); | ||
|  | #ifdef _DEBUG
 | ||
|  | 		abort(); | ||
|  | #else
 | ||
|  | 		exit(1); | ||
|  | #endif
 | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void free_domainrecords(struct resourcerecord* anchor) | ||
|  | { | ||
|  | 	struct resourcerecord* ptr; | ||
|  | 	for (ptr = anchor; ptr; ptr = anchor) { | ||
|  | 		anchor = anchor->next; | ||
|  | 		alloc_free(ptr); | ||
|  | 	}	 | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | void fill_resourcerecord(struct resourcerecord* rr, LDAPMessage* m, const char* zonename) | ||
|  | { | ||
|  | 	BerElement* ber = NULL; | ||
|  | 	char* attr; | ||
|  | 		 | ||
|  | 	byte_zero(rr, sizeof(struct resourcerecord)); | ||
|  | 	strcpy(rr->class, "IN"); | ||
|  | 	for (attr = ldap_first_attribute(ldap_con, m, &ber); attr; attr = ldap_next_attribute(ldap_con, m, ber)) { | ||
|  | 		struct berval** bvals = ldap_get_values_len(ldap_con, m, attr); | ||
|  | 		if (bvals && bvals[0] && bvals[0]->bv_len>0) { | ||
|  | 			if (strcasecmp(attr, "dnsdomainname")==0) { | ||
|  | 				char tmp[64]; | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%64s", tmp)==1) { | ||
|  | 					if (zonename[0]!='\0')  | ||
|  | 						snprintf(rr->qualifieddomainname, 256, "%s.%s", tmp, zonename); | ||
|  | 					else | ||
|  | 						strncpy(rr->qualifieddomainname, tmp, 256); | ||
|  | 				} | ||
|  | 			} else if (strcasecmp(attr, "dnstype")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%16s", rr->type)!=1) { | ||
|  | 					rr->type[0] = '\0'; | ||
|  | 				} | ||
|  | 			} else if (strcasecmp(attr, "dnsipaddr")==0) { | ||
|  | 				int k, ip[4]; | ||
|  | 				for (k = 0; bvals[k] && k < 8-rr->numipaddrs; k++) { | ||
|  | 					if (sscanf(bvals[k]->bv_val, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3])==4) { | ||
|  | 						rr->ipaddr[rr->numipaddrs][0] = (char)ip[0]; | ||
|  | 						rr->ipaddr[rr->numipaddrs][1] = (char)ip[1]; | ||
|  | 						rr->ipaddr[rr->numipaddrs][2] = (char)ip[2]; | ||
|  | 						rr->ipaddr[rr->numipaddrs][3] = (char)ip[3]; | ||
|  | 						rr->numipaddrs++; | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} else if (rr->numipaddrs<8 && 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) { | ||
|  | 					rr->ipaddr[rr->numipaddrs][0] = (char)ip[0]; | ||
|  | 					rr->ipaddr[rr->numipaddrs][1] = (char)ip[1]; | ||
|  | 					rr->ipaddr[rr->numipaddrs][2] = (char)ip[2]; | ||
|  | 					rr->ipaddr[rr->numipaddrs][3] = (char)ip[3]; | ||
|  | 					rr->numipaddrs++; | ||
|  | 				} | ||
|  | 			} else if (strcasecmp(attr, "dnscname")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%256s", rr->cname)==1) { | ||
|  | 					int len = strlen(rr->cname); | ||
|  | 					if (rr->cname[len-1]!='.' && zonename[0]!='\0') { | ||
|  | 						strcat(rr->cname, "."); | ||
|  | 						strncat(rr->cname, zonename, 252-len); | ||
|  | 						strcat(rr->cname, "."); | ||
|  | 					} | ||
|  | 				} else { | ||
|  | 					rr->cname[0] = '\0'; | ||
|  | 				} | ||
|  | 			} else if (strcasecmp(attr, "dnsttl")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%d", &rr->ttl)!=1) | ||
|  | 					rr->ttl = 0; | ||
|  | 			} else if (strcasecmp(attr, "dnstimestamp")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%d", &rr->timestamp)!=1) | ||
|  | 					rr->timestamp = 0; | ||
|  | 			} else if (strcasecmp(attr, "dnspreference")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%u", &rr->preference)!=1) | ||
|  | 					rr->preference = 1; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		ldap_value_free_len(bvals); | ||
|  | 	} | ||
|  | 	if (rr->qualifieddomainname[0]=='\0') | ||
|  | 		strncpy(rr->qualifieddomainname, zonename, 256); | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | void fill_zonerecord(struct zonerecord* zone, LDAPMessage* m) | ||
|  | { | ||
|  | 	BerElement* ber = NULL; | ||
|  | 	char* attr; | ||
|  | 
 | ||
|  | 	byte_zero(zone, sizeof(struct zonerecord)); | ||
|  | 	strcpy(zone->class, "IN"); | ||
|  | 	for (attr = ldap_first_attribute(ldap_con, m, &ber); attr; attr = ldap_next_attribute(ldap_con, m, ber)) { | ||
|  | 		struct berval** bvals = ldap_get_values_len(ldap_con, m, attr); | ||
|  | 		if (bvals && bvals[0] && bvals[0]->bv_len>0) { | ||
|  | 			if (strcasecmp(attr, "dnstype")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%16s", zone->type)!=1) | ||
|  | 					zone->type[0] = '\0'; | ||
|  | 			} else if (strcasecmp(attr, "dnsserial")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%lu", &zone->serial)!=1) | ||
|  | 					zone->serial = 0; | ||
|  | 			} else if (strcasecmp(attr, "dnsrefresh")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%lu", &zone->refresh)!=1) | ||
|  | 					zone->refresh = 0; | ||
|  | 			} else if (strcasecmp(attr, "dnsretry")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%lu", &zone->retry)!=1) | ||
|  | 					zone->retry = 0; | ||
|  | 			} else if (strcasecmp(attr, "dnsexpire")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%lu", &zone->expire)!=1) | ||
|  | 					zone->expire = 0; | ||
|  | 			} else if (strcasecmp(attr, "dnsminimum")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%lu", &zone->minimum)!=1) | ||
|  | 					zone->minimum = 0; | ||
|  | 			} else if (strcasecmp(attr, "dnsadminmailbox")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%64s", zone->adminmailbox)!=1) | ||
|  | 					zone->adminmailbox[0] = '\0'; | ||
|  | 			} else if (strcasecmp(attr, "dnszonemaster")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%64s", zone->zonemaster)!=1) | ||
|  | 					zone->zonemaster[0] = '\0'; | ||
|  | 			} else if (strcasecmp(attr, "dnsttl")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%d", &zone->ttl)!=1) | ||
|  | 					zone->ttl = 0; | ||
|  | 			} else if (strcasecmp(attr, "dnstimestamp")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%d", &zone->timestamp)!=1) | ||
|  | 					zone->timestamp = 0; | ||
|  | 			} else if (strcasecmp(attr, "dnszonename")==0) { | ||
|  | 				if (sscanf(bvals[0]->bv_val, "%s", zone->zonename)!=1) | ||
|  | 					zone->zonename[0] = '\0'; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		ldap_value_free_len(bvals); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | int find_ipaddr(const char* queryname, char ip[4]) | ||
|  | { | ||
|  | 	static char *rrattrs[] = { "dnsipaddr", "dnscipaddr", 0 }; | ||
|  | 	LDAPMessage* res = NULL; | ||
|  | 	LDAPMessage* m; | ||
|  | 	int ret = 0; | ||
|  | 	char filter[256], domainname[64]; | ||
|  | 	const char *zonename = queryname; | ||
|  | 	domainname[0] = '\0'; | ||
|  | 	while (*zonename) { | ||
|  | 		int len = snprintf(filter, 256, "(&(dnszonename=%s", zonename); | ||
|  | 		if (filter[len-1]=='.') | ||
|  | 			filter[len-1] = '\0'; | ||
|  | 	        strncat(filter, ")(objectclass=dnszone)(dnsclass=IN))", 256-len); | ||
|  | 		assert_ldap(ldap_search_st(ldap_con, options.basedn, LDAP_SCOPE_SUBTREE, filter, rrattrs, 0, &options.timeout, &res)); | ||
|  | 		if (m = ldap_first_entry(ldap_con, res)) { | ||
|  | 			char* zonedn = ldap_get_dn(ldap_con, m); | ||
|  | 			if (ldap_next_entry(ldap_con, m)) | ||
|  | 				printf("Warning: ambigous zonename for %s in %s\n", zonename, zonedn); | ||
|  | 			if (domainname[0]!='\0') { | ||
|  | 				len = strlen(domainname); | ||
|  | 				if (domainname[len-1]=='.') | ||
|  | 					domainname[len-1] = '\0'; | ||
|  | 				snprintf(filter, 256, "(&(|(dnsdomainname=%s)(dnscname=%s))(objectclass=dnsrrset)(dnsclass=IN)(|(dnsipaddr=*)(dnscipaddr=*)))", domainname, domainname); | ||
|  | 			} else { | ||
|  | 				strcpy(filter, "(&(!(dnsdomainname=*))(objectclass=dnsrrset)(dnsclass=IN)(|(dnsipaddr=*)(dnscipaddr=*)))"); | ||
|  | 			} | ||
|  | 			ldap_msgfree(res); | ||
|  | 			assert_ldap(ldap_search_st(ldap_con, zonedn, LDAP_SCOPE_SUBTREE, filter, rrattrs, 0, &options.timeout, &res)); | ||
|  | 			if (m = ldap_first_entry(ldap_con, res)) { | ||
|  | 				struct resourcerecord rr; | ||
|  | 				fill_resourcerecord(&rr, m, ""); | ||
|  | 				if (rr.numipaddrs>0) { | ||
|  | 					rr.numipaddrs = rand()%rr.numipaddrs; | ||
|  | 					ip[0] = rr.ipaddr[rr.numipaddrs][0]; | ||
|  | 					ip[1] = rr.ipaddr[rr.numipaddrs][1]; | ||
|  | 					ip[2] = rr.ipaddr[rr.numipaddrs][2]; | ||
|  | 					ip[3] = rr.ipaddr[rr.numipaddrs][3]; | ||
|  | 					ret = 1; | ||
|  | 				} | ||
|  | 			} | ||
|  | 			ldap_memfree(zonedn); | ||
|  | 			ldap_msgfree(res); res = NULL; | ||
|  | 			if (ret) | ||
|  | 				return 1; | ||
|  | 			break; | ||
|  | 		} | ||
|  | 		while (*zonename && *zonename!='.') { | ||
|  | 			domainname[zonename-queryname] = *zonename; | ||
|  | 			zonename++; | ||
|  | 		} | ||
|  | 		domainname[zonename-queryname] = *zonename; | ||
|  | 		if (*zonename=='.') { | ||
|  | 			zonename++; | ||
|  | 			domainname[zonename-queryname] = '\0'; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	/* sometimes the queryname resolves directly as cname in some other records */ | ||
|  | 	snprintf(filter, 256, "(&(dnscname=%s)(objectclass=dnsrrset)(dnsclass=IN)(|(dnsipaddr=*)(dnscipaddr=*)))", queryname); | ||
|  | 	assert_ldap(ldap_search_st(ldap_con, options.basedn, LDAP_SCOPE_SUBTREE, filter, rrattrs, 0, &options.timeout, &res)); | ||
|  | 	if (m = ldap_first_entry(ldap_con, res)) { | ||
|  | 		struct resourcerecord rr; | ||
|  | 		fill_resourcerecord(&rr, m, ""); | ||
|  | 		if (rr.numipaddrs>0) { | ||
|  | 			rr.numipaddrs = rand()%rr.numipaddrs; | ||
|  | 			ip[0] = rr.ipaddr[rr.numipaddrs][0]; | ||
|  | 			ip[1] = rr.ipaddr[rr.numipaddrs][1]; | ||
|  | 			ip[2] = rr.ipaddr[rr.numipaddrs][2]; | ||
|  | 			ip[3] = rr.ipaddr[rr.numipaddrs][3]; | ||
|  | 			ret = 1; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	ldap_msgfree(res); | ||
|  | 	return ret; | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | struct resourcerecord* find_reverserecord(const char* queryname, int ip[4]) | ||
|  | { | ||
|  | 	static char *rrattrs[] = { "dnstype", "dnsdomainname", "dnscname", "dnsttl", 0 }; | ||
|  | 	LDAPMessage* res = NULL; | ||
|  | 	struct resourcerecord* rr = NULL; | ||
|  | 	LDAPMessage* m; | ||
|  | 	char filter[256]; | ||
|  | 	snprintf(filter, 256, "(&(dnscipaddr=%u.%u.%u.%u)(objectclass=dnsrrset)(dnsclass=IN))", ip[0], ip[1], ip[2], ip[3]); | ||
|  | 	assert_ldap(ldap_search_st(ldap_con, options.basedn, LDAP_SCOPE_SUBTREE, filter, rrattrs, 0, &options.timeout, &res)); | ||
|  | 	if (m = ldap_first_entry(ldap_con, res)) { | ||
|  | 		char* rrsetdn = ldap_get_dn(ldap_con, m); | ||
|  | 		char** explodedn = NULL; | ||
|  | 		 | ||
|  | 		rr = (void*)alloc(sizeof(struct resourcerecord)); | ||
|  | 		fill_resourcerecord(rr, m, ""); | ||
|  | 		if (ldap_next_entry(ldap_con, m)) | ||
|  | 			printf("Warning: ambigous IP-address for %u.%u.%u.%u in dn: %s\n", ip[0], ip[1], ip[2], ip[3], rrsetdn); | ||
|  | 		explodedn = ldap_explode_dn(rrsetdn, 0); | ||
|  | 		if (explodedn[0]) { | ||
|  | 			static char *zoneattrs[] = { "dnszonename", 0 }; | ||
|  | 			char zonedn[256]; | ||
|  | 			int i, len = 0; | ||
|  | 			struct zonerecord zone; | ||
|  | 
 | ||
|  | 			zonedn[0] = '\0'; | ||
|  | 			for (i = 1; explodedn[i]; i++) | ||
|  | 				len += snprintf(zonedn+len, 256-len, "%s,", explodedn[i]); | ||
|  | 			zonedn[len-1] = '\0'; | ||
|  | 			ldap_msgfree(res); | ||
|  | 			assert_ldap(ldap_search_st(ldap_con, zonedn, LDAP_SCOPE_SUBTREE, "(objectclass=dnszone)", zoneattrs, 0, &options.timeout, &res)); | ||
|  | 			m = ldap_first_entry(ldap_con, res); | ||
|  | 			if (m==NULL) | ||
|  | 				printf("Error: parent dn: %s not found for %s\n", zonedn, rrsetdn); | ||
|  | 			fill_zonerecord(&zone, m); | ||
|  | 			len = strlen(rr->qualifieddomainname); | ||
|  | 			if (len==0) { | ||
|  | 				len = strlen(rr->cname); | ||
|  | 				if (rr->cname[len-1]!='.') { | ||
|  | 					strcat(rr->cname, "."); | ||
|  | 					strncat(rr->cname, zone.zonename, 252-len); | ||
|  | 				} | ||
|  | 			} else { | ||
|  | 				/* in those situations where a dnsrrset
 | ||
|  | 				 * defines something like MX or NS for a zone | ||
|  | 				 * and also sets a canonical name for the | ||
|  | 				 * service. */ | ||
|  | 				snprintf(rr->cname, 256, "%s.%s", rr->qualifieddomainname, zone.zonename); | ||
|  | 			} | ||
|  | 			strcpy(rr->type, "PTR"); | ||
|  | 			strncpy(rr->qualifieddomainname, queryname, 256); | ||
|  | 		} | ||
|  | 		ldap_memfree(rrsetdn); | ||
|  | 		ldap_value_free(explodedn); | ||
|  | 	} | ||
|  | 	ldap_msgfree(res); | ||
|  | 	return rr; | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | struct resourcerecord* read_domainrecords(const char* zonedn, const char* domainname, const char* zonename) | ||
|  | { | ||
|  | 	static char *rrattrs[] = { "dnsdomainname", "dnstype", "dnsttl", "dnscname", "dnsipaddr", "dnscipaddr", "dnstimestamp", "dnspreference", 0 }; | ||
|  | 	LDAPMessage* res = NULL; | ||
|  | 	LDAPMessage* m; | ||
|  | 	char filter[256]; | ||
|  | 	struct resourcerecord *prev, *anchor = NULL; | ||
|  | 	 | ||
|  | 	if (domainname[0]) { | ||
|  | 		if (strstr(zonename, "in-addr.arpa")) { | ||
|  | 			unsigned int ip[4]; | ||
|  | 			char queryname[256]; | ||
|  | 			snprintf(queryname, 256, "%s.%s", domainname, zonename); | ||
|  | 			if (sscanf(queryname, "%3u.%3u.%3u.%3u", &ip[3], &ip[2], &ip[1], &ip[0])!=4) | ||
|  | 				return NULL; | ||
|  | 			snprintf(filter, 256, "(&(dnsipaddr=%u.%u.%u.%u)(objectclass=dnsrrset)(dnsclass=IN))", ip[0], ip[1], ip[2], ip[3]); | ||
|  | 			assert_ldap(ldap_search_st(ldap_con, zonedn, LDAP_SCOPE_SUBTREE, filter, rrattrs, 0, &options.timeout, &res)); | ||
|  | 			if (m = ldap_first_entry(ldap_con, res)) { | ||
|  | 				struct resourcerecord* rr; | ||
|  | 				rr = (void*)alloc(sizeof(struct resourcerecord)); | ||
|  | 				fill_resourcerecord(rr, m, zonename); | ||
|  | 				strncpy(rr->qualifieddomainname, queryname, 256); | ||
|  | 				ldap_msgfree(res); | ||
|  | 				return rr; | ||
|  | 			} else { | ||
|  | 				/* ipaddr not in our baliwick, search the whole tree for canonical ipaddr */ | ||
|  | 				ldap_msgfree(res); | ||
|  | 				return find_reverserecord(queryname, ip); | ||
|  | 			} | ||
|  | 		} else { | ||
|  | 			snprintf(filter, 256, "(&(dnsdomainname=%s)(objectclass=dnsrrset)(dnsclass=IN))", domainname); | ||
|  | 			assert_ldap(ldap_search_st(ldap_con, zonedn, LDAP_SCOPE_SUBTREE, filter, rrattrs, 0, &options.timeout, &res)); | ||
|  | 		} | ||
|  | 	} else { | ||
|  | 		snprintf(filter, 256, "(&(!(dnsdomainname=*))(objectclass=dnsrrset)(dnsclass=IN))"); | ||
|  | 		assert_ldap(ldap_search_st(ldap_con, zonedn, LDAP_SCOPE_SUBTREE, filter, rrattrs, 0, &options.timeout, &res)); | ||
|  | 	} | ||
|  | 	for (m = ldap_first_entry(ldap_con, res); m; m = ldap_next_entry(ldap_con, m)) { | ||
|  | 		struct resourcerecord* rr; | ||
|  | 		rr = (void*)alloc(sizeof(struct resourcerecord)); | ||
|  | 		fill_resourcerecord(rr, m, zonename); | ||
|  | 		if (anchor==NULL) { | ||
|  | 			prev = anchor = rr; | ||
|  | 		} else { | ||
|  | 			prev->next = rr; | ||
|  | 			prev = rr; | ||
|  | 		} | ||
|  | 		if (options.verbose&1) | ||
|  | 			printf("\trr: %s %s\n", domainname, rr->type); | ||
|  | 	} | ||
|  | 	ldap_msgfree(res); | ||
|  | 	return anchor; | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | int read_dnszone(struct zonerecord* zone, const char* zonename) | ||
|  | { | ||
|  | 	static char *zoneattrs[] = { "dnszonename", "dnstype", "dnsserial", "dnsrefresh", "dnsretry", "dnsexpire", "dnsminimum", "dnszonemaster", "dnsadminmailbox", "dnsttl", "dnstimestamp", 0 }; | ||
|  | 	LDAPMessage* res = NULL; | ||
|  | 	LDAPMessage* m; | ||
|  | 	char* dn; | ||
|  | 	char filter[256]; | ||
|  | 	 | ||
|  | 	snprintf(filter, 256, "(&(dnszonename=%s)(objectclass=dnszone)(dnsclass=IN))", zonename); | ||
|  | 	assert_ldap(ldap_search_st(ldap_con, options.basedn, LDAP_SCOPE_SUBTREE, filter, zoneattrs, 0, &options.timeout, &res)); | ||
|  | 	m = ldap_first_entry(ldap_con, res); | ||
|  | 	if (m==NULL) { | ||
|  | 		ldap_msgfree(res); | ||
|  | 		return 0; | ||
|  | 	} | ||
|  | 	dn = ldap_get_dn(ldap_con, m); | ||
|  | 	fill_zonerecord(zone, m); | ||
|  | 	m = ldap_next_entry(ldap_con, m); | ||
|  | 	if (m) { | ||
|  | 		char* otherdn = ldap_get_dn(ldap_con, m); | ||
|  | 		printf("Warning: ambigous zonename found in dn: %s and dn: %s\n", dn, otherdn); | ||
|  | 		ldap_memfree(otherdn); | ||
|  | 	} | ||
|  | 	strncpy(zone->zonedn, dn, 256); | ||
|  | 	ldap_memfree(dn); | ||
|  | 	ldap_msgfree(res); | ||
|  | 	return 1; | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | void djb_name(const char* dotname, char* djbname) | ||
|  | { | ||
|  | 	const char* c = dotname; | ||
|  | 	int i, k; | ||
|  | 	for (i = 0; *c; c++) { | ||
|  | 		k = i; | ||
|  | 		while (*c!='.') { | ||
|  | 			k++; | ||
|  | 			djbname[k] = *c; | ||
|  | 			if (*c=='\0') { | ||
|  | 				djbname[i] = k-i-1; | ||
|  | 				return; | ||
|  | 			} | ||
|  | 			c++; | ||
|  | 		} | ||
|  | 		djbname[i] = k-i; | ||
|  | 		i = k+1; | ||
|  | 	} | ||
|  | 	djbname[i] = '\0'; | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | void djb_type(const char* dottype, char djbtype[2]) | ||
|  | { | ||
|  | 	djbtype[0] = '\0'; | ||
|  | 	if (strcasecmp(dottype, "A")==0) | ||
|  | 		djbtype[1] = 001; | ||
|  | 	else if (strcasecmp(dottype, "NS")==0) | ||
|  | 		djbtype[1] = 002; | ||
|  | 	else if (strcasecmp(dottype, "CNAME")==0) | ||
|  | 		djbtype[1] = 005; | ||
|  | 	else if (strcasecmp(dottype, "SOA")==0) | ||
|  | 		djbtype[1] = 006; | ||
|  | 	else if (strcasecmp(dottype, "PTR")==0) | ||
|  | 		djbtype[1] = 014; | ||
|  | 	else if (strcasecmp(dottype, "MX")==0) | ||
|  | 		djbtype[1] = 017; | ||
|  | 	else if (strcasecmp(dottype, "TXT")==0) | ||
|  | 		djbtype[1] = 020; | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | void split_djbstyle(const char* djbname, char* domainname, char* zonename, int offset) | ||
|  | { | ||
|  | 	int i, k, m = 0, n = 0; | ||
|  | 	for (i = *djbname; i; i = *++djbname) { | ||
|  | 		if (offset>0) { | ||
|  | 			offset--; | ||
|  | 			for (k = m; k<m+i; k++) { | ||
|  | 				domainname[k] = *++djbname; | ||
|  | 			} | ||
|  | 			domainname[k] = '.'; | ||
|  | 			m = k+1; | ||
|  | 		} else { | ||
|  | 			for (k = n; k<n+i; k++) { | ||
|  | 				zonename[k] = *++djbname; | ||
|  | 			} | ||
|  | 			zonename[k] = '.'; | ||
|  | 			n = k+1; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	domainname[m>0 ? m-1 : 0] = '\0'; | ||
|  | 	zonename[n>0 ? n-1 : 0] = '\0'; | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | void build_response_section(struct resourcerecord *rr, char qtype[2], int section) | ||
|  | { | ||
|  | 	char djbname[256], djbtype[2]; | ||
|  | 	djb_name(rr->qualifieddomainname, djbname); | ||
|  | 	djb_type(rr->type, djbtype); | ||
|  | 	if (byte_equal(djbtype, 2, DNS_T_A)) { | ||
|  | 		if (byte_equal(qtype, 2, DNS_T_A) || byte_equal(qtype, 2, DNS_T_ANY)) { | ||
|  | 			response_rstart(djbname, djbtype, rr->ttl); | ||
|  | 			response_addbytes(rr->ipaddr[rand()%rr->numipaddrs], 4); | ||
|  | 			response_rfinish(section); | ||
|  | 		} | ||
|  | 	} else if (byte_equal(djbtype, 2, DNS_T_CNAME)) { | ||
|  | 		response_rstart(djbname, djbtype, rr->ttl); | ||
|  | 		djb_name(rr->cname, djbname); | ||
|  | 		response_addname(djbname); | ||
|  | 		response_rfinish(section); | ||
|  | 	} else if (byte_equal(djbtype, 2, DNS_T_NS)) { | ||
|  | 		if (byte_equal(qtype, 2, DNS_T_NS) || byte_equal(qtype, 2, DNS_T_ANY)) { | ||
|  | 			response_rstart(djbname, djbtype, rr->ttl); | ||
|  | 			if (rr->cname[0]) { | ||
|  | 				djb_name(rr->cname, djbname); | ||
|  | 				response_addname(djbname); | ||
|  | 				rr->additionalinfo = 1; | ||
|  | 			} else { | ||
|  | 				response_addbytes(rr->ipaddr[rand()%rr->numipaddrs], 4); | ||
|  | 			} | ||
|  | 			response_rfinish(section); | ||
|  | 		} | ||
|  | 	} else if (byte_equal(djbtype, 2, DNS_T_PTR)) { | ||
|  | 		response_rstart(djbname, djbtype, rr->ttl); | ||
|  | 		djb_name(rr->cname, djbname); | ||
|  | 		response_addname(djbname); | ||
|  | 		response_rfinish(section); | ||
|  | 	} else if (byte_equal(djbtype, 2, DNS_T_MX)) { | ||
|  | 		if (byte_equal(qtype, 2, DNS_T_MX) || byte_equal(qtype, 2, DNS_T_ANY)) { | ||
|  | 			char tmp[2]; | ||
|  | 			response_rstart(djbname, djbtype, rr->ttl); | ||
|  | 			tmp[0] = rr->preference/0x100; | ||
|  | 			tmp[1] = rr->preference%0x100; | ||
|  | 			response_addbytes(tmp, 2); | ||
|  | 			if (rr->cname[0]) { | ||
|  | 				djb_name(rr->cname, djbname); | ||
|  | 				response_addname(djbname); | ||
|  | 				rr->additionalinfo = 1; | ||
|  | 			} else { | ||
|  | 				response_addbytes(rr->ipaddr[rand()%rr->numipaddrs], 4); | ||
|  | 			} | ||
|  | 			response_rfinish(section); | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | void build_soa_section(struct zonerecord *zone, int section) | ||
|  | { | ||
|  | 	time_t now; | ||
|  | 	char defaultsoa[20]; | ||
|  | 	char djbname[256]; | ||
|  | 	char zonesoa[20]; | ||
|  | 	unsigned long tmp; | ||
|  | 	time(&now); | ||
|  | 	djb_name(zone->zonename, djbname); | ||
|  | 	response_rstart(djbname, DNS_T_SOA, zone->ttl); | ||
|  | 	djb_name(zone->zonemaster, djbname); | ||
|  | 	response_addname(djbname); | ||
|  | 	djb_name(zone->adminmailbox, djbname); | ||
|  | 	response_addname(djbname); | ||
|  | 	uint32_pack_big(defaultsoa, now); | ||
|  | 	if (byte_equal(defaultsoa,4,"\0\0\0\0")) | ||
|  | 	defaultsoa[3] = 1; | ||
|  | 	byte_copy(defaultsoa + 4, 16, "\0\0\100\000\0\0\010\000\0\020\000\000\0\0\012\000"); | ||
|  | 	if (zone->serial==0) | ||
|  | 		uint32_unpack_big(defaultsoa, &tmp); | ||
|  | 	else | ||
|  | 		tmp = zone->serial; | ||
|  | 	uint32_pack_big(zonesoa, tmp); | ||
|  | 	if (zone->refresh==0) | ||
|  | 		uint32_unpack_big(defaultsoa+4, &tmp); | ||
|  | 	else | ||
|  | 		tmp = zone->refresh; | ||
|  | 	uint32_pack_big(zonesoa+4, tmp); | ||
|  | 	if (zone->retry==0) | ||
|  | 		uint32_unpack_big(defaultsoa+8, &tmp); | ||
|  | 	else | ||
|  | 		tmp = zone->retry; | ||
|  | 	uint32_pack_big(zonesoa+8, tmp); | ||
|  | 	if (zone->expire==0) | ||
|  | 		uint32_unpack_big(defaultsoa+12, &tmp); | ||
|  | 	else | ||
|  | 		tmp = zone->expire; | ||
|  | 	uint32_pack_big(zonesoa+12, tmp); | ||
|  | 	if (zone->minimum==0) | ||
|  | 		uint32_unpack_big(defaultsoa+16, &tmp); | ||
|  | 	else | ||
|  | 		tmp = zone->minimum; | ||
|  | 	uint32_pack_big(zonesoa+16, tmp); | ||
|  | 	response_addbytes(zonesoa, 20); | ||
|  | 	response_rfinish(section); | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | void build_additional_section(struct resourcerecord *rr) | ||
|  | { | ||
|  | 	char djbname[256], ip[4]; | ||
|  | 	if (rr->additionalinfo && find_ipaddr(rr->cname, ip)) { | ||
|  | 		djb_name(rr->cname, djbname); | ||
|  | 		response_rstart(djbname, DNS_T_A, rr->ttl); | ||
|  | 		response_addbytes(ip, 4); | ||
|  | 		response_rfinish(RESPONSE_ADDITIONAL); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static | ||
|  | int connect_and_bind() | ||
|  | { | ||
|  | 	ldap_con = ldap_init(options.ldaphosts, LDAP_PORT); | ||
|  | 	if (ldap_simple_bind_s(ldap_con, options.binddn, options.bindpwd)==LDAP_SUCCESS) { | ||
|  | 		printf("Connected to %s as \"%s\"\n", options.ldaphosts, options.binddn); | ||
|  | 		return 1; | ||
|  | 	} | ||
|  | 	ldap_con = NULL; | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | int askldap_query(const char* djbdomainname, char qtype[2]) | ||
|  | { | ||
|  | 	int offset; | ||
|  | 	char domainname[64], zonename[64]; | ||
|  | 	struct zonerecord zoneinfo; | ||
|  | 	int answer_ok = 0, flagsoa = 0, flagns = 0; | ||
|  | 	if (!options.initialized) | ||
|  | 		return 0; | ||
|  | 	switch (sigsetjmp(stack_context, 1)) { | ||
|  | 	    default: | ||
|  | 		    if (ldap_con==NULL && !connect_and_bind()) | ||
|  | 		    	return answer_ok; | ||
|  | 		    break; | ||
|  | 	    case ASKLDAP_RECONNECT: | ||
|  | 		    if (connect_and_bind()) | ||
|  | 			    break; | ||
|  | 		    return answer_ok; | ||
|  | 	    case ASKLDAP_RETURN: | ||
|  | 		    return answer_ok; | ||
|  | 	} | ||
|  | 	for (offset = 0; offset<32; offset++) { | ||
|  | 		struct resourcerecord *rransw, *rrauth, *rr; | ||
|  | 		 | ||
|  | 		split_djbstyle(djbdomainname, domainname, zonename, offset); | ||
|  | 		if (zonename[0]=='\0') return 0; | ||
|  | 		if (!read_dnszone(&zoneinfo, zonename)) | ||
|  | 			continue; | ||
|  | 		rransw = read_domainrecords(zoneinfo.zonedn, domainname, zonename); | ||
|  | 		rrauth = NULL; | ||
|  | 		if (offset==0) { | ||
|  | 			/* query is in our bailiwick */ | ||
|  | 			if (byte_equal(qtype, 2, DNS_T_ANY) || byte_equal(qtype, 2, DNS_T_SOA)) { | ||
|  | 				build_soa_section(&zoneinfo, RESPONSE_ANSWER); | ||
|  | 				flagsoa = 1; | ||
|  | 			} | ||
|  | 			for (rr = rransw; rr; rr = rr->next) { | ||
|  | 				build_response_section(rr, qtype, RESPONSE_ANSWER); | ||
|  | 				answer_ok = 1; | ||
|  | 			} | ||
|  | 			if (!flagsoa) { | ||
|  | 				build_soa_section(&zoneinfo, RESPONSE_AUTHORITY); | ||
|  | 				flagsoa = 1; | ||
|  | 			} | ||
|  | 			if (!byte_equal(qtype, 2, DNS_T_ANY) && !byte_equal(qtype, 2, DNS_T_NS)) { | ||
|  | 				for (rr = rransw; rr; rr = rr->next) | ||
|  | 					if (strcmp(rr->type, "NS")==0) { | ||
|  | 						build_response_section(rr, DNS_T_NS, RESPONSE_AUTHORITY); | ||
|  | 						flagns = 1; | ||
|  | 					} | ||
|  | 			} | ||
|  | 		} else { | ||
|  | 			for (rr = rransw; rr; rr = rr->next) { | ||
|  | 				if (strcmp(rr->type, "NS")==0) { | ||
|  | 					build_response_section(rr, qtype, RESPONSE_AUTHORITY); | ||
|  | 					flagns = 1; | ||
|  | 				} | ||
|  | 			} | ||
|  | 			if (!flagns) { | ||
|  | 				for (rr = rransw; rr; rr = rr->next) { | ||
|  | 					build_response_section(rr, qtype, RESPONSE_ANSWER); | ||
|  | 					answer_ok = 1; | ||
|  | 				} | ||
|  | 				if (answer_ok) { | ||
|  | 					rrauth = read_domainrecords(zoneinfo.zonedn, "", zonename); | ||
|  | 				} else { | ||
|  | 					build_soa_section(&zoneinfo, RESPONSE_AUTHORITY); | ||
|  | 					flagsoa = 1; | ||
|  | 				} | ||
|  | 			} | ||
|  | 			for (rr = rrauth; rr; rr = rr->next) { | ||
|  | 				if (strcmp(rr->type, "NS")==0) { | ||
|  | 					build_response_section(rr, DNS_T_NS, RESPONSE_AUTHORITY); | ||
|  | 					flagns = 1; | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 		for (rr = rransw; rr; rr = rr->next) | ||
|  | 			build_additional_section(rr); | ||
|  | 		for (rr = rrauth; rr; rr = rr->next) | ||
|  | 			build_additional_section(rr); | ||
|  | 		free_domainrecords(rransw); | ||
|  | 		free_domainrecords(rrauth); | ||
|  | 		break; | ||
|  | 	} | ||
|  | 	return answer_ok || flagsoa || flagns; | ||
|  | } | ||
|  | 
 | ||
|  | void askldap_init(const char* ldaphost, const char* basedn, const char* binddn, const char* passwd) | ||
|  | { | ||
|  | 	strncpy(options.ldaphosts, ldaphost, 256); | ||
|  | 	options.basedn = basedn; | ||
|  | 	if (binddn) strncpy(options.binddn, binddn, 256); | ||
|  | 	if (passwd) strncpy(options.bindpwd, passwd, 16); | ||
|  | 	/* LDAP timeout is hardcoded to 2/10 second.
 | ||
|  | 	 * This must be enough because bindoperations usually | ||
|  | 	 * timeout after one second and here we usually have to | ||
|  | 	 * send five queries to the LDAP-server */ | ||
|  | 	options.timeout.tv_sec = 1; | ||
|  | 	options.timeout.tv_usec = 200000; | ||
|  | 	options.verbose = 0; | ||
|  | 	options.initialized = 1; | ||
|  | 	connect_and_bind(); | ||
|  | } | ||
|  | 
 |