1 /* $OpenBSD: asr.c,v 1.68 2022/01/20 14:18:10 naddy Exp $ */ 2 /* 3 * Copyright (c) 2010-2012 Eric Faurot <eric@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/socket.h> 20 #include <sys/stat.h> 21 #include <netinet/in.h> 22 #include <arpa/inet.h> 23 #include <arpa/nameser.h> 24 #include <netdb.h> 25 26 #include <asr.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <resolv.h> 30 #include <poll.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <limits.h> 36 37 #include "asr_private.h" 38 39 #include "thread_private.h" 40 41 #define DEFAULT_CONF "lookup file\n" 42 #define DEFAULT_LOOKUP "lookup bind file" 43 44 #define RELOAD_DELAY 15 /* seconds */ 45 46 static void asr_check_reload(struct asr *); 47 static struct asr_ctx *asr_ctx_create(void); 48 static void asr_ctx_ref(struct asr_ctx *); 49 static void asr_ctx_free(struct asr_ctx *); 50 static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *); 51 static int asr_ctx_from_file(struct asr_ctx *, const char *); 52 static int asr_ctx_from_string(struct asr_ctx *, const char *); 53 static int asr_ctx_parse(struct asr_ctx *, const char *); 54 static int asr_parse_nameserver(struct sockaddr *, const char *); 55 static int asr_ndots(const char *); 56 static void pass0(char **, int, struct asr_ctx *); 57 static int strsplit(char *, char **, int); 58 static void asr_ctx_envopts(struct asr_ctx *); 59 static void *__THREAD_NAME(_asr); 60 61 static struct asr *_asr = NULL; 62 63 /* Allocate and configure an async "resolver". */ 64 static void * 65 _asr_resolver(void) 66 { 67 static int init = 0; 68 struct asr *asr; 69 70 if (init == 0) { 71 #ifdef DEBUG 72 if (getenv("ASR_DEBUG")) 73 _asr_debug = stderr; 74 #endif 75 init = 1; 76 } 77 78 if ((asr = calloc(1, sizeof(*asr))) == NULL) 79 goto fail; 80 81 asr_check_reload(asr); 82 if (asr->a_ctx == NULL) { 83 if ((asr->a_ctx = asr_ctx_create()) == NULL) 84 goto fail; 85 if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1) 86 goto fail; 87 asr_ctx_envopts(asr->a_ctx); 88 } 89 90 #ifdef DEBUG 91 _asr_dump_config(_asr_debug, asr); 92 #endif 93 return (asr); 94 95 fail: 96 if (asr) { 97 if (asr->a_ctx) 98 asr_ctx_free(asr->a_ctx); 99 free(asr); 100 } 101 102 return (NULL); 103 } 104 105 /* 106 * Free the "asr" async resolver (or the thread-local resolver if NULL). 107 * Drop the reference to the current context. 108 */ 109 void 110 _asr_resolver_done(void *arg) 111 { 112 struct asr_ctx *ac = arg; 113 struct asr *asr; 114 struct asr **priv; 115 116 if (ac) { 117 _asr_ctx_unref(ac); 118 return; 119 } else { 120 priv = _THREAD_PRIVATE_DT(_asr, _asr, NULL, &_asr); 121 if (*priv == NULL) 122 return; 123 asr = *priv; 124 *priv = NULL; 125 } 126 127 _asr_ctx_unref(asr->a_ctx); 128 free(asr); 129 } 130 131 static void 132 _asr_resolver_done_tp(void *arg) 133 { 134 struct asr **priv = arg; 135 struct asr *asr; 136 137 if (*priv == NULL) 138 return; 139 asr = *priv; 140 141 _asr_ctx_unref(asr->a_ctx); 142 free(asr); 143 free(priv); 144 } 145 146 void * 147 asr_resolver_from_string(const char *str) 148 { 149 struct asr_ctx *ac; 150 151 if ((ac = asr_ctx_create()) == NULL) 152 return NULL; 153 154 if (asr_ctx_from_string(ac, str) == -1) { 155 asr_ctx_free(ac); 156 return NULL; 157 } 158 159 return ac; 160 } 161 DEF_WEAK(asr_resolver_from_string); 162 163 void 164 asr_resolver_free(void *arg) 165 { 166 _asr_ctx_unref(arg); 167 } 168 DEF_WEAK(asr_resolver_free); 169 170 /* 171 * Cancel an async query. 172 */ 173 void 174 asr_abort(struct asr_query *as) 175 { 176 _asr_async_free(as); 177 } 178 179 /* 180 * Resume the "as" async query resolution. Return one of ASYNC_COND, 181 * or ASYNC_DONE and put query-specific return values in the user-allocated 182 * memory at "ar". 183 */ 184 int 185 asr_run(struct asr_query *as, struct asr_result *ar) 186 { 187 int r, saved_errno = errno; 188 189 memset(ar, 0, sizeof(*ar)); 190 191 DPRINT("asr: asr_run(%p, %p) %s ctx=[%p]\n", as, ar, 192 _asr_querystr(as->as_type), as->as_ctx); 193 r = as->as_run(as, ar); 194 195 DPRINT("asr: asr_run(%p, %p) -> %s", as, ar, _asr_transitionstr(r)); 196 #ifdef DEBUG 197 if (r == ASYNC_COND) 198 #endif 199 DPRINT(" fd=%i timeout=%i", ar->ar_fd, ar->ar_timeout); 200 DPRINT("\n"); 201 if (r == ASYNC_DONE) 202 _asr_async_free(as); 203 204 errno = saved_errno; 205 206 return (r); 207 } 208 DEF_WEAK(asr_run); 209 210 static int 211 poll_intrsafe(struct pollfd *fds, nfds_t nfds, int timeout) 212 { 213 struct timespec pollstart, pollend, elapsed; 214 int r; 215 216 if (WRAP(clock_gettime)(CLOCK_MONOTONIC, &pollstart)) 217 return -1; 218 219 while ((r = poll(fds, 1, timeout)) == -1 && errno == EINTR) { 220 if (WRAP(clock_gettime)(CLOCK_MONOTONIC, &pollend)) 221 return -1; 222 timespecsub(&pollend, &pollstart, &elapsed); 223 timeout -= elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000; 224 if (timeout < 1) 225 return 0; 226 } 227 228 return r; 229 } 230 231 /* 232 * Same as asr_run, but run in a loop that handles the fd conditions result. 233 */ 234 int 235 asr_run_sync(struct asr_query *as, struct asr_result *ar) 236 { 237 struct pollfd fds[1]; 238 int r, saved_errno = errno; 239 240 while ((r = asr_run(as, ar)) == ASYNC_COND) { 241 fds[0].fd = ar->ar_fd; 242 fds[0].events = (ar->ar_cond == ASR_WANT_READ) ? POLLIN:POLLOUT; 243 244 if (poll_intrsafe(fds, 1, ar->ar_timeout) == -1) { 245 memset(ar, 0, sizeof(*ar)); 246 ar->ar_errno = errno; 247 ar->ar_h_errno = NETDB_INTERNAL; 248 ar->ar_gai_errno = EAI_SYSTEM; 249 ar->ar_rrset_errno = NETDB_INTERNAL; 250 _asr_async_free(as); 251 errno = saved_errno; 252 return ASYNC_DONE; 253 } 254 255 /* 256 * Otherwise, just ignore the error and let asr_run() 257 * catch the failure. 258 */ 259 } 260 261 errno = saved_errno; 262 263 return (r); 264 } 265 DEF_WEAK(asr_run_sync); 266 267 /* 268 * Create a new async request of the given "type" on the async context "ac". 269 * Take a reference on it so it does not get deleted while the async query 270 * is running. 271 */ 272 struct asr_query * 273 _asr_async_new(struct asr_ctx *ac, int type) 274 { 275 struct asr_query *as; 276 277 DPRINT("asr: asr_async_new(ctx=%p) type=%i refcount=%i\n", ac, type, 278 ac ? ac->ac_refcount : 0); 279 if (ac == NULL || (as = calloc(1, sizeof(*as))) == NULL) 280 return (NULL); 281 282 ac->ac_refcount += 1; 283 as->as_ctx = ac; 284 as->as_fd = -1; 285 as->as_type = type; 286 as->as_state = ASR_STATE_INIT; 287 288 return (as); 289 } 290 291 /* 292 * Free an async query and unref the associated context. 293 */ 294 void 295 _asr_async_free(struct asr_query *as) 296 { 297 DPRINT("asr: asr_async_free(%p)\n", as); 298 299 if (as->as_subq) 300 _asr_async_free(as->as_subq); 301 302 switch (as->as_type) { 303 case ASR_SEND: 304 if (as->as_fd != -1) 305 close(as->as_fd); 306 if (as->as.dns.obuf && !(as->as_flags & ASYNC_EXTOBUF)) 307 free(as->as.dns.obuf); 308 if (as->as.dns.ibuf) 309 free(as->as.dns.ibuf); 310 if (as->as.dns.dname) 311 free(as->as.dns.dname); 312 break; 313 314 case ASR_SEARCH: 315 if (as->as.search.name) 316 free(as->as.search.name); 317 break; 318 319 case ASR_GETRRSETBYNAME: 320 if (as->as.rrset.name) 321 free(as->as.rrset.name); 322 break; 323 324 case ASR_GETHOSTBYNAME: 325 case ASR_GETHOSTBYADDR: 326 if (as->as.hostnamadr.name) 327 free(as->as.hostnamadr.name); 328 break; 329 330 case ASR_GETADDRINFO: 331 if (as->as.ai.aifirst) 332 freeaddrinfo(as->as.ai.aifirst); 333 if (as->as.ai.hostname) 334 free(as->as.ai.hostname); 335 if (as->as.ai.servname) 336 free(as->as.ai.servname); 337 if (as->as.ai.fqdn) 338 free(as->as.ai.fqdn); 339 break; 340 341 case ASR_GETNAMEINFO: 342 break; 343 } 344 345 _asr_ctx_unref(as->as_ctx); 346 free(as); 347 } 348 349 /* 350 * Get a context from the given resolver. This takes a new reference to 351 * the returned context, which *must* be explicitly dropped when done 352 * using this context. 353 */ 354 struct asr_ctx * 355 _asr_use_resolver(void *arg) 356 { 357 struct asr_ctx *ac = arg; 358 struct asr *asr; 359 struct asr **priv; 360 361 if (ac) { 362 asr_ctx_ref(ac); 363 return ac; 364 } 365 else { 366 DPRINT("using thread-local resolver\n"); 367 priv = _THREAD_PRIVATE_DT(_asr, _asr, _asr_resolver_done_tp, 368 &_asr); 369 if (*priv == NULL) { 370 DPRINT("setting up thread-local resolver\n"); 371 *priv = _asr_resolver(); 372 } 373 asr = *priv; 374 } 375 if (asr != NULL) { 376 asr_check_reload(asr); 377 asr_ctx_ref(asr->a_ctx); 378 return (asr->a_ctx); 379 } 380 return (NULL); 381 } 382 383 static void 384 asr_ctx_ref(struct asr_ctx *ac) 385 { 386 DPRINT("asr: asr_ctx_ref(ctx=%p) refcount=%i\n", ac, ac->ac_refcount); 387 ac->ac_refcount += 1; 388 } 389 390 /* 391 * Drop a reference to an async context, freeing it if the reference 392 * count drops to 0. 393 */ 394 void 395 _asr_ctx_unref(struct asr_ctx *ac) 396 { 397 DPRINT("asr: asr_ctx_unref(ctx=%p) refcount=%i\n", ac, 398 ac ? ac->ac_refcount : 0); 399 if (ac == NULL) 400 return; 401 if (--ac->ac_refcount) 402 return; 403 404 asr_ctx_free(ac); 405 } 406 407 static void 408 asr_ctx_free(struct asr_ctx *ac) 409 { 410 int i; 411 412 if (ac->ac_domain) 413 free(ac->ac_domain); 414 for (i = 0; i < ASR_MAXNS; i++) 415 free(ac->ac_ns[i]); 416 for (i = 0; i < ASR_MAXDOM; i++) 417 free(ac->ac_dom[i]); 418 419 free(ac); 420 } 421 422 /* 423 * Reload the configuration file if it has changed on disk. 424 */ 425 static void 426 asr_check_reload(struct asr *asr) 427 { 428 struct asr_ctx *ac; 429 struct stat st; 430 struct timespec ts; 431 pid_t pid; 432 433 pid = getpid(); 434 if (pid != asr->a_pid) { 435 asr->a_pid = pid; 436 asr->a_rtime = 0; 437 } 438 439 if (WRAP(clock_gettime)(CLOCK_MONOTONIC, &ts) == -1) 440 return; 441 442 if ((ts.tv_sec - asr->a_rtime) < RELOAD_DELAY && asr->a_rtime != 0) 443 return; 444 asr->a_rtime = ts.tv_sec; 445 446 DPRINT("asr: checking for update of \"%s\"\n", _PATH_RESCONF); 447 if (stat(_PATH_RESCONF, &st) == -1 || 448 asr->a_mtime == st.st_mtime || 449 (ac = asr_ctx_create()) == NULL) 450 return; 451 asr->a_mtime = st.st_mtime; 452 453 DPRINT("asr: reloading config file\n"); 454 if (asr_ctx_from_file(ac, _PATH_RESCONF) == -1) { 455 asr_ctx_free(ac); 456 return; 457 } 458 459 asr_ctx_envopts(ac); 460 if (asr->a_ctx) 461 _asr_ctx_unref(asr->a_ctx); 462 asr->a_ctx = ac; 463 } 464 465 /* 466 * Construct a fully-qualified domain name for the given name and domain. 467 * If "name" ends with a '.' it is considered as a FQDN by itself. 468 * Otherwise, the domain, which must be a FQDN, is appended to "name" (it 469 * may have a leading dot which would be ignored). If the domain is null, 470 * then "." is used. Return the length of the constructed FQDN or (0) on 471 * error. 472 */ 473 size_t 474 _asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen) 475 { 476 size_t len; 477 478 if (domain == NULL) 479 domain = "."; 480 else if ((len = strlen(domain)) == 0) 481 return (0); 482 else if (domain[len -1] != '.') 483 return (0); 484 485 len = strlen(name); 486 if (len == 0) { 487 if (strlcpy(buf, domain, buflen) >= buflen) 488 return (0); 489 } else if (name[len - 1] != '.') { 490 if (domain[0] == '.') 491 domain += 1; 492 if (strlcpy(buf, name, buflen) >= buflen || 493 strlcat(buf, ".", buflen) >= buflen || 494 strlcat(buf, domain, buflen) >= buflen) 495 return (0); 496 } else { 497 if (strlcpy(buf, name, buflen) >= buflen) 498 return (0); 499 } 500 501 return (strlen(buf)); 502 } 503 504 /* 505 * Count the dots in a string. 506 */ 507 static int 508 asr_ndots(const char *s) 509 { 510 int n; 511 512 for (n = 0; *s; s++) 513 if (*s == '.') 514 n += 1; 515 516 return (n); 517 } 518 519 /* 520 * Allocate a new empty context. 521 */ 522 static struct asr_ctx * 523 asr_ctx_create(void) 524 { 525 struct asr_ctx *ac; 526 527 if ((ac = calloc(1, sizeof(*ac))) == NULL) 528 return (NULL); 529 530 ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; 531 ac->ac_refcount = 1; 532 ac->ac_ndots = 1; 533 ac->ac_family[0] = AF_INET; 534 ac->ac_family[1] = AF_INET6; 535 ac->ac_family[2] = -1; 536 537 ac->ac_nscount = 0; 538 ac->ac_nstimeout = 5; 539 ac->ac_nsretries = 4; 540 541 return (ac); 542 } 543 544 struct asr_ctx * 545 _asr_no_resolver(void) 546 { 547 return asr_ctx_create(); 548 } 549 550 /* 551 * Add a search domain to the async context. 552 */ 553 static int 554 asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain) 555 { 556 char buf[MAXDNAME]; 557 558 if (ac->ac_domcount == ASR_MAXDOM) 559 return (-1); 560 561 if (_asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0) 562 return (-1); 563 564 if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL) 565 return (0); 566 567 ac->ac_domcount += 1; 568 569 return (1); 570 } 571 572 static int 573 strsplit(char *line, char **tokens, int ntokens) 574 { 575 int ntok; 576 char *cp, **tp; 577 578 for (cp = line, tp = tokens, ntok = 0; 579 ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; ) 580 if (**tp != '\0') { 581 tp++; 582 ntok++; 583 } 584 585 return (ntok); 586 } 587 588 /* 589 * Pass on a split config line. 590 */ 591 static void 592 pass0(char **tok, int n, struct asr_ctx *ac) 593 { 594 int i, j, d; 595 const char *e; 596 struct sockaddr_storage ss; 597 598 if (!strcmp(tok[0], "nameserver")) { 599 if (ac->ac_nscount == ASR_MAXNS) 600 return; 601 if (n != 2) 602 return; 603 if (asr_parse_nameserver((struct sockaddr *)&ss, tok[1])) 604 return; 605 if ((ac->ac_ns[ac->ac_nscount] = calloc(1, ss.ss_len)) == NULL) 606 return; 607 memmove(ac->ac_ns[ac->ac_nscount], &ss, ss.ss_len); 608 ac->ac_nscount += 1; 609 610 } else if (!strcmp(tok[0], "domain")) { 611 if (n != 2) 612 return; 613 if (ac->ac_domain) 614 return; 615 ac->ac_domain = strdup(tok[1]); 616 617 } else if (!strcmp(tok[0], "lookup")) { 618 /* ensure that each lookup is only given once */ 619 for (i = 1; i < n; i++) 620 for (j = i + 1; j < n; j++) 621 if (!strcmp(tok[i], tok[j])) 622 return; 623 ac->ac_dbcount = 0; 624 for (i = 1; i < n && ac->ac_dbcount < ASR_MAXDB; i++) { 625 if (!strcmp(tok[i], "yp")) { 626 /* silently deprecated */ 627 } else if (!strcmp(tok[i], "bind")) 628 ac->ac_db[ac->ac_dbcount++] = ASR_DB_DNS; 629 else if (!strcmp(tok[i], "file")) 630 ac->ac_db[ac->ac_dbcount++] = ASR_DB_FILE; 631 } 632 } else if (!strcmp(tok[0], "search")) { 633 /* resolv.conf says the last line wins */ 634 for (i = 0; i < ASR_MAXDOM; i++) { 635 free(ac->ac_dom[i]); 636 ac->ac_dom[i] = NULL; 637 } 638 ac->ac_domcount = 0; 639 for (i = 1; i < n; i++) 640 asr_ctx_add_searchdomain(ac, tok[i]); 641 642 } else if (!strcmp(tok[0], "family")) { 643 if (n == 1 || n > 3) 644 return; 645 for (i = 1; i < n; i++) 646 if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6")) 647 return; 648 for (i = 1; i < n; i++) 649 ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \ 650 AF_INET6 : AF_INET; 651 ac->ac_family[i - 1] = -1; 652 653 } else if (!strcmp(tok[0], "options")) { 654 for (i = 1; i < n; i++) { 655 if (!strcmp(tok[i], "tcp")) 656 ac->ac_options |= RES_USEVC; 657 else if (!strcmp(tok[i], "edns0")) 658 ac->ac_options |= RES_USE_EDNS0; 659 else if ((!strncmp(tok[i], "ndots:", 6))) { 660 e = NULL; 661 d = strtonum(tok[i] + 6, 1, 16, &e); 662 if (e == NULL) 663 ac->ac_ndots = d; 664 } else if (!strcmp(tok[i], "trust-ad")) 665 ac->ac_options |= RES_TRUSTAD; 666 } 667 } 668 } 669 670 /* 671 * Setup an async context with the config specified in the string "str". 672 */ 673 static int 674 asr_ctx_from_string(struct asr_ctx *ac, const char *str) 675 { 676 struct sockaddr_in6 *sin6; 677 struct sockaddr_in *sin; 678 int i, trustad; 679 char buf[512], *ch; 680 681 asr_ctx_parse(ac, str); 682 683 if (ac->ac_dbcount == 0) { 684 /* No lookup directive */ 685 asr_ctx_parse(ac, DEFAULT_LOOKUP); 686 } 687 688 if (ac->ac_nscount == 0) 689 asr_ctx_parse(ac, "nameserver 127.0.0.1"); 690 691 if (ac->ac_domain == NULL) 692 if (gethostname(buf, sizeof buf) == 0) { 693 ch = strchr(buf, '.'); 694 if (ch) 695 ac->ac_domain = strdup(ch + 1); 696 else /* Assume root. see resolv.conf(5) */ 697 ac->ac_domain = strdup(""); 698 } 699 700 /* If no search domain was specified, use the local subdomains */ 701 if (ac->ac_domcount == 0) 702 for (ch = ac->ac_domain; ch; ) { 703 asr_ctx_add_searchdomain(ac, ch); 704 ch = strchr(ch, '.'); 705 if (ch && asr_ndots(++ch) == 0) 706 break; 707 } 708 709 trustad = 1; 710 for (i = 0; i < ac->ac_nscount && trustad; i++) { 711 switch (ac->ac_ns[i]->sa_family) { 712 case AF_INET: 713 sin = (struct sockaddr_in *)ac->ac_ns[i]; 714 if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) 715 trustad = 0; 716 break; 717 case AF_INET6: 718 sin6 = (struct sockaddr_in6 *)ac->ac_ns[i]; 719 if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) 720 trustad = 0; 721 break; 722 default: 723 trustad = 0; 724 break; 725 } 726 } 727 if (trustad) 728 ac->ac_options |= RES_TRUSTAD; 729 730 return (0); 731 } 732 733 /* 734 * Setup the "ac" async context from the file at location "path". 735 */ 736 static int 737 asr_ctx_from_file(struct asr_ctx *ac, const char *path) 738 { 739 FILE *cf; 740 char buf[4096]; 741 ssize_t r; 742 743 cf = fopen(path, "re"); 744 if (cf == NULL) 745 return (-1); 746 747 r = fread(buf, 1, sizeof buf - 1, cf); 748 if (feof(cf) == 0) { 749 DPRINT("asr: config file too long: \"%s\"\n", path); 750 r = -1; 751 } 752 fclose(cf); 753 if (r == -1) 754 return (-1); 755 buf[r] = '\0'; 756 757 return asr_ctx_from_string(ac, buf); 758 } 759 760 /* 761 * Parse lines in the configuration string. For each one, split it into 762 * tokens and pass them to "pass0" for processing. 763 */ 764 static int 765 asr_ctx_parse(struct asr_ctx *ac, const char *str) 766 { 767 size_t len; 768 const char *line; 769 char buf[1024]; 770 char *tok[10]; 771 int ntok; 772 773 line = str; 774 while (*line) { 775 len = strcspn(line, "\n\0"); 776 if (len < sizeof buf) { 777 memmove(buf, line, len); 778 buf[len] = '\0'; 779 } else 780 buf[0] = '\0'; 781 line += len; 782 if (*line == '\n') 783 line++; 784 buf[strcspn(buf, ";#")] = '\0'; 785 if ((ntok = strsplit(buf, tok, 10)) == 0) 786 continue; 787 788 pass0(tok, ntok, ac); 789 } 790 791 return (0); 792 } 793 794 /* 795 * Check for environment variables altering the configuration as described 796 * in resolv.conf(5). Although not documented there, this feature is disabled 797 * for setuid/setgid programs. 798 */ 799 static void 800 asr_ctx_envopts(struct asr_ctx *ac) 801 { 802 char buf[4096], *e; 803 size_t s; 804 805 if (issetugid()) { 806 ac->ac_options |= RES_NOALIASES; 807 return; 808 } 809 810 if ((e = getenv("RES_OPTIONS")) != NULL) { 811 strlcpy(buf, "options ", sizeof buf); 812 strlcat(buf, e, sizeof buf); 813 s = strlcat(buf, "\n", sizeof buf); 814 if (s < sizeof buf) 815 asr_ctx_parse(ac, buf); 816 } 817 818 if ((e = getenv("LOCALDOMAIN")) != NULL) { 819 strlcpy(buf, "search ", sizeof buf); 820 strlcat(buf, e, sizeof buf); 821 s = strlcat(buf, "\n", sizeof buf); 822 if (s < sizeof buf) 823 asr_ctx_parse(ac, buf); 824 } 825 } 826 827 /* 828 * Parse a resolv.conf(5) nameserver string into a sockaddr. 829 */ 830 static int 831 asr_parse_nameserver(struct sockaddr *sa, const char *s) 832 { 833 in_port_t portno = 53; 834 835 if (_asr_sockaddr_from_str(sa, PF_UNSPEC, s) == -1) 836 return (-1); 837 838 if (sa->sa_family == PF_INET) 839 ((struct sockaddr_in *)sa)->sin_port = htons(portno); 840 else if (sa->sa_family == PF_INET6) 841 ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); 842 843 return (0); 844 } 845 846 /* 847 * Turn a (uncompressed) DNS domain name into a regular nul-terminated string 848 * where labels are separated by dots. The result is put into the "buf" buffer, 849 * truncated if it exceeds "max" chars. The function returns "buf". 850 */ 851 char * 852 _asr_strdname(const char *_dname, char *buf, size_t max) 853 { 854 const unsigned char *dname = _dname; 855 char *res; 856 size_t left, count; 857 858 if (_dname[0] == 0) { 859 strlcpy(buf, ".", max); 860 return buf; 861 } 862 863 res = buf; 864 left = max - 1; 865 while (dname[0] && left) { 866 count = (dname[0] < (left - 1)) ? dname[0] : (left - 1); 867 memmove(buf, dname + 1, count); 868 dname += dname[0] + 1; 869 left -= count; 870 buf += count; 871 if (left) { 872 left -= 1; 873 *buf++ = '.'; 874 } 875 } 876 buf[0] = 0; 877 878 return (res); 879 } 880 881 /* 882 * Read and split the next line from the given namedb file. 883 * Return -1 on error, or put the result in the "tokens" array of 884 * size "ntoken" and returns the number of token on the line. 885 */ 886 int 887 _asr_parse_namedb_line(FILE *file, char **tokens, int ntoken, char *lbuf, size_t sz) 888 { 889 size_t len; 890 char *buf; 891 int ntok; 892 893 again: 894 if ((buf = fgetln(file, &len)) == NULL) 895 return (-1); 896 897 if (len >= sz) 898 goto again; 899 900 if (buf[len - 1] == '\n') 901 len--; 902 else { 903 memcpy(lbuf, buf, len); 904 buf = lbuf; 905 } 906 907 buf[len] = '\0'; 908 buf[strcspn(buf, "#")] = '\0'; 909 if ((ntok = strsplit(buf, tokens, ntoken)) == 0) 910 goto again; 911 912 return (ntok); 913 } 914 915 /* 916 * Update the async context so that it uses the next configured DB. 917 * Return 0 on success, or -1 if no more DBs is available. 918 */ 919 int 920 _asr_iter_db(struct asr_query *as) 921 { 922 if (as->as_db_idx >= as->as_ctx->ac_dbcount) { 923 DPRINT("asr_iter_db: done\n"); 924 return (-1); 925 } 926 927 as->as_db_idx += 1; 928 DPRINT("asr_iter_db: %i\n", as->as_db_idx); 929 930 return (0); 931 } 932