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