res_findzonecut.c revision 158782
1158782Sume#if !defined(lint) && !defined(SABER) 2158782Sumestatic const char rcsid[] = "$Id: res_findzonecut.c,v 1.2.2.3.4.4 2005/10/11 00:48:16 marka Exp $"; 3158782Sume#endif /* not lint */ 4158782Sume 5158782Sume/* 6158782Sume * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 7158782Sume * Copyright (c) 1999 by Internet Software Consortium. 8158782Sume * 9158782Sume * Permission to use, copy, modify, and distribute this software for any 10158782Sume * purpose with or without fee is hereby granted, provided that the above 11158782Sume * copyright notice and this permission notice appear in all copies. 12158782Sume * 13158782Sume * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 14158782Sume * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15158782Sume * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 16158782Sume * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17158782Sume * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18158782Sume * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19158782Sume * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20158782Sume */ 21158782Sume 22158782Sume/* Import. */ 23158782Sume 24158782Sume#include "port_before.h" 25158782Sume 26158782Sume#include <sys/param.h> 27158782Sume#include <sys/socket.h> 28158782Sume#include <sys/time.h> 29158782Sume 30158782Sume#include <netinet/in.h> 31158782Sume#include <arpa/inet.h> 32158782Sume#include <arpa/nameser.h> 33158782Sume 34158782Sume#include <errno.h> 35158782Sume#include <limits.h> 36158782Sume#include <netdb.h> 37158782Sume#include <stdarg.h> 38158782Sume#include <stdio.h> 39158782Sume#include <stdlib.h> 40158782Sume#include <string.h> 41158782Sume 42158782Sume#include <isc/list.h> 43158782Sume 44158782Sume#include "port_after.h" 45158782Sume 46158782Sume#include <resolv.h> 47158782Sume 48158782Sume/* Data structures. */ 49158782Sume 50158782Sumetypedef struct rr_a { 51158782Sume LINK(struct rr_a) link; 52158782Sume union res_sockaddr_union addr; 53158782Sume} rr_a; 54158782Sumetypedef LIST(rr_a) rrset_a; 55158782Sume 56158782Sumetypedef struct rr_ns { 57158782Sume LINK(struct rr_ns) link; 58158782Sume const char * name; 59158782Sume unsigned int flags; 60158782Sume rrset_a addrs; 61158782Sume} rr_ns; 62158782Sumetypedef LIST(rr_ns) rrset_ns; 63158782Sume 64158782Sume#define RR_NS_HAVE_V4 0x01 65158782Sume#define RR_NS_HAVE_V6 0x02 66158782Sume 67158782Sume/* Forward. */ 68158782Sume 69158782Sumestatic int satisfy(res_state, const char *, rrset_ns *, 70158782Sume union res_sockaddr_union *, int); 71158782Sumestatic int add_addrs(res_state, rr_ns *, 72158782Sume union res_sockaddr_union *, int); 73158782Sumestatic int get_soa(res_state, const char *, ns_class, int, 74158782Sume char *, size_t, char *, size_t, 75158782Sume rrset_ns *); 76158782Sumestatic int get_ns(res_state, const char *, ns_class, int, rrset_ns *); 77158782Sumestatic int get_glue(res_state, ns_class, int, rrset_ns *); 78158782Sumestatic int save_ns(res_state, ns_msg *, ns_sect, 79158782Sume const char *, ns_class, int, rrset_ns *); 80158782Sumestatic int save_a(res_state, ns_msg *, ns_sect, 81158782Sume const char *, ns_class, int, rr_ns *); 82158782Sumestatic void free_nsrrset(rrset_ns *); 83158782Sumestatic void free_nsrr(rrset_ns *, rr_ns *); 84158782Sumestatic rr_ns * find_ns(rrset_ns *, const char *); 85158782Sumestatic int do_query(res_state, const char *, ns_class, ns_type, 86158782Sume u_char *, ns_msg *); 87158782Sumestatic void res_dprintf(const char *, ...) ISC_FORMAT_PRINTF(1, 2); 88158782Sume 89158782Sume/* Macros. */ 90158782Sume 91158782Sume#define DPRINTF(x) do {\ 92158782Sume int save_errno = errno; \ 93158782Sume if ((statp->options & RES_DEBUG) != 0U) res_dprintf x; \ 94158782Sume errno = save_errno; \ 95158782Sume } while (0) 96158782Sume 97158782Sume/* Public. */ 98158782Sume 99158782Sume/* 100158782Sume * int 101158782Sume * res_findzonecut(res, dname, class, zname, zsize, addrs, naddrs) 102158782Sume * find enclosing zone for a <dname,class>, and some server addresses 103158782Sume * parameters: 104158782Sume * res - resolver context to work within (is modified) 105158782Sume * dname - domain name whose enclosing zone is desired 106158782Sume * class - class of dname (and its enclosing zone) 107158782Sume * zname - found zone name 108158782Sume * zsize - allocated size of zname 109158782Sume * addrs - found server addresses 110158782Sume * naddrs - max number of addrs 111158782Sume * return values: 112158782Sume * < 0 - an error occurred (check errno) 113158782Sume * = 0 - zname is now valid, but addrs[] wasn't changed 114158782Sume * > 0 - zname is now valid, and return value is number of addrs[] found 115158782Sume * notes: 116158782Sume * this function calls res_nsend() which means it depends on correctly 117158782Sume * functioning recursive nameservers (usually defined in /etc/resolv.conf 118158782Sume * or its local equivilent). 119158782Sume * 120158782Sume * we start by asking for an SOA<dname,class>. if we get one as an 121158782Sume * answer, that just means <dname,class> is a zone top, which is fine. 122158782Sume * more than likely we'll be told to go pound sand, in the form of a 123158782Sume * negative answer. 124158782Sume * 125158782Sume * note that we are not prepared to deal with referrals since that would 126158782Sume * only come from authority servers and our correctly functioning local 127158782Sume * recursive server would have followed the referral and got us something 128158782Sume * more definite. 129158782Sume * 130158782Sume * if the authority section contains an SOA, this SOA should also be the 131158782Sume * closest enclosing zone, since any intermediary zone cuts would've been 132158782Sume * returned as referrals and dealt with by our correctly functioning local 133158782Sume * recursive name server. but an SOA in the authority section should NOT 134158782Sume * match our dname (since that would have been returned in the answer 135158782Sume * section). an authority section SOA has to be "above" our dname. 136158782Sume * 137158782Sume * however, since authority section SOA's were once optional, it's 138158782Sume * possible that we'll have to go hunting for the enclosing SOA by 139158782Sume * ripping labels off the front of our dname -- this is known as "doing 140158782Sume * it the hard way." 141158782Sume * 142158782Sume * ultimately we want some server addresses, which are ideally the ones 143158782Sume * pertaining to the SOA.MNAME, but only if there is a matching NS RR. 144158782Sume * so the second phase (after we find an SOA) is to go looking for the 145158782Sume * NS RRset for that SOA's zone. 146158782Sume * 147158782Sume * no answer section processed by this code is allowed to contain CNAME 148158782Sume * or DNAME RR's. for the SOA query this means we strip a label and 149158782Sume * keep going. for the NS and A queries this means we just give up. 150158782Sume */ 151158782Sume 152158782Sumeint 153158782Sumeres_findzonecut(res_state statp, const char *dname, ns_class class, int opts, 154158782Sume char *zname, size_t zsize, struct in_addr *addrs, int naddrs) 155158782Sume{ 156158782Sume int result, i; 157158782Sume union res_sockaddr_union *u; 158158782Sume 159158782Sume 160158782Sume opts |= RES_IPV4ONLY; 161158782Sume opts &= ~RES_IPV6ONLY; 162158782Sume 163158782Sume u = calloc(naddrs, sizeof(*u)); 164158782Sume if (u == NULL) 165158782Sume return(-1); 166158782Sume 167158782Sume result = res_findzonecut2(statp, dname, class, opts, zname, zsize, 168158782Sume u, naddrs); 169158782Sume 170158782Sume for (i = 0; i < result; i++) { 171158782Sume addrs[i] = u[i].sin.sin_addr; 172158782Sume } 173158782Sume free(u); 174158782Sume return (result); 175158782Sume} 176158782Sume 177158782Sumeint 178158782Sumeres_findzonecut2(res_state statp, const char *dname, ns_class class, int opts, 179158782Sume char *zname, size_t zsize, union res_sockaddr_union *addrs, 180158782Sume int naddrs) 181158782Sume{ 182158782Sume char mname[NS_MAXDNAME]; 183158782Sume u_long save_pfcode; 184158782Sume rrset_ns nsrrs; 185158782Sume int n; 186158782Sume 187158782Sume DPRINTF(("START dname='%s' class=%s, zsize=%ld, naddrs=%d", 188158782Sume dname, p_class(class), (long)zsize, naddrs)); 189158782Sume save_pfcode = statp->pfcode; 190158782Sume statp->pfcode |= RES_PRF_HEAD2 | RES_PRF_HEAD1 | RES_PRF_HEADX | 191158782Sume RES_PRF_QUES | RES_PRF_ANS | 192158782Sume RES_PRF_AUTH | RES_PRF_ADD; 193158782Sume INIT_LIST(nsrrs); 194158782Sume 195158782Sume DPRINTF(("get the soa, and see if it has enough glue")); 196158782Sume if ((n = get_soa(statp, dname, class, opts, zname, zsize, 197158782Sume mname, sizeof mname, &nsrrs)) < 0 || 198158782Sume ((opts & RES_EXHAUSTIVE) == 0 && 199158782Sume (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0)) 200158782Sume goto done; 201158782Sume 202158782Sume DPRINTF(("get the ns rrset and see if it has enough glue")); 203158782Sume if ((n = get_ns(statp, zname, class, opts, &nsrrs)) < 0 || 204158782Sume ((opts & RES_EXHAUSTIVE) == 0 && 205158782Sume (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0)) 206158782Sume goto done; 207158782Sume 208158782Sume DPRINTF(("get the missing glue and see if it's finally enough")); 209158782Sume if ((n = get_glue(statp, class, opts, &nsrrs)) >= 0) 210158782Sume n = satisfy(statp, mname, &nsrrs, addrs, naddrs); 211158782Sume 212158782Sume done: 213158782Sume DPRINTF(("FINISH n=%d (%s)", n, (n < 0) ? strerror(errno) : "OK")); 214158782Sume free_nsrrset(&nsrrs); 215158782Sume statp->pfcode = save_pfcode; 216158782Sume return (n); 217158782Sume} 218158782Sume 219158782Sume/* Private. */ 220158782Sume 221158782Sumestatic int 222158782Sumesatisfy(res_state statp, const char *mname, rrset_ns *nsrrsp, 223158782Sume union res_sockaddr_union *addrs, int naddrs) 224158782Sume{ 225158782Sume rr_ns *nsrr; 226158782Sume int n, x; 227158782Sume 228158782Sume n = 0; 229158782Sume nsrr = find_ns(nsrrsp, mname); 230158782Sume if (nsrr != NULL) { 231158782Sume x = add_addrs(statp, nsrr, addrs, naddrs); 232158782Sume addrs += x; 233158782Sume naddrs -= x; 234158782Sume n += x; 235158782Sume } 236158782Sume for (nsrr = HEAD(*nsrrsp); 237158782Sume nsrr != NULL && naddrs > 0; 238158782Sume nsrr = NEXT(nsrr, link)) 239158782Sume if (ns_samename(nsrr->name, mname) != 1) { 240158782Sume x = add_addrs(statp, nsrr, addrs, naddrs); 241158782Sume addrs += x; 242158782Sume naddrs -= x; 243158782Sume n += x; 244158782Sume } 245158782Sume DPRINTF(("satisfy(%s): %d", mname, n)); 246158782Sume return (n); 247158782Sume} 248158782Sume 249158782Sumestatic int 250158782Sumeadd_addrs(res_state statp, rr_ns *nsrr, 251158782Sume union res_sockaddr_union *addrs, int naddrs) 252158782Sume{ 253158782Sume rr_a *arr; 254158782Sume int n = 0; 255158782Sume 256158782Sume for (arr = HEAD(nsrr->addrs); arr != NULL; arr = NEXT(arr, link)) { 257158782Sume if (naddrs <= 0) 258158782Sume return (0); 259158782Sume *addrs++ = arr->addr; 260158782Sume naddrs--; 261158782Sume n++; 262158782Sume } 263158782Sume DPRINTF(("add_addrs: %d", n)); 264158782Sume return (n); 265158782Sume} 266158782Sume 267158782Sumestatic int 268158782Sumeget_soa(res_state statp, const char *dname, ns_class class, int opts, 269158782Sume char *zname, size_t zsize, char *mname, size_t msize, 270158782Sume rrset_ns *nsrrsp) 271158782Sume{ 272158782Sume char tname[NS_MAXDNAME]; 273158782Sume u_char *resp = NULL; 274158782Sume int n, i, ancount, nscount; 275158782Sume ns_sect sect; 276158782Sume ns_msg msg; 277158782Sume u_int rcode; 278158782Sume 279158782Sume /* 280158782Sume * Find closest enclosing SOA, even if it's for the root zone. 281158782Sume */ 282158782Sume 283158782Sume /* First canonicalize dname (exactly one unescaped trailing "."). */ 284158782Sume if (ns_makecanon(dname, tname, sizeof tname) < 0) 285158782Sume goto cleanup; 286158782Sume dname = tname; 287158782Sume 288158782Sume resp = malloc(NS_MAXMSG); 289158782Sume if (resp == NULL) 290158782Sume goto cleanup; 291158782Sume 292158782Sume /* Now grovel the subdomains, hunting for an SOA answer or auth. */ 293158782Sume for (;;) { 294158782Sume /* Leading or inter-label '.' are skipped here. */ 295158782Sume while (*dname == '.') 296158782Sume dname++; 297158782Sume 298158782Sume /* Is there an SOA? */ 299158782Sume n = do_query(statp, dname, class, ns_t_soa, resp, &msg); 300158782Sume if (n < 0) { 301158782Sume DPRINTF(("get_soa: do_query('%s', %s) failed (%d)", 302158782Sume dname, p_class(class), n)); 303158782Sume goto cleanup; 304158782Sume } 305158782Sume if (n > 0) { 306158782Sume DPRINTF(("get_soa: CNAME or DNAME found")); 307158782Sume sect = ns_s_max, n = 0; 308158782Sume } else { 309158782Sume rcode = ns_msg_getflag(msg, ns_f_rcode); 310158782Sume ancount = ns_msg_count(msg, ns_s_an); 311158782Sume nscount = ns_msg_count(msg, ns_s_ns); 312158782Sume if (ancount > 0 && rcode == ns_r_noerror) 313158782Sume sect = ns_s_an, n = ancount; 314158782Sume else if (nscount > 0) 315158782Sume sect = ns_s_ns, n = nscount; 316158782Sume else 317158782Sume sect = ns_s_max, n = 0; 318158782Sume } 319158782Sume for (i = 0; i < n; i++) { 320158782Sume const char *t; 321158782Sume const u_char *rdata; 322158782Sume ns_rr rr; 323158782Sume 324158782Sume if (ns_parserr(&msg, sect, i, &rr) < 0) { 325158782Sume DPRINTF(("get_soa: ns_parserr(%s, %d) failed", 326158782Sume p_section(sect, ns_o_query), i)); 327158782Sume goto cleanup; 328158782Sume } 329158782Sume if (ns_rr_type(rr) == ns_t_cname || 330158782Sume ns_rr_type(rr) == ns_t_dname) 331158782Sume break; 332158782Sume if (ns_rr_type(rr) != ns_t_soa || 333158782Sume ns_rr_class(rr) != class) 334158782Sume continue; 335158782Sume t = ns_rr_name(rr); 336158782Sume switch (sect) { 337158782Sume case ns_s_an: 338158782Sume if (ns_samedomain(dname, t) == 0) { 339158782Sume DPRINTF( 340158782Sume ("get_soa: ns_samedomain('%s', '%s') == 0", 341158782Sume dname, t) 342158782Sume ); 343158782Sume errno = EPROTOTYPE; 344158782Sume goto cleanup; 345158782Sume } 346158782Sume break; 347158782Sume case ns_s_ns: 348158782Sume if (ns_samename(dname, t) == 1 || 349158782Sume ns_samedomain(dname, t) == 0) { 350158782Sume DPRINTF( 351158782Sume ("get_soa: ns_samename() || !ns_samedomain('%s', '%s')", 352158782Sume dname, t) 353158782Sume ); 354158782Sume errno = EPROTOTYPE; 355158782Sume goto cleanup; 356158782Sume } 357158782Sume break; 358158782Sume default: 359158782Sume abort(); 360158782Sume } 361158782Sume if (strlen(t) + 1 > zsize) { 362158782Sume DPRINTF(("get_soa: zname(%lu) too small (%lu)", 363158782Sume (unsigned long)zsize, 364158782Sume (unsigned long)strlen(t) + 1)); 365158782Sume errno = EMSGSIZE; 366158782Sume goto cleanup; 367158782Sume } 368158782Sume strcpy(zname, t); 369158782Sume rdata = ns_rr_rdata(rr); 370158782Sume if (ns_name_uncompress(resp, ns_msg_end(msg), rdata, 371158782Sume mname, msize) < 0) { 372158782Sume DPRINTF(("get_soa: ns_name_uncompress failed") 373158782Sume ); 374158782Sume goto cleanup; 375158782Sume } 376158782Sume if (save_ns(statp, &msg, ns_s_ns, 377158782Sume zname, class, opts, nsrrsp) < 0) { 378158782Sume DPRINTF(("get_soa: save_ns failed")); 379158782Sume goto cleanup; 380158782Sume } 381158782Sume free(resp); 382158782Sume return (0); 383158782Sume } 384158782Sume 385158782Sume /* If we're out of labels, then not even "." has an SOA! */ 386158782Sume if (*dname == '\0') 387158782Sume break; 388158782Sume 389158782Sume /* Find label-terminating "."; top of loop will skip it. */ 390158782Sume while (*dname != '.') { 391158782Sume if (*dname == '\\') 392158782Sume if (*++dname == '\0') { 393158782Sume errno = EMSGSIZE; 394158782Sume goto cleanup; 395158782Sume } 396158782Sume dname++; 397158782Sume } 398158782Sume } 399158782Sume DPRINTF(("get_soa: out of labels")); 400158782Sume errno = EDESTADDRREQ; 401158782Sume cleanup: 402158782Sume if (resp != NULL) 403158782Sume free(resp); 404158782Sume return (-1); 405158782Sume} 406158782Sume 407158782Sumestatic int 408158782Sumeget_ns(res_state statp, const char *zname, ns_class class, int opts, 409158782Sume rrset_ns *nsrrsp) 410158782Sume{ 411158782Sume u_char *resp; 412158782Sume ns_msg msg; 413158782Sume int n; 414158782Sume 415158782Sume resp = malloc(NS_MAXMSG); 416158782Sume if (resp == NULL) 417158782Sume return (-1); 418158782Sume 419158782Sume /* Go and get the NS RRs for this zone. */ 420158782Sume n = do_query(statp, zname, class, ns_t_ns, resp, &msg); 421158782Sume if (n != 0) { 422158782Sume DPRINTF(("get_ns: do_query('%s', %s) failed (%d)", 423158782Sume zname, p_class(class), n)); 424158782Sume free(resp); 425158782Sume return (-1); 426158782Sume } 427158782Sume 428158782Sume /* Remember the NS RRs and associated A RRs that came back. */ 429158782Sume if (save_ns(statp, &msg, ns_s_an, zname, class, opts, nsrrsp) < 0) { 430158782Sume DPRINTF(("get_ns save_ns('%s', %s) failed", 431158782Sume zname, p_class(class))); 432158782Sume free(resp); 433158782Sume return (-1); 434158782Sume } 435158782Sume 436158782Sume free(resp); 437158782Sume return (0); 438158782Sume} 439158782Sume 440158782Sumestatic int 441158782Sumeget_glue(res_state statp, ns_class class, int opts, rrset_ns *nsrrsp) { 442158782Sume rr_ns *nsrr, *nsrr_n; 443158782Sume u_char *resp; 444158782Sume 445158782Sume resp = malloc(NS_MAXMSG); 446158782Sume if (resp == NULL) 447158782Sume return(-1); 448158782Sume 449158782Sume /* Go and get the A RRs for each empty NS RR on our list. */ 450158782Sume for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = nsrr_n) { 451158782Sume ns_msg msg; 452158782Sume int n; 453158782Sume 454158782Sume nsrr_n = NEXT(nsrr, link); 455158782Sume 456158782Sume if ((nsrr->flags & RR_NS_HAVE_V4) == 0) { 457158782Sume n = do_query(statp, nsrr->name, class, ns_t_a, 458158782Sume resp, &msg); 459158782Sume if (n < 0) { 460158782Sume DPRINTF( 461158782Sume ("get_glue: do_query('%s', %s') failed", 462158782Sume nsrr->name, p_class(class))); 463158782Sume goto cleanup; 464158782Sume } 465158782Sume if (n > 0) { 466158782Sume DPRINTF(( 467158782Sume "get_glue: do_query('%s', %s') CNAME or DNAME found", 468158782Sume nsrr->name, p_class(class))); 469158782Sume } 470158782Sume if (save_a(statp, &msg, ns_s_an, nsrr->name, class, 471158782Sume opts, nsrr) < 0) { 472158782Sume DPRINTF(("get_glue: save_r('%s', %s) failed", 473158782Sume nsrr->name, p_class(class))); 474158782Sume goto cleanup; 475158782Sume } 476158782Sume } 477158782Sume 478158782Sume if ((nsrr->flags & RR_NS_HAVE_V6) == 0) { 479158782Sume n = do_query(statp, nsrr->name, class, ns_t_aaaa, 480158782Sume resp, &msg); 481158782Sume if (n < 0) { 482158782Sume DPRINTF( 483158782Sume ("get_glue: do_query('%s', %s') failed", 484158782Sume nsrr->name, p_class(class))); 485158782Sume goto cleanup; 486158782Sume } 487158782Sume if (n > 0) { 488158782Sume DPRINTF(( 489158782Sume "get_glue: do_query('%s', %s') CNAME or DNAME found", 490158782Sume nsrr->name, p_class(class))); 491158782Sume } 492158782Sume if (save_a(statp, &msg, ns_s_an, nsrr->name, class, 493158782Sume opts, nsrr) < 0) { 494158782Sume DPRINTF(("get_glue: save_r('%s', %s) failed", 495158782Sume nsrr->name, p_class(class))); 496158782Sume goto cleanup; 497158782Sume } 498158782Sume } 499158782Sume 500158782Sume /* If it's still empty, it's just chaff. */ 501158782Sume if (EMPTY(nsrr->addrs)) { 502158782Sume DPRINTF(("get_glue: removing empty '%s' NS", 503158782Sume nsrr->name)); 504158782Sume free_nsrr(nsrrsp, nsrr); 505158782Sume } 506158782Sume } 507158782Sume free(resp); 508158782Sume return (0); 509158782Sume 510158782Sume cleanup: 511158782Sume free(resp); 512158782Sume return (-1); 513158782Sume} 514158782Sume 515158782Sumestatic int 516158782Sumesave_ns(res_state statp, ns_msg *msg, ns_sect sect, 517158782Sume const char *owner, ns_class class, int opts, 518158782Sume rrset_ns *nsrrsp) 519158782Sume{ 520158782Sume int i; 521158782Sume 522158782Sume for (i = 0; i < ns_msg_count(*msg, sect); i++) { 523158782Sume char tname[MAXDNAME]; 524158782Sume const u_char *rdata; 525158782Sume rr_ns *nsrr; 526158782Sume ns_rr rr; 527158782Sume 528158782Sume if (ns_parserr(msg, sect, i, &rr) < 0) { 529158782Sume DPRINTF(("save_ns: ns_parserr(%s, %d) failed", 530158782Sume p_section(sect, ns_o_query), i)); 531158782Sume return (-1); 532158782Sume } 533158782Sume if (ns_rr_type(rr) != ns_t_ns || 534158782Sume ns_rr_class(rr) != class || 535158782Sume ns_samename(ns_rr_name(rr), owner) != 1) 536158782Sume continue; 537158782Sume nsrr = find_ns(nsrrsp, ns_rr_name(rr)); 538158782Sume if (nsrr == NULL) { 539158782Sume nsrr = malloc(sizeof *nsrr); 540158782Sume if (nsrr == NULL) { 541158782Sume DPRINTF(("save_ns: malloc failed")); 542158782Sume return (-1); 543158782Sume } 544158782Sume rdata = ns_rr_rdata(rr); 545158782Sume if (ns_name_uncompress(ns_msg_base(*msg), 546158782Sume ns_msg_end(*msg), rdata, 547158782Sume tname, sizeof tname) < 0) { 548158782Sume DPRINTF(("save_ns: ns_name_uncompress failed") 549158782Sume ); 550158782Sume free(nsrr); 551158782Sume return (-1); 552158782Sume } 553158782Sume nsrr->name = strdup(tname); 554158782Sume if (nsrr->name == NULL) { 555158782Sume DPRINTF(("save_ns: strdup failed")); 556158782Sume free(nsrr); 557158782Sume return (-1); 558158782Sume } 559158782Sume INIT_LINK(nsrr, link); 560158782Sume INIT_LIST(nsrr->addrs); 561158782Sume nsrr->flags = 0; 562158782Sume APPEND(*nsrrsp, nsrr, link); 563158782Sume } 564158782Sume if (save_a(statp, msg, ns_s_ar, 565158782Sume nsrr->name, class, opts, nsrr) < 0) { 566158782Sume DPRINTF(("save_ns: save_r('%s', %s) failed", 567158782Sume nsrr->name, p_class(class))); 568158782Sume return (-1); 569158782Sume } 570158782Sume } 571158782Sume return (0); 572158782Sume} 573158782Sume 574158782Sumestatic int 575158782Sumesave_a(res_state statp, ns_msg *msg, ns_sect sect, 576158782Sume const char *owner, ns_class class, int opts, 577158782Sume rr_ns *nsrr) 578158782Sume{ 579158782Sume int i; 580158782Sume 581158782Sume for (i = 0; i < ns_msg_count(*msg, sect); i++) { 582158782Sume ns_rr rr; 583158782Sume rr_a *arr; 584158782Sume 585158782Sume if (ns_parserr(msg, sect, i, &rr) < 0) { 586158782Sume DPRINTF(("save_a: ns_parserr(%s, %d) failed", 587158782Sume p_section(sect, ns_o_query), i)); 588158782Sume return (-1); 589158782Sume } 590158782Sume if ((ns_rr_type(rr) != ns_t_a && 591158782Sume ns_rr_type(rr) != ns_t_aaaa) || 592158782Sume ns_rr_class(rr) != class || 593158782Sume ns_samename(ns_rr_name(rr), owner) != 1 || 594158782Sume ns_rr_rdlen(rr) != NS_INADDRSZ) 595158782Sume continue; 596158782Sume if ((opts & RES_IPV6ONLY) != 0 && ns_rr_type(rr) != ns_t_aaaa) 597158782Sume continue; 598158782Sume if ((opts & RES_IPV4ONLY) != 0 && ns_rr_type(rr) != ns_t_a) 599158782Sume continue; 600158782Sume arr = malloc(sizeof *arr); 601158782Sume if (arr == NULL) { 602158782Sume DPRINTF(("save_a: malloc failed")); 603158782Sume return (-1); 604158782Sume } 605158782Sume INIT_LINK(arr, link); 606158782Sume memset(&arr->addr, 0, sizeof(arr->addr)); 607158782Sume switch (ns_rr_type(rr)) { 608158782Sume case ns_t_a: 609158782Sume arr->addr.sin.sin_family = AF_INET; 610158782Sume#ifdef HAVE_SA_LEN 611158782Sume arr->addr.sin.sin_len = sizeof(arr->addr.sin); 612158782Sume#endif 613158782Sume memcpy(&arr->addr.sin.sin_addr, ns_rr_rdata(rr), 614158782Sume NS_INADDRSZ); 615158782Sume arr->addr.sin.sin_port = htons(NAMESERVER_PORT); 616158782Sume nsrr->flags |= RR_NS_HAVE_V4; 617158782Sume break; 618158782Sume case ns_t_aaaa: 619158782Sume arr->addr.sin6.sin6_family = AF_INET6; 620158782Sume#ifdef HAVE_SA_LEN 621158782Sume arr->addr.sin6.sin6_len = sizeof(arr->addr.sin6); 622158782Sume#endif 623158782Sume memcpy(&arr->addr.sin6.sin6_addr, ns_rr_rdata(rr), 16); 624158782Sume arr->addr.sin.sin_port = htons(NAMESERVER_PORT); 625158782Sume nsrr->flags |= RR_NS_HAVE_V6; 626158782Sume break; 627158782Sume default: 628158782Sume abort(); 629158782Sume } 630158782Sume APPEND(nsrr->addrs, arr, link); 631158782Sume } 632158782Sume return (0); 633158782Sume} 634158782Sume 635158782Sumestatic void 636158782Sumefree_nsrrset(rrset_ns *nsrrsp) { 637158782Sume rr_ns *nsrr; 638158782Sume 639158782Sume while ((nsrr = HEAD(*nsrrsp)) != NULL) 640158782Sume free_nsrr(nsrrsp, nsrr); 641158782Sume} 642158782Sume 643158782Sumestatic void 644158782Sumefree_nsrr(rrset_ns *nsrrsp, rr_ns *nsrr) { 645158782Sume rr_a *arr; 646158782Sume char *tmp; 647158782Sume 648158782Sume while ((arr = HEAD(nsrr->addrs)) != NULL) { 649158782Sume UNLINK(nsrr->addrs, arr, link); 650158782Sume free(arr); 651158782Sume } 652158782Sume DE_CONST(nsrr->name, tmp); 653158782Sume free(tmp); 654158782Sume UNLINK(*nsrrsp, nsrr, link); 655158782Sume free(nsrr); 656158782Sume} 657158782Sume 658158782Sumestatic rr_ns * 659158782Sumefind_ns(rrset_ns *nsrrsp, const char *dname) { 660158782Sume rr_ns *nsrr; 661158782Sume 662158782Sume for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = NEXT(nsrr, link)) 663158782Sume if (ns_samename(nsrr->name, dname) == 1) 664158782Sume return (nsrr); 665158782Sume return (NULL); 666158782Sume} 667158782Sume 668158782Sumestatic int 669158782Sumedo_query(res_state statp, const char *dname, ns_class class, ns_type qtype, 670158782Sume u_char *resp, ns_msg *msg) 671158782Sume{ 672158782Sume u_char req[NS_PACKETSZ]; 673158782Sume int i, n; 674158782Sume 675158782Sume n = res_nmkquery(statp, ns_o_query, dname, class, qtype, 676158782Sume NULL, 0, NULL, req, NS_PACKETSZ); 677158782Sume if (n < 0) { 678158782Sume DPRINTF(("do_query: res_nmkquery failed")); 679158782Sume return (-1); 680158782Sume } 681158782Sume n = res_nsend(statp, req, n, resp, NS_MAXMSG); 682158782Sume if (n < 0) { 683158782Sume DPRINTF(("do_query: res_nsend failed")); 684158782Sume return (-1); 685158782Sume } 686158782Sume if (n == 0) { 687158782Sume DPRINTF(("do_query: res_nsend returned 0")); 688158782Sume errno = EMSGSIZE; 689158782Sume return (-1); 690158782Sume } 691158782Sume if (ns_initparse(resp, n, msg) < 0) { 692158782Sume DPRINTF(("do_query: ns_initparse failed")); 693158782Sume return (-1); 694158782Sume } 695158782Sume n = 0; 696158782Sume for (i = 0; i < ns_msg_count(*msg, ns_s_an); i++) { 697158782Sume ns_rr rr; 698158782Sume 699158782Sume if (ns_parserr(msg, ns_s_an, i, &rr) < 0) { 700158782Sume DPRINTF(("do_query: ns_parserr failed")); 701158782Sume return (-1); 702158782Sume } 703158782Sume n += (ns_rr_class(rr) == class && 704158782Sume (ns_rr_type(rr) == ns_t_cname || 705158782Sume ns_rr_type(rr) == ns_t_dname)); 706158782Sume } 707158782Sume return (n); 708158782Sume} 709158782Sume 710158782Sumestatic void 711158782Sumeres_dprintf(const char *fmt, ...) { 712158782Sume va_list ap; 713158782Sume 714158782Sume va_start(ap, fmt); 715158782Sume fputs(";; res_findzonecut: ", stderr); 716158782Sume vfprintf(stderr, fmt, ap); 717158782Sume fputc('\n', stderr); 718158782Sume va_end(ap); 719158782Sume} 720