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