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