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