/* this file contains information for navigating the various LDAP schema */
#include "ldapdns.h"
#include "config.h"
#include "dns.h"
#include "ip.h"

#include <time.h>

void inline ldapdns_list_unique(list_t *p)
{
	list_t x = 0;
	list_t seen = 0;
	list_t lp, sp;

	for (lp = (*p); lp; lp = lp->next) {
		for (sp = seen; sp; sp = sp->next) {
			if (str_equal(sp->str, lp->str)) {
				goto HIT;
			}
		}
		list_push(&x, lp->str);
		list_push(&seen, lp->str);
HIT:
		continue;
	}

	lp = *p;
	while (list_pop(&lp));
	while (list_pop(&seen));
	*p = x;
}

static void inline ldap_escape(str_t retbuf, char *s)
{
	while (*s) {
		if (*s == ',') {
			str_addch(retbuf, '\\');
		}
		str_addch(retbuf, *s);
		s++;
	}
}

void name_to_ldap(str_t retbuf, char *name)
{
	list_t p = 0;
	char *x;
	int i;

	str_init(retbuf);

	switch (ldapdns.dn_mode) {
	case DN_MODE_COSINE:
	case DN_MODE_LDAPDNS: /* dc=www, dc=example, dc=com */
	case DN_MODE_RFC1279: /* dc=www, dc=example, dc=com */
		p = split_name_parts(name);
		i = 0; while ((x = list_pop(&p))) { 
			str_cat(retbuf, "dc=");
			ldap_escape(retbuf, x);
			str_cat(retbuf, ", ");
			i++;
		}
		if (i) str_chopn(retbuf, 2);
		break;
	case DN_MODE_MSDNS:   /* dc=www.example.com */
		str_cat(retbuf, "dc=");
		ldap_escape(retbuf, x);
		break;
	};
}

list_t ldap_into_parts(char *dn)
{
	/* safe if dn is infact an asciiZ string */
	list_t p = 0;
	register char *x = dn, *y;
	str_t s;

	/* split into parts */
	str_init(s);
	while (*x == ' ' || *x == ',') x++; /* mostly safe :) */
	while (*x) {
		/* looking for: \s*,\s* but !~ \\. */
		if (*x == '\\') {
			x++;
			str_addch(s, *x);
			x++;
		} else if (*x == ',') {
			/* pull off excess whitespace from previous */
			y = strchr(str(s), '\0');
			if (y) {
				y--;
				while (*y == ' ') y--;
				if (*y) y++;
				if (*y) *y = 0;
			}

			list_push(&p, str(s));
			str_init(s);
			while (*x == ',' || *x == ' ') x++;
		} else {
			str_addch(s, *x);
			x++;
		}
	}

	return p;
}

void ldap_to_name(str_t retbuf, char *dn)
{
	/* safe if dn is asciiz */
	list_t z, b, p = ldap_into_parts(dn);
	char *x, *y, *q;

	/* we need the suffix here in order to pull it off */
	if (ldapdns.ldap_suffix && *ldapdns.ldap_suffix) {
		b = ldap_into_parts(ldapdns.ldap_suffix);

		for (z = b; z; z = z->next) {
			if (str_diffi(z->str, p->str)) {
				break;
			} else {
				q = list_pop(&p);
				mem_free(q);
			}
		}
		while ((x = list_pop(&b))) {
			if (str_diffi(x, p->str)) {
				mem_free(x);
				while ((x = list_pop(&b)))
					mem_free(x);
				break;
			}
			q = list_pop(&p);
			mem_free(q);
			mem_free(x);
		}
	}

	/* flip p around; we have to have this in it's own list */
	list_reverse(&p);

	str_init(retbuf);
	while ((x = list_pop(&p))) {
		if (str_start(x, "dc=")) {
			y = x + 3;
			while (*y == ' ') y++;
			if (*y != ',') {
				str_cat(retbuf, y);
				str_addch(retbuf, '.');
			}
		}
		mem_free(x);
	}
	str_chop(retbuf);
}

static int inline __parse_soa(dns_ctx *c, bin_t rrdata, int flag)
{
	register unsigned int i, j;
	unsigned long ar[5];
	register char *q;
	str_t tmp, tmp2;

	q = caddr(rrdata);
	if (flag) {
		if (clen(rrdata) >= 1 && *q == '*') {
			c->wantdie=1;
			return 0;
		}
		/* don't want anything else */
		if (clen(rrdata) == 1)
			return 0;
	}
	if (c->soahack) return 1;	/* skip */

	/* remainder is of the format is serial [ttl retry expire minimum] */
	for (j = i = 0; j < 5; j++) { /* safe: bounded */
		if (!(i < clen(rrdata))) break;
		while (isspace(((unsigned int)q[i])) && i < clen(rrdata)) i++;
		if (!(i < clen(rrdata))) break;
		ar[j] = 0;
		while (isdigit(((unsigned int)q[i])) && i < clen(rrdata)) {
			ar[j] *= 10;
			switch (q[i]) {
			case '0': ar[j] += 0; break;
			case '1': ar[j] += 1; break;
			case '2': ar[j] += 2; break;
			case '3': ar[j] += 3; break;
			case '4': ar[j] += 4; break;
			case '5': ar[j] += 5; break;
			case '6': ar[j] += 6; break;
			case '7': ar[j] += 7; break;
			case '8': ar[j] += 8; break;
			case '9': ar[j] += 9; break;
			};
			i++;
		}
	}
	if (j < 4 || ar[4] == 0) {
		/* if j == 1 then... */
		switch (j) {
		case 2: c->refresh = ar[1];
		case 1: if (ar[0]) c->serial = ar[0];
		};
		return 0;
	}
	/* these are kept in host-endian form (for now) */
	if (ar[0]) c->serial = ar[0];
	c->refresh = ar[1];
	c->retry = ar[2];
	c->expire = ar[3];
	c->minimum = ar[4];
	c->ttl = c->minimum;
	if (i < clen(rrdata)) {
		/* grap ADM as well */
		str_init(tmp);
		str_catb(tmp, caddr(rrdata)+i, clen(rrdata)-i);
		name_to_dns_fix(tmp2, str(tmp), 2); /* email address? */
		list_push(&c->ADM, str(tmp2));
		mem_free(str(tmp));
	}
	c->soahack = 1;

	return 1;
}
static char inline *__parse_name(dns_ctx *c, bin_t rrdata)
{
	str_t tmp;
	bin_t retval;
	int addzone;

	/* name is in ascii form.... convert to DNS */
	bin_init(retval);
	bin_copy(retval, caddr(rrdata), clen(rrdata));
	addzone = 0;
	if (ldapdns.relative_names) {
		/* if . is the last character... */
		if (clen(retval) && caddr(retval)[clen(retval)-1] != '.') {
			addzone = 1;
		}
	}
	bin_0(retval);
	name_to_dns(tmp, (char *)caddr(retval));
	if (addzone && c->request_name_zone) {
		/* add the zone to the end */
		str_cat(tmp, c->request_name_zone);
	}
	bin_copy(retval, str(tmp), dns_domain_length(str(tmp)));

	return (char *)caddr(retval);
}
static char inline *__parse_mx(dns_ctx *c, bin_t rrdata)
{
	str_t tmp;
	bin_t retval;
	unsigned short pref;
	char *q;
	register int b, len;
	int addzone;

	/* name is in ascii form.... convert to DNS */
	bin_init(retval);
	q = (char *)caddr(rrdata);
	len = clen(rrdata);
	pref = 0;
	while (len > 0 && *q) {
		switch (*q) {
		case '0': b = 0; break;
		case '1': b = 1; break;
		case '2': b = 2; break;
		case '3': b = 3; break;
		case '4': b = 4; break;
		case '5': b = 5; break;
		case '6': b = 6; break;
		case '7': b = 7; break;
		case '8': b = 8; break;
		case '9': b = 9; break;
		default: b = -1; break;
		};
		if (b == -1) break;
		pref *= 10;
		pref += b;
		q++; len--;
	}
	while (len > 0 && *q == ' ') { q++; len--; }

	bin_copy(retval, q, len);
	addzone = 0;
	if (ldapdns.relative_names) {
		/* if . is the last character... */
		if (caddr(retval)[clen(retval)-1] != '.') {
			addzone = 1;
		}
	}
	bin_0(retval);
	name_to_dns(tmp, (char *)caddr(retval));
	if (addzone && c->request_name_zone) {
		/* add the zone to the end */
		str_cat(tmp, c->request_name_zone);
	}

	/* Clib */
	pref = htons(pref);
	bin_copy(retval, (char *)&pref, 2);
	bin_cat(retval, str(tmp), dns_domain_length(str(tmp)));
	mem_free(str(tmp));

	return (char *)caddr(retval);
}
static char inline *__parse_ipv4(dns_ctx *c, bin_t rrdata)
{
	/* rrdata is in the form of A.B.C.D; so length bind it... */
	unsigned char ip[8];
	register int i, j, a, b, r;
	bin_t retval;
	int cflag;

	bin_init(retval);
	ip[4] = ip[5] = ip[6] = ip[7] = 0;
	for (i = j = a = cflag = 0; (cflag || j < 4) && i < clen(rrdata); i++) {
		switch (rrdata->buf[i]) {
		case '0': b = 0; break;
		case '1': b = 1; break;
		case '2': b = 2; break;
		case '3': b = 3; break;
		case '4': b = 4; break;
		case '5': b = 5; break;
		case '6': b = 6; break;
		case '7': b = 7; break;
		case '8': b = 8; break;
		case '9': b = 9; break;
		case '.': ip[j] = a; a = 0; j++; continue;
		case '/': /* i think we're making a subnet-switch */
			if (!c) /* fail early */
				return 0;
			ip[j] = a;
			j = 4;
			a = 0;
			cflag = 1;
			continue;
		case '=':
			if (!c) /* fail early */
				return 0;
			ip[j] = a;
			if (j == 0) {
				ip[4] = 0xff;
				ip[5] = ip[6] = ip[7] = 0;
			} else if (j == 1) {
				ip[4] = ip[5] = 0xff;
				ip[6] = ip[7] = 0;
			} else if (j == 2) {
				ip[4] = ip[5] = ip[6] = 0xff;
				ip[7] = 0;
			} else if (j == 3) {
				ip[4] = ip[5] = ip[6] = ip[7] = 0xff;
			} else if (j == 4) {
				if (ip[4] == 0) {
					/* exact match */
					ip[4] = ip[5] = ip[6] = ip[7] = 255;
				} else if (ip[4] == 255) {
					/* 255.0.0.0 */
					ip[4] = 255;
					ip[5] = ip[6] = ip[7] = 0;
				} else {
					/* okay /CIDR notation */
					r = ip[4];
					ip[4] = ip[5] = ip[6] = ip[7] = 255;
					if (r < 8) {
						ip[4] = (255 << (8 - r));
						ip[5] = ip[6] = ip[7] = 0;
					} else if (r < 16) {
						ip[5] = (255 << (16 - r));
						ip[6] = ip[7] = 0;
					} else if (r < 24) {
						ip[6] = (255 << (24 - r));
						ip[7] = 0;
					} else {
						ip[7] = (255 << (32 - r));
					}
				}
			}
			if (ipv4_in_subnet(ip, c->ip)) {
				/* reset and start over */
				j = b = a = 0;
				cflag = 0;
				/* note, we also blop
				 * 'c' so we can't do this again */
				c = (dns_ctx *)0;
				continue;
			}
			return 0; /* don't return anything */
		case '%':
			if (c && j == 3) {
				if (!c->swm) return 0;
				/* next chars are "switch" */
				b = 0; i++;
				while (i < clen(rrdata)) {
					/* make sure it matches switch */
					if (c->swm[b] != rrdata->buf[i])
						return 0;
					b++; i++;
				}
				goto done_parsing_l;
			}
			/* fall through */
		default:
			/* invalid */
			warning("invalid IPV4 address (%c, %d)", rrdata->buf[i], i);
			return 0;
		};

		/* digit */
		a *= 10; a += b;
	}
done_parsing_l:
	ip[j] = a;
	bin_copy(retval, ip, 4);
	return (char *)caddr(retval);
}
static char inline *__parse_email(bin_t rrdata)
{
	str_t tmp;
	bin_t retval;

	/* name is in ascii form.... convert to DNS */
	bin_init(retval);
	bin_copy(retval, caddr(rrdata), clen(rrdata));
	bin_0(retval);
	name_to_dns_fix(tmp, (char *)caddr(retval), 2);
	bin_copy(retval, str(tmp), dns_domain_length(str(tmp)));

	return (char *)caddr(retval);
}
static char inline *__parse_txt(bin_t rrdata)
{
	str_t tmp;
	bin_t retval;

	/* don't dns-encode the TXT segment */
	bin_init(retval);
	bin_addch(retval, clen(rrdata));
	bin_cat(retval, caddr(rrdata), clen(rrdata));
	bin_0(retval);

	return (char *)caddr(retval);
}
static char inline *__parse_generic(bin_t rrdata)
{
	/* return MUST contain length:
	 * order:	rr len data....
	 */
	bin_t retval;
	unsigned short n;

	bin_init(retval);
	bin_copy(retval, caddr(rrdata), 2);
	/* n is in host-byte order */
	n = clen(rrdata)-2;
	bin_copy(retval, (char *)&n, 2);
	bin_copy(retval, caddr(rrdata)+2, n);

	return (char *)caddr(retval);
}

/* bind-style */
static int inline ldap_dnsrecord_rfc1279(dns_ctx *c, bin_t rrdata)
{
	register char *q;
	register int len;
	char rr[2];
	bin_t tmp;
	int r;

	/* IN rr [str] */
	if (clen(rrdata) < 4) return 0;
	if (!(tolower((unsigned int)(caddr(rrdata)[0])) == 'i'
	&& tolower((unsigned int)(caddr(rrdata)[1])) == 'n')) {
		return 0;
	}

	/* step over whitespace[s] */
	q = caddr(rrdata) + 2; len = clen(rrdata) - 2;
	while (len > 0 && (*q == ' ' || *q == '\t')) { q++; len--; }
	if (len < 2) return 0;

	/* parse word */
#define _setrr(x) do { rr[0] = x[0]; rr[1] = x[1]; } while (0)
	q[0] = toupper(((unsigned int)q[0])); /* modifying rrdata! */
	if (q[0] == 'A') {
		/* modifying rrdata */
		if (toupper(((unsigned int)q[1])) == 'A') {
			/* AAAA: not supported (yet) */
			return 0;
		}
		/* A: address record */
		_setrr(DNS_T_A);
	} else if (q[0] == 'C') {
		/* CNAME: alias */
		_setrr(DNS_T_CNAME);
	} else if (q[0] == 'T') {
		/* TXT: text record */
		_setrr(DNS_T_TXT);
	} else if (q[0] == 'P') {
		/* PTR: pointer-name */
		_setrr(DNS_T_PTR);
	} else if (q[0] == 'M') {
		/* MX: mail exchanger */
		_setrr(DNS_T_MX);
	} else if (q[0] == 'N') {
		/* NS: name server */
		_setrr(DNS_T_NS);
	} else if (q[0] == 'S') {
		/* SOA: start of authority */
		_setrr(DNS_T_SOA);
	}
#undef _setrr

	/* parse RR -- pass everything but SOA to their ldapdns parser */
	while (len > 0 && (*q != ' ' && *q != '\t')) { q++; len--; }
	if (len < 2) return 0;

	while (len > 0 && (*q == ' ' || *q == '\t')) { q++; len--; }
	if (len < 2) return 0;

#define _eq4(x) (rr[0] == x[0] && rr[1] == x[1])
	if (_eq4(DNS_T_SOA)) {
		/* start of authority */
		if (*q == '(') {
			q++; len--;
			if (len < 2) return 0;
			while (len > 0 && (*q == ' ' || *q == '\t')) { q++; len--; }
			if (len < 2) return 0;
		}
		for (r = 0; r < len; r++) {
			if (q[r] == ')') {
				r--;
				while ((q[r] == ' ' || q[r] == '\t') && r > 0)
					r--;
				len = r;
				break;
			}
		}
		/* fall through */
	}

	bin_init(tmp);
	bin_copy(tmp, q, len);
	r = 1;
	if (_eq4(DNS_T_A)) {
		/* disables client differentiation */
		list_push(&c->A, __parse_ipv4((dns_ctx *)0, tmp));
	} else if (_eq4(DNS_T_PTR)) {
		list_push(&c->PTR, __parse_name(c, tmp));
	} else if (!c->subreq) {
		if (_eq4(DNS_T_MX)) {
			list_push(&c->MX, __parse_mx(c, tmp));
		} else if (_eq4(DNS_T_CNAME)) {
			list_push(&c->CNAME, __parse_name(c, tmp));
		} else if (_eq4(DNS_T_NS)) {
			list_push(&c->NS, __parse_name(c, tmp));
		} else if (_eq4(DNS_T_TXT)) {
			list_push(&c->TXT, __parse_txt(tmp));
		} else if (_eq4(DNS_T_SOA)) {
			r = __parse_soa(c, tmp, 0);
		} else
			r = 0;
	} else
		r = 0;
#undef _eq4
	mem_free(caddr(tmp));
	return r;
}
/* microsoft-style */
static int inline ldap_dnsrecord_msdns(dns_ctx *c, bin_t rrdata)
{
	register char *q;
	bin_t retval;

	if (clen(rrdata) < 24) return 0;

	/* data is in a binary-packed format which i'm not 100% on:
	 * each two-character blob is a single octet. some things are
	 * in network byte-order, other things are NOT. this can be
	 * confusing quick...
	 *
	 * note that places where I've marked the field as 'xx' means i don't
	 * know what it is. 0x means that it's always 00 (in my tests)
	 * but that i still don't know what it is...
	 *
	 * HEADER:
	 *
	 * xx xx	[0-1]
	 * RR RR	16-bit: resource record [2-3]
	 * xx xx 0x 0x xx xx 0x 0x [4-11]
	 * TT TT TT TT	32-bit: time to live [12-15]
	 * 0x 0x 0x 0x xx xx xx xx [16-23]
	 * 		-- last dword has significance for A-records
	 *
	 * what follows is RR-data [24]:
	 *
	 * SOA-RR:
	 * 	SS SS SS SS	serial
	 * 	AA AA AA AA	refresh
	 * 	BB BB BB BB	retry
	 * 	CC CC CC CC	expire
	 * 	DD DD DD DD	minimum
	 *
	 * (44)	ll el d.d.d	length (ll), elements (el), dns-encoded NS1
	 * 	ll el d.d.d	length (ll), elements (el), hostmaster
	 *
	 * NS-RR
	 * 	ll el d.d.d	length (ll), elements (el), dns-encoded name
	 *
	 * A-RR
	 *	a. b. c. d.	ip address
	 *
	 * MX-RR
	 *	pp pp		preference
	 *	ll el d.d.d	length (ll), elements (el), mail server
	 *
	 * CNAME-RR
	 *	(same as ns)
	 * PTR-RR
	 *	(same as ns)
	 * TXT-RR
	 *	(same as ns; BUT there's ALWAYS only 1 segment)
	 *
	 */
#define _eq3(a) (a[0] == (caddr(rrdata)[2]) && a[1] == (caddr(rrdata)[3]))
	bin_init(retval);
	if (_eq3(DNS_T_SOA)) {
		/* Clib */
		c->refresh = ntohl(*((unsigned long *)(caddr(rrdata)+12)));
		c->serial = ntohl(*((unsigned long *)(caddr(rrdata)+24)));
		c->retry  = ntohl(*((unsigned long *)(caddr(rrdata)+32)));
		c->expire = ntohl(*((unsigned long *)(caddr(rrdata)+36)));
		c->minimum = ntohl(*((unsigned long *)(caddr(rrdata)+40)));
		c->ttl = c->minimum;

		q = caddr(rrdata) + 44;
		q++; q++; q += dns_domain_length(q)+1;
		q++; q++;
		bin_copy(retval, q, dns_domain_length(q));
		list_push(&c->ADM, caddr(retval));
	} else if (_eq3(DNS_T_NS)) {
		q = caddr(rrdata) + 26;
		bin_copy(retval, q, dns_domain_length(q));
		list_push(&c->NS, caddr(retval));
	} else if (_eq3(DNS_T_A)) {
		q = caddr(rrdata) + 24;
		bin_copy(retval, q, 4);
		list_push(&c->MX, caddr(retval));
	} else if (_eq3(DNS_T_MX)) {
		q = caddr(rrdata) + 24;
		bin_copy(retval, q, 2);
		q += 4; /* pref and ll,el */
		bin_cat(retval, q, dns_domain_length(q));
		list_push(&c->MX, caddr(retval));
	} else if (_eq3(DNS_T_CNAME)) {
		q = caddr(rrdata) + 26;
		bin_copy(retval, q, dns_domain_length(q));
		list_push(&c->CNAME, caddr(retval));
	} else if (_eq3(DNS_T_PTR)) {
		q = caddr(rrdata) + 26;
		bin_copy(retval, q, dns_domain_length(q));
		list_push(&c->PTR, caddr(retval));
	} else if (_eq3(DNS_T_TXT)) {
		q = caddr(rrdata) + 26;
		bin_copy(retval, q, dns_domain_length(q));
		list_push(&c->TXT, caddr(retval));
	} else {
		/* didn't really need it */
		mem_free(caddr(retval));
		return 0;
	}

	return 1;
}
/* me-style */
static int inline ldap_arecord(dns_ctx *c, bin_t rrdata)
{
	list_push(&c->A, __parse_ipv4(c, rrdata));
	return 1;
}
static int inline ldap_serial(dns_ctx *c, bin_t rrdata)
{
	register int i, ylen;
	unsigned int Y,M,D, h,m,s;
	struct tm tp;
	time_t trymk;

	if (c->soahack) return 1;	/* skip */

	/* this is NOT a linear number:
	 * it is a timestamp... and should be treated as such
	 */
	if (clen(rrdata) < 15) return 0;

	ylen = clen(rrdata) - 11;
	for (i = Y = 0; i < ylen; i++) {
		Y *= 10;
		Y += str_chtoi(caddr(rrdata)[i]);
	}
	M = str_chtoi(caddr(rrdata)[i]) * 10; i++;
	M += str_chtoi(caddr(rrdata)[i]); i++;
	D = str_chtoi(caddr(rrdata)[i]) * 10; i++;
	D += str_chtoi(caddr(rrdata)[i]); i++;

	h = str_chtoi(caddr(rrdata)[i]) * 10; i++;
	h += str_chtoi(caddr(rrdata)[i]); i++;
	m = str_chtoi(caddr(rrdata)[i]) * 10; i++;
	m += str_chtoi(caddr(rrdata)[i]); i++;
	s = str_chtoi(caddr(rrdata)[i]) * 10; i++;
	s += str_chtoi(caddr(rrdata)[i]); i++;

	tp.tm_sec = s;
	tp.tm_min = m;
	tp.tm_hour = h;
	tp.tm_mday = D;
	tp.tm_mon = M-1;
	tp.tm_year = Y-1900;
	tp.tm_wday = tp.tm_yday = 0;
	if (caddr(rrdata)[i] == 'z') {
		tp.tm_isdst = 0;
		c->serial = mktime(&tp);
	} else {
		tp.tm_isdst = 1;
		trymk  = mktime(&tp);
		if (trymk == -1 || trymk == 11 || trymk == 1) {
			/*
			 * it would not happen if the ldapserver was always
			 * using GMT....
			 */
			tp.tm_isdst = 0;
			trymk  = mktime(&tp);
		}
		c->serial = trymk;
	}

	return 1;
}
static int inline ldap_mxrecord(dns_ctx *c, bin_t rrdata)
{
	if (!c->subreq) {
		list_push(&c->MX, __parse_mx(c, rrdata));
	}
	return 1;
}
static int inline ldap_mail(dns_ctx *c, bin_t rrdata)
{
	if (!c->subreq) {
		list_push(&c->ADM, __parse_email(rrdata));
	}
	return 1;
}
static int inline ldap_cnamerecord(dns_ctx *c, bin_t rrdata)
{
	static char *inaddr_str = "\007in-addr\004arpa";
	/* check to see if we're in in_addr space */
	register char *q;
	
	if (c->request_name) {
		q = c->request_name + dns_domain_length(c->request_name);
		q -= 14; /* in-addr.arpa. */

		if (str_equal(q, inaddr_str)) {
			/* yeehaw */
			list_push(&c->PTR, __parse_name(c, rrdata));
			return 1;
		}
	}
	if (!c->subreq) {
		/* ignore CNAME on subrequest */
		list_push(&c->CNAME, __parse_name(c, rrdata));
	}
	return 1;
}
static int inline ldap_description(dns_ctx *c, bin_t rrdata)
{
	if (!c->subreq) {
		list_push(&c->TXT, __parse_txt(rrdata));
	}
	return 1;
}
static int inline ldap_photo(dns_ctx *c, bin_t rrdata)
{
	list_push(&c->Generic, __parse_generic(rrdata));
	return 1;
}
static int inline ldap_nsrecord(dns_ctx *c, bin_t rrdata)
{
	if (!c->subreq) {
		list_push(&c->NS, __parse_name(c, rrdata));
	} else {
		c->subreq_valid++;
	}
	return 1;
}
static int inline ldap_seealso(dns_ctx *c, bin_t rrdata)
{
	list_push(&c->PTR, __parse_name(c, rrdata));
	return 1;
}
static int inline ldap_soarecord(dns_ctx *c, bin_t rrdata)
{
	if (!c->subreq) {
		return __parse_soa(c, rrdata, 1);
	}
	return 0;
}
static void inline handle_ldap_rrdata(dns_ctx *c, char *attr, bin_t rrdata)
{
	switch (attr[0]) {
	case 'a':
		/* aRecord */
		ldap_arecord(c, rrdata);
		break;
	case 'c': /* cNAMERecord */
		ldap_cnamerecord(c, rrdata);
		break;
	case 'd':
		if (attr[1] == 'n') {
			if (attr[2] == 's') {
				/* dnsRecord */
				switch (ldapdns.dn_mode) {
				case DN_MODE_RFC1279:
					ldap_dnsrecord_rfc1279(c, rrdata);
					break;
				case DN_MODE_MSDNS:
					ldap_dnsrecord_msdns(c, rrdata);
					break;
				};
			}
		} else {
			/* description */
			ldap_description(c, rrdata);
		}
		break;
	case 'm':
		if (attr[1] == 'o') {
			break;

		} else if (attr[1] == 'x') {
			/* mXRecord */
			ldap_mxrecord(c, rrdata);
		} else {
			/* mail */
			ldap_mail(c, rrdata);
		}
		break;
	case 'n':
		/* nSRecord */
		ldap_nsrecord(c, rrdata);
		break;
	case 'p': /* generic record (photo) */
		ldap_photo(c, rrdata);
		break;
	case 's':
		/* sOARecord */
		if (attr[1] == 'e') {
			/* seeAlso */
			ldap_seealso(c, rrdata);
		} else {
			/* sOARecord */
			ldap_soarecord(c, rrdata);
		}
		break;
	};
}
int ldap_load_dns_attributes(dns_ctx *c, char **dn, int zonef)
{
	/* should be safe; barring any strangeness from ldap client lib */
	BerElement *ber;
	char *attr, *val;
	struct berval **bvals;
	bin_t rrdata;
	int i, len;
	LDAPMessage *m;

	m = ldap_first_entry(c->c->ldap_con, c->message);
	if (!m) {
		return 0; /* out of entries */
	}

	if (dn) {
		*dn = ldap_get_dn(c->c->ldap_con, m);
	}

	bin_init(rrdata);
	attr = ldap_first_attribute(c->c->ldap_con, m, &ber);
	while (attr) {
		if (!m) break; /* weird */

		bvals = ldap_get_values_len(c->c->ldap_con, m, attr);
		if (!bvals) /* server problem could halt here */
			break;

		str_lc(attr);

		for (i = 0; bvals[i]; i++) {
			len = bvals[i]->bv_len;
			if (len < 1)
				continue;

			val = bvals[i]->bv_val;
			if (!val) /* should never happen */
				continue;

			if (attr[0] == 'a' && attr[1] == 's') {
				/* associated Domain */
				c->adlen = bvals[i]->bv_len;
				continue;
			}


			/* convert to bin */
			bin_copy(rrdata, val, bvals[i]->bv_len);

			if (attr[0] == 'm' && attr[1] == 'o') {
				/* modify timestamp */
				if (zonef) ldap_serial(c, rrdata);
				continue;
			}

			handle_ldap_rrdata(c, attr, rrdata);
		}

		ldap_value_free_len(bvals);
		ldap_memfree(attr);

		attr = ldap_next_attribute(c->c->ldap_con, m, ber);
	}
	ber_free(ber, 0);
	mem_free(caddr(rrdata));
	while (m) m = ldap_next_entry(c->c->ldap_con, m);
	return 1;
}


syntax highlighted by Code2HTML, v. 0.9.1