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