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