1 /* $OpenBSD: asr.c,v 1.42 2015/09/20 12:50:58 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 #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_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 = _PATH_RESCONF; 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 DEF_WEAK(asr_run); 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 DEF_WEAK(asr_run_sync); 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 pid_t pid; 417 #endif 418 419 if (asr->a_path == NULL) 420 return; 421 422 #if ASR_OPT_RELOADCONF 423 424 pid = getpid(); 425 if (pid != asr->a_pid) { 426 asr->a_pid = pid; 427 asr->a_rtime = 0; 428 } 429 430 if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) 431 return; 432 433 if ((ts.tv_sec - asr->a_rtime) < RELOAD_DELAY && asr->a_rtime != 0) 434 return; 435 asr->a_rtime = ts.tv_sec; 436 437 DPRINT("asr: checking for update of \"%s\"\n", asr->a_path); 438 if (stat(asr->a_path, &st) == -1 || 439 asr->a_mtime == st.st_mtime || 440 (ac = asr_ctx_create()) == NULL) 441 return; 442 asr->a_mtime = st.st_mtime; 443 #else 444 if ((ac = asr_ctx_create()) == NULL) 445 return; 446 #endif 447 448 DPRINT("asr: reloading config file\n"); 449 if (asr_ctx_from_file(ac, asr->a_path) == -1) { 450 asr_ctx_free(ac); 451 return; 452 } 453 454 #if ASR_OPT_ENVOPTS 455 asr_ctx_envopts(ac); 456 #endif 457 if (asr->a_ctx) 458 _asr_ctx_unref(asr->a_ctx); 459 asr->a_ctx = ac; 460 } 461 462 /* 463 * Construct a fully-qualified domain name for the given name and domain. 464 * If "name" ends with a '.' it is considered as a FQDN by itself. 465 * Otherwise, the domain, which must be a FQDN, is appended to "name" (it 466 * may have a leading dot which would be ignored). If the domain is null, 467 * then "." is used. Return the length of the constructed FQDN or (0) on 468 * error. 469 */ 470 size_t 471 _asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen) 472 { 473 size_t len; 474 475 if (domain == NULL) 476 domain = "."; 477 else if ((len = strlen(domain)) == 0) 478 return (0); 479 else if (domain[len -1] != '.') 480 return (0); 481 482 len = strlen(name); 483 if (len == 0) { 484 if (strlcpy(buf, domain, buflen) >= buflen) 485 return (0); 486 } else if (name[len - 1] != '.') { 487 if (domain[0] == '.') 488 domain += 1; 489 if (strlcpy(buf, name, buflen) >= buflen || 490 strlcat(buf, ".", buflen) >= buflen || 491 strlcat(buf, domain, buflen) >= buflen) 492 return (0); 493 } else { 494 if (strlcpy(buf, name, buflen) >= buflen) 495 return (0); 496 } 497 498 return (strlen(buf)); 499 } 500 501 /* 502 * Count the dots in a string. 503 */ 504 static int 505 asr_ndots(const char *s) 506 { 507 int n; 508 509 for (n = 0; *s; s++) 510 if (*s == '.') 511 n += 1; 512 513 return (n); 514 } 515 516 /* 517 * Allocate a new empty context. 518 */ 519 static struct asr_ctx * 520 asr_ctx_create(void) 521 { 522 struct asr_ctx *ac; 523 524 if ((ac = calloc(1, sizeof(*ac))) == NULL) 525 return (NULL); 526 527 ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; 528 ac->ac_refcount = 1; 529 ac->ac_ndots = 1; 530 ac->ac_family[0] = AF_INET; 531 ac->ac_family[1] = AF_INET6; 532 ac->ac_family[2] = -1; 533 534 ac->ac_nscount = 0; 535 ac->ac_nstimeout = 5; 536 ac->ac_nsretries = 4; 537 538 return (ac); 539 } 540 541 /* 542 * Add a search domain to the async context. 543 */ 544 static int 545 asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain) 546 { 547 char buf[MAXDNAME]; 548 549 if (ac->ac_domcount == ASR_MAXDOM) 550 return (-1); 551 552 if (_asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0) 553 return (-1); 554 555 if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL) 556 return (0); 557 558 ac->ac_domcount += 1; 559 560 return (1); 561 } 562 563 static int 564 strsplit(char *line, char **tokens, int ntokens) 565 { 566 int ntok; 567 char *cp, **tp; 568 569 for (cp = line, tp = tokens, ntok = 0; 570 ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; ) 571 if (**tp != '\0') { 572 tp++; 573 ntok++; 574 } 575 576 return (ntok); 577 } 578 579 /* 580 * Pass on a split config line. 581 */ 582 static void 583 pass0(char **tok, int n, struct asr_ctx *ac) 584 { 585 int i, j, d; 586 const char *e; 587 struct sockaddr_storage ss; 588 589 if (!strcmp(tok[0], "nameserver")) { 590 if (ac->ac_nscount == ASR_MAXNS) 591 return; 592 if (n != 2) 593 return; 594 if (asr_parse_nameserver((struct sockaddr *)&ss, tok[1])) 595 return; 596 if ((ac->ac_ns[ac->ac_nscount] = calloc(1, ss.ss_len)) == NULL) 597 return; 598 memmove(ac->ac_ns[ac->ac_nscount], &ss, ss.ss_len); 599 ac->ac_nscount += 1; 600 601 } else if (!strcmp(tok[0], "domain")) { 602 if (n != 2) 603 return; 604 if (ac->ac_domain) 605 return; 606 ac->ac_domain = strdup(tok[1]); 607 608 } else if (!strcmp(tok[0], "lookup")) { 609 /* ensure that each lookup is only given once */ 610 for (i = 1; i < n; i++) 611 for (j = i + 1; j < n; j++) 612 if (!strcmp(tok[i], tok[j])) 613 return; 614 ac->ac_dbcount = 0; 615 for (i = 1; i < n && ac->ac_dbcount < ASR_MAXDB; i++) { 616 if (!strcmp(tok[i], "yp")) 617 ac->ac_db[ac->ac_dbcount++] = ASR_DB_YP; 618 else if (!strcmp(tok[i], "bind")) 619 ac->ac_db[ac->ac_dbcount++] = ASR_DB_DNS; 620 else if (!strcmp(tok[i], "file")) 621 ac->ac_db[ac->ac_dbcount++] = ASR_DB_FILE; 622 } 623 } else if (!strcmp(tok[0], "search")) { 624 /* resolv.conf says the last line wins */ 625 for (i = 0; i < ASR_MAXDOM; i++) 626 free(ac->ac_dom[i]); 627 ac->ac_domcount = 0; 628 for (i = 1; i < n; i++) 629 asr_ctx_add_searchdomain(ac, tok[i]); 630 631 } else if (!strcmp(tok[0], "family")) { 632 if (n == 1 || n > 3) 633 return; 634 for (i = 1; i < n; i++) 635 if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6")) 636 return; 637 for (i = 1; i < n; i++) 638 ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \ 639 AF_INET6 : AF_INET; 640 ac->ac_family[i - 1] = -1; 641 642 } else if (!strcmp(tok[0], "options")) { 643 for (i = 1; i < n; i++) { 644 if (!strcmp(tok[i], "tcp")) 645 ac->ac_options |= RES_USEVC; 646 else if ((!strncmp(tok[i], "ndots:", 6))) { 647 e = NULL; 648 d = strtonum(tok[i] + 6, 1, 16, &e); 649 if (e == NULL) 650 ac->ac_ndots = d; 651 } 652 } 653 } 654 } 655 656 /* 657 * Setup an async context with the config specified in the string "str". 658 */ 659 static int 660 asr_ctx_from_string(struct asr_ctx *ac, const char *str) 661 { 662 char buf[512], *ch; 663 664 asr_ctx_parse(ac, str); 665 666 if (ac->ac_dbcount == 0) { 667 /* No lookup directive */ 668 asr_ctx_parse(ac, DEFAULT_LOOKUP); 669 } 670 671 if (ac->ac_nscount == 0) 672 asr_ctx_parse(ac, "nameserver 127.0.0.1"); 673 674 if (ac->ac_domain == NULL) 675 if (gethostname(buf, sizeof buf) == 0) { 676 ch = strchr(buf, '.'); 677 if (ch) 678 ac->ac_domain = strdup(ch + 1); 679 else /* Assume root. see resolv.conf(5) */ 680 ac->ac_domain = strdup(""); 681 } 682 683 /* If no search domain was specified, use the local subdomains */ 684 if (ac->ac_domcount == 0) 685 for (ch = ac->ac_domain; ch; ) { 686 asr_ctx_add_searchdomain(ac, ch); 687 ch = strchr(ch, '.'); 688 if (ch && asr_ndots(++ch) == 0) 689 break; 690 } 691 692 return (0); 693 } 694 695 /* 696 * Setup the "ac" async context from the file at location "path". 697 */ 698 static int 699 asr_ctx_from_file(struct asr_ctx *ac, const char *path) 700 { 701 FILE *cf; 702 char buf[4096]; 703 ssize_t r; 704 705 cf = fopen(path, "re"); 706 if (cf == NULL) 707 return (-1); 708 709 r = fread(buf, 1, sizeof buf - 1, cf); 710 if (feof(cf) == 0) { 711 DPRINT("asr: config file too long: \"%s\"\n", path); 712 r = -1; 713 } 714 fclose(cf); 715 if (r == -1) 716 return (-1); 717 buf[r] = '\0'; 718 719 return asr_ctx_from_string(ac, buf); 720 } 721 722 /* 723 * Parse lines in the configuration string. For each one, split it into 724 * tokens and pass them to "pass0" for processing. 725 */ 726 static int 727 asr_ctx_parse(struct asr_ctx *ac, const char *str) 728 { 729 size_t len; 730 const char *line; 731 char buf[1024]; 732 char *tok[10]; 733 int ntok; 734 735 line = str; 736 while (*line) { 737 len = strcspn(line, "\n\0"); 738 if (len < sizeof buf) { 739 memmove(buf, line, len); 740 buf[len] = '\0'; 741 } else 742 buf[0] = '\0'; 743 line += len; 744 if (*line == '\n') 745 line++; 746 buf[strcspn(buf, ";#")] = '\0'; 747 if ((ntok = strsplit(buf, tok, 10)) == 0) 748 continue; 749 750 pass0(tok, ntok, ac); 751 } 752 753 return (0); 754 } 755 756 #if ASR_OPT_ENVOPTS 757 /* 758 * Check for environment variables altering the configuration as described 759 * in resolv.conf(5). Altough not documented there, this feature is disabled 760 * for setuid/setgid programs. 761 */ 762 static void 763 asr_ctx_envopts(struct asr_ctx *ac) 764 { 765 char buf[4096], *e; 766 size_t s; 767 768 if (issetugid()) { 769 ac->ac_options |= RES_NOALIASES; 770 return; 771 } 772 773 if ((e = getenv("RES_OPTIONS")) != NULL) { 774 strlcpy(buf, "options ", sizeof buf); 775 strlcat(buf, e, sizeof buf); 776 s = strlcat(buf, "\n", sizeof buf); 777 s = strlcat(buf, "\n", sizeof buf); 778 if (s < sizeof buf) 779 asr_ctx_parse(ac, buf); 780 } 781 782 if ((e = getenv("LOCALDOMAIN")) != NULL) { 783 strlcpy(buf, "search ", sizeof buf); 784 strlcat(buf, e, sizeof buf); 785 s = strlcat(buf, "\n", sizeof buf); 786 if (s < sizeof buf) 787 asr_ctx_parse(ac, buf); 788 } 789 } 790 #endif 791 792 /* 793 * Parse a resolv.conf(5) nameserver string into a sockaddr. 794 */ 795 static int 796 asr_parse_nameserver(struct sockaddr *sa, const char *s) 797 { 798 const char *estr; 799 char buf[256]; 800 char *port = NULL; 801 in_port_t portno = 53; 802 803 if (*s == '[') { 804 strlcpy(buf, s + 1, sizeof buf); 805 s = buf; 806 port = strchr(buf, ']'); 807 if (port == NULL) 808 return (-1); 809 *port++ = '\0'; 810 if (*port != ':') 811 return (-1); 812 port++; 813 } 814 815 if (port) { 816 portno = strtonum(port, 1, USHRT_MAX, &estr); 817 if (estr) 818 return (-1); 819 } 820 821 if (_asr_sockaddr_from_str(sa, PF_UNSPEC, s) == -1) 822 return (-1); 823 824 if (sa->sa_family == PF_INET) 825 ((struct sockaddr_in *)sa)->sin_port = htons(portno); 826 else if (sa->sa_family == PF_INET6) 827 ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); 828 829 return (0); 830 } 831 832 /* 833 * Turn a (uncompressed) DNS domain name into a regular nul-terminated string 834 * where labels are separated by dots. The result is put into the "buf" buffer, 835 * truncated if it exceeds "max" chars. The function returns "buf". 836 */ 837 char * 838 _asr_strdname(const char *_dname, char *buf, size_t max) 839 { 840 const unsigned char *dname = _dname; 841 char *res; 842 size_t left, n, count; 843 844 if (_dname[0] == 0) { 845 strlcpy(buf, ".", max); 846 return buf; 847 } 848 849 res = buf; 850 left = max - 1; 851 for (n = 0; dname[0] && left; n += dname[0]) { 852 count = (dname[0] < (left - 1)) ? dname[0] : (left - 1); 853 memmove(buf, dname + 1, count); 854 dname += dname[0] + 1; 855 left -= count; 856 buf += count; 857 if (left) { 858 left -= 1; 859 *buf++ = '.'; 860 } 861 } 862 buf[0] = 0; 863 864 return (res); 865 } 866 867 /* 868 * Read and split the next line from the given namedb file. 869 * Return -1 on error, or put the result in the "tokens" array of 870 * size "ntoken" and returns the number of token on the line. 871 */ 872 int 873 _asr_parse_namedb_line(FILE *file, char **tokens, int ntoken, char *lbuf, size_t sz) 874 { 875 size_t len; 876 char *buf; 877 int ntok; 878 879 again: 880 if ((buf = fgetln(file, &len)) == NULL) 881 return (-1); 882 883 if (len >= sz) 884 goto again; 885 886 if (buf[len - 1] == '\n') 887 len--; 888 else { 889 memcpy(lbuf, buf, len); 890 buf = lbuf; 891 } 892 893 buf[len] = '\0'; 894 buf[strcspn(buf, "#")] = '\0'; 895 if ((ntok = strsplit(buf, tokens, ntoken)) == 0) 896 goto again; 897 898 return (ntok); 899 } 900 901 /* 902 * Update the async context so that it uses the next configured DB. 903 * Return 0 on success, or -1 if no more DBs is available. 904 */ 905 int 906 _asr_iter_db(struct asr_query *as) 907 { 908 if (as->as_db_idx >= as->as_ctx->ac_dbcount) { 909 DPRINT("asr_iter_db: done\n"); 910 return (-1); 911 } 912 913 as->as_db_idx += 1; 914 DPRINT("asr_iter_db: %i\n", as->as_db_idx); 915 916 return (0); 917 } 918 919 /* 920 * Check if the hostname "name" is a user-defined alias as per hostname(7). 921 * If so, copies the result in the buffer "abuf" of size "abufsz" and 922 * return "abuf". Otherwise return NULL. 923 */ 924 char * 925 _asr_hostalias(struct asr_ctx *ac, const char *name, char *abuf, size_t abufsz) 926 { 927 #if ASR_OPT_HOSTALIASES 928 FILE *fp; 929 size_t len; 930 char *file, *buf, *tokens[2]; 931 int ntok; 932 933 if (ac->ac_options & RES_NOALIASES || 934 asr_ndots(name) != 0 || 935 issetugid() || 936 (file = getenv("HOSTALIASES")) == NULL || 937 (fp = fopen(file, "re")) == NULL) 938 return (NULL); 939 940 DPRINT("asr: looking up aliases in \"%s\"\n", file); 941 942 while ((buf = fgetln(fp, &len)) != NULL) { 943 if (buf[len - 1] == '\n') 944 len--; 945 buf[len] = '\0'; 946 if ((ntok = strsplit(buf, tokens, 2)) != 2) 947 continue; 948 if (!strcasecmp(tokens[0], name)) { 949 if (strlcpy(abuf, tokens[1], abufsz) > abufsz) 950 continue; 951 DPRINT("asr: found alias \"%s\"\n", abuf); 952 fclose(fp); 953 return (abuf); 954 } 955 } 956 957 fclose(fp); 958 #endif 959 return (NULL); 960 } 961