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