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