1/* 2 * Copyright (c) 1988, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34/* 35 * Portions Copyright (c) 1993 by Digital Equipment Corporation. 36 * 37 * Permission to use, copy, modify, and distribute this software for any 38 * purpose with or without fee is hereby granted, provided that the above 39 * copyright notice and this permission notice appear in all copies, and that 40 * the name of Digital Equipment Corporation not be used in advertising or 41 * publicity pertaining to distribution of the document or software without 42 * specific, written prior permission. 43 * 44 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL 45 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES 46 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT 47 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 48 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 49 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 50 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 51 * SOFTWARE. 52 */ 53 54/* 55 * Portions Copyright (c) 1996-1999 by Internet Software Consortium. 56 * 57 * Permission to use, copy, modify, and distribute this software for any 58 * purpose with or without fee is hereby granted, provided that the above 59 * copyright notice and this permission notice appear in all copies. 60 * 61 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 62 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 63 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 64 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 65 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 66 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 67 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 68 * SOFTWARE. 69 */ 70 71#if defined(LIBC_SCCS) && !defined(lint) 72static const char sccsid[] = "@(#)res_query.c 8.1 (Berkeley) 6/4/93"; 73static const char rcsid[] = "$Id: res_query.c,v 1.1 2006/03/01 19:01:38 majka Exp $"; 74#endif /* LIBC_SCCS and not lint */ 75 76#ifndef __APPLE__ 77#include "port_before.h" 78#endif 79#include <sys/types.h> 80#include <sys/param.h> 81#include <netinet/in.h> 82#include <arpa/inet.h> 83#include <arpa/nameser.h> 84#include <ctype.h> 85#include <errno.h> 86#include <netdb.h> 87#include <resolv.h> 88#include <stdio.h> 89#include <stdlib.h> 90#include <string.h> 91#include "res_private.h" 92#include <dns_sd.h> 93#include <sys/event.h> 94#include <sys/time.h> 95#include <time.h> 96#include <unistd.h> 97#include <notify.h> 98#include <pthread.h> 99#ifndef __APPLE__ 100#include "port_after.h" 101#endif 102 103/* interrupt mechanism is implemented in res_send.c */ 104__private_extern__ int interrupt_pipe_enabled; 105__private_extern__ pthread_key_t interrupt_pipe_key; 106 107/* Options. Leave them on. */ 108#define DEBUG 109 110#if PACKETSZ > 1024 111#define MAXPACKET PACKETSZ 112#else 113#define MAXPACKET 1024 114#endif 115 116#define BILLION 1000000000 117 118/* length of a reverse DNS IPv6 address query name, e.g. "9.4.a.f.c.e.e.f.e.e.1.5.4.1.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa" */ 119#define IPv6_REVERSE_LEN 72 120 121/* index of the trailing char that must be "8", "9", "A", "a", "b", or "B" */ 122#define IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR 58 123 124/* index of low-order nibble of embedded scope id */ 125#define IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW 48 126 127const static uint8_t hexval[128] = 128{ 129 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */ 130 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */ 131 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 - 47 */ 132 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 48 - 63 */ 133 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 - 79 */ 134 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 95 */ 135 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 - 111 */ 136 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 112 - 127 */ 137}; 138 139struct res_query_context 140{ 141 u_char *answer; 142 size_t anslen; 143 size_t ansmaxlen; 144 uint16_t lastanstype; 145 uint32_t ifnum; 146 uint32_t res_flags; 147 DNSServiceFlags flags; 148 DNSServiceErrorType error; 149}; 150 151static void 152res_query_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *ctx) 153{ 154 struct res_query_context *context; 155 int n; 156 size_t buflen; 157 u_char *dnlist[2], *cp; 158 HEADER *ans; 159 struct in6_addr a6; 160 161 context = (struct res_query_context *)ctx; 162 163 context->flags = flags; 164 context->error = errorCode; 165 166 if (errorCode != kDNSServiceErr_NoError) 167 { 168 if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback [%s %hu %hu]: error %u\n", fullname, rrtype, rrclass, errorCode); 169 return; 170 } 171 172 buflen = context->ansmaxlen - context->anslen; 173 if (buflen < NS_HFIXEDSZ) 174 { 175 if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback [%s %hu %hu]: malformed reply\n", fullname, rrtype, rrclass); 176 return; 177 } 178 179 dnlist[0] = context->answer + NS_HFIXEDSZ; 180 dnlist[1] = NULL; 181 182 cp = context->answer + context->anslen; 183 184 n = dn_comp((char *)fullname, cp, buflen, dnlist, &dnlist[1]); 185 if (n < 0) 186 { 187 if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback [%s %hu %hu]: name mismatch\n", fullname, rrtype, rrclass); 188 return; 189 } 190 191 /* 192 * Check that there is enough space in the buffer for the resource name (n), 193 * the resource record data (rdlen) and the resource record header (10). 194 */ 195 if (buflen < n + rdlen + 10) 196 { 197 if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback [%s %hu %hu]: insufficient buffer space for reply\n", fullname, rrtype, rrclass); 198 return; 199 } 200 201 if (context->res_flags & RES_DEBUG) printf(";; res_query_mDNSResponder callback for %s %hu %hu\n", fullname, rrtype, rrclass); 202 203 cp += n; 204 buflen -= n; 205 206 putshort(rrtype, cp); 207 cp += sizeof(uint16_t); 208 209 putshort(rrclass, cp); 210 cp += sizeof(uint16_t); 211 212 putlong(ttl, cp); 213 cp += sizeof(uint32_t); 214 215 putshort(rdlen, cp); 216 cp += sizeof(uint16_t); 217 218 memcpy(cp, rdata, rdlen); 219 cp += rdlen; 220 221 ans = (HEADER *)context->answer; 222 ans->ancount = htons(ntohs(ans->ancount) + 1); 223 224 context->anslen = (size_t)(cp - context->answer); 225 226 context->lastanstype = rrtype; 227 228 /* 229 * Save the interface number for the first AAAA record for link-local addresses. 230 * It's used by getaddrinfo to set the scope id. 231 */ 232 if ((context->ifnum == 0) && (rrtype == ns_t_aaaa)) 233 { 234 memset(&a6, 0, sizeof(struct in6_addr)); 235 memcpy(&a6, rdata, sizeof(struct in6_addr)); 236 if (IN6_IS_ADDR_LINKLOCAL(&a6)) context->ifnum = ifIndex; 237 } 238} 239 240static void 241h_errno_for_dnssd_err(DNSServiceErrorType dnssd_err, int *h_errno_err) 242{ 243 switch (dnssd_err) 244 { 245 case kDNSServiceErr_NoError: 246 *h_errno_err = NETDB_SUCCESS; 247 break; 248 case kDNSServiceErr_Unknown: 249 *h_errno_err = NO_RECOVERY; 250 break; 251 case kDNSServiceErr_NoSuchRecord: 252 *h_errno_err = NO_DATA; 253 break; 254 case kDNSServiceErr_NoSuchName: 255 *h_errno_err = HOST_NOT_FOUND; 256 break; 257 case kDNSServiceErr_NoMemory: 258 default: 259 *h_errno_err = NETDB_INTERNAL; 260 break; 261 } 262} 263 264static int 265_is_rev_link_local(const char *name) 266{ 267 int len, i; 268 269 if (name == NULL) return 0; 270 271 len = strlen(name); 272 if (len == 0) return 0; 273 274 /* check for trailing '.' */ 275 if (name[len - 1] == '.') len--; 276 277 if (len != IPv6_REVERSE_LEN) return 0; 278 279 i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR; 280 if ((name[i] != '8') && (name[i] != '9') && (name[i] != 'A') && (name[i] != 'a') && (name[i] != 'B') && (name[i] != 'b')) return 0; 281 282 i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR + 1; 283 if (strncasecmp(name + i, ".e.f.ip6.arpa", 13)) return 0; 284 285 for (i = 0; i < IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR; i += 2) 286 { 287 if (name[i] < '0') return 0; 288 if ((name[i] > '9') && (name[i] < 'A')) return 0; 289 if ((name[i] > 'F') && (name[i] < 'a')) return 0; 290 if (name[i] > 'f') return 0; 291 if (name[i + 1] != '.') return 0; 292 } 293 294 return 1; 295} 296 297int 298res_query_mDNSResponder(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen) 299{ 300 DNSServiceRef sdRef; 301 DNSServiceErrorType result; 302 struct res_query_context context; 303 int i, kq, n, wait, cancelled, notify_token, status; 304 struct kevent mevent, ievent, event; 305 struct timeval ctv; 306 struct timespec now, finish, timeout; 307 HEADER *ans; 308 uint32_t iface; 309 uint16_t nibble; 310 char *qname, *notify_name; 311 int *interrupt_pipe; 312 uint64_t exit_requested; 313 314 interrupt_pipe = NULL; 315 result = 0; 316 kq = -1; 317 ans = (HEADER *)answer; 318 cancelled = 0; 319 ans->rcode = 0; 320 321 memset(&context, 0, sizeof(struct res_query_context)); 322 323 /* Build a dummy DNS header with question for the answer */ 324 context.res_flags = statp->options; 325 context.answer = answer; 326 context.ansmaxlen = anslen; 327 context.anslen = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, answer, anslen); 328 if (context.anslen <= 0) return 0; 329 330 /* Mark DNS packet as a response */ 331 ans->qr = 1; 332 ans->qr = htons(ans->qr); 333 334 /* Pull out Scope ID in link-local reverse queries */ 335 qname = (char *)name; 336 iface = 0; 337 if (_is_rev_link_local(name)) 338 { 339 /* _is_rev_link_local rejects chars > 127 so it's safe to index into hexval */ 340 i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW; 341 nibble = hexval[(uint32_t)name[i]]; 342 iface = nibble; 343 344 i += 2; 345 nibble = hexval[(uint32_t)name[i]]; 346 iface += (nibble << 4); 347 348 i += 2; 349 nibble = hexval[(uint32_t)name[i]]; 350 iface += (nibble << 8); 351 352 i += 2; 353 nibble = hexval[(uint32_t)name[i]]; 354 iface += (nibble << 12); 355 356 if (iface != 0) 357 { 358 qname = strdup(name); 359 if (qname == NULL) 360 { 361 h_errno = NO_RECOVERY; 362 errno = ENOMEM; 363 return -1; 364 } 365 366 i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW; 367 qname[i] = '0'; 368 qname[i + 2] = '0'; 369 qname[i + 4] = '0'; 370 qname[i + 6] = '0'; 371 } 372 } 373 374 if (statp->options & RES_DEBUG) printf(";; res_query_mDNSResponder\n"); 375 376 result = DNSServiceQueryRecord(&sdRef, kDNSServiceFlagsReturnIntermediates, iface, qname, type, class, res_query_callback, &context); 377 if (iface != 0) free(qname); 378 379 if (result != 0) return 0; 380 381 /* Use a kqueue to wait for a response from mDNSResponder */ 382 kq = kqueue(); 383 384 /* determine the maximum time to wait for a result */ 385 gettimeofday(&ctv, NULL); 386 387 /* N.B. statp->retrans is actually the total timeount in seconds */ 388 timeout.tv_sec = statp->retrans; 389 timeout.tv_nsec = 0; 390 391 finish.tv_sec = ctv.tv_sec + statp->retrans; 392 finish.tv_nsec = ctv.tv_usec * 1000; 393 394 /* add mdns reply FD to kqueue */ 395 EV_SET(&mevent, DNSServiceRefSockFD(sdRef), EVFILT_READ, EV_ADD, 0, 0, 0); 396 n = kevent(kq, &mevent, 1, NULL, 0, NULL); 397 if (n != 0) return 0; 398 399 /* add interrupt pipe to kqueue if interrupt is enabled */ 400 if (interrupt_pipe_enabled != 0) 401 { 402 interrupt_pipe = pthread_getspecific(interrupt_pipe_key); 403 if (interrupt_pipe != NULL) 404 { 405 if (interrupt_pipe[0] >= 0) 406 { 407 EV_SET(&ievent, interrupt_pipe[0], EVFILT_READ, EV_ADD, 0, 0, (void *)name); 408 /* allow this to fail silently (should never happen, but it would only break interrupts */ 409 n = kevent(kq, &ievent, 1, NULL, 0, NULL); 410 } 411 } 412 } 413 414 /* 415 * Get notification token 416 * we use a self-notification token to allow a caller 417 * to signal the thread doing this DNS query to quit. 418 */ 419 notify_name = NULL; 420 notify_token = -1; 421 422 asprintf(¬ify_name, "self.thread.%lu", (unsigned long)pthread_self()); 423 if (notify_name != NULL) 424 { 425 status = notify_register_plain(notify_name, ¬ify_token); 426 free(notify_name); 427 } 428 429 wait = 1; 430 while (wait == 1) 431 { 432 memset(&event, 0, sizeof(struct kevent)); 433 n = kevent(kq, NULL, 0, &event, 1, &timeout); 434 435 if (notify_token != -1) 436 { 437 exit_requested = 0; 438 status = notify_get_state(notify_token, &exit_requested); 439 if (exit_requested == ThreadStateExitRequested) 440 { 441 /* interrupted */ 442 if (statp->options & RES_DEBUG) printf(";; cancelled\n"); 443 cancelled = 1; 444 break; 445 } 446 } 447 448 if (n < 0) 449 { 450 if (errno == EINTR) goto keep_waiting; 451 h_errno = NO_RECOVERY; 452 wait = 0; 453 } 454 else if ((n == 0) && (ans->ancount == 0)) 455 { 456 h_errno = TRY_AGAIN; 457 wait = 0; 458 } 459 else if (event.udata == (void *)name) 460 { 461 /* interrupted */ 462 if (statp->options & RES_DEBUG) printf(";; cancelled\n"); 463 cancelled = 1; 464 break; 465 } 466 else 467 { 468 result = DNSServiceProcessResult(sdRef); 469 if ((result != 0) || (context.error != 0)) 470 { 471 if (result == 0) result = context.error; 472 h_errno_for_dnssd_err(result, &h_errno); 473 wait = 0; 474 } 475 476 if ((ans->ancount > 0) && ((context.flags & kDNSServiceFlagsMoreComing) == 0) && ((context.lastanstype != ns_t_cname) || (type == ns_t_cname))) wait = 0; 477 } 478 479 keep_waiting: 480 481 if (wait == 1) 482 { 483 /* calculate remaining timeout */ 484 gettimeofday(&ctv, NULL); 485 486 now.tv_sec = ctv.tv_sec; 487 now.tv_nsec = ctv.tv_usec * 1000; 488 489 timeout.tv_sec = finish.tv_sec - now.tv_sec; 490 if (finish.tv_nsec >= now.tv_nsec) 491 { 492 timeout.tv_nsec = finish.tv_nsec - now.tv_nsec; 493 } 494 else 495 { 496 timeout.tv_nsec = BILLION - now.tv_nsec + finish.tv_nsec; 497 timeout.tv_sec--; 498 } 499 } 500 } 501 502 if (notify_token != -1) notify_cancel(notify_token); 503 DNSServiceRefDeallocate(sdRef); 504 close(kq); 505 506 if ((ans->ancount == 0) || (cancelled == 1)) context.anslen = -1; 507 508 if ((from != NULL) && (fromlen != NULL) && (context.ifnum != 0)) 509 { 510 ((struct sockaddr_in6 *)from)->sin6_len = sizeof(struct sockaddr_in6); 511 ((struct sockaddr_in6 *)from)->sin6_family = AF_INET6; 512 ((struct sockaddr_in6 *)from)->sin6_addr.__u6_addr.__u6_addr8[15] = 1; 513 ((struct sockaddr_in6 *)from)->sin6_scope_id = context.ifnum; 514 *fromlen = sizeof(struct sockaddr_in6); 515 } 516 517 return context.anslen; 518} 519 520static int 521res_soa_minimum(const u_char *msg, int len) 522{ 523 ns_msg handle; 524 const u_char *eom; 525 uint32_t i; 526 int32_t b; 527 int min, soa_min; 528 529 eom = msg + len; 530 531 handle._msg = msg; 532 handle._eom = eom; 533 534 if (msg + NS_INT16SZ > eom) return -1; 535 NS_GET16(handle._id, msg); 536 537 if (msg + NS_INT16SZ > eom) return -1; 538 NS_GET16(handle._flags, msg); 539 540 for (i = 0; i < ns_s_max; i++) 541 { 542 if (msg + NS_INT16SZ > eom) return -1; 543 NS_GET16(handle._counts[i], msg); 544 } 545 546 if (handle._counts[ns_s_ns] == 0) return -1; 547 548 /* Skip forward to nameserver section */ 549 for (i = 0; i < ns_s_ns; i++) 550 { 551 if (handle._counts[i] == 0) handle._sections[i] = NULL; 552 else 553 { 554 b = ns_skiprr(msg, eom, (ns_sect)i, handle._counts[i]); 555 if (b < 0) return -1; 556 557 handle._sections[i] = msg; 558 msg += b; 559 } 560 } 561 562 min = -1; 563 for (i = 0; i < handle._counts[ns_s_ns]; i++) 564 { 565 b = ns_skiprr(msg, eom, ns_s_ns, 1); 566 if (b < 0) return -1; 567 568 memcpy(&soa_min, msg + b - sizeof(int32_t), sizeof(int32_t)); 569 soa_min = ntohl(soa_min); 570 if ((i == 0) || (soa_min < min)) min = soa_min; 571 msg += b; 572 } 573 574 return min; 575} 576 577/* 578 * Formulate a normal query, send, and await answer. 579 * Returned answer is placed in supplied buffer "answer". 580 * Perform preliminary check of answer, returning success only 581 * if no error is indicated and the answer count is nonzero. 582 * Return the size of the response on success, -1 on error. 583 * Error number is left in H_ERRNO. 584 * 585 * Caller must parse answer and determine whether it answers the question. 586 */ 587__private_extern__ int 588res_nquery_soa_min(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen, int *min) 589{ 590 u_char buf[MAXPACKET]; 591 HEADER *hp = (HEADER *) answer; 592 int n; 593 u_int oflags; 594 595 if (min != NULL) *min = -1; 596 597 oflags = statp->_flags; 598 599again: 600 __h_errno_set(statp, 0); 601 hp->rcode = ns_r_noerror; /* default */ 602 603#ifdef DEBUG 604 if (statp->options & RES_DEBUG) printf(";; res_query(%s, %d, %d)\n", name, class, type); 605#endif 606 607 n = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, buf, sizeof(buf)); 608#ifdef RES_USE_EDNS0 609 if (n > 0 && (statp->_flags & RES_F_EDNS0ERR) == 0 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) 610 n = res_nopt(statp, n, buf, sizeof(buf), anslen); 611#endif 612 if (n <= 0) 613 { 614#ifdef DEBUG 615 if (statp->options & RES_DEBUG) printf(";; res_query: mkquery failed\n"); 616#endif 617 __h_errno_set(statp, NO_RECOVERY); 618 return (n); 619 } 620 621 n = res_nsend_2(statp, buf, n, answer, anslen, from, fromlen); 622 if (n < 0) 623 { 624#ifdef RES_USE_EDNS0 625 /* if the query choked with EDNS0, retry without EDNS0 */ 626 if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0 && ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0) 627 { 628 statp->_flags |= RES_F_EDNS0ERR; 629 if (statp->options & RES_DEBUG) printf(";; res_nquery: retry without EDNS0\n"); 630 goto again; 631 } 632#endif 633#ifdef DEBUG 634 if (statp->options & RES_DEBUG) printf(";; res_query: send error\n"); 635#endif 636 __h_errno_set(statp, TRY_AGAIN); 637 return (n); 638 } 639 640 if ((hp->rcode == ns_r_nxdomain) || ((hp->rcode == ns_r_noerror) && (ntohs(hp->ancount) == 0))) 641 { 642 if (min != NULL) 643 { 644 *min = res_soa_minimum(answer, anslen); 645 if (statp->options & RES_DEBUG) printf(";; res_nquery: SOA minimum TTL = %d\n", *min); 646 } 647 } 648 649 if (hp->rcode != ns_r_noerror || ntohs(hp->ancount) == 0) 650 { 651#ifdef DEBUG 652 if (statp->options & RES_DEBUG) printf(";; rcode = %d, ancount=%d\n", hp->rcode, ntohs(hp->ancount)); 653#endif 654 switch (hp->rcode) 655 { 656 case ns_r_nxdomain: 657 __h_errno_set(statp, HOST_NOT_FOUND); 658 break; 659 case ns_r_servfail: 660 __h_errno_set(statp, TRY_AGAIN); 661 break; 662 case ns_r_noerror: 663 __h_errno_set(statp, NO_DATA); 664 break; 665 case ns_r_formerr: 666 case ns_r_notimpl: 667 case ns_r_refused: 668 default: 669 __h_errno_set(statp, NO_RECOVERY); 670 break; 671 } 672 673 return (-1); 674 } 675 676 return (n); 677} 678 679int 680res_nquery_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen) 681{ 682 int unused = 0; 683 684 return res_nquery_soa_min(statp, name, class, type, answer, anslen, from, fromlen, &unused); 685} 686 687int 688res_nquery(res_state statp, const char *name, int class, int type, u_char *answer, int anslen) 689{ 690 struct sockaddr_storage f; 691 int l; 692 693 l = sizeof(struct sockaddr_storage); 694 695 return res_nquery_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l); 696} 697 698/* 699 * Perform a call on res_query on the concatenation of name and domain, 700 * removing a trailing dot from name if domain is NULL. 701 */ 702int 703res_nquerydomain_2(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen) 704{ 705 char nbuf[NS_MAXDNAME]; 706 const char *longname = nbuf; 707 int n, d; 708 709#ifdef DEBUG 710 if (statp->options & RES_DEBUG) printf(";; res_nquerydomain(%s, %s, %d, %d)\n", name, domain?domain:"<Nil>", class, type); 711#endif 712 if (domain == NULL) 713 { 714 /* 715 * Check for trailing '.'; 716 * copy without '.' if present. 717 */ 718 n = strlen(name); 719 if (n >= NS_MAXDNAME) 720 { 721 __h_errno_set(statp, NO_RECOVERY); 722 return (-1); 723 } 724 725 n--; 726 if (n >= 0 && name[n] == '.') 727 { 728 strncpy(nbuf, name, n); 729 nbuf[n] = '\0'; 730 } 731 else 732 { 733 longname = name; 734 } 735 } 736 else 737 { 738 n = strlen(name); 739 d = strlen(domain); 740 if (n + d + 1 >= NS_MAXDNAME) 741 { 742 __h_errno_set(statp, NO_RECOVERY); 743 return (-1); 744 } 745 746 sprintf(nbuf, "%s.%s", name, domain); 747 } 748 749 return (res_nquery_2(statp, longname, class, type, answer, anslen, from, fromlen)); 750} 751 752int 753res_nquerydomain(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen) 754{ 755 struct sockaddr_storage f; 756 int l; 757 758 l = sizeof(struct sockaddr_storage); 759 760 return res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, (struct sockaddr *)&f, &l); 761} 762 763/* 764 * Formulate a normal query, send, and retrieve answer in supplied buffer. 765 * Return the size of the response on success, -1 on error. 766 * If enabled, implement search rules until answer or unrecoverable failure 767 * is detected. Error code, if any, is left in H_ERRNO. 768 */ 769int 770res_nsearch_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen) 771{ 772 const char *cp, * const *domain; 773 HEADER *hp = (HEADER *) answer; 774 char tmp[NS_MAXDNAME]; 775 u_int dots; 776 int trailing_dot, ret, saved_herrno; 777 int got_nodata = 0, got_servfail = 0, root_on_list = 0; 778 int tried_as_is = 0; 779 int searched = 0; 780 781 errno = 0; 782 __h_errno_set(statp, HOST_NOT_FOUND); /* True if we never query. */ 783 784 dots = 0; 785 for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.'); 786 787 trailing_dot = 0; 788 if (cp > name && *--cp == '.') trailing_dot++; 789 790 /* If there aren't any dots, it could be a user-level alias. */ 791 if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL) 792 return (res_nquery(statp, cp, class, type, answer, anslen)); 793 794 /* 795 * If there are enough dots in the name, let's just give it a 796 * try 'as is'. The threshold can be set with the "ndots" option. 797 * Also, query 'as is', if there is a trailing dot in the name. 798 */ 799 saved_herrno = -1; 800 if (dots >= statp->ndots || trailing_dot) 801 { 802 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen); 803 if (ret > 0 || trailing_dot) return (ret); 804 saved_herrno = h_errno; 805 tried_as_is++; 806 } 807 808 /* 809 * We do at least one level of search if 810 * - there is no dot and RES_DEFNAME is set, or 811 * - there is at least one dot, there is no trailing dot, 812 * and RES_DNSRCH is set. 813 */ 814 if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0)) 815 { 816 int done = 0; 817 818 for (domain = (const char * const *)statp->dnsrch; *domain && !done; domain++) 819 { 820 searched = 1; 821 822 if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++; 823 824 ret = res_nquerydomain_2(statp, name, *domain, class, type, answer, anslen, from, fromlen); 825 if (ret > 0) return (ret); 826 827 /* 828 * If no server present, give up. 829 * If name isn't found in this domain, 830 * keep trying higher domains in the search list 831 * (if that's enabled). 832 * On a NO_DATA error, keep trying, otherwise 833 * a wildcard entry of another type could keep us 834 * from finding this entry higher in the domain. 835 * If we get some other error (negative answer or 836 * server failure), then stop searching up, 837 * but try the input name below in case it's 838 * fully-qualified. 839 */ 840 if (errno == ECONNREFUSED) 841 { 842 __h_errno_set(statp, TRY_AGAIN); 843 return (-1); 844 } 845 846 switch (statp->res_h_errno) 847 { 848 case NO_DATA: 849 got_nodata++; 850 /* FALLTHROUGH */ 851 case HOST_NOT_FOUND: 852 /* keep trying */ 853 break; 854 case TRY_AGAIN: 855 if (hp->rcode == ns_r_refused) 856 { 857 /* try next search element, if any */ 858 got_servfail++; 859 break; 860 } 861 /* FALLTHROUGH */ 862 default: 863 /* anything else implies that we're done */ 864 done++; 865 } 866 867 /* if we got here for some reason other than DNSRCH, 868 * we only wanted one iteration of the loop, so stop. 869 */ 870 if ((statp->options & RES_DNSRCH) == 0) done++; 871 } 872 } 873 874 /* 875 * If the query has not already been tried as is then try it 876 * unless RES_NOTLDQUERY is set and there were no dots. 877 */ 878 if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list)) 879 { 880 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen); 881 if (ret > 0) return (ret); 882 } 883 884 /* if we got here, we didn't satisfy the search. 885 * if we did an initial full query, return that query's H_ERRNO 886 * (note that we wouldn't be here if that query had succeeded). 887 * else if we ever got a nodata, send that back as the reason. 888 * else send back meaningless H_ERRNO, that being the one from 889 * the last DNSRCH we did. 890 */ 891 if (saved_herrno != -1) 892 __h_errno_set(statp, saved_herrno); 893 else if (got_nodata) 894 __h_errno_set(statp, NO_DATA); 895 else if (got_servfail) 896 __h_errno_set(statp, TRY_AGAIN); 897 return (-1); 898} 899 900int 901__res_nsearch_list_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen, int nsearch, char **search) 902{ 903 const char *cp, *domain; 904 HEADER *hp = (HEADER *) answer; 905 char tmp[NS_MAXDNAME]; 906 u_int dots; 907 int trailing_dot, ret, saved_herrno, i; 908 int got_nodata = 0, got_servfail = 0, root_on_list = 0; 909 int tried_as_is = 0; 910 int searched = 0; 911 912 errno = 0; 913 __h_errno_set(statp, HOST_NOT_FOUND); /* True if we never query. */ 914 915 dots = 0; 916 for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.'); 917 918 trailing_dot = 0; 919 if (cp > name && *--cp == '.') trailing_dot++; 920 921 /* If there aren't any dots, it could be a user-level alias. */ 922 if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp)) != NULL) 923 return (res_nquery(statp, cp, class, type, answer, anslen)); 924 925 /* 926 * If there are enough dots in the name, let's just give it a 927 * try 'as is'. The threshold can be set with the "ndots" option. 928 * Also, query 'as is', if there is a trailing dot in the name. 929 */ 930 saved_herrno = -1; 931 if (dots >= statp->ndots || trailing_dot) 932 { 933 ret = res_nquerydomain(statp, name, NULL, class, type, answer, anslen); 934 if (ret > 0 || trailing_dot) return ret; 935 saved_herrno = h_errno; 936 tried_as_is++; 937 } 938 939 /* 940 * We do at least one level of search if 941 * - there is no dot and RES_DEFNAME is set, or 942 * - there is at least one dot, there is no trailing dot, 943 * and RES_DNSRCH is set. 944 */ 945 if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0)) 946 { 947 int done = 0; 948 949 for (i = 0; i < nsearch; i++) 950 { 951 domain = search[i]; 952 searched = 1; 953 954 if (domain[0] == '\0' || (domain[0] == '.' && domain[1] == '\0')) root_on_list++; 955 956 ret = res_nquerydomain_2(statp, name, domain, class, type, answer, anslen, from, fromlen); 957 if (ret > 0) return ret; 958 959 /* 960 * If no server present, give up. 961 * If name isn't found in this domain, 962 * keep trying higher domains in the search list 963 * (if that's enabled). 964 * On a NO_DATA error, keep trying, otherwise 965 * a wildcard entry of another type could keep us 966 * from finding this entry higher in the domain. 967 * If we get some other error (negative answer or 968 * server failure), then stop searching up, 969 * but try the input name below in case it's 970 * fully-qualified. 971 */ 972 if (errno == ECONNREFUSED) 973 { 974 __h_errno_set(statp, TRY_AGAIN); 975 return -1; 976 } 977 978 switch (statp->res_h_errno) 979 { 980 case NO_DATA: 981 got_nodata++; 982 /* FALLTHROUGH */ 983 case HOST_NOT_FOUND: 984 /* keep trying */ 985 break; 986 case TRY_AGAIN: 987 if (hp->rcode == ns_r_refused) 988 { 989 /* try next search element, if any */ 990 got_servfail++; 991 break; 992 } 993 /* FALLTHROUGH */ 994 default: 995 /* anything else implies that we're done */ 996 done++; 997 } 998 999 /* 1000 * if we got here for some reason other than DNSRCH, 1001 * we only wanted one iteration of the loop, so stop. 1002 */ 1003 if ((statp->options & RES_DNSRCH) == 0) done++; 1004 } 1005 } 1006 1007 /* 1008 * If the query has not already been tried as is then try it 1009 * unless RES_NOTLDQUERY is set and there were no dots. 1010 */ 1011 if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0) && !(tried_as_is || root_on_list)) 1012 { 1013 ret = res_nquerydomain_2(statp, name, NULL, class, type, answer, anslen, from, fromlen); 1014 if (ret > 0) return ret; 1015 } 1016 1017 /* 1018 * we got here, we didn't satisfy the search. 1019 * if we did an initial full query, return that query's H_ERRNO 1020 * (note that we wouldn't be here if that query had succeeded). 1021 * else if we ever got a nodata, send that back as the reason. 1022 * else send back meaningless H_ERRNO, that being the one from 1023 * the last DNSRCH we did. 1024 */ 1025 if (saved_herrno != -1) __h_errno_set(statp, saved_herrno); 1026 else if (got_nodata) __h_errno_set(statp, NO_DATA); 1027 else if (got_servfail) __h_errno_set(statp, TRY_AGAIN); 1028 return -1; 1029} 1030 1031int 1032res_nsearch(res_state statp, const char *name, int class, int type, u_char *answer, int anslen) 1033{ 1034 struct sockaddr_storage f; 1035 int l; 1036 1037 l = sizeof(struct sockaddr_storage); 1038 1039 return res_nsearch_2(statp, name, class, type, answer, anslen, (struct sockaddr *)&f, &l); 1040} 1041 1042const char * 1043res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) 1044{ 1045 char *file, *cp1, *cp2; 1046 char buf[BUFSIZ]; 1047 FILE *fp; 1048 1049 if (statp->options & RES_NOALIASES) return (NULL); 1050 1051 file = getenv("HOSTALIASES"); 1052 if (file == NULL || (fp = fopen(file, "r")) == NULL) return (NULL); 1053 1054 setbuf(fp, NULL); 1055 buf[sizeof(buf) - 1] = '\0'; 1056 while (fgets(buf, sizeof(buf), fp)) 1057 { 1058 for (cp1 = buf; *cp1 && !isspace((unsigned char)*cp1); ++cp1) ; 1059 1060 if (!*cp1) break; 1061 *cp1 = '\0'; 1062 1063 if (ns_samename(buf, name) == 1) 1064 { 1065 while (isspace((unsigned char)*++cp1)) ; 1066 1067 if (!*cp1) break; 1068 1069 for (cp2 = cp1 + 1; *cp2 && !isspace((unsigned char)*cp2); ++cp2) ; 1070 1071 *cp2 = '\0'; 1072 strncpy(dst, cp1, siz - 1); 1073 dst[siz - 1] = '\0'; 1074 fclose(fp); 1075 return (dst); 1076 } 1077 } 1078 1079 fclose(fp); 1080 return (NULL); 1081} 1082