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