1 /* this file contains information for navigating the various LDAP schema */
2 #include "ldapdns.h"
3 #include "config.h"
4 #include "dns.h"
5 #include "ip.h"
6 
7 #include <time.h>
8 
ldapdns_list_unique(list_t * p)9 void inline ldapdns_list_unique(list_t *p)
10 {
11 	list_t x = 0;
12 	list_t seen = 0;
13 	list_t lp, sp;
14 
15 	for (lp = (*p); lp; lp = lp->next) {
16 		for (sp = seen; sp; sp = sp->next) {
17 			if (str_equal(sp->str, lp->str)) {
18 				goto HIT;
19 			}
20 		}
21 		list_push(&x, lp->str);
22 		list_push(&seen, lp->str);
23 HIT:
24 		continue;
25 	}
26 
27 	lp = *p;
28 	while (list_pop(&lp));
29 	while (list_pop(&seen));
30 	*p = x;
31 }
32 
ldap_escape(str_t retbuf,char * s)33 static void inline ldap_escape(str_t retbuf, char *s)
34 {
35 	while (*s) {
36 		if (*s == ',') {
37 			str_addch(retbuf, '\\');
38 		}
39 		str_addch(retbuf, *s);
40 		s++;
41 	}
42 }
43 
name_to_ldap(str_t retbuf,char * name)44 void name_to_ldap(str_t retbuf, char *name)
45 {
46 	list_t p = 0;
47 	char *x;
48 	int i;
49 
50 	str_init(retbuf);
51 
52 	switch (ldapdns.dn_mode) {
53 	case DN_MODE_COSINE:
54 	case DN_MODE_LDAPDNS: /* dc=www, dc=example, dc=com */
55 	case DN_MODE_RFC1279: /* dc=www, dc=example, dc=com */
56 		p = split_name_parts(name);
57 		i = 0; while ((x = list_pop(&p))) {
58 			str_cat(retbuf, "dc=");
59 			ldap_escape(retbuf, x);
60 			str_cat(retbuf, ", ");
61 			i++;
62 		}
63 		if (i) str_chopn(retbuf, 2);
64 		break;
65 	case DN_MODE_MSDNS:   /* dc=www.example.com */
66 		str_cat(retbuf, "dc=");
67 		ldap_escape(retbuf, x);
68 		break;
69 	};
70 }
71 
ldap_into_parts(char * dn)72 list_t ldap_into_parts(char *dn)
73 {
74 	/* safe if dn is infact an asciiZ string */
75 	list_t p = 0;
76 	register char *x = dn, *y;
77 	str_t s;
78 
79 	/* split into parts */
80 	str_init(s);
81 	while (*x == ' ' || *x == ',') x++; /* mostly safe :) */
82 	while (*x) {
83 		/* looking for: \s*,\s* but !~ \\. */
84 		if (*x == '\\') {
85 			x++;
86 			str_addch(s, *x);
87 			x++;
88 		} else if (*x == ',') {
89 			/* pull off excess whitespace from previous */
90 			y = strchr(str(s), '\0');
91 			if (y) {
92 				y--;
93 				while (*y == ' ') y--;
94 				if (*y) y++;
95 				if (*y) *y = 0;
96 			}
97 
98 			list_push(&p, str(s));
99 			str_init(s);
100 			while (*x == ',' || *x == ' ') x++;
101 		} else {
102 			str_addch(s, *x);
103 			x++;
104 		}
105 	}
106 
107 	return p;
108 }
109 
ldap_to_name(str_t retbuf,char * dn)110 void ldap_to_name(str_t retbuf, char *dn)
111 {
112 	/* safe if dn is asciiz */
113 	list_t z, b, p = ldap_into_parts(dn);
114 	char *x, *y, *q;
115 
116 	/* we need the suffix here in order to pull it off */
117 	if (ldapdns.ldap_suffix && *ldapdns.ldap_suffix) {
118 		b = ldap_into_parts(ldapdns.ldap_suffix);
119 
120 		for (z = b; z; z = z->next) {
121 			if (str_diffi(z->str, p->str)) {
122 				break;
123 			} else {
124 				q = list_pop(&p);
125 				mem_free(q);
126 			}
127 		}
128 		while ((x = list_pop(&b))) {
129 			if (str_diffi(x, p->str)) {
130 				mem_free(x);
131 				while ((x = list_pop(&b)))
132 					mem_free(x);
133 				break;
134 			}
135 			q = list_pop(&p);
136 			mem_free(q);
137 			mem_free(x);
138 		}
139 	}
140 
141 	/* flip p around; we have to have this in it's own list */
142 	list_reverse(&p);
143 
144 	str_init(retbuf);
145 	while ((x = list_pop(&p))) {
146 		if (str_start(x, "dc=")) {
147 			y = x + 3;
148 			while (*y == ' ') y++;
149 			if (*y != ',') {
150 				str_cat(retbuf, y);
151 				str_addch(retbuf, '.');
152 			}
153 		}
154 		mem_free(x);
155 	}
156 	str_chop(retbuf);
157 }
158 
__parse_soa(dns_ctx * c,bin_t rrdata,int flag)159 static int inline __parse_soa(dns_ctx *c, bin_t rrdata, int flag)
160 {
161 	register unsigned int i, j;
162 	unsigned long ar[5];
163 	register char *q;
164 	str_t tmp, tmp2;
165 
166 	q = caddr(rrdata);
167 	if (flag) {
168 		if (clen(rrdata) >= 1 && *q == '*') {
169 			c->wantdie=1;
170 			return 0;
171 		}
172 		/* don't want anything else */
173 		if (clen(rrdata) == 1)
174 			return 0;
175 	}
176 	if (c->soahack) return 1;	/* skip */
177 
178 	/* remainder is of the format is serial [ttl retry expire minimum] */
179 	for (j = i = 0; j < 5; j++) { /* safe: bounded */
180 		if (!(i < clen(rrdata))) break;
181 		while (isspace(((unsigned int)q[i])) && i < clen(rrdata)) i++;
182 		if (!(i < clen(rrdata))) break;
183 		ar[j] = 0;
184 		while (isdigit(((unsigned int)q[i])) && i < clen(rrdata)) {
185 			ar[j] *= 10;
186 			switch (q[i]) {
187 			case '0': ar[j] += 0; break;
188 			case '1': ar[j] += 1; break;
189 			case '2': ar[j] += 2; break;
190 			case '3': ar[j] += 3; break;
191 			case '4': ar[j] += 4; break;
192 			case '5': ar[j] += 5; break;
193 			case '6': ar[j] += 6; break;
194 			case '7': ar[j] += 7; break;
195 			case '8': ar[j] += 8; break;
196 			case '9': ar[j] += 9; break;
197 			};
198 			i++;
199 		}
200 	}
201 	if (j < 4 || ar[4] == 0) {
202 		/* if j == 1 then... */
203 		switch (j) {
204 		case 2: c->refresh = ar[1];
205 		case 1: if (ar[0]) c->serial = ar[0];
206 		};
207 		return 0;
208 	}
209 	/* these are kept in host-endian form (for now) */
210 	if (ar[0]) c->serial = ar[0];
211 	c->refresh = ar[1];
212 	c->retry = ar[2];
213 	c->expire = ar[3];
214 	c->minimum = ar[4];
215 	c->ttl = c->minimum;
216 	if (i < clen(rrdata)) {
217 		/* grap ADM as well */
218 		str_init(tmp);
219 		str_catb(tmp, caddr(rrdata)+i, clen(rrdata)-i);
220 		name_to_dns_fix(tmp2, str(tmp), 2); /* email address? */
221 		list_push(&c->ADM, str(tmp2));
222 		mem_free(str(tmp));
223 	}
224 	c->soahack = 1;
225 
226 	return 1;
227 }
__parse_name(dns_ctx * c,bin_t rrdata)228 static char inline *__parse_name(dns_ctx *c, bin_t rrdata)
229 {
230 	str_t tmp;
231 	bin_t retval;
232 	int addzone;
233 
234 	/* name is in ascii form.... convert to DNS */
235 	bin_init(retval);
236 	bin_copy(retval, caddr(rrdata), clen(rrdata));
237 	addzone = 0;
238 	if (ldapdns.relative_names) {
239 		/* if . is the last character... */
240 		if (clen(retval) && caddr(retval)[clen(retval)-1] != '.') {
241 			addzone = 1;
242 		}
243 	}
244 	bin_0(retval);
245 	name_to_dns(tmp, (char *)caddr(retval));
246 	if (addzone && c->request_name_zone) {
247 		/* add the zone to the end */
248 		str_cat(tmp, c->request_name_zone);
249 	}
250 	bin_copy(retval, str(tmp), dns_domain_length(str(tmp)));
251 
252 	return (char *)caddr(retval);
253 }
__parse_mx(dns_ctx * c,bin_t rrdata)254 static char inline *__parse_mx(dns_ctx *c, bin_t rrdata)
255 {
256 	str_t tmp;
257 	bin_t retval;
258 	unsigned short pref;
259 	char *q;
260 	register int b, len;
261 	int addzone;
262 
263 	/* name is in ascii form.... convert to DNS */
264 	bin_init(retval);
265 	q = (char *)caddr(rrdata);
266 	len = clen(rrdata);
267 	pref = 0;
268 	while (len > 0 && *q) {
269 		switch (*q) {
270 		case '0': b = 0; break;
271 		case '1': b = 1; break;
272 		case '2': b = 2; break;
273 		case '3': b = 3; break;
274 		case '4': b = 4; break;
275 		case '5': b = 5; break;
276 		case '6': b = 6; break;
277 		case '7': b = 7; break;
278 		case '8': b = 8; break;
279 		case '9': b = 9; break;
280 		default: b = -1; break;
281 		};
282 		if (b == -1) break;
283 		pref *= 10;
284 		pref += b;
285 		q++; len--;
286 	}
287 	while (len > 0 && *q == ' ') { q++; len--; }
288 
289 	bin_copy(retval, q, len);
290 	addzone = 0;
291 	if (ldapdns.relative_names) {
292 		/* if . is the last character... */
293 		if (caddr(retval)[clen(retval)-1] != '.') {
294 			addzone = 1;
295 		}
296 	}
297 	bin_0(retval);
298 	name_to_dns(tmp, (char *)caddr(retval));
299 	if (addzone && c->request_name_zone) {
300 		/* add the zone to the end */
301 		str_cat(tmp, c->request_name_zone);
302 	}
303 
304 	/* Clib */
305 	pref = htons(pref);
306 	bin_copy(retval, (char *)&pref, 2);
307 	bin_cat(retval, str(tmp), dns_domain_length(str(tmp)));
308 	mem_free(str(tmp));
309 
310 	return (char *)caddr(retval);
311 }
__parse_ipv4(dns_ctx * c,bin_t rrdata)312 static char inline *__parse_ipv4(dns_ctx *c, bin_t rrdata)
313 {
314 	/* rrdata is in the form of A.B.C.D; so length bind it... */
315 	unsigned char ip[8];
316 	register int i, j, a, b, r;
317 	bin_t retval;
318 	int cflag;
319 
320 	bin_init(retval);
321 	ip[4] = ip[5] = ip[6] = ip[7] = 0;
322 	for (i = j = a = cflag = 0; (cflag || j < 4) && i < clen(rrdata); i++) {
323 		switch (rrdata->buf[i]) {
324 		case '0': b = 0; break;
325 		case '1': b = 1; break;
326 		case '2': b = 2; break;
327 		case '3': b = 3; break;
328 		case '4': b = 4; break;
329 		case '5': b = 5; break;
330 		case '6': b = 6; break;
331 		case '7': b = 7; break;
332 		case '8': b = 8; break;
333 		case '9': b = 9; break;
334 		case '.': ip[j] = a; a = 0; j++; continue;
335 		case '/': /* i think we're making a subnet-switch */
336 			if (!c) /* fail early */
337 				return 0;
338 			ip[j] = a;
339 			j = 4;
340 			a = 0;
341 			cflag = 1;
342 			continue;
343 		case '=':
344 			if (!c) /* fail early */
345 				return 0;
346 			ip[j] = a;
347 			if (j == 0) {
348 				ip[4] = 0xff;
349 				ip[5] = ip[6] = ip[7] = 0;
350 			} else if (j == 1) {
351 				ip[4] = ip[5] = 0xff;
352 				ip[6] = ip[7] = 0;
353 			} else if (j == 2) {
354 				ip[4] = ip[5] = ip[6] = 0xff;
355 				ip[7] = 0;
356 			} else if (j == 3) {
357 				ip[4] = ip[5] = ip[6] = ip[7] = 0xff;
358 			} else if (j == 4) {
359 				if (ip[4] == 0) {
360 					/* exact match */
361 					ip[4] = ip[5] = ip[6] = ip[7] = 255;
362 				} else if (ip[4] == 255) {
363 					/* 255.0.0.0 */
364 					ip[4] = 255;
365 					ip[5] = ip[6] = ip[7] = 0;
366 				} else {
367 					/* okay /CIDR notation */
368 					r = ip[4];
369 					ip[4] = ip[5] = ip[6] = ip[7] = 255;
370 					if (r < 8) {
371 						ip[4] = (255 << (8 - r));
372 						ip[5] = ip[6] = ip[7] = 0;
373 					} else if (r < 16) {
374 						ip[5] = (255 << (16 - r));
375 						ip[6] = ip[7] = 0;
376 					} else if (r < 24) {
377 						ip[6] = (255 << (24 - r));
378 						ip[7] = 0;
379 					} else {
380 						ip[7] = (255 << (32 - r));
381 					}
382 				}
383 			}
384 			if (ipv4_in_subnet(ip, c->ip)) {
385 				/* reset and start over */
386 				j = b = a = 0;
387 				cflag = 0;
388 				/* note, we also blop
389 				 * 'c' so we can't do this again */
390 				c = (dns_ctx *)0;
391 				continue;
392 			}
393 			return 0; /* don't return anything */
394 		case '%':
395 			if (c && j == 3) {
396 				if (!c->swm) return 0;
397 				/* next chars are "switch" */
398 				b = 0; i++;
399 				while (i < clen(rrdata)) {
400 					/* make sure it matches switch */
401 					if (c->swm[b] != rrdata->buf[i])
402 						return 0;
403 					b++; i++;
404 				}
405 				goto done_parsing_l;
406 			}
407 			/* fall through */
408 		default:
409 			/* invalid */
410 			warning("invalid IPV4 address (%c, %d)", rrdata->buf[i], i);
411 			return 0;
412 		};
413 
414 		/* digit */
415 		a *= 10; a += b;
416 	}
417 done_parsing_l:
418 	ip[j] = a;
419 	bin_copy(retval, ip, 4);
420 	return (char *)caddr(retval);
421 }
__parse_email(bin_t rrdata)422 static char inline *__parse_email(bin_t rrdata)
423 {
424 	str_t tmp;
425 	bin_t retval;
426 
427 	/* name is in ascii form.... convert to DNS */
428 	bin_init(retval);
429 	bin_copy(retval, caddr(rrdata), clen(rrdata));
430 	bin_0(retval);
431 	name_to_dns_fix(tmp, (char *)caddr(retval), 2);
432 	bin_copy(retval, str(tmp), dns_domain_length(str(tmp)));
433 
434 	return (char *)caddr(retval);
435 }
__parse_txt(bin_t rrdata)436 static char inline *__parse_txt(bin_t rrdata)
437 {
438 	str_t tmp;
439 	bin_t retval;
440 
441 	/* don't dns-encode the TXT segment */
442 	bin_init(retval);
443 	bin_addch(retval, clen(rrdata));
444 	bin_cat(retval, caddr(rrdata), clen(rrdata));
445 	bin_0(retval);
446 
447 	return (char *)caddr(retval);
448 }
__parse_generic(bin_t rrdata)449 static char inline *__parse_generic(bin_t rrdata)
450 {
451 	/* return MUST contain length:
452 	 * order:	rr len data....
453 	 */
454 	bin_t retval;
455 	unsigned short n;
456 
457 	bin_init(retval);
458 	bin_copy(retval, caddr(rrdata), 2);
459 	/* n is in host-byte order */
460 	n = clen(rrdata)-2;
461 	bin_copy(retval, (char *)&n, 2);
462 	bin_copy(retval, caddr(rrdata)+2, n);
463 
464 	return (char *)caddr(retval);
465 }
466 
467 /* bind-style */
ldap_dnsrecord_rfc1279(dns_ctx * c,bin_t rrdata)468 static int inline ldap_dnsrecord_rfc1279(dns_ctx *c, bin_t rrdata)
469 {
470 	register char *q;
471 	register int len;
472 	char rr[2];
473 	bin_t tmp;
474 	int r;
475 
476 	/* IN rr [str] */
477 	if (clen(rrdata) < 4) return 0;
478 	if (!(tolower((unsigned int)(caddr(rrdata)[0])) == 'i'
479 	&& tolower((unsigned int)(caddr(rrdata)[1])) == 'n')) {
480 		return 0;
481 	}
482 
483 	/* step over whitespace[s] */
484 	q = caddr(rrdata) + 2; len = clen(rrdata) - 2;
485 	while (len > 0 && (*q == ' ' || *q == '\t')) { q++; len--; }
486 	if (len < 2) return 0;
487 
488 	/* parse word */
489 #define _setrr(x) do { rr[0] = x[0]; rr[1] = x[1]; } while (0)
490 	q[0] = toupper(((unsigned int)q[0])); /* modifying rrdata! */
491 	if (q[0] == 'A') {
492 		/* modifying rrdata */
493 		if (toupper(((unsigned int)q[1])) == 'A') {
494 			/* AAAA: not supported (yet) */
495 			return 0;
496 		}
497 		/* A: address record */
498 		_setrr(DNS_T_A);
499 	} else if (q[0] == 'C') {
500 		/* CNAME: alias */
501 		_setrr(DNS_T_CNAME);
502 	} else if (q[0] == 'T') {
503 		/* TXT: text record */
504 		_setrr(DNS_T_TXT);
505 	} else if (q[0] == 'P') {
506 		/* PTR: pointer-name */
507 		_setrr(DNS_T_PTR);
508 	} else if (q[0] == 'M') {
509 		/* MX: mail exchanger */
510 		_setrr(DNS_T_MX);
511 	} else if (q[0] == 'N') {
512 		/* NS: name server */
513 		_setrr(DNS_T_NS);
514 	} else if (q[0] == 'S') {
515 		/* SOA: start of authority */
516 		_setrr(DNS_T_SOA);
517 	}
518 #undef _setrr
519 
520 	/* parse RR -- pass everything but SOA to their ldapdns parser */
521 	while (len > 0 && (*q != ' ' && *q != '\t')) { q++; len--; }
522 	if (len < 2) return 0;
523 
524 	while (len > 0 && (*q == ' ' || *q == '\t')) { q++; len--; }
525 	if (len < 2) return 0;
526 
527 #define _eq4(x) (rr[0] == x[0] && rr[1] == x[1])
528 	if (_eq4(DNS_T_SOA)) {
529 		/* start of authority */
530 		if (*q == '(') {
531 			q++; len--;
532 			if (len < 2) return 0;
533 			while (len > 0 && (*q == ' ' || *q == '\t')) { q++; len--; }
534 			if (len < 2) return 0;
535 		}
536 		for (r = 0; r < len; r++) {
537 			if (q[r] == ')') {
538 				r--;
539 				while ((q[r] == ' ' || q[r] == '\t') && r > 0)
540 					r--;
541 				len = r;
542 				break;
543 			}
544 		}
545 		/* fall through */
546 	}
547 
548 	bin_init(tmp);
549 	bin_copy(tmp, q, len);
550 	r = 1;
551 	if (_eq4(DNS_T_A)) {
552 		/* disables client differentiation */
553 		list_push(&c->A, __parse_ipv4((dns_ctx *)0, tmp));
554 	} else if (_eq4(DNS_T_PTR)) {
555 		list_push(&c->PTR, __parse_name(c, tmp));
556 	} else if (!c->subreq) {
557 		if (_eq4(DNS_T_MX)) {
558 			list_push(&c->MX, __parse_mx(c, tmp));
559 		} else if (_eq4(DNS_T_CNAME)) {
560 			list_push(&c->CNAME, __parse_name(c, tmp));
561 		} else if (_eq4(DNS_T_NS)) {
562 			list_push(&c->NS, __parse_name(c, tmp));
563 		} else if (_eq4(DNS_T_TXT)) {
564 			list_push(&c->TXT, __parse_txt(tmp));
565 		} else if (_eq4(DNS_T_SOA)) {
566 			r = __parse_soa(c, tmp, 0);
567 		} else
568 			r = 0;
569 	} else
570 		r = 0;
571 #undef _eq4
572 	mem_free(caddr(tmp));
573 	return r;
574 }
575 /* microsoft-style */
ldap_dnsrecord_msdns(dns_ctx * c,bin_t rrdata)576 static int inline ldap_dnsrecord_msdns(dns_ctx *c, bin_t rrdata)
577 {
578 	register char *q;
579 	bin_t retval;
580 
581 	if (clen(rrdata) < 24) return 0;
582 
583 	/* data is in a binary-packed format which i'm not 100% on:
584 	 * each two-character blob is a single octet. some things are
585 	 * in network byte-order, other things are NOT. this can be
586 	 * confusing quick...
587 	 *
588 	 * note that places where I've marked the field as 'xx' means i don't
589 	 * know what it is. 0x means that it's always 00 (in my tests)
590 	 * but that i still don't know what it is...
591 	 *
592 	 * HEADER:
593 	 *
594 	 * xx xx	[0-1]
595 	 * RR RR	16-bit: resource record [2-3]
596 	 * xx xx 0x 0x xx xx 0x 0x [4-11]
597 	 * TT TT TT TT	32-bit: time to live [12-15]
598 	 * 0x 0x 0x 0x xx xx xx xx [16-23]
599 	 * 		-- last dword has significance for A-records
600 	 *
601 	 * what follows is RR-data [24]:
602 	 *
603 	 * SOA-RR:
604 	 * 	SS SS SS SS	serial
605 	 * 	AA AA AA AA	refresh
606 	 * 	BB BB BB BB	retry
607 	 * 	CC CC CC CC	expire
608 	 * 	DD DD DD DD	minimum
609 	 *
610 	 * (44)	ll el d.d.d	length (ll), elements (el), dns-encoded NS1
611 	 * 	ll el d.d.d	length (ll), elements (el), hostmaster
612 	 *
613 	 * NS-RR
614 	 * 	ll el d.d.d	length (ll), elements (el), dns-encoded name
615 	 *
616 	 * A-RR
617 	 *	a. b. c. d.	ip address
618 	 *
619 	 * MX-RR
620 	 *	pp pp		preference
621 	 *	ll el d.d.d	length (ll), elements (el), mail server
622 	 *
623 	 * CNAME-RR
624 	 *	(same as ns)
625 	 * PTR-RR
626 	 *	(same as ns)
627 	 * TXT-RR
628 	 *	(same as ns; BUT there's ALWAYS only 1 segment)
629 	 *
630 	 */
631 #define _eq3(a) (a[0] == (caddr(rrdata)[2]) && a[1] == (caddr(rrdata)[3]))
632 	bin_init(retval);
633 	if (_eq3(DNS_T_SOA)) {
634 		/* Clib */
635 		c->refresh = ntohl(*((unsigned long *)(caddr(rrdata)+12)));
636 		c->serial = ntohl(*((unsigned long *)(caddr(rrdata)+24)));
637 		c->retry  = ntohl(*((unsigned long *)(caddr(rrdata)+32)));
638 		c->expire = ntohl(*((unsigned long *)(caddr(rrdata)+36)));
639 		c->minimum = ntohl(*((unsigned long *)(caddr(rrdata)+40)));
640 		c->ttl = c->minimum;
641 
642 		q = caddr(rrdata) + 44;
643 		q++; q++; q += dns_domain_length(q)+1;
644 		q++; q++;
645 		bin_copy(retval, q, dns_domain_length(q));
646 		list_push(&c->ADM, caddr(retval));
647 	} else if (_eq3(DNS_T_NS)) {
648 		q = caddr(rrdata) + 26;
649 		bin_copy(retval, q, dns_domain_length(q));
650 		list_push(&c->NS, caddr(retval));
651 	} else if (_eq3(DNS_T_A)) {
652 		q = caddr(rrdata) + 24;
653 		bin_copy(retval, q, 4);
654 		list_push(&c->MX, caddr(retval));
655 	} else if (_eq3(DNS_T_MX)) {
656 		q = caddr(rrdata) + 24;
657 		bin_copy(retval, q, 2);
658 		q += 4; /* pref and ll,el */
659 		bin_cat(retval, q, dns_domain_length(q));
660 		list_push(&c->MX, caddr(retval));
661 	} else if (_eq3(DNS_T_CNAME)) {
662 		q = caddr(rrdata) + 26;
663 		bin_copy(retval, q, dns_domain_length(q));
664 		list_push(&c->CNAME, caddr(retval));
665 	} else if (_eq3(DNS_T_PTR)) {
666 		q = caddr(rrdata) + 26;
667 		bin_copy(retval, q, dns_domain_length(q));
668 		list_push(&c->PTR, caddr(retval));
669 	} else if (_eq3(DNS_T_TXT)) {
670 		q = caddr(rrdata) + 26;
671 		bin_copy(retval, q, dns_domain_length(q));
672 		list_push(&c->TXT, caddr(retval));
673 	} else {
674 		/* didn't really need it */
675 		mem_free(caddr(retval));
676 		return 0;
677 	}
678 
679 	return 1;
680 }
681 /* me-style */
ldap_arecord(dns_ctx * c,bin_t rrdata)682 static int inline ldap_arecord(dns_ctx *c, bin_t rrdata)
683 {
684 	list_push(&c->A, __parse_ipv4(c, rrdata));
685 	return 1;
686 }
ldap_serial(dns_ctx * c,bin_t rrdata)687 static int inline ldap_serial(dns_ctx *c, bin_t rrdata)
688 {
689 	register int i, ylen;
690 	unsigned int Y,M,D, h,m,s;
691 	struct tm tp;
692 	time_t trymk;
693 
694 	if (c->soahack) return 1;	/* skip */
695 
696 	/* this is NOT a linear number:
697 	 * it is a timestamp... and should be treated as such
698 	 */
699 	if (clen(rrdata) < 15) return 0;
700 
701 	ylen = clen(rrdata) - 11;
702 	for (i = Y = 0; i < ylen; i++) {
703 		Y *= 10;
704 		Y += str_chtoi(caddr(rrdata)[i]);
705 	}
706 	M = str_chtoi(caddr(rrdata)[i]) * 10; i++;
707 	M += str_chtoi(caddr(rrdata)[i]); i++;
708 	D = str_chtoi(caddr(rrdata)[i]) * 10; i++;
709 	D += str_chtoi(caddr(rrdata)[i]); i++;
710 
711 	h = str_chtoi(caddr(rrdata)[i]) * 10; i++;
712 	h += str_chtoi(caddr(rrdata)[i]); i++;
713 	m = str_chtoi(caddr(rrdata)[i]) * 10; i++;
714 	m += str_chtoi(caddr(rrdata)[i]); i++;
715 	s = str_chtoi(caddr(rrdata)[i]) * 10; i++;
716 	s += str_chtoi(caddr(rrdata)[i]); i++;
717 
718 	tp.tm_sec = s;
719 	tp.tm_min = m;
720 	tp.tm_hour = h;
721 	tp.tm_mday = D;
722 	tp.tm_mon = M-1;
723 	tp.tm_year = Y-1900;
724 	tp.tm_wday = tp.tm_yday = 0;
725 	if (caddr(rrdata)[i] == 'z') {
726 		tp.tm_isdst = 0;
727 		c->serial = mktime(&tp);
728 	} else {
729 		tp.tm_isdst = 1;
730 		trymk  = mktime(&tp);
731 		if (trymk == -1 || trymk == 11 || trymk == 1) {
732 			/*
733 			 * it would not happen if the ldapserver was always
734 			 * using GMT....
735 			 */
736 			tp.tm_isdst = 0;
737 			trymk  = mktime(&tp);
738 		}
739 		c->serial = trymk;
740 	}
741 
742 	return 1;
743 }
ldap_mxrecord(dns_ctx * c,bin_t rrdata)744 static int inline ldap_mxrecord(dns_ctx *c, bin_t rrdata)
745 {
746 	if (!c->subreq) {
747 		list_push(&c->MX, __parse_mx(c, rrdata));
748 	}
749 	return 1;
750 }
ldap_mail(dns_ctx * c,bin_t rrdata)751 static int inline ldap_mail(dns_ctx *c, bin_t rrdata)
752 {
753 	if (!c->subreq) {
754 		list_push(&c->ADM, __parse_email(rrdata));
755 	}
756 	return 1;
757 }
ldap_cnamerecord(dns_ctx * c,bin_t rrdata)758 static int inline ldap_cnamerecord(dns_ctx *c, bin_t rrdata)
759 {
760 	static char *inaddr_str = "\007in-addr\004arpa";
761 	/* check to see if we're in in_addr space */
762 	register char *q;
763 
764 	if (c->request_name) {
765 		q = c->request_name + dns_domain_length(c->request_name);
766 		q -= 14; /* in-addr.arpa. */
767 
768 		if (str_equal(q, inaddr_str)) {
769 			/* yeehaw */
770 			list_push(&c->PTR, __parse_name(c, rrdata));
771 			return 1;
772 		}
773 	}
774 	if (!c->subreq) {
775 		/* ignore CNAME on subrequest */
776 		list_push(&c->CNAME, __parse_name(c, rrdata));
777 	}
778 	return 1;
779 }
ldap_description(dns_ctx * c,bin_t rrdata)780 static int inline ldap_description(dns_ctx *c, bin_t rrdata)
781 {
782 	if (!c->subreq) {
783 		list_push(&c->TXT, __parse_txt(rrdata));
784 	}
785 	return 1;
786 }
ldap_photo(dns_ctx * c,bin_t rrdata)787 static int inline ldap_photo(dns_ctx *c, bin_t rrdata)
788 {
789 	list_push(&c->Generic, __parse_generic(rrdata));
790 	return 1;
791 }
ldap_nsrecord(dns_ctx * c,bin_t rrdata)792 static int inline ldap_nsrecord(dns_ctx *c, bin_t rrdata)
793 {
794 	if (!c->subreq) {
795 		list_push(&c->NS, __parse_name(c, rrdata));
796 	} else {
797 		c->subreq_valid++;
798 	}
799 	return 1;
800 }
ldap_seealso(dns_ctx * c,bin_t rrdata)801 static int inline ldap_seealso(dns_ctx *c, bin_t rrdata)
802 {
803 	list_push(&c->PTR, __parse_name(c, rrdata));
804 	return 1;
805 }
ldap_soarecord(dns_ctx * c,bin_t rrdata)806 static int inline ldap_soarecord(dns_ctx *c, bin_t rrdata)
807 {
808 	if (!c->subreq) {
809 		return __parse_soa(c, rrdata, 1);
810 	}
811 	return 0;
812 }
handle_ldap_rrdata(dns_ctx * c,char * attr,bin_t rrdata)813 static void inline handle_ldap_rrdata(dns_ctx *c, char *attr, bin_t rrdata)
814 {
815 	switch (attr[0]) {
816 	case 'a':
817 		/* aRecord */
818 		ldap_arecord(c, rrdata);
819 		break;
820 	case 'c': /* cNAMERecord */
821 		ldap_cnamerecord(c, rrdata);
822 		break;
823 	case 'd':
824 		if (attr[1] == 'n') {
825 			if (attr[2] == 's') {
826 				/* dnsRecord */
827 				switch (ldapdns.dn_mode) {
828 				case DN_MODE_RFC1279:
829 					ldap_dnsrecord_rfc1279(c, rrdata);
830 					break;
831 				case DN_MODE_MSDNS:
832 					ldap_dnsrecord_msdns(c, rrdata);
833 					break;
834 				};
835 			}
836 		} else {
837 			/* description */
838 			ldap_description(c, rrdata);
839 		}
840 		break;
841 	case 'm':
842 		if (attr[1] == 'o') {
843 			break;
844 
845 		} else if (attr[1] == 'x') {
846 			/* mXRecord */
847 			ldap_mxrecord(c, rrdata);
848 		} else {
849 			/* mail */
850 			ldap_mail(c, rrdata);
851 		}
852 		break;
853 	case 'n':
854 		/* nSRecord */
855 		ldap_nsrecord(c, rrdata);
856 		break;
857 	case 'p': /* generic record (photo) */
858 		ldap_photo(c, rrdata);
859 		break;
860 	case 's':
861 		/* sOARecord */
862 		if (attr[1] == 'e') {
863 			/* seeAlso */
864 			ldap_seealso(c, rrdata);
865 		} else {
866 			/* sOARecord */
867 			ldap_soarecord(c, rrdata);
868 		}
869 		break;
870 	};
871 }
ldap_load_dns_attributes(dns_ctx * c,char ** dn,int zonef)872 int ldap_load_dns_attributes(dns_ctx *c, char **dn, int zonef)
873 {
874 	/* should be safe; barring any strangeness from ldap client lib */
875 	BerElement *ber;
876 	char *attr, *val;
877 	struct berval **bvals;
878 	bin_t rrdata;
879 	int i, len;
880 	LDAPMessage *m;
881 
882 	m = ldap_first_entry(c->c->ldap_con, c->message);
883 	if (!m) {
884 		return 0; /* out of entries */
885 	}
886 
887 	if (dn) {
888 		*dn = ldap_get_dn(c->c->ldap_con, m);
889 	}
890 
891 	bin_init(rrdata);
892 	attr = ldap_first_attribute(c->c->ldap_con, m, &ber);
893 	while (attr) {
894 		if (!m) break; /* weird */
895 
896 		bvals = ldap_get_values_len(c->c->ldap_con, m, attr);
897 		if (!bvals) /* server problem could halt here */
898 			break;
899 
900 		str_lc(attr);
901 
902 		for (i = 0; bvals[i]; i++) {
903 			len = bvals[i]->bv_len;
904 			if (len < 1)
905 				continue;
906 
907 			val = bvals[i]->bv_val;
908 			if (!val) /* should never happen */
909 				continue;
910 
911 			if (attr[0] == 'a' && attr[1] == 's') {
912 				/* associated Domain */
913 				c->adlen = bvals[i]->bv_len;
914 				continue;
915 			}
916 
917 
918 			/* convert to bin */
919 			bin_copy(rrdata, val, bvals[i]->bv_len);
920 
921 			if (attr[0] == 'm' && attr[1] == 'o') {
922 				/* modify timestamp */
923 				if (zonef) ldap_serial(c, rrdata);
924 				continue;
925 			}
926 
927 			handle_ldap_rrdata(c, attr, rrdata);
928 		}
929 
930 		ldap_value_free_len(bvals);
931 		ldap_memfree(attr);
932 
933 		attr = ldap_next_attribute(c->c->ldap_con, m, ber);
934 	}
935 	ber_free(ber, 0);
936 	mem_free(caddr(rrdata));
937 	while (m) m = ldap_next_entry(c->c->ldap_con, m);
938 	return 1;
939 }
940