1 /* $OpenBSD: asr.c,v 1.4 2012/08/18 16:48:17 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 #include <sys/types.h> 18 #include <sys/stat.h> 19 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 #include "thread_private.h" 38 39 #define DEFAULT_CONFFILE "/etc/resolv.conf" 40 #define DEFAULT_HOSTFILE "/etc/hosts" 41 #define DEFAULT_CONF "lookup bind file\nnameserver 127.0.0.1\n" 42 #define DEFAULT_LOOKUP "lookup bind file" 43 44 #define RELOAD_DELAY 15 /* seconds */ 45 46 static void asr_check_reload(struct asr *); 47 static struct asr_ctx *asr_ctx_create(void); 48 static void asr_ctx_ref(struct asr_ctx *); 49 static void asr_ctx_free(struct asr_ctx *); 50 static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *); 51 static int asr_ctx_from_file(struct asr_ctx *, const char *); 52 static int asr_ctx_from_string(struct asr_ctx *, const char *); 53 static int asr_ctx_parse(const char*, int(*)(char**, int, struct asr_ctx*), 54 struct asr_ctx *); 55 static int asr_parse_nameserver(struct sockaddr *, const char *); 56 static char *asr_hostalias(const char *, char *, size_t); 57 static int asr_ndots(const char *); 58 static void asr_ctx_envopts(struct asr_ctx *); 59 static int pass0(char **, int, struct asr_ctx *); 60 61 static void *__THREAD_NAME(_asr); 62 static struct asr *_asr = NULL; 63 64 /* Allocate and configure an async "resolver". */ 65 struct asr * 66 async_resolver(const char *conf) 67 { 68 static int init = 0; 69 struct asr *asr; 70 71 #ifdef DEBUG 72 if (init == 0) { 73 if (getenv("ASR_DEBUG")) 74 asr_debug = 1; 75 init = 1; 76 } 77 #endif 78 if ((asr = calloc(1, sizeof(*asr))) == NULL) 79 goto fail; 80 81 /* If not setuid/setgid, allow to use an alternate config. */ 82 if (conf == NULL && !issetugid()) 83 conf = getenv("ASR_CONFIG"); 84 85 if (conf == NULL) 86 conf = DEFAULT_CONFFILE; 87 88 if (conf[0] == '!') { 89 /* Use the rest of the string as config file */ 90 if ((asr->a_ctx = asr_ctx_create()) == NULL) 91 goto fail; 92 if (asr_ctx_from_string(asr->a_ctx, conf + 1) == -1) 93 goto fail; 94 } else { 95 /* Use the given config file */ 96 asr->a_path = strdup(conf); 97 asr_check_reload(asr); 98 if (asr->a_ctx == NULL) { 99 if ((asr->a_ctx = asr_ctx_create()) == NULL) 100 goto fail; 101 if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1) 102 goto fail; 103 asr_ctx_envopts(asr->a_ctx); 104 } 105 } 106 107 #ifdef DEBUG 108 asr_dump(asr); 109 #endif 110 return (asr); 111 112 fail: 113 if (asr) { 114 if (asr->a_ctx) 115 asr_ctx_free(asr->a_ctx); 116 free(asr); 117 } 118 119 return (NULL); 120 } 121 122 /* 123 * Free the "asr" async resolver (or the thread-local resolver if NULL). 124 * Drop the reference to the current context. 125 */ 126 void 127 async_resolver_done(struct asr *asr) 128 { 129 struct asr **priv; 130 131 if (asr == NULL) { 132 priv = _THREAD_PRIVATE(_asr, asr, &_asr); 133 if (*priv == NULL) 134 return; 135 asr = *priv; 136 *priv = NULL; 137 } 138 139 asr_ctx_unref(asr->a_ctx); 140 if (asr->a_path) 141 free(asr->a_path); 142 free(asr); 143 } 144 145 /* 146 * Cancel an async query. 147 */ 148 void 149 async_abort(struct async *as) 150 { 151 async_free(as); 152 } 153 154 /* 155 * Resume the "as" async query resolution. Return one of ASYNC_COND, 156 * ASYNC_YIELD or ASYNC_DONE and put query-specific return values in 157 * the user-allocated memory at "ar". 158 */ 159 int 160 async_run(struct async *as, struct async_res *ar) 161 { 162 int r, saved_errno = errno; 163 164 #ifdef DEBUG 165 asr_printf("asr: async_run(%p, %p) %s ctx=[%p]\n", 166 as, ar, asr_querystr(as->as_type), as->as_ctx); 167 #endif 168 r = as->as_run(as, ar); 169 170 #ifdef DEBUG 171 if (asr_debug) { 172 asr_printf("asr: async_run(%p, %p) -> %s", as, ar, 173 asr_transitionstr(r)); 174 if (r == ASYNC_COND) 175 asr_printf(" fd=%i timeout=%i\n", 176 ar->ar_fd, ar->ar_timeout); 177 else 178 asr_printf("\n"); 179 fflush(stderr); 180 } 181 #endif 182 if (r == ASYNC_DONE) 183 async_free(as); 184 185 errno = saved_errno; 186 187 return (r); 188 } 189 190 /* 191 * Same as above, but run in a loop that handles the fd conditions result. 192 */ 193 int 194 async_run_sync(struct async *as, struct async_res *ar) 195 { 196 struct pollfd fds[1]; 197 int r, saved_errno = errno; 198 199 while((r = async_run(as, ar)) == ASYNC_COND) { 200 fds[0].fd = ar->ar_fd; 201 fds[0].events = (ar->ar_cond == ASYNC_READ) ? POLLIN : POLLOUT; 202 again: 203 r = poll(fds, 1, ar->ar_timeout); 204 if (r == -1 && errno == EINTR) 205 goto again; 206 /* 207 * Otherwise, just ignore the error and let async_run() 208 * catch the failure. 209 */ 210 } 211 212 errno = saved_errno; 213 214 return (r); 215 } 216 217 /* 218 * Create a new async request of the given "type" on the async context "ac". 219 * Take a reference on it so it does not gets deleted while the async query 220 * is running. 221 */ 222 struct async * 223 async_new(struct asr_ctx *ac, int type) 224 { 225 struct async *as; 226 #ifdef DEBUG 227 asr_printf("asr: async_new(ctx=%p) type=%i refcount=%i\n", 228 ac, type, ac->ac_refcount); 229 #endif 230 if ((as = calloc(1, sizeof(*as))) == NULL) 231 return (NULL); 232 233 ac->ac_refcount += 1; 234 as->as_ctx = ac; 235 as->as_fd = -1; 236 as->as_type = type; 237 as->as_state = ASR_STATE_INIT; 238 239 return (as); 240 } 241 242 /* 243 * Free an async query and unref the associated context. 244 */ 245 void 246 async_free(struct async *as) 247 { 248 #ifdef DEBUG 249 asr_printf("asr: async_free(%p)\n", as); 250 #endif 251 switch(as->as_type) { 252 case ASR_SEND: 253 if (as->as_fd != -1) 254 close(as->as_fd); 255 if (as->as.dns.obuf && !(as->as.dns.flags & ASYNC_EXTOBUF)) 256 free (as->as.dns.obuf); 257 if (as->as.dns.ibuf && !(as->as.dns.flags & ASYNC_EXTIBUF)) 258 free (as->as.dns.ibuf); 259 if (as->as.dns.dname) 260 free(as->as.dns.dname); 261 break; 262 263 case ASR_SEARCH: 264 if (as->as.search.subq) 265 async_free(as->as.search.subq); 266 if (as->as.search.name) 267 free(as->as.search.name); 268 break; 269 270 case ASR_GETRRSETBYNAME: 271 if (as->as.rrset.subq) 272 async_free(as->as.rrset.subq); 273 if (as->as.rrset.name) 274 free(as->as.rrset.name); 275 break; 276 277 case ASR_GETHOSTBYNAME: 278 case ASR_GETHOSTBYADDR: 279 if (as->as.hostnamadr.subq) 280 async_free(as->as.hostnamadr.subq); 281 if (as->as.hostnamadr.name) 282 free(as->as.hostnamadr.name); 283 if (as->as.hostnamadr.dname) 284 free(as->as.hostnamadr.dname); 285 break; 286 287 case ASR_GETNETBYNAME: 288 case ASR_GETNETBYADDR: 289 if (as->as.netnamadr.subq) 290 async_free(as->as.netnamadr.subq); 291 if (as->as.netnamadr.name) 292 free(as->as.netnamadr.name); 293 break; 294 295 case ASR_GETADDRINFO: 296 if (as->as.ai.subq) 297 async_free(as->as.ai.subq); 298 if (as->as.ai.aifirst) 299 freeaddrinfo(as->as.ai.aifirst); 300 if (as->as.ai.hostname) 301 free(as->as.ai.hostname); 302 if (as->as.ai.servname) 303 free(as->as.ai.servname); 304 break; 305 306 case ASR_GETNAMEINFO: 307 if (as->as.ni.subq) 308 async_free(as->as.ni.subq); 309 break; 310 311 case ASR_HOSTADDR: 312 if (as->as.host.name) 313 free(as->as.host.name); 314 if (as->as.host.subq) 315 async_free(as->as.host.subq); 316 if (as->as.host.pkt) 317 free(as->as.host.pkt); 318 if (as->as.host.file) 319 fclose(as->as.host.file); 320 break; 321 } 322 323 asr_ctx_unref(as->as_ctx); 324 free(as); 325 } 326 327 /* 328 * Get a context from the given resolver. This takes a new reference to 329 * the returned context, which *must* be explicitely dropped when done 330 * using this context. 331 */ 332 struct asr_ctx * 333 asr_use_resolver(struct asr *asr) 334 { 335 struct asr **priv; 336 337 if (asr == NULL) { 338 /* Use the thread-local resolver. */ 339 #ifdef DEBUG 340 asr_printf("using thread-local resolver\n"); 341 #endif 342 priv = _THREAD_PRIVATE(_asr, asr, &_asr); 343 if (*priv == NULL) { 344 #ifdef DEBUG 345 asr_printf("setting up thread-local resolver\n"); 346 #endif 347 *priv = async_resolver(NULL); 348 } 349 asr = *priv; 350 } 351 352 asr_check_reload(asr); 353 asr_ctx_ref(asr->a_ctx); 354 return (asr->a_ctx); 355 } 356 357 static void 358 asr_ctx_ref(struct asr_ctx *ac) 359 { 360 #ifdef DEBUG 361 asr_printf("asr: asr_ctx_ref(ctx=%p) refcount=%i\n", 362 ac, ac->ac_refcount); 363 #endif 364 ac->ac_refcount += 1; 365 } 366 367 /* 368 * Drop a reference to an async context, freeing it if the reference 369 * count drops to 0. 370 */ 371 void 372 asr_ctx_unref(struct asr_ctx *ac) 373 { 374 #ifdef DEBUG 375 asr_printf("asr: asr_ctx_unref(ctx=%p) refcount=%i\n", 376 ac, ac->ac_refcount); 377 #endif 378 if (--ac->ac_refcount) 379 return; 380 381 asr_ctx_free(ac); 382 } 383 384 static void 385 asr_ctx_free(struct asr_ctx *ac) 386 { 387 int i; 388 389 if (ac->ac_domain) 390 free(ac->ac_domain); 391 for(i = 0; i < ac->ac_nscount; i++) 392 free(ac->ac_ns[i]); 393 for(i = 0; i < ac->ac_domcount; i++) 394 free(ac->ac_dom[i]); 395 396 free(ac); 397 } 398 399 /* 400 * Reload the configuration file if it has changed on disk. 401 */ 402 static void 403 asr_check_reload(struct asr *asr) 404 { 405 struct stat st; 406 struct asr_ctx *ac; 407 struct timespec tp; 408 409 if (asr->a_path == NULL) 410 return; 411 412 if (clock_gettime(CLOCK_MONOTONIC, &tp) == -1) 413 return; 414 415 if ((tp.tv_sec - asr->a_rtime) < RELOAD_DELAY) 416 return; 417 asr->a_rtime = tp.tv_sec; 418 419 #ifdef DEBUG 420 asr_printf("asr: checking for update of \"%s\"\n", asr->a_path); 421 #endif 422 423 if (stat(asr->a_path, &st) == -1 || 424 asr->a_mtime == st.st_mtime || 425 (ac = asr_ctx_create()) == NULL) 426 return; 427 asr->a_mtime = st.st_mtime; 428 429 #ifdef DEBUG 430 asr_printf("asr: reloading config file\n"); 431 #endif 432 433 if (asr_ctx_from_file(ac, asr->a_path) == -1) { 434 asr_ctx_free(ac); 435 return; 436 } 437 438 asr_ctx_envopts(ac); 439 if (asr->a_ctx) 440 asr_ctx_unref(asr->a_ctx); 441 asr->a_ctx = ac; 442 } 443 444 /* 445 * Construct a fully-qualified domain name for the given name and domain. 446 * If "name" ends with a '.' it is considered as a FQDN by itself. 447 * Otherwise, the domain, which must be a FQDN, is appended to "name" (it 448 * may have a leading dot which would be ignored). If the domain is null, 449 * then "." is used. Return the length of the constructed FQDN or (0) on 450 * error. 451 */ 452 size_t 453 asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen) 454 { 455 size_t len; 456 457 if (domain == NULL) 458 domain = "."; 459 else if ((len = strlen(domain)) == 0) 460 return (0); 461 else if (domain[len -1] != '.') 462 return (0); 463 464 len = strlen(name); 465 if (len == 0) { 466 strlcpy(buf, domain, buflen); 467 } else if (name[len - 1] != '.') { 468 if (domain[0] == '.') 469 domain += 1; 470 strlcpy(buf, name, buflen); 471 strlcat(buf, ".", buflen); 472 strlcat(buf, domain, buflen); 473 } else { 474 strlcpy(buf, name, buflen); 475 } 476 477 return (strlen(buf)); 478 } 479 480 /* 481 * Concatenate a name and a domain name. The result has no trailing dot. 482 */ 483 size_t 484 asr_domcat(const char *name, const char *domain, char *buf, size_t buflen) 485 { 486 size_t r; 487 488 r = asr_make_fqdn(name, domain, buf, buflen); 489 if (r == 0) 490 return (0); 491 buf[r - 1] = '\0'; 492 493 return (r - 1); 494 } 495 496 /* 497 * Count the dots in a string. 498 */ 499 static int 500 asr_ndots(const char *s) 501 { 502 int n; 503 504 for(n = 0; *s; s++) 505 if (*s == '.') 506 n += 1; 507 508 return (n); 509 } 510 511 /* 512 * Allocate a new empty context. 513 */ 514 static struct asr_ctx * 515 asr_ctx_create(void) 516 { 517 struct asr_ctx *ac; 518 519 if ((ac = calloc(1, sizeof(*ac))) == NULL) 520 return (NULL); 521 522 ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; 523 ac->ac_refcount = 1; 524 ac->ac_ndots = 1; 525 ac->ac_family[0] = AF_INET; 526 ac->ac_family[1] = AF_INET6; 527 ac->ac_family[2] = -1; 528 529 ac->ac_hostfile = DEFAULT_HOSTFILE; 530 531 ac->ac_nscount = 0; 532 ac->ac_nstimeout = 1000; 533 ac->ac_nsretries = 3; 534 535 return (ac); 536 } 537 538 /* 539 * Add a search domain to the async context. 540 */ 541 static int 542 asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain) 543 { 544 char buf[MAXDNAME]; 545 546 if (ac->ac_domcount == ASR_MAXDOM) 547 return (-1); 548 549 if (asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0) 550 return (-1); 551 552 if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL) 553 return (0); 554 555 ac->ac_domcount += 1; 556 557 return (1); 558 } 559 560 /* 561 * Pass on a split config line. 562 */ 563 static int 564 pass0(char **tok, int n, struct asr_ctx *ac) 565 { 566 int i, j, d; 567 const char *e; 568 struct sockaddr_storage ss; 569 570 if (!strcmp(tok[0], "nameserver")) { 571 if (ac->ac_nscount == ASR_MAXNS) 572 return (0); 573 if (n != 2) 574 return (0); 575 if (asr_parse_nameserver((struct sockaddr*)&ss, tok[1])) 576 return (0); 577 if ((ac->ac_ns[ac->ac_nscount] = calloc(1, ss.ss_len)) == NULL) 578 return (0); 579 memmove(ac->ac_ns[ac->ac_nscount], &ss, ss.ss_len); 580 ac->ac_nscount += 1; 581 582 } else if (!strcmp(tok[0], "domain")) { 583 if (n != 2) 584 return (0); 585 if (ac->ac_domain) 586 return (0); 587 ac->ac_domain = strdup(tok[1]); 588 589 } else if (!strcmp(tok[0], "lookup")) { 590 /* ignore the line if we already set lookup */ 591 if (ac->ac_dbcount != 0) 592 return (0); 593 if (n - 1 > ASR_MAXDB) 594 return (0); 595 /* ensure that each lookup is only given once */ 596 for(i = 1; i < n; i++) 597 for(j = i + 1; j < n; j++) 598 if (!strcmp(tok[i], tok[j])) 599 return (0); 600 for(i = 1; i < n; i++, ac->ac_dbcount++) { 601 if (!strcmp(tok[i], "yp")) { 602 ac->ac_db[i-1] = ASR_DB_YP; 603 } else if (!strcmp(tok[i], "bind")) { 604 ac->ac_db[i-1] = ASR_DB_DNS; 605 } else if (!strcmp(tok[i], "file")) { 606 ac->ac_db[i-1] = ASR_DB_FILE; 607 } else { 608 /* ignore the line */ 609 ac->ac_dbcount = 0; 610 return (0); 611 } 612 } 613 } else if (!strcmp(tok[0], "search")) { 614 /* resolv.conf says the last line wins */ 615 for(i = 0; i < ac->ac_domcount; i++) 616 free(ac->ac_dom[i]); 617 ac->ac_domcount = 0; 618 for(i = 1; i < n; i++) 619 asr_ctx_add_searchdomain(ac, tok[i]); 620 621 } else if (!strcmp(tok[0], "family")) { 622 if (n == 1 || n > 3) 623 return (0); 624 for (i = 1; i < n; i++) 625 if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6")) 626 return (0); 627 for (i = 1; i < n; i++) 628 ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \ 629 AF_INET6 : AF_INET; 630 ac->ac_family[i - 1] = -1; 631 632 } else if (!strcmp(tok[0], "options")) { 633 for(i = 1; i < n; i++) { 634 if (!strcmp(tok[i], "tcp")) 635 ac->ac_options |= RES_USEVC; 636 else if ((!strncmp(tok[i], "ndots:", 6))) { 637 e = NULL; 638 d = strtonum(tok[i] + 6, 1, 16, &e); 639 if (e == NULL) 640 ac->ac_ndots = d; 641 } 642 } 643 } 644 645 return (0); 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(str, pass0, ac); 657 658 if (ac->ac_dbcount == 0) { 659 /* No lookup directive */ 660 asr_ctx_parse(DEFAULT_LOOKUP, pass0, ac); 661 } 662 663 if (ac->ac_nscount == 0) 664 asr_ctx_parse("nameserver 127.0.0.1", pass0, ac); 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 #ifdef DEBUG 704 asr_printf("asr: config file too long: \"%s\"\n", path); 705 #endif 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 a configuration string. Lines are read one by one, comments are 718 * stripped and the remaining line is split into tokens which are passed 719 * to the "cb" callback function. Parsing stops if the callback returns 720 * non-zero. 721 */ 722 static int 723 asr_ctx_parse(const char *str, int (*cb)(char**, int, struct asr_ctx*), 724 struct asr_ctx *ac) 725 { 726 size_t len; 727 const char *line; 728 char buf[1024]; 729 char *tok[10], **tp, *cp; 730 int ntok; 731 732 line = str; 733 while (*line) { 734 len = strcspn(line, "\n\0"); 735 if (len < sizeof buf) { 736 memmove(buf, line, len); 737 buf[len] = '\0'; 738 } else 739 buf[0] = '\0'; 740 line += len; 741 if (*line == '\n') 742 line++; 743 buf[strcspn(buf, ";#")] = '\0'; 744 for(cp = buf, tp = tok, ntok = 0; 745 tp < &tok[10] && (*tp = strsep(&cp, " \t")) != NULL; ) 746 if (**tp != '\0') { 747 tp++; 748 ntok++; 749 } 750 *tp = NULL; 751 752 if (tok[0] == NULL) 753 continue; 754 755 if (cb(tok, ntok, ac)) 756 break; 757 } 758 759 return (0); 760 } 761 762 /* 763 * Check for environment variables altering the configuration as described 764 * in resolv.conf(5). Altough not documented there, this feature is disabled 765 * for setuid/setgid programs. 766 */ 767 static void 768 asr_ctx_envopts(struct asr_ctx *ac) 769 { 770 char buf[4096], *e; 771 size_t s; 772 773 if (issetugid()) { 774 ac->ac_options |= RES_NOALIASES; 775 return; 776 } 777 778 if ((e = getenv("RES_OPTIONS")) != NULL) { 779 strlcpy(buf, "options ", sizeof buf); 780 strlcat(buf, e, sizeof buf); 781 s = strlcat(buf, "\n", sizeof buf); 782 s = strlcat(buf, "\n", sizeof buf); 783 if (s < sizeof buf) 784 asr_ctx_parse(buf, pass0, ac); 785 } 786 787 if ((e = getenv("LOCALDOMAIN")) != NULL) { 788 strlcpy(buf, "search ", sizeof buf); 789 strlcat(buf, e, sizeof buf); 790 s = strlcat(buf, "\n", sizeof buf); 791 if (s < sizeof buf) 792 asr_ctx_parse(buf, pass0, ac); 793 } 794 } 795 796 /* 797 * Parse a resolv.conf(5) nameserver string into a sockaddr. 798 */ 799 static int 800 asr_parse_nameserver(struct sockaddr *sa, const char *s) 801 { 802 const char *estr; 803 char buf[256]; 804 char *port = NULL; 805 in_port_t portno = 53; 806 807 if (*s == '[') { 808 strlcpy(buf, s + 1, sizeof buf); 809 s = buf; 810 port = strchr(buf, ']'); 811 if (port == NULL) 812 return (-1); 813 *port++ = '\0'; 814 if (*port != ':') 815 return (-1); 816 port++; 817 } 818 819 if (port) { 820 portno = strtonum(port, 1, USHRT_MAX, &estr); 821 if (estr) 822 return (-1); 823 } 824 825 if (sockaddr_from_str(sa, PF_UNSPEC, s) == -1) 826 return (-1); 827 828 if (sa->sa_family == PF_INET) 829 ((struct sockaddr_in *)sa)->sin_port = htons(portno); 830 else if (sa->sa_family == PF_INET6) 831 ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); 832 833 return (0); 834 } 835 836 /* 837 * Turn a (uncompressed) DNS domain name into a regular nul-terminated string 838 * where labels are separated by dots. The result is put into the "buf" buffer, 839 * truncated if it exceeds "max" chars. The function returns "buf". 840 */ 841 char* 842 asr_strdname(const char *_dname, char *buf, size_t max) 843 { 844 const unsigned char *dname = _dname; 845 char *res; 846 size_t left, n, count; 847 848 if (_dname[0] == 0) { 849 strlcpy(buf, ".", max); 850 return buf; 851 } 852 853 res = buf; 854 left = max - 1; 855 for (n = 0; dname[0] && left; n += dname[0]) { 856 count = (dname[0] < (left - 1)) ? dname[0] : (left - 1); 857 memmove(buf, dname + 1, count); 858 dname += dname[0] + 1; 859 left -= count; 860 buf += count; 861 if (left) { 862 left -= 1; 863 *buf++ = '.'; 864 } 865 } 866 buf[0] = 0; 867 868 return (res); 869 } 870 871 /* 872 * Read and split the next line from the given namedb file. 873 * Return -1 on error, or put the result in the "tokens" array of 874 * size "ntoken" and returns the number of token on the line. 875 */ 876 int 877 asr_parse_namedb_line(FILE *file, char **tokens, int ntoken) 878 { 879 size_t len; 880 char *buf, *cp, **tp; 881 int ntok; 882 883 again: 884 if ((buf = fgetln(file, &len)) == NULL) 885 return (-1); 886 887 if (buf[len - 1] == '\n') 888 len--; 889 890 buf[len] = '\0'; 891 buf[strcspn(buf, "#")] = '\0'; 892 for(cp = buf, tp = tokens, ntok = 0; 893 ntok < ntoken && (*tp = strsep(&cp, " \t")) != NULL;) 894 if (**tp != '\0') { 895 tp++; 896 ntok++; 897 } 898 *tp = NULL; 899 if (tokens[0] == NULL) 900 goto again; 901 902 return (ntok); 903 } 904 905 /* 906 * Update the async context so that it uses the next configured DB. 907 * Return 0 on success, or -1 if no more DBs is available. 908 */ 909 int 910 asr_iter_db(struct async *as) 911 { 912 if (as->as_db_idx >= as->as_ctx->ac_dbcount) { 913 #ifdef DEBUG 914 asr_printf("asr_iter_db: done\n"); 915 #endif 916 return (-1); 917 } 918 919 as->as_db_idx += 1; 920 as->as_ns_idx = 0; 921 #ifdef DEBUG 922 asr_printf("asr_iter_db: %i\n", as->as_db_idx); 923 #endif 924 return (0); 925 } 926 927 /* 928 * Set the async context nameserver index to the next nameserver of the 929 * currently used DB (assuming it is DNS), cycling over the list until the 930 * maximum retry counter is reached. Return 0 on success, or -1 if all 931 * nameservers were used. 932 */ 933 int 934 asr_iter_ns(struct async *as) 935 { 936 for (;;) { 937 if (as->as_ns_cycles >= as->as_ctx->ac_nsretries) 938 return (-1); 939 940 as->as_ns_idx += 1; 941 if (as->as_ns_idx <= as->as_ctx->ac_nscount) 942 break; 943 as->as_ns_idx = 0; 944 as->as_ns_cycles++; 945 #ifdef DEBUG 946 asr_printf("asr: asr_iter_ns(): cycle %i\n", as->as_ns_cycles); 947 #endif 948 } 949 950 return (0); 951 } 952 953 enum { 954 DOM_INIT, 955 DOM_DOMAIN, 956 DOM_DONE 957 }; 958 959 /* 960 * Implement the search domain strategy. 961 * 962 * This function works as a generator that constructs complete domains in 963 * buffer "buf" of size "len" for the given host name "name", according to the 964 * search rules defined by the resolving context. It is supposed to be called 965 * multiple times (with the same name) to generate the next possible domain 966 * name, if any. 967 * 968 * It returns 0 if it could generate a new domain name, or -1 when all 969 * possibilites have been exhausted. 970 */ 971 int 972 asr_iter_domain(struct async *as, const char *name, char * buf, size_t len) 973 { 974 char *alias; 975 976 switch(as->as_dom_step) { 977 978 case DOM_INIT: 979 /* First call */ 980 981 /* 982 * If "name" is an FQDN, that's the only result and we 983 * don't try anything else. 984 */ 985 if (strlen(name) && name[strlen(name) - 1] == '.') { 986 #ifdef DEBUG 987 asr_printf("asr: asr_iter_domain(\"%s\") fqdn\n", name); 988 #endif 989 as->as_dom_flags |= ASYNC_DOM_FQDN; 990 as->as_dom_step = DOM_DONE; 991 return (asr_domcat(name, NULL, buf, len)); 992 } 993 994 /* 995 * If "name" has no dots, it might be an alias. If so, 996 * That's also the only result. 997 */ 998 if ((as->as_ctx->ac_options & RES_NOALIASES) == 0 && 999 asr_ndots(name) == 0 && 1000 (alias = asr_hostalias(name, buf, len)) != NULL) { 1001 #ifdef DEBUG 1002 asr_printf("asr: asr_iter_domain(\"%s\") is alias " 1003 "\"%s\"\n", name, alias); 1004 #endif 1005 as->as_dom_flags |= ASYNC_DOM_HOSTALIAS; 1006 as->as_dom_step = DOM_DONE; 1007 return (asr_domcat(alias, NULL, buf, len)); 1008 } 1009 1010 /* 1011 * Otherwise, we iterate through the specified search domains. 1012 */ 1013 as->as_dom_step = DOM_DOMAIN; 1014 as->as_dom_idx = 0; 1015 1016 /* 1017 * If "name" as enough dots, use it as-is first, as indicated 1018 * in resolv.conf(5). 1019 */ 1020 if ((asr_ndots(name)) >= as->as_ctx->ac_ndots) { 1021 #ifdef DEBUG 1022 asr_printf("asr: asr_iter_domain(\"%s\") ndots\n", 1023 name); 1024 #endif 1025 as->as_dom_flags |= ASYNC_DOM_NDOTS; 1026 strlcpy(buf, name, len); 1027 return (0); 1028 } 1029 /* Otherwise, starts using the search domains */ 1030 /* FALLTHROUGH */ 1031 1032 case DOM_DOMAIN: 1033 if (as->as_dom_idx < as->as_ctx->ac_domcount) { 1034 #ifdef DEBUG 1035 asr_printf("asr: asr_iter_domain(\"%s\") " 1036 "domain \"%s\"\n", name, 1037 as->as_ctx->ac_dom[as->as_dom_idx]); 1038 #endif 1039 as->as_dom_flags |= ASYNC_DOM_DOMAIN; 1040 return (asr_domcat(name, 1041 as->as_ctx->ac_dom[as->as_dom_idx++], buf, len)); 1042 } 1043 1044 /* No more domain to try. */ 1045 1046 as->as_dom_step = DOM_DONE; 1047 1048 /* 1049 * If the name was not tried as an absolute name before, 1050 * do it now. 1051 */ 1052 if (!(as->as_dom_flags & ASYNC_DOM_NDOTS)) { 1053 #ifdef DEBUG 1054 asr_printf("asr: asr_iter_domain(\"%s\") as is\n", 1055 name); 1056 #endif 1057 as->as_dom_flags |= ASYNC_DOM_ASIS; 1058 strlcpy(buf, name, len); 1059 return (0); 1060 } 1061 /* Otherwise, we are done. */ 1062 1063 case DOM_DONE: 1064 default: 1065 #ifdef DEBUG 1066 asr_printf("asr: asr_iter_domain(\"%s\") done\n", name); 1067 #endif 1068 return (-1); 1069 } 1070 } 1071 1072 /* 1073 * Check if the hostname "name" is a user-defined alias as per hostname(7). 1074 * If so, copies the result in the buffer "abuf" of size "abufsz" and 1075 * return "abuf". Otherwise return NULL. 1076 */ 1077 static char * 1078 asr_hostalias(const char *name, char *abuf, size_t abufsz) 1079 { 1080 FILE *fp; 1081 size_t len; 1082 char *file, *buf, *cp, **tp, *tokens[2]; 1083 int ntok; 1084 1085 file = getenv("HOSTALIASES"); 1086 if (file == NULL || issetugid() != 0 || (fp = fopen(file, "r")) == NULL) 1087 return (NULL); 1088 1089 #ifdef DEBUG 1090 asr_printf("asr: looking up aliases in \"%s\"\n", file); 1091 #endif 1092 1093 while ((buf = fgetln(fp, &len)) != NULL) { 1094 if (buf[len - 1] == '\n') 1095 len--; 1096 buf[len] = '\0'; 1097 for(cp = buf, tp = tokens, ntok = 0; 1098 ntok < 2 && (*tp = strsep(&cp, " \t")) != NULL; ) 1099 if (**tp != '\0') { 1100 tp++; 1101 ntok++; 1102 } 1103 if (ntok != 2) 1104 continue; 1105 if (!strcasecmp(tokens[0], name)) { 1106 if (strlcpy(abuf, tokens[1], abufsz) > abufsz) 1107 continue; 1108 #ifdef DEBUG 1109 asr_printf("asr: found alias \"%s\"\n", abuf); 1110 #endif 1111 fclose(fp); 1112 return (abuf); 1113 } 1114 } 1115 1116 fclose(fp); 1117 return (NULL); 1118 } 1119