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