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