res_findzonecut.c revision 158787
11556Srgrimes#if !defined(lint) && !defined(SABER)
21556Srgrimesstatic const char rcsid[] = "$Id: res_findzonecut.c,v 1.2.2.3.4.4 2005/10/11 00:48:16 marka Exp $";
31556Srgrimes#endif /* not lint */
41556Srgrimes
51556Srgrimes/*
61556Srgrimes * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
71556Srgrimes * Copyright (c) 1999 by Internet Software Consortium.
81556Srgrimes *
91556Srgrimes * Permission to use, copy, modify, and distribute this software for any
101556Srgrimes * purpose with or without fee is hereby granted, provided that the above
111556Srgrimes * copyright notice and this permission notice appear in all copies.
121556Srgrimes *
131556Srgrimes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
141556Srgrimes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
151556Srgrimes * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
161556Srgrimes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
171556Srgrimes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
181556Srgrimes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
191556Srgrimes * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
201556Srgrimes */
211556Srgrimes
221556Srgrimes#include <sys/cdefs.h>
231556Srgrimes__FBSDID("$FreeBSD: head/lib/libc/resolv/res_findzonecut.c 158787 2006-05-21 11:19:36Z ume $");
241556Srgrimes
251556Srgrimes/* Import. */
261556Srgrimes
271556Srgrimes#include "port_before.h"
281556Srgrimes
291556Srgrimes#include <sys/param.h>
301556Srgrimes#include <sys/socket.h>
311556Srgrimes#include <sys/time.h>
321556Srgrimes
331556Srgrimes#include <netinet/in.h>
341556Srgrimes#include <arpa/inet.h>
351556Srgrimes#include <arpa/nameser.h>
361556Srgrimes
371556Srgrimes#include <errno.h>
3836150Scharnier#include <limits.h>
3936150Scharnier#include <netdb.h>
4036150Scharnier#include <stdarg.h>
411556Srgrimes#include <stdio.h>
4299110Sobrien#include <stdlib.h>
4399110Sobrien#include <string.h>
441556Srgrimes
4517987Speter#include <isc/list.h>
4617987Speter
4717987Speter#include "port_after.h"
4899762Stjr
4917987Speter#include <resolv.h>
5017987Speter
5117987Speter/* Data structures. */
5217987Speter
5317987Spetertypedef struct rr_a {
5417987Speter	LINK(struct rr_a)	link;
5569793Sobrien	union res_sockaddr_union addr;
5618018Speter} rr_a;
5717987Spetertypedef LIST(rr_a) rrset_a;
581556Srgrimes
591556Srgrimestypedef struct rr_ns {
6017987Speter	LINK(struct rr_ns)	link;
611556Srgrimes	const char *		name;
621556Srgrimes	unsigned int		flags;
6317987Speter	rrset_a			addrs;
6417987Speter} rr_ns;
651556Srgrimestypedef LIST(rr_ns) rrset_ns;
661556Srgrimes
671556Srgrimes#define	RR_NS_HAVE_V4		0x01
681556Srgrimes#define	RR_NS_HAVE_V6		0x02
691556Srgrimes
701556Srgrimes/* Forward. */
711556Srgrimes
721556Srgrimesstatic int	satisfy(res_state, const char *, rrset_ns *,
731556Srgrimes			union res_sockaddr_union *, int);
741556Srgrimesstatic int	add_addrs(res_state, rr_ns *,
751556Srgrimes			  union res_sockaddr_union *, int);
761556Srgrimesstatic int	get_soa(res_state, const char *, ns_class, int,
771556Srgrimes			char *, size_t, char *, size_t,
781556Srgrimes			rrset_ns *);
791556Srgrimesstatic int	get_ns(res_state, const char *, ns_class, int, rrset_ns *);
801556Srgrimesstatic int	get_glue(res_state, ns_class, int, rrset_ns *);
8128346Sstevestatic int	save_ns(res_state, ns_msg *, ns_sect,
821556Srgrimes			const char *, ns_class, int, rrset_ns *);
8397659Stjrstatic int	save_a(res_state, ns_msg *, ns_sect,
84100308Stjr		       const char *, ns_class, int, rr_ns *);
851556Srgrimesstatic void	free_nsrrset(rrset_ns *);
8638536Scracauerstatic void	free_nsrr(rrset_ns *, rr_ns *);
8738950Scracauerstatic rr_ns *	find_ns(rrset_ns *, const char *);
8838536Scracauerstatic int	do_query(res_state, const char *, ns_class, ns_type,
8999762Stjr			 u_char *, ns_msg *);
901556Srgrimesstatic void	res_dprintf(const char *, ...) ISC_FORMAT_PRINTF(1, 2);
9120425Ssteve
9290111Simp/* Macros. */
9320425Ssteve
9490111Simp#define DPRINTF(x) do {\
9590111Simp		int save_errno = errno; \
96100308Stjr		if ((statp->options & RES_DEBUG) != 0U) res_dprintf x; \
97100308Stjr		errno = save_errno; \
9890111Simp	} while (0)
9990111Simp
10097659Stjr/* Public. */
10197659Stjr
10297659Stjr/*
10397659Stjr * int
10497659Stjr * res_findzonecut(res, dname, class, zname, zsize, addrs, naddrs)
10597822Stjr *	find enclosing zone for a <dname,class>, and some server addresses
1061556Srgrimes * parameters:
1071556Srgrimes *	res - resolver context to work within (is modified)
1081556Srgrimes *	dname - domain name whose enclosing zone is desired
1091556Srgrimes *	class - class of dname (and its enclosing zone)
1101556Srgrimes *	zname - found zone name
1111556Srgrimes *	zsize - allocated size of zname
1121556Srgrimes *	addrs - found server addresses
1131556Srgrimes *	naddrs - max number of addrs
11420425Ssteve * return values:
1151556Srgrimes *	< 0 - an error occurred (check errno)
11690111Simp *	= 0 - zname is now valid, but addrs[] wasn't changed
11717987Speter *	> 0 - zname is now valid, and return value is number of addrs[] found
11899762Stjr * notes:
1191556Srgrimes *	this function calls res_nsend() which means it depends on correctly
1201556Srgrimes *	functioning recursive nameservers (usually defined in /etc/resolv.conf
1211556Srgrimes *	or its local equivilent).
1221556Srgrimes *
12399762Stjr *	we start by asking for an SOA<dname,class>.  if we get one as an
12499762Stjr *	answer, that just means <dname,class> is a zone top, which is fine.
12599762Stjr *	more than likely we'll be told to go pound sand, in the form of a
12699762Stjr *	negative answer.
12799762Stjr *
12899762Stjr *	note that we are not prepared to deal with referrals since that would
129109927Stjr *	only come from authority servers and our correctly functioning local
13099762Stjr *	recursive server would have followed the referral and got us something
13199762Stjr *	more definite.
132109927Stjr *
133109927Stjr *	if the authority section contains an SOA, this SOA should also be the
134109927Stjr *	closest enclosing zone, since any intermediary zone cuts would've been
135109927Stjr *	returned as referrals and dealt with by our correctly functioning local
136109927Stjr *	recursive name server.  but an SOA in the authority section should NOT
137109927Stjr *	match our dname (since that would have been returned in the answer
138109927Stjr *	section).  an authority section SOA has to be "above" our dname.
139109927Stjr *
140109927Stjr *	however, since authority section SOA's were once optional, it's
141109927Stjr *	possible that we'll have to go hunting for the enclosing SOA by
142109927Stjr *	ripping labels off the front of our dname -- this is known as "doing
143109927Stjr *	it the hard way."
144109927Stjr *
145103223Snectar *	ultimately we want some server addresses, which are ideally the ones
14699762Stjr *	pertaining to the SOA.MNAME, but only if there is a matching NS RR.
14799762Stjr *	so the second phase (after we find an SOA) is to go looking for the
14899762Stjr *	NS RRset for that SOA's zone.
14999762Stjr *
1501556Srgrimes *	no answer section processed by this code is allowed to contain CNAME
15199762Stjr *	or DNAME RR's.  for the SOA query this means we strip a label and
15220425Ssteve *	keep going.  for the NS and A queries this means we just give up.
15399762Stjr */
1541556Srgrimes
1551556Srgrimes#ifndef _LIBC
1561556Srgrimesint
1571556Srgrimesres_findzonecut(res_state statp, const char *dname, ns_class class, int opts,
15817987Speter		char *zname, size_t zsize, struct in_addr *addrs, int naddrs)
15917987Speter{
16099762Stjr	int result, i;
1611556Srgrimes	union res_sockaddr_union *u;
1621556Srgrimes
1631556Srgrimes
1641556Srgrimes	opts |= RES_IPV4ONLY;
1651556Srgrimes	opts &= ~RES_IPV6ONLY;
1661556Srgrimes
16717987Speter	u = calloc(naddrs, sizeof(*u));
16899762Stjr	if (u == NULL)
1691556Srgrimes		return(-1);
17017987Speter
17199762Stjr	result = res_findzonecut2(statp, dname, class, opts, zname, zsize,
17299762Stjr				  u, naddrs);
17399762Stjr
1741556Srgrimes	for (i = 0; i < result; i++) {
1751556Srgrimes		addrs[i] = u[i].sin.sin_addr;
1761556Srgrimes	}
1771556Srgrimes	free(u);
1781556Srgrimes	return (result);
1791556Srgrimes}
18020425Ssteve#endif
1811556Srgrimes
1821556Srgrimesint
1831556Srgrimesres_findzonecut2(res_state statp, const char *dname, ns_class class, int opts,
18428346Ssteve		 char *zname, size_t zsize, union res_sockaddr_union *addrs,
18517987Speter		 int naddrs)
1861556Srgrimes{
1871556Srgrimes	char mname[NS_MAXDNAME];
1881556Srgrimes	u_long save_pfcode;
1891556Srgrimes	rrset_ns nsrrs;
1901556Srgrimes	int n;
1911556Srgrimes
1921556Srgrimes	DPRINTF(("START dname='%s' class=%s, zsize=%ld, naddrs=%d",
1931556Srgrimes		 dname, p_class(class), (long)zsize, naddrs));
1941556Srgrimes	save_pfcode = statp->pfcode;
1951556Srgrimes	statp->pfcode |= RES_PRF_HEAD2 | RES_PRF_HEAD1 | RES_PRF_HEADX |
1961556Srgrimes			 RES_PRF_QUES | RES_PRF_ANS |
1971556Srgrimes			 RES_PRF_AUTH | RES_PRF_ADD;
1981556Srgrimes	INIT_LIST(nsrrs);
19917987Speter
20090111Simp	DPRINTF(("get the soa, and see if it has enough glue"));
20117987Speter	if ((n = get_soa(statp, dname, class, opts, zname, zsize,
2021556Srgrimes			 mname, sizeof mname, &nsrrs)) < 0 ||
203100308Stjr	    ((opts & RES_EXHAUSTIVE) == 0 &&
2041556Srgrimes	     (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0))
2051556Srgrimes		goto done;
2061556Srgrimes
2071556Srgrimes	DPRINTF(("get the ns rrset and see if it has enough glue"));
2081556Srgrimes	if ((n = get_ns(statp, zname, class, opts, &nsrrs)) < 0 ||
20996933Stjr	    ((opts & RES_EXHAUSTIVE) == 0 &&
21096933Stjr	     (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0))
21196933Stjr		goto done;
2121556Srgrimes
21399762Stjr	DPRINTF(("get the missing glue and see if it's finally enough"));
2141556Srgrimes	if ((n = get_glue(statp, class, opts, &nsrrs)) >= 0)
215100305Stjr		n = satisfy(statp, mname, &nsrrs, addrs, naddrs);
2161556Srgrimes
21745916Scracauer done:
2181556Srgrimes	DPRINTF(("FINISH n=%d (%s)", n, (n < 0) ? strerror(errno) : "OK"));
2191556Srgrimes	free_nsrrset(&nsrrs);
2201556Srgrimes	statp->pfcode = save_pfcode;
2211556Srgrimes	return (n);
2221556Srgrimes}
22317987Speter
22490111Simp/* Private. */
22517987Speter
22696933Stjrstatic int
2271556Srgrimessatisfy(res_state statp, const char *mname, rrset_ns *nsrrsp,
2281556Srgrimes	union res_sockaddr_union *addrs, int naddrs)
2291556Srgrimes{
2301556Srgrimes	rr_ns *nsrr;
2311556Srgrimes	int n, x;
2321556Srgrimes
23396933Stjr	n = 0;
23496933Stjr	nsrr = find_ns(nsrrsp, mname);
2351556Srgrimes	if (nsrr != NULL) {
236100305Stjr		x = add_addrs(statp, nsrr, addrs, naddrs);
237104275Smux		addrs += x;
23896933Stjr		naddrs -= x;
23996933Stjr		n += x;
24096933Stjr	}
2411556Srgrimes	for (nsrr = HEAD(*nsrrsp);
2421556Srgrimes	     nsrr != NULL && naddrs > 0;
2431556Srgrimes	     nsrr = NEXT(nsrr, link))
2441556Srgrimes		if (ns_samename(nsrr->name, mname) != 1) {
2451556Srgrimes			x = add_addrs(statp, nsrr, addrs, naddrs);
2461556Srgrimes			addrs += x;
24790111Simp			naddrs -= x;
24817987Speter			n += x;
2491556Srgrimes		}
2501556Srgrimes	DPRINTF(("satisfy(%s): %d", mname, n));
2511556Srgrimes	return (n);
2521556Srgrimes}
2531556Srgrimes
25497660Stjrstatic int
2551556Srgrimesadd_addrs(res_state statp, rr_ns *nsrr,
2561556Srgrimes	  union res_sockaddr_union *addrs, int naddrs)
2571556Srgrimes{
25826104Ssteve	rr_a *arr;
2591556Srgrimes	int n = 0;
2601556Srgrimes
2611556Srgrimes	for (arr = HEAD(nsrr->addrs); arr != NULL; arr = NEXT(arr, link)) {
2621556Srgrimes		if (naddrs <= 0)
2631556Srgrimes			return (0);
2641556Srgrimes		*addrs++ = arr->addr;
2651556Srgrimes		naddrs--;
2661556Srgrimes		n++;
2671556Srgrimes	}
2681556Srgrimes	DPRINTF(("add_addrs: %d", n));
26997669Stjr	return (n);
27017987Speter}
27197669Stjr
27297669Stjrstatic int
27397669Stjrget_soa(res_state statp, const char *dname, ns_class class, int opts,
27497669Stjr	char *zname, size_t zsize, char *mname, size_t msize,
275100663Stjr	rrset_ns *nsrrsp)
27697669Stjr{
27797669Stjr	char tname[NS_MAXDNAME];
27897669Stjr	u_char *resp = NULL;
27997669Stjr	int n, i, ancount, nscount;
28097669Stjr	ns_sect sect;
28197669Stjr	ns_msg msg;
28297669Stjr	u_int rcode;
28397669Stjr
28497669Stjr	/*
28597669Stjr	 * Find closest enclosing SOA, even if it's for the root zone.
28697669Stjr	 */
28797669Stjr
28897669Stjr	/* First canonicalize dname (exactly one unescaped trailing "."). */
28997669Stjr	if (ns_makecanon(dname, tname, sizeof tname) < 0)
29097669Stjr		goto cleanup;
29197669Stjr	dname = tname;
29297669Stjr
29397669Stjr	resp = malloc(NS_MAXMSG);
29497669Stjr	if (resp == NULL)
29597669Stjr		goto cleanup;
29697669Stjr
29797822Stjr	/* Now grovel the subdomains, hunting for an SOA answer or auth. */
29897669Stjr	for (;;) {
29997669Stjr		/* Leading or inter-label '.' are skipped here. */
3001556Srgrimes		while (*dname == '.')
3011556Srgrimes			dname++;
30297663Stjr
30397822Stjr		/* Is there an SOA? */
30497663Stjr		n = do_query(statp, dname, class, ns_t_soa, resp, &msg);
30597663Stjr		if (n < 0) {
30697663Stjr			DPRINTF(("get_soa: do_query('%s', %s) failed (%d)",
30797669Stjr				 dname, p_class(class), n));
30897669Stjr			goto cleanup;
30997669Stjr		}
3101556Srgrimes		if (n > 0) {
31197663Stjr			DPRINTF(("get_soa: CNAME or DNAME found"));
31297663Stjr			sect = ns_s_max, n = 0;
31397669Stjr		} else {
31497669Stjr			rcode = ns_msg_getflag(msg, ns_f_rcode);
31597669Stjr			ancount = ns_msg_count(msg, ns_s_an);
31697669Stjr			nscount = ns_msg_count(msg, ns_s_ns);
31797669Stjr			if (ancount > 0 && rcode == ns_r_noerror)
31897669Stjr				sect = ns_s_an, n = ancount;
31997669Stjr			else if (nscount > 0)
32097669Stjr				sect = ns_s_ns, n = nscount;
32197663Stjr			else
32297669Stjr				sect = ns_s_max, n = 0;
323100308Stjr		}
32497669Stjr		for (i = 0; i < n; i++) {
32597669Stjr			const char *t;
32697822Stjr			const u_char *rdata;
32797669Stjr			ns_rr rr;
32897822Stjr
32997822Stjr			if (ns_parserr(&msg, sect, i, &rr) < 0) {
33097819Stjr				DPRINTF(("get_soa: ns_parserr(%s, %d) failed",
33197669Stjr					 p_section(sect, ns_o_query), i));
33297819Stjr				goto cleanup;
33397669Stjr			}
33497669Stjr			if (ns_rr_type(rr) == ns_t_cname ||
33597669Stjr			    ns_rr_type(rr) == ns_t_dname)
33697663Stjr				break;
33797669Stjr			if (ns_rr_type(rr) != ns_t_soa ||
33897663Stjr			    ns_rr_class(rr) != class)
33997816Stjr				continue;
34097663Stjr			t = ns_rr_name(rr);
34197663Stjr			switch (sect) {
34297669Stjr			case ns_s_an:
343100308Stjr				if (ns_samedomain(dname, t) == 0) {
34497669Stjr					DPRINTF(
34597669Stjr				    ("get_soa: ns_samedomain('%s', '%s') == 0",
34697669Stjr						dname, t)
34797663Stjr						);
34897819Stjr					errno = EPROTOTYPE;
34997819Stjr					goto cleanup;
35097819Stjr				}
35197669Stjr				break;
35297663Stjr			case ns_s_ns:
35397820Stjr				if (ns_samename(dname, t) == 1 ||
35497820Stjr				    ns_samedomain(dname, t) == 0) {
35597820Stjr					DPRINTF(
35697820Stjr		       ("get_soa: ns_samename() || !ns_samedomain('%s', '%s')",
35797820Stjr						dname, t)
35897663Stjr						);
35997663Stjr					errno = EPROTOTYPE;
36097663Stjr					goto cleanup;
36197663Stjr				}
36297663Stjr				break;
36397663Stjr			default:
36497663Stjr				abort();
36597663Stjr			}
36697663Stjr			if (strlen(t) + 1 > zsize) {
36797663Stjr				DPRINTF(("get_soa: zname(%lu) too small (%lu)",
36897663Stjr					 (unsigned long)zsize,
36997663Stjr					 (unsigned long)strlen(t) + 1));
37097663Stjr				errno = EMSGSIZE;
37197663Stjr				goto cleanup;
37297663Stjr			}
37397663Stjr			strcpy(zname, t);
37497663Stjr			rdata = ns_rr_rdata(rr);
37597663Stjr			if (ns_name_uncompress(resp, ns_msg_end(msg), rdata,
37697663Stjr					       mname, msize) < 0) {
37797663Stjr				DPRINTF(("get_soa: ns_name_uncompress failed")
37897663Stjr					);
37997663Stjr				goto cleanup;
38097669Stjr			}
38197663Stjr			if (save_ns(statp, &msg, ns_s_ns,
38297663Stjr				    zname, class, opts, nsrrsp) < 0) {
38397663Stjr				DPRINTF(("get_soa: save_ns failed"));
38497663Stjr				goto cleanup;
3851556Srgrimes			}
3861556Srgrimes			free(resp);
3871556Srgrimes			return (0);
3881556Srgrimes		}
3891556Srgrimes
3901556Srgrimes		/* If we're out of labels, then not even "." has an SOA! */
3911556Srgrimes		if (*dname == '\0')
3921556Srgrimes			break;
3931556Srgrimes
3941556Srgrimes		/* Find label-terminating "."; top of loop will skip it. */
39597669Stjr		while (*dname != '.') {
39617987Speter			if (*dname == '\\')
3971556Srgrimes				if (*++dname == '\0') {
3981556Srgrimes					errno = EMSGSIZE;
3991556Srgrimes					goto cleanup;
4001556Srgrimes				}
4011556Srgrimes			dname++;
4021556Srgrimes		}
4031556Srgrimes	}
4041556Srgrimes	DPRINTF(("get_soa: out of labels"));
4051556Srgrimes	errno = EDESTADDRREQ;
4061556Srgrimes cleanup:
4071556Srgrimes	if (resp != NULL)
4081556Srgrimes		free(resp);
4091556Srgrimes	return (-1);
4101556Srgrimes}
41197822Stjr
4121556Srgrimesstatic int
4131556Srgrimesget_ns(res_state statp, const char *zname, ns_class class, int opts,
4141556Srgrimes      rrset_ns *nsrrsp)
4151556Srgrimes{
4161556Srgrimes	u_char *resp;
4171556Srgrimes	ns_msg msg;
4181556Srgrimes	int n;
4191556Srgrimes
4201556Srgrimes	resp = malloc(NS_MAXMSG);
4211556Srgrimes	if (resp == NULL)
4221556Srgrimes		return (-1);
4231556Srgrimes
4241556Srgrimes	/* Go and get the NS RRs for this zone. */
42590111Simp	n = do_query(statp, zname, class, ns_t_ns, resp, &msg);
42690111Simp	if (n != 0) {
4271556Srgrimes		DPRINTF(("get_ns: do_query('%s', %s) failed (%d)",
4281556Srgrimes			 zname, p_class(class), n));
4291556Srgrimes		free(resp);
4301556Srgrimes		return (-1);
4311556Srgrimes	}
4321556Srgrimes
4331556Srgrimes	/* Remember the NS RRs and associated A RRs that came back. */
4341556Srgrimes	if (save_ns(statp, &msg, ns_s_an, zname, class, opts, nsrrsp) < 0) {
4351556Srgrimes		DPRINTF(("get_ns save_ns('%s', %s) failed",
4361556Srgrimes			 zname, p_class(class)));
4371556Srgrimes		free(resp);
4381556Srgrimes		return (-1);
43997659Stjr	}
4401556Srgrimes
4411556Srgrimes	free(resp);
4421556Srgrimes	return (0);
4431556Srgrimes}
4441556Srgrimes
4451556Srgrimesstatic int
4461556Srgrimesget_glue(res_state statp, ns_class class, int opts, rrset_ns *nsrrsp) {
44790111Simp	rr_ns *nsrr, *nsrr_n;
44817987Speter	u_char *resp;
4491556Srgrimes
45026104Ssteve	resp = malloc(NS_MAXMSG);
4511556Srgrimes	if (resp == NULL)
4521556Srgrimes		return(-1);
4531556Srgrimes
4541556Srgrimes	/* Go and get the A RRs for each empty NS RR on our list. */
4551556Srgrimes	for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = nsrr_n) {
4561556Srgrimes		ns_msg msg;
4571556Srgrimes		int n;
45838536Scracauer
45938536Scracauer		nsrr_n = NEXT(nsrr, link);
46038536Scracauer
46138536Scracauer		if ((nsrr->flags & RR_NS_HAVE_V4) == 0) {
46238536Scracauer			n = do_query(statp, nsrr->name, class, ns_t_a,
46338536Scracauer				     resp, &msg);
46438521Scracauer			if (n < 0) {
46538536Scracauer				DPRINTF(
4661556Srgrimes				       ("get_glue: do_query('%s', %s') failed",
4671556Srgrimes					nsrr->name, p_class(class)));
4681556Srgrimes				goto cleanup;
46926104Ssteve			}
47026104Ssteve			if (n > 0) {
4711556Srgrimes				DPRINTF((
47226104Ssteve			"get_glue: do_query('%s', %s') CNAME or DNAME found",
47326104Ssteve					 nsrr->name, p_class(class)));
4741556Srgrimes			}
4751556Srgrimes			if (save_a(statp, &msg, ns_s_an, nsrr->name, class,
47626104Ssteve				   opts, nsrr) < 0) {
4771556Srgrimes				DPRINTF(("get_glue: save_r('%s', %s) failed",
4781556Srgrimes					 nsrr->name, p_class(class)));
47938536Scracauer				goto cleanup;
48026104Ssteve			}
4811556Srgrimes		}
4821556Srgrimes
4831556Srgrimes		if ((nsrr->flags & RR_NS_HAVE_V6) == 0) {
4841556Srgrimes			n = do_query(statp, nsrr->name, class, ns_t_aaaa,
48538536Scracauer				     resp, &msg);
4861556Srgrimes			if (n < 0) {
4871556Srgrimes				DPRINTF(
4881556Srgrimes				       ("get_glue: do_query('%s', %s') failed",
4891556Srgrimes					nsrr->name, p_class(class)));
4901556Srgrimes				goto cleanup;
4911556Srgrimes			}
49238521Scracauer			if (n > 0) {
49338521Scracauer				DPRINTF((
49438521Scracauer			"get_glue: do_query('%s', %s') CNAME or DNAME found",
49538521Scracauer					 nsrr->name, p_class(class)));
4961556Srgrimes			}
4971556Srgrimes			if (save_a(statp, &msg, ns_s_an, nsrr->name, class,
4981556Srgrimes				   opts, nsrr) < 0) {
4991556Srgrimes				DPRINTF(("get_glue: save_r('%s', %s) failed",
50017987Speter					 nsrr->name, p_class(class)));
50190111Simp				goto cleanup;
50217987Speter			}
5031556Srgrimes		}
5041556Srgrimes
5051556Srgrimes		/* If it's still empty, it's just chaff. */
5061556Srgrimes		if (EMPTY(nsrr->addrs)) {
5071556Srgrimes			DPRINTF(("get_glue: removing empty '%s' NS",
508100308Stjr				 nsrr->name));
5091556Srgrimes			free_nsrr(nsrrsp, nsrr);
5101556Srgrimes		}
5111556Srgrimes	}
5121556Srgrimes	free(resp);
5131556Srgrimes	return (0);
5141556Srgrimes
5151556Srgrimes cleanup:
5161556Srgrimes	free(resp);
5171556Srgrimes	return (-1);
5181556Srgrimes}
5191556Srgrimes
5201556Srgrimesstatic int
52190111Simpsave_ns(res_state statp, ns_msg *msg, ns_sect sect,
52290111Simp	const char *owner, ns_class class, int opts,
5231556Srgrimes	rrset_ns *nsrrsp)
52497688Stjr{
525100308Stjr	int i;
5261556Srgrimes
5271556Srgrimes	for (i = 0; i < ns_msg_count(*msg, sect); i++) {
5281556Srgrimes		char tname[MAXDNAME];
5291556Srgrimes		const u_char *rdata;
53097659Stjr		rr_ns *nsrr;
5311556Srgrimes		ns_rr rr;
53297659Stjr
5331556Srgrimes		if (ns_parserr(msg, sect, i, &rr) < 0) {
5341556Srgrimes			DPRINTF(("save_ns: ns_parserr(%s, %d) failed",
5351556Srgrimes				 p_section(sect, ns_o_query), i));
5361556Srgrimes			return (-1);
5371556Srgrimes		}
5381556Srgrimes		if (ns_rr_type(rr) != ns_t_ns ||
5391556Srgrimes		    ns_rr_class(rr) != class ||
5401556Srgrimes		    ns_samename(ns_rr_name(rr), owner) != 1)
5411556Srgrimes			continue;
5421556Srgrimes		nsrr = find_ns(nsrrsp, ns_rr_name(rr));
5431556Srgrimes		if (nsrr == NULL) {
5441556Srgrimes			nsrr = malloc(sizeof *nsrr);
54597688Stjr			if (nsrr == NULL) {
54697688Stjr				DPRINTF(("save_ns: malloc failed"));
54797688Stjr				return (-1);
54897688Stjr			}
54997688Stjr			rdata = ns_rr_rdata(rr);
55097688Stjr			if (ns_name_uncompress(ns_msg_base(*msg),
55197688Stjr					       ns_msg_end(*msg), rdata,
5521556Srgrimes					       tname, sizeof tname) < 0) {
55397688Stjr				DPRINTF(("save_ns: ns_name_uncompress failed")
55497688Stjr					);
55597688Stjr				free(nsrr);
55697688Stjr				return (-1);
55797688Stjr			}
55897688Stjr			nsrr->name = strdup(tname);
55997688Stjr			if (nsrr->name == NULL) {
56097688Stjr				DPRINTF(("save_ns: strdup failed"));
56197688Stjr				free(nsrr);
56297688Stjr				return (-1);
56397688Stjr			}
56497688Stjr			INIT_LINK(nsrr, link);
5651556Srgrimes			INIT_LIST(nsrr->addrs);
56697688Stjr			nsrr->flags = 0;
5671556Srgrimes			APPEND(*nsrrsp, nsrr, link);
5681556Srgrimes		}
5691556Srgrimes		if (save_a(statp, msg, ns_s_ar,
5701556Srgrimes			   nsrr->name, class, opts, nsrr) < 0) {
5711556Srgrimes			DPRINTF(("save_ns: save_r('%s', %s) failed",
5721556Srgrimes				 nsrr->name, p_class(class)));
5731556Srgrimes			return (-1);
5741556Srgrimes		}
5751556Srgrimes	}
5761556Srgrimes	return (0);
5771556Srgrimes}
5781556Srgrimes
579100308Stjrstatic int
5801556Srgrimessave_a(res_state statp, ns_msg *msg, ns_sect sect,
5811556Srgrimes       const char *owner, ns_class class, int opts,
5821556Srgrimes       rr_ns *nsrr)
5831556Srgrimes{
5841556Srgrimes	int i;
5851556Srgrimes
5861556Srgrimes	for (i = 0; i < ns_msg_count(*msg, sect); i++) {
58717987Speter		ns_rr rr;
58817987Speter		rr_a *arr;
5891556Srgrimes
5901556Srgrimes		if (ns_parserr(msg, sect, i, &rr) < 0) {
5911556Srgrimes			DPRINTF(("save_a: ns_parserr(%s, %d) failed",
5921556Srgrimes				 p_section(sect, ns_o_query), i));
5931556Srgrimes			return (-1);
5941556Srgrimes		}
5951556Srgrimes		if ((ns_rr_type(rr) != ns_t_a &&
5961556Srgrimes		     ns_rr_type(rr) != ns_t_aaaa) ||
5971556Srgrimes		    ns_rr_class(rr) != class ||
59890111Simp		    ns_samename(ns_rr_name(rr), owner) != 1 ||
59917987Speter		    ns_rr_rdlen(rr) != NS_INADDRSZ)
6001556Srgrimes			continue;
6011556Srgrimes		if ((opts & RES_IPV6ONLY) != 0 && ns_rr_type(rr) != ns_t_aaaa)
6021556Srgrimes			continue;
6031556Srgrimes		if ((opts & RES_IPV4ONLY) != 0 && ns_rr_type(rr) != ns_t_a)
6041556Srgrimes			continue;
6051556Srgrimes		arr = malloc(sizeof *arr);
6061556Srgrimes		if (arr == NULL) {
6071556Srgrimes			DPRINTF(("save_a: malloc failed"));
60897664Stjr			return (-1);
60997659Stjr		}
61097664Stjr		INIT_LINK(arr, link);
6111556Srgrimes		memset(&arr->addr, 0, sizeof(arr->addr));
6121556Srgrimes		switch (ns_rr_type(rr)) {
61317987Speter		case ns_t_a:
61497659Stjr			arr->addr.sin.sin_family = AF_INET;
61597659Stjr#ifdef HAVE_SA_LEN
61699760Stjr			arr->addr.sin.sin_len = sizeof(arr->addr.sin);
61799760Stjr#endif
61897659Stjr			memcpy(&arr->addr.sin.sin_addr, ns_rr_rdata(rr),
61997659Stjr			       NS_INADDRSZ);
62097659Stjr			arr->addr.sin.sin_port = htons(NAMESERVER_PORT);
62197659Stjr			nsrr->flags |= RR_NS_HAVE_V4;
62297659Stjr			break;
62320425Ssteve		case ns_t_aaaa:
62420425Ssteve			arr->addr.sin6.sin6_family = AF_INET6;
62520425Ssteve#ifdef HAVE_SA_LEN
62620425Ssteve			arr->addr.sin6.sin6_len = sizeof(arr->addr.sin6);
6271556Srgrimes#endif
6281556Srgrimes			memcpy(&arr->addr.sin6.sin6_addr, ns_rr_rdata(rr), 16);
6291556Srgrimes			arr->addr.sin.sin_port = htons(NAMESERVER_PORT);
6301556Srgrimes			nsrr->flags |= RR_NS_HAVE_V6;
6311556Srgrimes			break;
6321556Srgrimes		default:
6331556Srgrimes			abort();
6341556Srgrimes		}
6351556Srgrimes		APPEND(nsrr->addrs, arr, link);
6361556Srgrimes	}
6371556Srgrimes	return (0);
6381556Srgrimes}
6391556Srgrimes
6401556Srgrimesstatic void
6411556Srgrimesfree_nsrrset(rrset_ns *nsrrsp) {
6421556Srgrimes	rr_ns *nsrr;
643100305Stjr
6441556Srgrimes	while ((nsrr = HEAD(*nsrrsp)) != NULL)
6451556Srgrimes		free_nsrr(nsrrsp, nsrr);
64697659Stjr}
6471556Srgrimes
6481556Srgrimesstatic void
6491556Srgrimesfree_nsrr(rrset_ns *nsrrsp, rr_ns *nsrr) {
6501556Srgrimes	rr_a *arr;
6511556Srgrimes	char *tmp;
6521556Srgrimes
6531556Srgrimes	while ((arr = HEAD(nsrr->addrs)) != NULL) {
65417987Speter		UNLINK(nsrr->addrs, arr, link);
65517987Speter		free(arr);
6561556Srgrimes	}
6578855Srgrimes	DE_CONST(nsrr->name, tmp);
6581556Srgrimes	free(tmp);
65997659Stjr	UNLINK(*nsrrsp, nsrr, link);
66097659Stjr	free(nsrr);
66197659Stjr}
66297659Stjr
66397659Stjrstatic rr_ns *
6641556Srgrimesfind_ns(rrset_ns *nsrrsp, const char *dname) {
66597659Stjr	rr_ns *nsrr;
66697659Stjr
66797659Stjr	for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = NEXT(nsrr, link))
66897659Stjr		if (ns_samename(nsrr->name, dname) == 1)
66997659Stjr			return (nsrr);
67097659Stjr	return (NULL);
67197659Stjr}
67297659Stjr
67397659Stjrstatic int
67497659Stjrdo_query(res_state statp, const char *dname, ns_class class, ns_type qtype,
67597659Stjr	 u_char *resp, ns_msg *msg)
67697659Stjr{
67797659Stjr	u_char req[NS_PACKETSZ];
67897659Stjr	int i, n;
67997659Stjr
68097659Stjr	n = res_nmkquery(statp, ns_o_query, dname, class, qtype,
68197659Stjr			 NULL, 0, NULL, req, NS_PACKETSZ);
68297659Stjr	if (n < 0) {
68397659Stjr		DPRINTF(("do_query: res_nmkquery failed"));
68497659Stjr		return (-1);
68597659Stjr	}
68697659Stjr	n = res_nsend(statp, req, n, resp, NS_MAXMSG);
68797659Stjr	if (n < 0) {
68897659Stjr		DPRINTF(("do_query: res_nsend failed"));
68997659Stjr		return (-1);
69097659Stjr	}
69197659Stjr	if (n == 0) {
69297659Stjr		DPRINTF(("do_query: res_nsend returned 0"));
69397659Stjr		errno = EMSGSIZE;
69497659Stjr		return (-1);
69597659Stjr	}
6961556Srgrimes	if (ns_initparse(resp, n, msg) < 0) {
69797659Stjr		DPRINTF(("do_query: ns_initparse failed"));
69897659Stjr		return (-1);
69997659Stjr	}
70097659Stjr	n = 0;
70197659Stjr	for (i = 0; i < ns_msg_count(*msg, ns_s_an); i++) {
70297659Stjr		ns_rr rr;
70397659Stjr
70497659Stjr		if (ns_parserr(msg, ns_s_an, i, &rr) < 0) {
70597659Stjr			DPRINTF(("do_query: ns_parserr failed"));
70697659Stjr			return (-1);
70797659Stjr		}
70897659Stjr		n += (ns_rr_class(rr) == class &&
70997659Stjr		      (ns_rr_type(rr) == ns_t_cname ||
71097659Stjr		       ns_rr_type(rr) == ns_t_dname));
71197659Stjr	}
71297659Stjr	return (n);
71397659Stjr}
71497659Stjr
71597659Stjrstatic void
71697659Stjrres_dprintf(const char *fmt, ...) {
71797659Stjr	va_list ap;
71897659Stjr
71997659Stjr	va_start(ap, fmt);
7201556Srgrimes	fputs(";; res_findzonecut: ", stderr);
7211556Srgrimes	vfprintf(stderr, fmt, ap);
7221556Srgrimes	fputc('\n', stderr);
7231556Srgrimes	va_end(ap);
7241556Srgrimes}
7251556Srgrimes