xref: /dragonfly/lib/libc/resolv/res_findzonecut.c (revision 8af44722)
1 /*
2  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (c) 1999 by Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  *
17  * $Id: res_findzonecut.c,v 1.10 2005/10/11 00:10:16 marka Exp $
18  */
19 
20 /* Import. */
21 
22 #include "port_before.h"
23 
24 #include <sys/param.h>
25 #include <sys/socket.h>
26 #include <sys/time.h>
27 
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <arpa/nameser.h>
31 
32 #include <errno.h>
33 #include <limits.h>
34 #include <netdb.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 
40 #include "isc/list.h"
41 
42 #include "port_after.h"
43 
44 #include <resolv.h>
45 
46 /* Data structures. */
47 
48 typedef struct rr_a {
49 	LINK(struct rr_a)	link;
50 	union res_sockaddr_union addr;
51 } rr_a;
52 typedef LIST(rr_a) rrset_a;
53 
54 typedef struct rr_ns {
55 	LINK(struct rr_ns)	link;
56 	const char *		name;
57 	unsigned int		flags;
58 	rrset_a			addrs;
59 } rr_ns;
60 typedef LIST(rr_ns) rrset_ns;
61 
62 #define	RR_NS_HAVE_V4		0x01
63 #define	RR_NS_HAVE_V6		0x02
64 
65 /* Forward. */
66 
67 static int	satisfy(res_state, const char *, rrset_ns *,
68 			union res_sockaddr_union *, int);
69 static int	add_addrs(res_state, rr_ns *,
70 			  union res_sockaddr_union *, int);
71 static int	get_soa(res_state, const char *, ns_class, int,
72 			char *, size_t, char *, size_t,
73 			rrset_ns *);
74 static int	get_ns(res_state, const char *, ns_class, int, rrset_ns *);
75 static int	get_glue(res_state, ns_class, int, rrset_ns *);
76 static int	save_ns(res_state, ns_msg *, ns_sect,
77 			const char *, ns_class, int, rrset_ns *);
78 static int	save_a(res_state, ns_msg *, ns_sect,
79 		       const char *, ns_class, int, rr_ns *);
80 static void	free_nsrrset(rrset_ns *);
81 static void	free_nsrr(rrset_ns *, rr_ns *);
82 static rr_ns *	find_ns(rrset_ns *, const char *);
83 static int	do_query(res_state, const char *, ns_class, ns_type,
84 			 u_char *, ns_msg *);
85 static void	res_dprintf(const char *, ...) ISC_FORMAT_PRINTF(1, 2);
86 
87 /* Macros. */
88 
89 #define DPRINTF(x) do {\
90 		int save_errno = errno; \
91 		if ((statp->options & RES_DEBUG) != 0U) res_dprintf x; \
92 		errno = save_errno; \
93 	} while (0)
94 
95 /* Public. */
96 
97 /*%
98  *	find enclosing zone for a <dname,class>, and some server addresses
99  *
100  * parameters:
101  *\li	res - resolver context to work within (is modified)
102  *\li	dname - domain name whose enclosing zone is desired
103  *\li	class - class of dname (and its enclosing zone)
104  *\li	zname - found zone name
105  *\li	zsize - allocated size of zname
106  *\li	addrs - found server addresses
107  *\li	naddrs - max number of addrs
108  *
109  * return values:
110  *\li	< 0 - an error occurred (check errno)
111  *\li	= 0 - zname is now valid, but addrs[] wasn't changed
112  *\li	> 0 - zname is now valid, and return value is number of addrs[] found
113  *
114  * notes:
115  *\li	this function calls res_nsend() which means it depends on correctly
116  *	functioning recursive nameservers (usually defined in /etc/resolv.conf
117  *	or its local equivilent).
118  *
119  *\li	we start by asking for an SOA<dname,class>.  if we get one as an
120  *	answer, that just means <dname,class> is a zone top, which is fine.
121  *	more than likely we'll be told to go pound sand, in the form of a
122  *	negative answer.
123  *
124  *\li	note that we are not prepared to deal with referrals since that would
125  *	only come from authority servers and our correctly functioning local
126  *	recursive server would have followed the referral and got us something
127  *	more definite.
128  *
129  *\li	if the authority section contains an SOA, this SOA should also be the
130  *	closest enclosing zone, since any intermediary zone cuts would've been
131  *	returned as referrals and dealt with by our correctly functioning local
132  *	recursive name server.  but an SOA in the authority section should NOT
133  *	match our dname (since that would have been returned in the answer
134  *	section).  an authority section SOA has to be "above" our dname.
135  *
136  *\li	however, since authority section SOA's were once optional, it's
137  *	possible that we'll have to go hunting for the enclosing SOA by
138  *	ripping labels off the front of our dname -- this is known as "doing
139  *	it the hard way."
140  *
141  *\li	ultimately we want some server addresses, which are ideally the ones
142  *	pertaining to the SOA.MNAME, but only if there is a matching NS RR.
143  *	so the second phase (after we find an SOA) is to go looking for the
144  *	NS RRset for that SOA's zone.
145  *
146  *\li	no answer section processed by this code is allowed to contain CNAME
147  *	or DNAME RR's.  for the SOA query this means we strip a label and
148  *	keep going.  for the NS and A queries this means we just give up.
149  */
150 
151 #ifndef _LIBC
152 int
153 res_findzonecut(res_state statp, const char *dname, ns_class class, int opts,
154 		char *zname, size_t zsize, struct in_addr *addrs, int naddrs)
155 {
156 	int result, i;
157 	union res_sockaddr_union *u;
158 
159 
160 	opts |= RES_IPV4ONLY;
161 	opts &= ~RES_IPV6ONLY;
162 
163 	u = calloc(naddrs, sizeof(*u));
164 	if (u == NULL)
165 		return(-1);
166 
167 	result = res_findzonecut2(statp, dname, class, opts, zname, zsize,
168 				  u, naddrs);
169 
170 	for (i = 0; i < result; i++) {
171 		addrs[i] = u[i].sin.sin_addr;
172 	}
173 	free(u);
174 	return (result);
175 }
176 #endif
177 int
178 res_findzonecut2(res_state statp, const char *dname, ns_class class, int opts,
179 		 char *zname, size_t zsize, union res_sockaddr_union *addrs,
180 		 int naddrs)
181 {
182 	char mname[NS_MAXDNAME];
183 	u_long save_pfcode;
184 	rrset_ns nsrrs;
185 	int n;
186 
187 	DPRINTF(("START dname='%s' class=%s, zsize=%ld, naddrs=%d",
188 		 dname, p_class(class), (long)zsize, naddrs));
189 	save_pfcode = statp->pfcode;
190 	statp->pfcode |= RES_PRF_HEAD2 | RES_PRF_HEAD1 | RES_PRF_HEADX |
191 			 RES_PRF_QUES | RES_PRF_ANS |
192 			 RES_PRF_AUTH | RES_PRF_ADD;
193 	INIT_LIST(nsrrs);
194 
195 	DPRINTF(("get the soa, and see if it has enough glue"));
196 	if ((n = get_soa(statp, dname, class, opts, zname, zsize,
197 			 mname, sizeof mname, &nsrrs)) < 0 ||
198 	    ((opts & RES_EXHAUSTIVE) == 0 &&
199 	     (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0))
200 		goto done;
201 
202 	DPRINTF(("get the ns rrset and see if it has enough glue"));
203 	if ((n = get_ns(statp, zname, class, opts, &nsrrs)) < 0 ||
204 	    ((opts & RES_EXHAUSTIVE) == 0 &&
205 	     (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0))
206 		goto done;
207 
208 	DPRINTF(("get the missing glue and see if it's finally enough"));
209 	if ((n = get_glue(statp, class, opts, &nsrrs)) >= 0)
210 		n = satisfy(statp, mname, &nsrrs, addrs, naddrs);
211 
212  done:
213 	DPRINTF(("FINISH n=%d (%s)", n, (n < 0) ? strerror(errno) : "OK"));
214 	free_nsrrset(&nsrrs);
215 	statp->pfcode = save_pfcode;
216 	return (n);
217 }
218 
219 /* Private. */
220 
221 static int
222 satisfy(res_state statp, const char *mname, rrset_ns *nsrrsp,
223 	union res_sockaddr_union *addrs, int naddrs)
224 {
225 	rr_ns *nsrr;
226 	int n, x;
227 
228 	n = 0;
229 	nsrr = find_ns(nsrrsp, mname);
230 	if (nsrr != NULL) {
231 		x = add_addrs(statp, nsrr, addrs, naddrs);
232 		addrs += x;
233 		naddrs -= x;
234 		n += x;
235 	}
236 	for (nsrr = HEAD(*nsrrsp);
237 	     nsrr != NULL && naddrs > 0;
238 	     nsrr = NEXT(nsrr, link))
239 		if (ns_samename(nsrr->name, mname) != 1) {
240 			x = add_addrs(statp, nsrr, addrs, naddrs);
241 			addrs += x;
242 			naddrs -= x;
243 			n += x;
244 		}
245 	DPRINTF(("satisfy(%s): %d", mname, n));
246 	return (n);
247 }
248 
249 static int
250 add_addrs(res_state statp, rr_ns *nsrr,
251 	  union res_sockaddr_union *addrs, int naddrs)
252 {
253 	rr_a *arr;
254 	int n = 0;
255 
256 	for (arr = HEAD(nsrr->addrs); arr != NULL; arr = NEXT(arr, link)) {
257 		if (naddrs <= 0)
258 			return (0);
259 		*addrs++ = arr->addr;
260 		naddrs--;
261 		n++;
262 	}
263 	DPRINTF(("add_addrs: %d", n));
264 	return (n);
265 }
266 
267 static int
268 get_soa(res_state statp, const char *dname, ns_class class, int opts,
269 	char *zname, size_t zsize, char *mname, size_t msize,
270 	rrset_ns *nsrrsp)
271 {
272 	char tname[NS_MAXDNAME];
273 	u_char *resp = NULL;
274 	int n, i, ancount, nscount;
275 	ns_sect sect;
276 	ns_msg msg;
277 	u_int rcode;
278 
279 	/*
280 	 * Find closest enclosing SOA, even if it's for the root zone.
281 	 */
282 
283 	/* First canonicalize dname (exactly one unescaped trailing "."). */
284 	if (ns_makecanon(dname, tname, sizeof tname) < 0)
285 		goto cleanup;
286 	dname = tname;
287 
288 	resp = malloc(NS_MAXMSG);
289 	if (resp == NULL)
290 		goto cleanup;
291 
292 	/* Now grovel the subdomains, hunting for an SOA answer or auth. */
293 	for (;;) {
294 		/* Leading or inter-label '.' are skipped here. */
295 		while (*dname == '.')
296 			dname++;
297 
298 		/* Is there an SOA? */
299 		n = do_query(statp, dname, class, ns_t_soa, resp, &msg);
300 		if (n < 0) {
301 			DPRINTF(("get_soa: do_query('%s', %s) failed (%d)",
302 				 dname, p_class(class), n));
303 			goto cleanup;
304 		}
305 		if (n > 0) {
306 			DPRINTF(("get_soa: CNAME or DNAME found"));
307 			sect = ns_s_max, n = 0;
308 		} else {
309 			rcode = ns_msg_getflag(msg, ns_f_rcode);
310 			ancount = ns_msg_count(msg, ns_s_an);
311 			nscount = ns_msg_count(msg, ns_s_ns);
312 			if (ancount > 0 && rcode == ns_r_noerror)
313 				sect = ns_s_an, n = ancount;
314 			else if (nscount > 0)
315 				sect = ns_s_ns, n = nscount;
316 			else
317 				sect = ns_s_max, n = 0;
318 		}
319 		for (i = 0; i < n; i++) {
320 			const char *t;
321 			const u_char *rdata;
322 			ns_rr rr;
323 
324 			if (ns_parserr(&msg, sect, i, &rr) < 0) {
325 				DPRINTF(("get_soa: ns_parserr(%s, %d) failed",
326 					 p_section(sect, ns_o_query), i));
327 				goto cleanup;
328 			}
329 			if (ns_rr_type(rr) == ns_t_cname ||
330 			    ns_rr_type(rr) == ns_t_dname)
331 				break;
332 			if (ns_rr_type(rr) != ns_t_soa ||
333 			    ns_rr_class(rr) != class)
334 				continue;
335 			t = ns_rr_name(rr);
336 			switch (sect) {
337 			case ns_s_an:
338 				if (ns_samedomain(dname, t) == 0) {
339 					DPRINTF(
340 				    ("get_soa: ns_samedomain('%s', '%s') == 0",
341 						dname, t)
342 						);
343 					errno = EPROTOTYPE;
344 					goto cleanup;
345 				}
346 				break;
347 			case ns_s_ns:
348 				if (ns_samename(dname, t) == 1 ||
349 				    ns_samedomain(dname, t) == 0) {
350 					DPRINTF(
351 		       ("get_soa: ns_samename() || !ns_samedomain('%s', '%s')",
352 						dname, t)
353 						);
354 					errno = EPROTOTYPE;
355 					goto cleanup;
356 				}
357 				break;
358 			default:
359 				abort();
360 			}
361 			if (strlen(t) + 1 > zsize) {
362 				DPRINTF(("get_soa: zname(%lu) too small (%lu)",
363 					 (unsigned long)zsize,
364 					 (unsigned long)strlen(t) + 1));
365 				errno = EMSGSIZE;
366 				goto cleanup;
367 			}
368 			strcpy(zname, t);
369 			rdata = ns_rr_rdata(rr);
370 			if (ns_name_uncompress(resp, ns_msg_end(msg), rdata,
371 					       mname, msize) < 0) {
372 				DPRINTF(("get_soa: ns_name_uncompress failed")
373 					);
374 				goto cleanup;
375 			}
376 			if (save_ns(statp, &msg, ns_s_ns,
377 				    zname, class, opts, nsrrsp) < 0) {
378 				DPRINTF(("get_soa: save_ns failed"));
379 				goto cleanup;
380 			}
381 			free(resp);
382 			return (0);
383 		}
384 
385 		/* If we're out of labels, then not even "." has an SOA! */
386 		if (*dname == '\0')
387 			break;
388 
389 		/* Find label-terminating "."; top of loop will skip it. */
390 		while (*dname != '.') {
391 			if (*dname == '\\')
392 				if (*++dname == '\0') {
393 					errno = EMSGSIZE;
394 					goto cleanup;
395 				}
396 			dname++;
397 		}
398 	}
399 	DPRINTF(("get_soa: out of labels"));
400 	errno = EDESTADDRREQ;
401  cleanup:
402 	if (resp != NULL)
403 		free(resp);
404 	return (-1);
405 }
406 
407 static int
408 get_ns(res_state statp, const char *zname, ns_class class, int opts,
409       rrset_ns *nsrrsp)
410 {
411 	u_char *resp;
412 	ns_msg msg;
413 	int n;
414 
415 	resp = malloc(NS_MAXMSG);
416 	if (resp == NULL)
417 		return (-1);
418 
419 	/* Go and get the NS RRs for this zone. */
420 	n = do_query(statp, zname, class, ns_t_ns, resp, &msg);
421 	if (n != 0) {
422 		DPRINTF(("get_ns: do_query('%s', %s) failed (%d)",
423 			 zname, p_class(class), n));
424 		free(resp);
425 		return (-1);
426 	}
427 
428 	/* Remember the NS RRs and associated A RRs that came back. */
429 	if (save_ns(statp, &msg, ns_s_an, zname, class, opts, nsrrsp) < 0) {
430 		DPRINTF(("get_ns save_ns('%s', %s) failed",
431 			 zname, p_class(class)));
432 		free(resp);
433 		return (-1);
434 	}
435 
436 	free(resp);
437 	return (0);
438 }
439 
440 static int
441 get_glue(res_state statp, ns_class class, int opts, rrset_ns *nsrrsp) {
442 	rr_ns *nsrr, *nsrr_n;
443 	u_char *resp;
444 
445 	resp = malloc(NS_MAXMSG);
446 	if (resp == NULL)
447 		return(-1);
448 
449 	/* Go and get the A RRs for each empty NS RR on our list. */
450 	for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = nsrr_n) {
451 		ns_msg msg;
452 		int n;
453 
454 		nsrr_n = NEXT(nsrr, link);
455 
456 		if ((nsrr->flags & RR_NS_HAVE_V4) == 0) {
457 			n = do_query(statp, nsrr->name, class, ns_t_a,
458 				     resp, &msg);
459 			if (n < 0) {
460 				DPRINTF(
461 				       ("get_glue: do_query('%s', %s') failed",
462 					nsrr->name, p_class(class)));
463 				goto cleanup;
464 			}
465 			if (n > 0) {
466 				DPRINTF((
467 			"get_glue: do_query('%s', %s') CNAME or DNAME found",
468 					 nsrr->name, p_class(class)));
469 			}
470 			if (save_a(statp, &msg, ns_s_an, nsrr->name, class,
471 				   opts, nsrr) < 0) {
472 				DPRINTF(("get_glue: save_r('%s', %s) failed",
473 					 nsrr->name, p_class(class)));
474 				goto cleanup;
475 			}
476 		}
477 
478 		if ((nsrr->flags & RR_NS_HAVE_V6) == 0) {
479 			n = do_query(statp, nsrr->name, class, ns_t_aaaa,
480 				     resp, &msg);
481 			if (n < 0) {
482 				DPRINTF(
483 				       ("get_glue: do_query('%s', %s') failed",
484 					nsrr->name, p_class(class)));
485 				goto cleanup;
486 			}
487 			if (n > 0) {
488 				DPRINTF((
489 			"get_glue: do_query('%s', %s') CNAME or DNAME found",
490 					 nsrr->name, p_class(class)));
491 			}
492 			if (save_a(statp, &msg, ns_s_an, nsrr->name, class,
493 				   opts, nsrr) < 0) {
494 				DPRINTF(("get_glue: save_r('%s', %s) failed",
495 					 nsrr->name, p_class(class)));
496 				goto cleanup;
497 			}
498 		}
499 
500 		/* If it's still empty, it's just chaff. */
501 		if (EMPTY(nsrr->addrs)) {
502 			DPRINTF(("get_glue: removing empty '%s' NS",
503 				 nsrr->name));
504 			free_nsrr(nsrrsp, nsrr);
505 		}
506 	}
507 	free(resp);
508 	return (0);
509 
510  cleanup:
511 	free(resp);
512 	return (-1);
513 }
514 
515 static int
516 save_ns(res_state statp, ns_msg *msg, ns_sect sect,
517 	const char *owner, ns_class class, int opts,
518 	rrset_ns *nsrrsp)
519 {
520 	int i;
521 
522 	for (i = 0; i < ns_msg_count(*msg, sect); i++) {
523 		char tname[MAXDNAME];
524 		const u_char *rdata;
525 		rr_ns *nsrr;
526 		ns_rr rr;
527 
528 		if (ns_parserr(msg, sect, i, &rr) < 0) {
529 			DPRINTF(("save_ns: ns_parserr(%s, %d) failed",
530 				 p_section(sect, ns_o_query), i));
531 			return (-1);
532 		}
533 		if (ns_rr_type(rr) != ns_t_ns ||
534 		    ns_rr_class(rr) != class ||
535 		    ns_samename(ns_rr_name(rr), owner) != 1)
536 			continue;
537 		nsrr = find_ns(nsrrsp, ns_rr_name(rr));
538 		if (nsrr == NULL) {
539 			nsrr = malloc(sizeof *nsrr);
540 			if (nsrr == NULL) {
541 				DPRINTF(("save_ns: malloc failed"));
542 				return (-1);
543 			}
544 			rdata = ns_rr_rdata(rr);
545 			if (ns_name_uncompress(ns_msg_base(*msg),
546 					       ns_msg_end(*msg), rdata,
547 					       tname, sizeof tname) < 0) {
548 				DPRINTF(("save_ns: ns_name_uncompress failed")
549 					);
550 				free(nsrr);
551 				return (-1);
552 			}
553 			nsrr->name = strdup(tname);
554 			if (nsrr->name == NULL) {
555 				DPRINTF(("save_ns: strdup failed"));
556 				free(nsrr);
557 				return (-1);
558 			}
559 			INIT_LINK(nsrr, link);
560 			INIT_LIST(nsrr->addrs);
561 			nsrr->flags = 0;
562 			APPEND(*nsrrsp, nsrr, link);
563 		}
564 		if (save_a(statp, msg, ns_s_ar,
565 			   nsrr->name, class, opts, nsrr) < 0) {
566 			DPRINTF(("save_ns: save_r('%s', %s) failed",
567 				 nsrr->name, p_class(class)));
568 			return (-1);
569 		}
570 	}
571 	return (0);
572 }
573 
574 static int
575 save_a(res_state statp, ns_msg *msg, ns_sect sect,
576        const char *owner, ns_class class, int opts,
577        rr_ns *nsrr)
578 {
579 	int i;
580 
581 	for (i = 0; i < ns_msg_count(*msg, sect); i++) {
582 		ns_rr rr;
583 		rr_a *arr;
584 
585 		if (ns_parserr(msg, sect, i, &rr) < 0) {
586 			DPRINTF(("save_a: ns_parserr(%s, %d) failed",
587 				 p_section(sect, ns_o_query), i));
588 			return (-1);
589 		}
590 		if ((ns_rr_type(rr) != ns_t_a &&
591 		     ns_rr_type(rr) != ns_t_aaaa) ||
592 		    ns_rr_class(rr) != class ||
593 		    ns_samename(ns_rr_name(rr), owner) != 1 ||
594 		    ns_rr_rdlen(rr) != NS_INADDRSZ)
595 			continue;
596 		if ((opts & RES_IPV6ONLY) != 0 && ns_rr_type(rr) != ns_t_aaaa)
597 			continue;
598 		if ((opts & RES_IPV4ONLY) != 0 && ns_rr_type(rr) != ns_t_a)
599 			continue;
600 		arr = malloc(sizeof *arr);
601 		if (arr == NULL) {
602 			DPRINTF(("save_a: malloc failed"));
603 			return (-1);
604 		}
605 		INIT_LINK(arr, link);
606 		memset(&arr->addr, 0, sizeof(arr->addr));
607 		switch (ns_rr_type(rr)) {
608 		case ns_t_a:
609 			arr->addr.sin.sin_family = AF_INET;
610 #ifdef HAVE_SA_LEN
611 			arr->addr.sin.sin_len = sizeof(arr->addr.sin);
612 #endif
613 			memcpy(&arr->addr.sin.sin_addr, ns_rr_rdata(rr),
614 			       NS_INADDRSZ);
615 			arr->addr.sin.sin_port = htons(NAMESERVER_PORT);
616 			nsrr->flags |= RR_NS_HAVE_V4;
617 			break;
618 		case ns_t_aaaa:
619 			arr->addr.sin6.sin6_family = AF_INET6;
620 #ifdef HAVE_SA_LEN
621 			arr->addr.sin6.sin6_len = sizeof(arr->addr.sin6);
622 #endif
623 			memcpy(&arr->addr.sin6.sin6_addr, ns_rr_rdata(rr), 16);
624 			arr->addr.sin.sin_port = htons(NAMESERVER_PORT);
625 			nsrr->flags |= RR_NS_HAVE_V6;
626 			break;
627 		default:
628 			abort();
629 		}
630 		APPEND(nsrr->addrs, arr, link);
631 	}
632 	return (0);
633 }
634 
635 static void
636 free_nsrrset(rrset_ns *nsrrsp) {
637 	rr_ns *nsrr;
638 
639 	while ((nsrr = HEAD(*nsrrsp)) != NULL)
640 		free_nsrr(nsrrsp, nsrr);
641 }
642 
643 static void
644 free_nsrr(rrset_ns *nsrrsp, rr_ns *nsrr) {
645 	rr_a *arr;
646 	char *tmp;
647 
648 	while ((arr = HEAD(nsrr->addrs)) != NULL) {
649 		UNLINK(nsrr->addrs, arr, link);
650 		free(arr);
651 	}
652 	DE_CONST(nsrr->name, tmp);
653 	free(tmp);
654 	UNLINK(*nsrrsp, nsrr, link);
655 	free(nsrr);
656 }
657 
658 static rr_ns *
659 find_ns(rrset_ns *nsrrsp, const char *dname) {
660 	rr_ns *nsrr;
661 
662 	for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = NEXT(nsrr, link))
663 		if (ns_samename(nsrr->name, dname) == 1)
664 			return (nsrr);
665 	return (NULL);
666 }
667 
668 static int
669 do_query(res_state statp, const char *dname, ns_class class, ns_type qtype,
670 	 u_char *resp, ns_msg *msg)
671 {
672 	u_char req[NS_PACKETSZ];
673 	int i, n;
674 
675 	n = res_nmkquery(statp, ns_o_query, dname, class, qtype,
676 			 NULL, 0, NULL, req, NS_PACKETSZ);
677 	if (n < 0) {
678 		DPRINTF(("do_query: res_nmkquery failed"));
679 		return (-1);
680 	}
681 	n = res_nsend(statp, req, n, resp, NS_MAXMSG);
682 	if (n < 0) {
683 		DPRINTF(("do_query: res_nsend failed"));
684 		return (-1);
685 	}
686 	if (n == 0) {
687 		DPRINTF(("do_query: res_nsend returned 0"));
688 		errno = EMSGSIZE;
689 		return (-1);
690 	}
691 	if (ns_initparse(resp, n, msg) < 0) {
692 		DPRINTF(("do_query: ns_initparse failed"));
693 		return (-1);
694 	}
695 	n = 0;
696 	for (i = 0; i < ns_msg_count(*msg, ns_s_an); i++) {
697 		ns_rr rr;
698 
699 		if (ns_parserr(msg, ns_s_an, i, &rr) < 0) {
700 			DPRINTF(("do_query: ns_parserr failed"));
701 			return (-1);
702 		}
703 		n += (ns_rr_class(rr) == class &&
704 		      (ns_rr_type(rr) == ns_t_cname ||
705 		       ns_rr_type(rr) == ns_t_dname));
706 	}
707 	return (n);
708 }
709 
710 static void
711 res_dprintf(const char *fmt, ...) {
712 	va_list ap;
713 
714 	va_start(ap, fmt);
715 	fputs(";; res_findzonecut: ", stderr);
716 	vfprintf(stderr, fmt, ap);
717 	fputc('\n', stderr);
718 	va_end(ap);
719 }
720 
721 /*! \file */
722