1 /* $NetBSD: nss_mdnsd.c,v 1.3 2009/11/04 23:34:59 tsarna Exp $ */ 2 3 /*- 4 * Copyright (c) 2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Tyler C. Sarna 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Multicast DNS ("Bonjour") hosts name service switch 34 * 35 * Documentation links: 36 * 37 * http://developer.apple.com/bonjour/ 38 * http://www.multicastdns.org/ 39 * http://www.dns-sd.org/ 40 */ 41 42 #include <errno.h> 43 #include <nsswitch.h> 44 #include <stdarg.h> 45 #include <stdlib.h> 46 #include <sys/socket.h> 47 #include <sys/param.h> 48 #include <sys/queue.h> 49 #include <netdb.h> 50 #include <netinet/in.h> 51 #include <arpa/nameser.h> 52 #include <resolv.h> 53 #include <dns_sd.h> 54 #include <poll.h> 55 #include <string.h> 56 #include <stdio.h> 57 #include <stdbool.h> 58 #include <pthread.h> 59 #include <fcntl.h> 60 #include <unistd.h> 61 #include <time.h> 62 63 64 /* 65 * Pool of mdnsd connections 66 */ 67 static SLIST_HEAD(, svc_ref) conn_list = LIST_HEAD_INITIALIZER(&conn_list); 68 static unsigned int conn_count = 0; 69 static pid_t my_pid; 70 struct timespec last_config; 71 72 typedef struct svc_ref { 73 SLIST_ENTRY(svc_ref) entries; 74 DNSServiceRef sdRef; 75 unsigned int uses; 76 } svc_ref; 77 78 /* 79 * There is a large class of programs that do a few lookups at startup 80 * and then never again (ping, telnet, etc). Keeping a persistent connection 81 * for these would be a waste, so there is a kind of slow start mechanism. 82 * The first SLOWSTART_LOOKUPS times, dispose of the connection after use. 83 * After that we assume the program is a serious consumer of host lookup 84 * services and start keeping connections. 85 */ 86 #define SLOWSTART_LOOKUPS 5 87 static unsigned int svc_puts = 0; 88 89 /* 90 * Age out connections. Free connection instead of putting on the list 91 * if used more than REUSE_TIMES and there are others on the list. 92 */ 93 #define REUSE_TIMES 32 94 95 /* protects above data */ 96 static pthread_mutex_t conn_list_lock = PTHREAD_MUTEX_INITIALIZER; 97 98 extern int __isthreaded; /* libc private -- wish there was a better way */ 99 100 #define LOCK(x) do { if (__isthreaded) pthread_mutex_lock(x); } while (0) 101 #define UNLOCK(x) do { if (__isthreaded) pthread_mutex_unlock(x); } while (0) 102 103 104 #ifndef lint 105 #define UNUSED(a) (void)&a 106 #else 107 #define UNUSED(a) a = a 108 #endif 109 110 #define MAXALIASES 35 111 #define MAXADDRS 35 112 113 typedef struct callback_ctx { 114 bool done; 115 } callback_ctx; 116 117 typedef struct hostent_ctx { 118 callback_ctx cb_ctx; /* must come first */ 119 struct hostent host; 120 char *h_addr_ptrs[MAXADDRS + 1]; 121 char *host_aliases[MAXALIASES]; 122 char addrs[MAXADDRS * 16]; 123 char buf[8192], *next; 124 int naliases, naddrs; 125 } hostent_ctx; 126 127 typedef struct addrinfo_ctx { 128 callback_ctx cb_ctx; /* must come first */ 129 struct addrinfo start, *last; 130 } addrinfo_ctx; 131 132 #define HCTX_BUFLEFT(c) (sizeof((c)->buf) - ((c)->next - (c)->buf)) 133 134 typedef struct res_conf { 135 unsigned int refcount; 136 char **search_domains; 137 char **no_search; 138 short ndots; 139 short timeout; 140 } res_conf; 141 142 static res_conf *cur_res_conf; 143 144 /* protects above data */ 145 static pthread_mutex_t res_conf_lock = PTHREAD_MUTEX_INITIALIZER; 146 147 typedef struct search_iter { 148 res_conf *conf; 149 const char *name; 150 char **next_search; 151 size_t baselen; 152 bool abs_first; 153 bool abs_last; 154 char buf[MAXHOSTNAMELEN]; 155 } search_iter; 156 157 static hostent_ctx h_ctx; 158 static DNSServiceFlags svc_flags = 0; 159 160 ns_mtab *nss_module_register(const char *, u_int *, nss_module_unregister_fn *); 161 static int load_config(res_state); 162 163 static int _mdns_getaddrinfo(void *, void *, va_list); 164 static int _mdns_gethtbyaddr(void *, void *, va_list); 165 static int _mdns_gethtbyname(void *, void *, va_list); 166 167 static int _mdns_getaddrinfo_abs(const char *, DNSServiceProtocol, 168 svc_ref **, addrinfo_ctx *, short); 169 static void _mdns_addrinfo_init(addrinfo_ctx *, const struct addrinfo *); 170 static void _mdns_addrinfo_add_ai(addrinfo_ctx *, struct addrinfo *); 171 static struct addrinfo *_mdns_addrinfo_done(addrinfo_ctx *); 172 173 static int _mdns_gethtbyname_abs(const char *, int, svc_ref **, short); 174 static void _mdns_hostent_init(hostent_ctx *, int, int); 175 static void _mdns_hostent_add_host(hostent_ctx *, const char *); 176 static void _mdns_hostent_add_addr(hostent_ctx *, const void *, uint16_t); 177 static struct hostent *_mdns_hostent_done(hostent_ctx *); 178 179 static void _mdns_addrinfo_cb(DNSServiceRef, DNSServiceFlags, 180 uint32_t, DNSServiceErrorType, const char *, const struct sockaddr *, 181 uint32_t, void *); 182 static void _mdns_hostent_cb(DNSServiceRef, DNSServiceFlags, 183 uint32_t, DNSServiceErrorType, const char *, uint16_t, uint16_t, uint16_t, 184 const void *, uint32_t, void *); 185 static void _mdns_eventloop(svc_ref *, callback_ctx *, short); 186 187 static char *_mdns_rdata2name(const unsigned char *, uint16_t, 188 char *, size_t); 189 190 static int search_init(search_iter *, const char *, const char **); 191 static void search_done(search_iter *); 192 static const char *search_next(search_iter *); 193 static bool searchable_domain(char *); 194 195 static void destroy_svc_ref(svc_ref *); 196 static svc_ref *get_svc_ref(void); 197 static void put_svc_ref(svc_ref *); 198 static bool retry_query(svc_ref **, DNSServiceErrorType); 199 200 static void decref_res_conf(res_conf *); 201 static res_conf *get_res_conf(void); 202 static void put_res_conf(res_conf *); 203 static res_conf *new_res_conf(res_state); 204 static short get_timeout(void); 205 206 static ns_mtab mtab[] = { 207 { NSDB_HOSTS, "getaddrinfo", _mdns_getaddrinfo, NULL }, 208 { NSDB_HOSTS, "gethostbyaddr", _mdns_gethtbyaddr, NULL }, 209 { NSDB_HOSTS, "gethostbyname", _mdns_gethtbyname, NULL }, 210 }; 211 212 213 214 ns_mtab * 215 nss_module_register(const char *source, u_int *nelems, 216 nss_module_unregister_fn *unreg) 217 { 218 *nelems = sizeof(mtab) / sizeof(mtab[0]); 219 *unreg = NULL; 220 221 my_pid = getpid(); 222 223 if (!strcmp(source, "multicast_dns")) { 224 svc_flags = kDNSServiceFlagsForceMulticast; 225 } 226 227 return mtab; 228 } 229 230 231 232 static int 233 _mdns_getaddrinfo(void *cbrv, void *cbdata, va_list ap) 234 { 235 const struct addrinfo *pai; 236 const char *name, *sname; 237 DNSServiceProtocol proto; 238 DNSServiceRef sdRef; 239 addrinfo_ctx ctx; 240 search_iter iter; 241 res_conf *rc; 242 svc_ref *sr; 243 int err; 244 245 UNUSED(cbdata); 246 247 name = va_arg(ap, char *); 248 pai = va_arg(ap, struct addrinfo *); 249 250 switch (pai->ai_family) { 251 case AF_UNSPEC: 252 proto = kDNSServiceProtocol_IPv6 | kDNSServiceProtocol_IPv4; 253 break; 254 255 case AF_INET6: 256 proto = kDNSServiceProtocol_IPv6; 257 break; 258 259 case AF_INET: 260 proto = kDNSServiceProtocol_IPv4; 261 break; 262 263 default: 264 h_errno = NO_RECOVERY; 265 return NS_UNAVAIL; 266 } 267 268 sr = get_svc_ref(); 269 if (!sr) { 270 h_errno = NETDB_INTERNAL; 271 return NS_UNAVAIL; 272 } 273 274 if ((err = search_init(&iter, name, &sname)) != NS_SUCCESS) { 275 put_svc_ref(sr); 276 return err; 277 } 278 279 _mdns_addrinfo_init(&ctx, pai); 280 281 err = NS_NOTFOUND; 282 while (sr && sname && (err != NS_SUCCESS)) { 283 err = _mdns_getaddrinfo_abs(sname, proto, &sr, &ctx, iter.conf->timeout); 284 if (err != NS_SUCCESS) { 285 sname = search_next(&iter); 286 } 287 }; 288 289 search_done(&iter); 290 put_svc_ref(sr); 291 292 if (err == NS_SUCCESS) { 293 *(struct addrinfo **)cbrv = _mdns_addrinfo_done(&ctx); 294 } 295 296 return err; 297 } 298 299 300 301 static int 302 _mdns_getaddrinfo_abs(const char *name, DNSServiceProtocol proto, 303 svc_ref **sr, addrinfo_ctx *ctx, short timeout) 304 { 305 DNSServiceErrorType err = kDNSServiceErr_ServiceNotRunning; 306 DNSServiceRef sdRef; 307 bool retry = true; 308 309 while (*sr && retry) { 310 /* We must always use a copy of the ref when using a shared 311 connection, per kDNSServiceFlagsShareConnection docs */ 312 313 sdRef = (*sr)->sdRef; 314 315 err = DNSServiceGetAddrInfo( 316 &sdRef, 317 svc_flags 318 | kDNSServiceFlagsShareConnection 319 | kDNSServiceFlagsReturnIntermediates, 320 kDNSServiceInterfaceIndexAny, 321 proto, 322 name, 323 _mdns_addrinfo_cb, 324 ctx 325 ); 326 327 retry = retry_query(sr, err); 328 } 329 330 if (err) { 331 h_errno = NETDB_INTERNAL; 332 return NS_UNAVAIL; 333 } 334 335 _mdns_eventloop(*sr, (void *)ctx, timeout); 336 337 DNSServiceRefDeallocate(sdRef); 338 339 if (ctx->start.ai_next) { 340 return NS_SUCCESS; 341 } else { 342 h_errno = HOST_NOT_FOUND; 343 return NS_NOTFOUND; 344 } 345 } 346 347 348 349 static int 350 _mdns_gethtbyaddr(void *cbrv, void *cbdata, va_list ap) 351 { 352 const unsigned char *addr; 353 int addrlen, af; 354 char qbuf[NS_MAXDNAME + 1], *qp, *ep; 355 int advance, n; 356 DNSServiceErrorType err; 357 DNSServiceRef sdRef; 358 svc_ref *sr; 359 bool retry = true; 360 361 UNUSED(cbdata); 362 363 addr = va_arg(ap, unsigned char *); 364 addrlen = va_arg(ap, int); 365 af = va_arg(ap, int); 366 367 switch (af) { 368 case AF_INET: 369 /* if mcast-only don't bother for non-LinkLocal addrs) */ 370 if (svc_flags & kDNSServiceFlagsForceMulticast) { 371 if ((addr[0] != 169) || (addr[1] != 254)) { 372 h_errno = HOST_NOT_FOUND; 373 return NS_NOTFOUND; 374 } 375 } 376 377 (void)snprintf(qbuf, sizeof(qbuf), "%u.%u.%u.%u.in-addr.arpa", 378 (addr[3] & 0xff), (addr[2] & 0xff), 379 (addr[1] & 0xff), (addr[0] & 0xff)); 380 break; 381 382 case AF_INET6: 383 /* if mcast-only don't bother for non-LinkLocal addrs) */ 384 if (svc_flags & kDNSServiceFlagsForceMulticast) { 385 if ((addr[0] != 0xfe) || ((addr[1] & 0xc0) != 0x80)) { 386 h_errno = HOST_NOT_FOUND; 387 return NS_NOTFOUND; 388 } 389 } 390 391 qp = qbuf; 392 ep = qbuf + sizeof(qbuf) - 1; 393 for (n = IN6ADDRSZ - 1; n >= 0; n--) { 394 advance = snprintf(qp, (size_t)(ep - qp), "%x.%x.", 395 addr[n] & 0xf, 396 ((unsigned int)addr[n] >> 4) & 0xf); 397 if (advance > 0 && qp + advance < ep) 398 qp += advance; 399 else { 400 h_errno = NETDB_INTERNAL; 401 return NS_NOTFOUND; 402 } 403 } 404 if (strlcat(qbuf, "ip6.arpa", sizeof(qbuf)) >= sizeof(qbuf)) { 405 h_errno = NETDB_INTERNAL; 406 return NS_NOTFOUND; 407 } 408 break; 409 410 default: 411 h_errno = NO_RECOVERY; 412 return NS_UNAVAIL; 413 } 414 415 _mdns_hostent_init(&h_ctx, af, addrlen); 416 _mdns_hostent_add_addr(&h_ctx, addr, addrlen); 417 418 sr = get_svc_ref(); 419 if (!sr) { 420 h_errno = NETDB_INTERNAL; 421 return NS_UNAVAIL; 422 } 423 424 while (sr && retry) { 425 /* We must always use a copy of the ref when using a shared 426 connection, per kDNSServiceFlagsShareConnection docs */ 427 sdRef = sr->sdRef; 428 429 err = DNSServiceQueryRecord( 430 &sdRef, 431 svc_flags 432 | kDNSServiceFlagsShareConnection 433 | kDNSServiceFlagsReturnIntermediates, 434 kDNSServiceInterfaceIndexAny, 435 qbuf, 436 kDNSServiceType_PTR, 437 kDNSServiceClass_IN, 438 _mdns_hostent_cb, 439 &h_ctx 440 ); 441 442 retry = retry_query(&sr, err); 443 } 444 445 if (err) { 446 put_svc_ref(sr); 447 h_errno = NETDB_INTERNAL; 448 return NS_UNAVAIL; 449 } 450 451 _mdns_eventloop(sr, (void *)&h_ctx, get_timeout()); 452 453 DNSServiceRefDeallocate(sdRef); 454 put_svc_ref(sr); 455 456 if (h_ctx.naliases) { 457 *(struct hostent **)cbrv = _mdns_hostent_done(&h_ctx); 458 459 return NS_SUCCESS; 460 } else { 461 h_errno = HOST_NOT_FOUND; 462 return NS_NOTFOUND; 463 } 464 } 465 466 467 468 static int 469 _mdns_gethtbyname(void *cbrv, void *cbdata, va_list ap) 470 { 471 int namelen, af, addrlen, rrtype, err; 472 const char *name, *sname; 473 DNSServiceRef sdRef; 474 search_iter iter; 475 svc_ref *sr; 476 477 UNUSED(cbdata); 478 479 name = va_arg(ap, char *); 480 namelen = va_arg(ap, int); 481 af = va_arg(ap, int); 482 483 UNUSED(namelen); 484 485 switch (af) { 486 case AF_INET: 487 rrtype = kDNSServiceType_A; 488 addrlen = 4; 489 break; 490 491 case AF_INET6: 492 rrtype = kDNSServiceType_AAAA; 493 addrlen = 16; 494 break; 495 496 default: 497 h_errno = NO_RECOVERY; 498 return NS_UNAVAIL; 499 } 500 501 sr = get_svc_ref(); 502 if (!sr) { 503 h_errno = NETDB_INTERNAL; 504 return NS_UNAVAIL; 505 } 506 507 if ((err = search_init(&iter, name, &sname)) != NS_SUCCESS) { 508 put_svc_ref(sr); 509 return err; 510 } 511 512 _mdns_hostent_init(&h_ctx, af, addrlen); 513 514 err = NS_NOTFOUND; 515 while (sr && sname && (err != NS_SUCCESS)) { 516 err = _mdns_gethtbyname_abs(sname, rrtype, &sr, iter.conf->timeout); 517 if (err != NS_SUCCESS) { 518 sname = search_next(&iter); 519 } 520 }; 521 522 search_done(&iter); 523 put_svc_ref(sr); 524 525 if (err == NS_SUCCESS) { 526 _mdns_hostent_add_host(&h_ctx, sname); 527 _mdns_hostent_add_host(&h_ctx, name); 528 *(struct hostent **)cbrv = _mdns_hostent_done(&h_ctx); 529 } 530 531 return err; 532 } 533 534 535 536 static int 537 _mdns_gethtbyname_abs(const char *name, int rrtype, svc_ref **sr, short timeout) 538 { 539 DNSServiceErrorType err = kDNSServiceErr_ServiceNotRunning; 540 DNSServiceRef sdRef; 541 bool retry = true; 542 543 while (*sr && retry) { 544 /* We must always use a copy of the ref when using a shared 545 connection, per kDNSServiceFlagsShareConnection docs */ 546 sdRef = (*sr)->sdRef; 547 548 err = DNSServiceQueryRecord( 549 &sdRef, 550 svc_flags 551 | kDNSServiceFlagsShareConnection 552 | kDNSServiceFlagsReturnIntermediates, 553 kDNSServiceInterfaceIndexAny, 554 name, 555 rrtype, 556 kDNSServiceClass_IN, 557 _mdns_hostent_cb, 558 &h_ctx 559 ); 560 561 retry = retry_query(sr, err); 562 } 563 564 if (err) { 565 h_errno = NETDB_INTERNAL; 566 return NS_UNAVAIL; 567 } 568 569 _mdns_eventloop(*sr, (void *)&h_ctx, timeout); 570 571 DNSServiceRefDeallocate(sdRef); 572 573 if (h_ctx.naddrs) { 574 return NS_SUCCESS; 575 } else { 576 h_errno = HOST_NOT_FOUND; 577 return NS_NOTFOUND; 578 } 579 } 580 581 582 583 static void 584 _mdns_addrinfo_init(addrinfo_ctx *ctx, const struct addrinfo *ai) 585 { 586 ctx->cb_ctx.done = false; 587 ctx->start = *ai; 588 ctx->start.ai_next = NULL; 589 ctx->start.ai_canonname = NULL; 590 ctx->last = &(ctx->start); 591 } 592 593 594 595 static void 596 _mdns_addrinfo_add_ai(addrinfo_ctx *ctx, struct addrinfo *ai) 597 { 598 ctx->last->ai_next = ai; 599 while (ctx->last->ai_next) 600 ctx->last = ctx->last->ai_next; 601 } 602 603 604 605 static struct addrinfo * 606 _mdns_addrinfo_done(addrinfo_ctx *ctx) 607 { 608 struct addrinfo head, *t, *p; 609 610 /* sort v6 up */ 611 612 t = &head; 613 p = ctx->start.ai_next; 614 615 while (p->ai_next) { 616 if (p->ai_next->ai_family == AF_INET6) { 617 t->ai_next = p->ai_next; 618 t = t->ai_next; 619 p->ai_next = p->ai_next->ai_next; 620 } else { 621 p = p->ai_next; 622 } 623 } 624 625 /* add rest of list and reset start to the new list */ 626 627 t->ai_next = ctx->start.ai_next; 628 ctx->start.ai_next = head.ai_next; 629 630 return ctx->start.ai_next; 631 } 632 633 634 635 static void 636 _mdns_hostent_init(hostent_ctx *ctx, int af, int addrlen) 637 { 638 int i; 639 640 ctx->cb_ctx.done = false; 641 ctx->naliases = ctx->naddrs = 0; 642 ctx->next = ctx->buf; 643 644 ctx->host.h_aliases = ctx->host_aliases; 645 ctx->host.h_addr_list = ctx->h_addr_ptrs; 646 ctx->host.h_name = ctx->host.h_aliases[0] = NULL; 647 ctx->host.h_addrtype = af; 648 ctx->host.h_length = addrlen; 649 650 for (i = 0; i < MAXADDRS; i++) { 651 ctx->host.h_addr_list[i] = &(ctx->addrs[i * 16]); 652 } 653 } 654 655 656 657 static void 658 _mdns_hostent_add_host(hostent_ctx *ctx, const char *name) 659 { 660 size_t len; 661 int i; 662 663 if (name && (len = strlen(name)) 664 && (HCTX_BUFLEFT(ctx) > len) && (ctx->naliases < MAXALIASES)) { 665 if (len && (name[len - 1] == '.')) { 666 len--; 667 } 668 669 /* skip dupe names */ 670 671 if ((ctx->host.h_name) && !strncmp(ctx->host.h_name, name, len) 672 && (strlen(ctx->host.h_name) == len)) { 673 return; 674 } 675 676 for (i = 0; i < ctx->naliases - 1; i++) { 677 if (!strncmp(ctx->host.h_aliases[i], name, len) 678 && (strlen(ctx->host.h_aliases[i]) == len)) { 679 return; 680 } 681 } 682 683 strncpy(ctx->next, name, len); 684 ctx->next[len] = 0; 685 686 if (ctx->naliases == 0) { 687 ctx->host.h_name = ctx->next; 688 } else { 689 ctx->host.h_aliases[ctx->naliases - 1] = ctx->next; 690 } 691 692 ctx->next += (len + 1); 693 ctx->naliases++; 694 } /* else silently ignore */ 695 } 696 697 698 699 static void 700 _mdns_hostent_add_addr(hostent_ctx *ctx, const void *addr, uint16_t len) 701 { 702 if ((len == ctx->host.h_length) && (ctx->naddrs < MAXADDRS)) { 703 memcpy(ctx->host.h_addr_list[ctx->naddrs++], addr, (size_t)len); 704 } /* else wrong address type or out of room... silently skip */ 705 } 706 707 708 709 static struct hostent * 710 _mdns_hostent_done(hostent_ctx *ctx) 711 { 712 if (ctx->naliases) { 713 /* terminate array */ 714 ctx->host.h_aliases[ctx->naliases - 1] = NULL; 715 ctx->host.h_addr_list[ctx->naddrs] = NULL; 716 } 717 718 return &(ctx->host); 719 } 720 721 722 723 static void 724 _mdns_addrinfo_cb( 725 DNSServiceRef sdRef, 726 DNSServiceFlags flags, 727 uint32_t interfaceIndex, 728 DNSServiceErrorType errorCode, 729 const char *hostname, 730 const struct sockaddr *address, 731 uint32_t ttl, 732 void *context 733 ) { 734 addrinfo_ctx *ctx = context; 735 struct addrinfo *ai; 736 737 UNUSED(sdRef); 738 UNUSED(interfaceIndex); 739 UNUSED(ttl); 740 741 if (errorCode == kDNSServiceErr_NoError) { 742 if (! (flags & kDNSServiceFlagsMoreComing)) { 743 ctx->cb_ctx.done = true; 744 } 745 746 ai = allocaddrinfo((socklen_t)(address->sa_len)); 747 if (ai) { 748 ai->ai_flags = ctx->start.ai_flags; 749 ai->ai_family = address->sa_family; 750 ai->ai_socktype = ctx->start.ai_socktype; 751 ai->ai_protocol = ctx->start.ai_protocol; 752 memcpy(ai->ai_addr, address, (size_t)(address->sa_len)); 753 754 if ((ctx->start.ai_flags & AI_CANONNAME) && hostname) { 755 ai->ai_canonname = strdup(hostname); 756 if (ai->ai_canonname[strlen(ai->ai_canonname) - 1] == '.') { 757 ai->ai_canonname[strlen(ai->ai_canonname) - 1] = '\0'; 758 } 759 } 760 761 _mdns_addrinfo_add_ai(ctx, ai); 762 } 763 } 764 } 765 766 767 768 static void 769 _mdns_hostent_cb( 770 DNSServiceRef sdRef, 771 DNSServiceFlags flags, 772 uint32_t interfaceIndex, 773 DNSServiceErrorType errorCode, 774 const char *fullname, 775 uint16_t rrtype, 776 uint16_t rrclass, 777 uint16_t rdlen, 778 const void *rdata, 779 uint32_t ttl, 780 void *context 781 ) { 782 hostent_ctx *ctx = (hostent_ctx *)context; 783 char buf[NS_MAXDNAME+1]; 784 785 UNUSED(sdRef); 786 UNUSED(interfaceIndex); 787 UNUSED(rrclass); 788 UNUSED(ttl); 789 790 if (! (flags & kDNSServiceFlagsMoreComing)) { 791 ctx->cb_ctx.done = true; 792 } 793 794 if (errorCode == kDNSServiceErr_NoError) { 795 switch (rrtype) { 796 case kDNSServiceType_PTR: 797 if (!_mdns_rdata2name(rdata, rdlen, buf, sizeof(buf))) { 798 /* corrupt response -- skip */ 799 return; 800 } 801 802 _mdns_hostent_add_host(ctx, buf); 803 break; 804 805 case kDNSServiceType_A: 806 if (ctx->host.h_addrtype == AF_INET) { 807 _mdns_hostent_add_host(ctx, fullname); 808 _mdns_hostent_add_addr(ctx, rdata, rdlen); 809 } 810 break; 811 812 case kDNSServiceType_AAAA: 813 if (ctx->host.h_addrtype == AF_INET6) { 814 _mdns_hostent_add_host(ctx, fullname); 815 _mdns_hostent_add_addr(ctx, rdata, rdlen); 816 } 817 break; 818 } 819 } else if (errorCode == kDNSServiceErr_NoSuchRecord) { 820 ctx->cb_ctx.done = true; 821 } 822 } 823 824 825 826 static void 827 _mdns_eventloop(svc_ref *sr, callback_ctx *ctx, short timeout) 828 { 829 struct pollfd fds; 830 int fd, ret; 831 832 fd = DNSServiceRefSockFD(sr->sdRef); 833 fds.fd = fd; 834 fds.events = POLLRDNORM; 835 836 while (!ctx->done) { 837 ret = poll(&fds, 1, timeout * 1000); 838 if (ret > 0) { 839 DNSServiceProcessResult(sr->sdRef); 840 } else { 841 break; 842 } 843 } 844 } 845 846 847 848 static char * 849 _mdns_rdata2name(const unsigned char *rdata, uint16_t rdlen, char *buf, size_t buflen) 850 { 851 unsigned char l; 852 char *r = buf; 853 854 /* illegal 0-size answer or not enough room for even "." */ 855 if ((!rdlen) || (rdlen < 2)) { 856 return NULL; 857 } 858 859 buflen--; /* reserve space for terminating NUL now */ 860 861 /* special case empty as "." */ 862 if ((rdlen == 1) && (!*rdata)) { 863 strcpy(buf, "."); 864 865 return r; 866 } 867 868 while (rdlen && *rdata) { 869 /* label length byte */ 870 l = *rdata++; rdlen--; 871 872 if (l > 63) { 873 /* compression or bitstrings -- shouldn't happen */ 874 return NULL; 875 } else if (l > buflen) { 876 /* not enough space */ 877 return NULL; 878 } else if (l > rdlen) { 879 /* label shouldn't be longer than remaining rdata */ 880 return NULL; 881 } else if (!l) { 882 /* empty label -- should be done */ 883 if (rdlen) { 884 /* but more left!? */ 885 return NULL; 886 } else { 887 break; 888 } 889 } 890 891 memcpy(buf, rdata, (size_t)l); 892 rdata += l; buf += l; 893 rdlen -= l; buflen -= l; 894 895 /* Another label to come? add a separator */ 896 if (rdlen && *rdata) { 897 if (!buflen) { 898 return NULL; 899 } 900 901 *buf++ = '.'; buflen--; 902 } 903 } 904 905 /* we reserved space above, so we know we have space 906 to add this termination */ 907 908 *buf = '\0'; 909 910 return r; 911 } 912 913 914 915 int 916 search_init(search_iter *iter, const char *name, const char **first) 917 { 918 const char *c = name, *cmp; 919 int dots = 0, enddot = 0; 920 size_t len, cl; 921 922 iter->conf = get_res_conf(); 923 if (!iter->conf) { 924 h_errno = NETDB_INTERNAL; 925 return NS_UNAVAIL; 926 } 927 928 iter->name = name; 929 iter->baselen = 0; 930 iter->abs_first = iter->abs_last = false; 931 iter->next_search = iter->conf->search_domains; 932 933 while (*c) { 934 if (*c == '.') { 935 dots++; 936 enddot = 1; 937 } else { 938 enddot = 0; 939 } 940 c++; 941 } 942 943 if (svc_flags & kDNSServiceFlagsForceMulticast) { 944 if (dots) { 945 iter->next_search = iter->conf->no_search; 946 if ((dots - enddot) == 1) { 947 len = strlen(iter->name); 948 cl = strlen(".local") + enddot; 949 if (len > cl) { 950 cmp = enddot ? ".local." : ".local"; 951 c = iter->name + len - cl; 952 953 if (!strcasecmp(c, cmp)) { 954 iter->abs_first = true; 955 } 956 } 957 } 958 } 959 } else { 960 if (dots >= iter->conf->ndots) { 961 iter->abs_first = true; 962 } else { 963 iter->abs_last = true; 964 } 965 966 if (enddot) { 967 /* absolute; don't search */ 968 iter->next_search = iter->conf->no_search; 969 } 970 } 971 972 *first = search_next(iter); 973 if (!first) { 974 search_done(iter); 975 h_errno = HOST_NOT_FOUND; 976 return NS_NOTFOUND; 977 } 978 979 return NS_SUCCESS; 980 } 981 982 983 984 const char * 985 search_next(search_iter *iter) 986 { 987 const char *a = NULL; 988 res_state res; 989 size_t len; 990 991 if (iter->abs_first) { 992 iter->abs_first = false; 993 return iter->name; 994 } 995 996 while (*(iter->next_search)) { 997 if (!iter->baselen) { 998 iter->baselen = strlcpy(iter->buf, iter->name, sizeof(iter->buf)); 999 if (iter->baselen >= sizeof(iter->buf) - 1) { 1000 /* original is too long, don't try any search domains */ 1001 iter->next_search = iter->conf->no_search; 1002 break; 1003 } 1004 1005 iter->buf[iter->baselen++] = '.'; 1006 } 1007 1008 len = strlcpy(&(iter->buf[iter->baselen]), 1009 *(iter->next_search), 1010 sizeof(iter->buf) - iter->baselen); 1011 1012 iter->next_search++; 1013 1014 if (len >= sizeof(iter->buf) - iter->baselen) { 1015 /* result was too long */ 1016 continue; 1017 } 1018 1019 return iter->buf; 1020 } 1021 1022 if (iter->abs_last) { 1023 iter->abs_last = false; 1024 return iter->name; 1025 } 1026 1027 return NULL; 1028 } 1029 1030 1031 1032 void 1033 search_done(search_iter *iter) 1034 { 1035 if (iter->conf) { 1036 put_res_conf(iter->conf); 1037 iter->conf = NULL; 1038 } 1039 } 1040 1041 1042 1043 /* 1044 * Is domain appropriate to be in the domain search list? 1045 * For mdnsd, take everything. For multicast_dns, only "local" 1046 * if present. 1047 */ 1048 bool 1049 searchable_domain(char *d) 1050 { 1051 if (!(svc_flags & kDNSServiceFlagsForceMulticast)) { 1052 return true; 1053 } 1054 1055 if (!strcasecmp(d, "local") || !strcasecmp(d, "local.")) { 1056 return true; 1057 } 1058 1059 return false; 1060 } 1061 1062 1063 1064 static void 1065 destroy_svc_ref(svc_ref *sr) 1066 { 1067 /* assumes not on conn list */ 1068 1069 if (sr) { 1070 DNSServiceRefDeallocate(sr->sdRef); 1071 free(sr); 1072 } 1073 } 1074 1075 1076 1077 static svc_ref * 1078 get_svc_ref(void) 1079 { 1080 svc_ref *sr; 1081 1082 LOCK(&conn_list_lock); 1083 1084 if (getpid() != my_pid) { 1085 my_pid = getpid(); 1086 1087 /* 1088 * We forked and kept running. We don't want to share 1089 * connections with the parent or we'll garble each others 1090 * comms, so throw away the parent's list and start over 1091 */ 1092 while ((sr = SLIST_FIRST(&conn_list))) { 1093 SLIST_REMOVE_HEAD(&conn_list, entries); 1094 destroy_svc_ref(sr); 1095 } 1096 1097 sr = NULL; 1098 } else { 1099 /* try to recycle a connection */ 1100 sr = SLIST_FIRST(&conn_list); 1101 if (sr) { 1102 SLIST_REMOVE_HEAD(&conn_list, entries); 1103 conn_count--; 1104 } 1105 } 1106 1107 UNLOCK(&conn_list_lock); 1108 1109 if (!sr) { 1110 /* none available, we need a new one */ 1111 1112 sr = calloc(sizeof(svc_ref), 1); 1113 if (sr) { 1114 if (DNSServiceCreateConnection(&(sr->sdRef))) { 1115 free(sr); 1116 return NULL; 1117 } 1118 1119 if (fcntl(DNSServiceRefSockFD(sr->sdRef), F_SETFD, FD_CLOEXEC) < 0) { 1120 destroy_svc_ref(sr); 1121 sr = NULL; 1122 } 1123 } 1124 } 1125 1126 return sr; 1127 } 1128 1129 1130 1131 static void 1132 put_svc_ref(svc_ref *sr) 1133 { 1134 if (sr) { 1135 LOCK(&conn_list_lock); 1136 1137 sr->uses++; 1138 1139 /* if slow start or aged out, destroy */ 1140 if ((svc_puts++ < SLOWSTART_LOOKUPS) 1141 || (conn_count && (sr->uses > REUSE_TIMES))) { 1142 UNLOCK(&conn_list_lock); 1143 destroy_svc_ref(sr); 1144 return; 1145 } 1146 1147 conn_count++; 1148 1149 SLIST_INSERT_HEAD(&conn_list, sr, entries); 1150 1151 UNLOCK(&conn_list_lock); 1152 } 1153 } 1154 1155 1156 1157 /* 1158 * determine if this is a call we should retry with a fresh 1159 * connection, for example if mdnsd went away and came back. 1160 */ 1161 static bool 1162 retry_query(svc_ref **sr, DNSServiceErrorType err) 1163 { 1164 if ((err == kDNSServiceErr_Unknown) 1165 || (err == kDNSServiceErr_ServiceNotRunning)) { 1166 /* these errors might indicate a stale socket */ 1167 if ((*sr)->uses) { 1168 /* this was an old socket, so kill it and get another */ 1169 destroy_svc_ref(*sr); 1170 *sr = get_svc_ref(); 1171 if (*sr) { 1172 /* we can retry */ 1173 return true; 1174 } 1175 } 1176 } 1177 1178 return false; 1179 } 1180 1181 1182 1183 static void 1184 decref_res_conf(res_conf *rc) 1185 { 1186 rc->refcount--; 1187 1188 if (rc->refcount < 1) { 1189 if ((rc->no_search = rc->search_domains)) { 1190 for (; *(rc->no_search); rc->no_search++) { 1191 free(*(rc->no_search)); 1192 } 1193 1194 free(rc->search_domains); 1195 } 1196 1197 free(rc); 1198 } 1199 } 1200 1201 1202 1203 res_conf * 1204 get_res_conf(void) 1205 { 1206 struct timespec last_change; 1207 res_state res; 1208 res_conf *rc; 1209 1210 LOCK(&res_conf_lock); 1211 1212 /* check if resolver config changed */ 1213 1214 res = __res_get_state(); 1215 if (res) { 1216 res_check(res, &last_change); 1217 if (timespeccmp(&last_config, &last_change, <)) { 1218 if (cur_res_conf) { 1219 decref_res_conf(cur_res_conf); 1220 } 1221 cur_res_conf = new_res_conf(res); 1222 if (cur_res_conf) { 1223 last_config = last_change; 1224 } 1225 } 1226 __res_put_state(res); 1227 } 1228 1229 rc = cur_res_conf; 1230 if (rc) { 1231 rc->refcount++; 1232 } 1233 1234 UNLOCK(&res_conf_lock); 1235 1236 return rc; 1237 } 1238 1239 1240 1241 static void 1242 put_res_conf(res_conf *rc) 1243 { 1244 LOCK(&res_conf_lock); 1245 1246 decref_res_conf(rc); 1247 1248 UNLOCK(&res_conf_lock); 1249 } 1250 1251 1252 1253 static res_conf * 1254 new_res_conf(res_state res) 1255 { 1256 res_conf *rc; 1257 int count = 0; 1258 char **sd, *p; 1259 1260 rc = calloc(sizeof(res_conf), 1); 1261 if (rc) { 1262 rc->refcount = 1; 1263 1264 sd = res->dnsrch; 1265 while (*sd) { 1266 if (searchable_domain(*sd)) { 1267 count++; 1268 } 1269 sd++; 1270 } 1271 1272 rc->search_domains = calloc(sizeof(char *), count + 1); 1273 if (!(rc->search_domains)) { 1274 decref_res_conf(rc); 1275 return NULL; 1276 } 1277 1278 sd = res->dnsrch; 1279 rc->no_search = rc->search_domains; 1280 while (*sd) { 1281 if (searchable_domain(*sd)) { 1282 *(rc->no_search) = p = strdup(*sd); 1283 if (!p) { 1284 decref_res_conf(rc); 1285 return NULL; 1286 } 1287 1288 rc->no_search++; 1289 } 1290 sd++; 1291 } 1292 1293 rc->timeout = res->retrans; 1294 1295 if (svc_flags & kDNSServiceFlagsForceMulticast) { 1296 rc->ndots = 1; 1297 if (rc->timeout > 2) { 1298 rc->timeout = 2; 1299 } 1300 } else { 1301 rc->ndots = res->ndots; 1302 } 1303 } 1304 1305 return rc; 1306 } 1307 1308 1309 1310 static short 1311 get_timeout(void) 1312 { 1313 short timeout = 5; 1314 res_conf *rc; 1315 1316 rc = get_res_conf(); 1317 if (rc) { 1318 timeout = rc->timeout; 1319 put_res_conf(rc); 1320 } 1321 1322 return timeout; 1323 } 1324