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