Sync usage with man page.
[netbsd-mini2440.git] / external / apache2 / mDNSResponder / nss / nss_mdnsd.c
blob7629b90442cf52564a5770f95bb972b136a7b5ed
1 /* $NetBSD: nss_mdnsd.c,v 1.2 2009/10/26 00:46:19 tsarna Exp $ */
3 /*-
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tyler C. Sarna
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.
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.
33 * Multicast DNS ("Bonjour") hosts name service switch
35 * Documentation links:
37 * http://developer.apple.com/bonjour/
38 * http://www.multicastdns.org/
39 * http://www.dns-sd.org/
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>
65 * Pool of mdnsd connections
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;
72 typedef struct svc_ref {
73 SLIST_ENTRY(svc_ref) entries;
74 DNSServiceRef sdRef;
75 unsigned int uses;
76 } svc_ref;
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.
86 #define SLOWSTART_LOOKUPS 5
87 static unsigned int svc_puts = 0;
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.
93 #define REUSE_TIMES 32
95 /* protects above data */
96 static pthread_mutex_t conn_list_lock = PTHREAD_MUTEX_INITIALIZER;
98 extern int __isthreaded; /* libc private -- wish there was a better way */
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)
104 #ifndef lint
105 #define UNUSED(a) (void)&a
106 #else
107 #define UNUSED(a) a = a
108 #endif
110 #define MAXALIASES 35
111 #define MAXADDRS 35
113 typedef struct callback_ctx {
114 bool done;
115 } callback_ctx;
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;
127 typedef struct addrinfo_ctx {
128 callback_ctx cb_ctx; /* must come first */
129 struct addrinfo start, *last;
130 } addrinfo_ctx;
132 #define HCTX_BUFLEFT(c) (sizeof((c)->buf) - ((c)->next - (c)->buf))
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;
142 static res_conf *cur_res_conf;
144 /* protects above data */
145 static pthread_mutex_t res_conf_lock = PTHREAD_MUTEX_INITIALIZER;
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;
157 static hostent_ctx h_ctx;
158 static DNSServiceFlags svc_flags = 0;
160 ns_mtab *nss_module_register(const char *, u_int *, nss_module_unregister_fn *);
161 static int load_config(res_state);
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);
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 *);
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 *);
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);
187 static char *_mdns_rdata2name(const unsigned char *, uint16_t,
188 char *, size_t);
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 *);
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);
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);
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 },
214 ns_mtab *
215 nss_module_register(const char *source, u_int *nelems,
216 nss_module_unregister_fn *unreg)
218 *nelems = sizeof(mtab) / sizeof(mtab[0]);
219 *unreg = NULL;
221 my_pid = getpid();
223 if (!strcmp(source, "multicast_dns")) {
224 svc_flags = kDNSServiceFlagsForceMulticast;
227 return mtab;
232 static int
233 _mdns_getaddrinfo(void *cbrv, void *cbdata, va_list ap)
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;
245 UNUSED(cbdata);
247 name = va_arg(ap, char *);
248 pai = va_arg(ap, struct addrinfo *);
250 switch (pai->ai_family) {
251 case AF_UNSPEC:
252 proto = kDNSServiceProtocol_IPv6 | kDNSServiceProtocol_IPv4;
253 break;
255 case AF_INET6:
256 proto = kDNSServiceProtocol_IPv6;
257 break;
259 case AF_INET:
260 proto = kDNSServiceProtocol_IPv4;
261 break;
263 default:
264 h_errno = NO_RECOVERY;
265 return NS_UNAVAIL;
268 sr = get_svc_ref();
269 if (!sr) {
270 h_errno = NETDB_INTERNAL;
271 return NS_UNAVAIL;
274 if ((err = search_init(&iter, name, &sname)) != NS_SUCCESS) {
275 put_svc_ref(sr);
276 return err;
279 _mdns_addrinfo_init(&ctx, pai);
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);
289 search_done(&iter);
290 put_svc_ref(sr);
292 if (err == NS_SUCCESS) {
293 *(struct addrinfo **)cbrv = _mdns_addrinfo_done(&ctx);
296 return err;
301 static int
302 _mdns_getaddrinfo_abs(const char *name, DNSServiceProtocol proto,
303 svc_ref **sr, addrinfo_ctx *ctx, short timeout)
305 DNSServiceErrorType err = kDNSServiceErr_ServiceNotRunning;
306 DNSServiceRef sdRef;
307 bool retry = true;
309 while (*sr && retry) {
310 /* We must always use a copy of the ref when using a shared
311 connection, per kDNSServiceFlagsShareConnection docs */
313 sdRef = (*sr)->sdRef;
315 err = DNSServiceGetAddrInfo(
316 &sdRef,
317 svc_flags
318 | kDNSServiceFlagsShareConnection
319 | kDNSServiceFlagsReturnIntermediates,
320 kDNSServiceInterfaceIndexAny,
321 proto,
322 name,
323 _mdns_addrinfo_cb,
327 retry = retry_query(sr, err);
330 if (err) {
331 h_errno = NETDB_INTERNAL;
332 return NS_UNAVAIL;
335 _mdns_eventloop(*sr, (void *)ctx, timeout);
337 DNSServiceRefDeallocate(sdRef);
339 if (ctx->start.ai_next) {
340 return NS_SUCCESS;
341 } else {
342 h_errno = HOST_NOT_FOUND;
343 return NS_NOTFOUND;
349 static int
350 _mdns_gethtbyaddr(void *cbrv, void *cbdata, va_list ap)
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;
361 UNUSED(cbdata);
363 addr = va_arg(ap, unsigned char *);
364 addrlen = va_arg(ap, int);
365 af = va_arg(ap, int);
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;
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;
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;
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;
404 if (strlcat(qbuf, "ip6.arpa", sizeof(qbuf)) >= sizeof(qbuf)) {
405 h_errno = NETDB_INTERNAL;
406 return NS_NOTFOUND;
408 break;
410 default:
411 h_errno = NO_RECOVERY;
412 return NS_UNAVAIL;
415 _mdns_hostent_init(&h_ctx, af, addrlen);
416 _mdns_hostent_add_addr(&h_ctx, addr, addrlen);
418 sr = get_svc_ref();
419 if (!sr) {
420 h_errno = NETDB_INTERNAL;
421 return NS_UNAVAIL;
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;
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
442 retry = retry_query(&sr, err);
445 if (err) {
446 put_svc_ref(sr);
447 h_errno = NETDB_INTERNAL;
448 return NS_UNAVAIL;
451 _mdns_eventloop(sr, (void *)&h_ctx, get_timeout());
453 DNSServiceRefDeallocate(sdRef);
454 put_svc_ref(sr);
456 if (h_ctx.naliases) {
457 *(struct hostent **)cbrv = _mdns_hostent_done(&h_ctx);
459 return NS_SUCCESS;
460 } else {
461 h_errno = HOST_NOT_FOUND;
462 return NS_NOTFOUND;
468 static int
469 _mdns_gethtbyname(void *cbrv, void *cbdata, va_list ap)
471 int namelen, af, addrlen, rrtype, err;
472 const char *name, *sname;
473 DNSServiceRef sdRef;
474 search_iter iter;
475 svc_ref *sr;
477 UNUSED(cbdata);
479 name = va_arg(ap, char *);
480 namelen = va_arg(ap, int);
481 af = va_arg(ap, int);
483 UNUSED(namelen);
485 switch (af) {
486 case AF_INET:
487 rrtype = kDNSServiceType_A;
488 addrlen = 4;
489 break;
491 case AF_INET6:
492 rrtype = kDNSServiceType_AAAA;
493 addrlen = 16;
494 break;
496 default:
497 h_errno = NO_RECOVERY;
498 return NS_UNAVAIL;
501 sr = get_svc_ref();
502 if (!sr) {
503 h_errno = NETDB_INTERNAL;
504 return NS_UNAVAIL;
507 if ((err = search_init(&iter, name, &sname)) != NS_SUCCESS) {
508 put_svc_ref(sr);
509 return err;
512 _mdns_hostent_init(&h_ctx, af, addrlen);
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);
522 search_done(&iter);
523 put_svc_ref(sr);
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);
531 return err;
536 static int
537 _mdns_gethtbyname_abs(const char *name, int rrtype, svc_ref **sr, short timeout)
539 DNSServiceErrorType err = kDNSServiceErr_ServiceNotRunning;
540 DNSServiceRef sdRef;
541 bool retry = true;
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;
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
561 retry = retry_query(sr, err);
564 if (err) {
565 h_errno = NETDB_INTERNAL;
566 return NS_UNAVAIL;
569 _mdns_eventloop(*sr, (void *)&h_ctx, timeout);
571 DNSServiceRefDeallocate(sdRef);
573 if (h_ctx.naddrs) {
574 return NS_SUCCESS;
575 } else {
576 h_errno = HOST_NOT_FOUND;
577 return NS_NOTFOUND;
583 static void
584 _mdns_addrinfo_init(addrinfo_ctx *ctx, const struct addrinfo *ai)
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);
595 static void
596 _mdns_addrinfo_add_ai(addrinfo_ctx *ctx, struct addrinfo *ai)
598 ctx->last->ai_next = ai;
599 while (ctx->last->ai_next)
600 ctx->last = ctx->last->ai_next;
605 static struct addrinfo *
606 _mdns_addrinfo_done(addrinfo_ctx *ctx)
608 struct addrinfo head, *t, *p;
610 /* sort v6 up */
612 t = &head;
613 p = ctx->start.ai_next;
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;
625 /* add rest of list and reset start to the new list */
627 t->ai_next = ctx->start.ai_next;
628 ctx->start.ai_next = head.ai_next;
630 return ctx->start.ai_next;
635 static void
636 _mdns_hostent_init(hostent_ctx *ctx, int af, int addrlen)
638 int i;
640 ctx->cb_ctx.done = false;
641 ctx->naliases = ctx->naddrs = 0;
642 ctx->next = ctx->buf;
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;
650 for (i = 0; i < MAXADDRS; i++) {
651 ctx->host.h_addr_list[i] = &(ctx->addrs[i * 16]);
657 static void
658 _mdns_hostent_add_host(hostent_ctx *ctx, const char *name)
660 size_t len;
661 int i;
663 if (name && (len = strlen(name))
664 && (HCTX_BUFLEFT(ctx) > len) && (ctx->naliases < MAXALIASES)) {
665 if (len && (name[len - 1] == '.')) {
666 len--;
669 /* skip dupe names */
671 if ((ctx->host.h_name) && !strncmp(ctx->host.h_name, name, len)
672 && (strlen(ctx->host.h_name) == len)) {
673 return;
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;
683 strncpy(ctx->next, name, len);
684 ctx->next[len] = 0;
686 if (ctx->naliases == 0) {
687 ctx->host.h_name = ctx->next;
688 } else {
689 ctx->host.h_aliases[ctx->naliases - 1] = ctx->next;
692 ctx->next += (len + 1);
693 ctx->naliases++;
694 } /* else silently ignore */
699 static void
700 _mdns_hostent_add_addr(hostent_ctx *ctx, const void *addr, uint16_t len)
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 */
709 static struct hostent *
710 _mdns_hostent_done(hostent_ctx *ctx)
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;
718 return &(ctx->host);
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
734 addrinfo_ctx *ctx = context;
735 struct addrinfo *ai;
737 UNUSED(sdRef);
738 UNUSED(interfaceIndex);
739 UNUSED(ttl);
741 if (errorCode == kDNSServiceErr_NoError) {
742 if (! (flags & kDNSServiceFlagsMoreComing)) {
743 ctx->cb_ctx.done = true;
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));
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';
761 _mdns_addrinfo_add_ai(ctx, ai);
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
782 hostent_ctx *ctx = (hostent_ctx *)context;
783 char buf[NS_MAXDNAME+1];
785 UNUSED(sdRef);
786 UNUSED(interfaceIndex);
787 UNUSED(rrclass);
788 UNUSED(ttl);
790 if (! (flags & kDNSServiceFlagsMoreComing)) {
791 ctx->cb_ctx.done = true;
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;
802 _mdns_hostent_add_host(ctx, buf);
803 break;
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);
810 break;
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);
817 break;
819 } else if (errorCode == kDNSServiceErr_NoSuchRecord) {
820 ctx->cb_ctx.done = true;
826 static void
827 _mdns_eventloop(svc_ref *sr, callback_ctx *ctx, short timeout)
829 struct pollfd fds;
830 int fd, ret;
832 fd = DNSServiceRefSockFD(sr->sdRef);
833 fds.fd = fd;
834 fds.events = POLLRDNORM;
836 while (!ctx->done) {
837 ret = poll(&fds, 1, timeout * 1000);
838 if (ret > 0) {
839 DNSServiceProcessResult(sr->sdRef);
840 } else {
841 break;
848 static char *
849 _mdns_rdata2name(const unsigned char *rdata, uint16_t rdlen, char *buf, size_t buflen)
851 unsigned char l;
852 char *r = buf;
854 /* illegal 0-size answer or not enough room for even "." */
855 if ((!rdlen) || (rdlen < 2)) {
856 return NULL;
859 buflen--; /* reserve space for terminating NUL now */
861 /* special case empty as "." */
862 if ((rdlen == 1) && (!*rdata)) {
863 strcpy(buf, ".");
865 return r;
868 while (rdlen && *rdata) {
869 /* label length byte */
870 l = *rdata++; rdlen--;
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;
891 memcpy(buf, rdata, (size_t)l);
892 rdata += l; buf += l;
893 rdlen -= l; buflen -= l;
895 /* Another label to come? add a separator */
896 if (rdlen && *rdata) {
897 if (!buflen) {
898 return NULL;
901 *buf++ = '.'; buflen--;
905 /* we reserved space above, so we know we have space
906 to add this termination */
908 *buf = '\0';
910 return r;
916 search_init(search_iter *iter, const char *name, const char **first)
918 const char *c = name, *cmp;
919 int dots = 0, enddot = 0;
920 size_t len, cl;
922 iter->conf = get_res_conf();
923 if (!iter->conf) {
924 h_errno = NETDB_INTERNAL;
925 return NS_UNAVAIL;
928 iter->name = name;
929 iter->baselen = 0;
930 iter->abs_first = iter->abs_last = false;
931 iter->next_search = iter->conf->search_domains;
933 while (*c) {
934 if (*c == '.') {
935 dots++;
936 enddot = 1;
937 } else {
938 enddot = 0;
940 c++;
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;
953 if (!strcasecmp(c, cmp)) {
954 iter->abs_first = true;
959 } else {
960 if (dots >= iter->conf->ndots) {
961 iter->abs_first = true;
962 } else {
963 iter->abs_last = true;
966 if (enddot) {
967 /* absolute; don't search */
968 iter->next_search = iter->conf->no_search;
972 *first = search_next(iter);
973 if (!first) {
974 search_done(iter);
975 h_errno = HOST_NOT_FOUND;
976 return NS_NOTFOUND;
979 return NS_SUCCESS;
984 const char *
985 search_next(search_iter *iter)
987 const char *a = NULL;
988 res_state res;
989 size_t len;
991 if (iter->abs_first) {
992 iter->abs_first = false;
993 return iter->name;
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;
1005 iter->buf[iter->baselen++] = '.';
1008 len = strlcpy(&(iter->buf[iter->baselen]),
1009 *(iter->next_search),
1010 sizeof(iter->buf) - iter->baselen);
1012 iter->next_search++;
1014 if (len >= sizeof(iter->buf) - iter->baselen) {
1015 /* result was too long */
1016 continue;
1019 return iter->buf;
1022 if (iter->abs_last) {
1023 iter->abs_last = false;
1024 return iter->name;
1027 return NULL;
1032 void
1033 search_done(search_iter *iter)
1035 if (iter->conf) {
1036 put_res_conf(iter->conf);
1037 iter->conf = NULL;
1044 * Is domain appropriate to be in the domain search list?
1045 * For mdnsd, take everything. For multicast_dns, only "local"
1046 * if present.
1048 bool
1049 searchable_domain(char *d)
1051 if (!(svc_flags & kDNSServiceFlagsForceMulticast)) {
1052 return true;
1055 if (!strcasecmp(d, "local") || !strcasecmp(d, "local.")) {
1056 return true;
1059 return false;
1064 static void
1065 destroy_svc_ref(svc_ref *sr)
1067 /* assumes not on conn list */
1069 if (sr) {
1070 DNSServiceRefDeallocate(sr->sdRef);
1071 free(sr);
1077 static svc_ref *
1078 get_svc_ref(void)
1080 svc_ref *sr;
1082 LOCK(&conn_list_lock);
1084 if (getpid() != my_pid) {
1085 my_pid = getpid();
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
1092 while ((sr = SLIST_FIRST(&conn_list))) {
1093 SLIST_REMOVE_HEAD(&conn_list, entries);
1094 destroy_svc_ref(sr);
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--;
1107 UNLOCK(&conn_list_lock);
1109 if (!sr) {
1110 /* none available, we need a new one */
1112 sr = calloc(sizeof(svc_ref), 1);
1113 if (sr) {
1114 if (DNSServiceCreateConnection(&(sr->sdRef))) {
1115 free(sr);
1116 return NULL;
1119 if (fcntl(DNSServiceRefSockFD(sr->sdRef), F_SETFD, FD_CLOEXEC) < 0) {
1120 destroy_svc_ref(sr);
1121 sr = NULL;
1126 return sr;
1131 static void
1132 put_svc_ref(svc_ref *sr)
1134 if (sr) {
1135 LOCK(&conn_list_lock);
1137 sr->uses++;
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;
1147 conn_count++;
1149 SLIST_INSERT_HEAD(&conn_list, sr, entries);
1151 UNLOCK(&conn_list_lock);
1158 * determine if this is a call we should retry with a fresh
1159 * connection, for example if mdnsd went away and came back.
1161 static bool
1162 retry_query(svc_ref **sr, DNSServiceErrorType err)
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;
1178 return false;
1183 static void
1184 decref_res_conf(res_conf *rc)
1186 rc->refcount--;
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));
1194 free(rc->search_domains);
1197 free(rc);
1203 res_conf *
1204 get_res_conf(void)
1206 struct timespec last_change;
1207 res_state res;
1208 res_conf *rc;
1210 LOCK(&res_conf_lock);
1212 /* check if resolver config changed */
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);
1221 cur_res_conf = new_res_conf(res);
1222 if (cur_res_conf) {
1223 last_config = last_change;
1226 __res_put_state(res);
1229 rc = cur_res_conf;
1230 if (rc) {
1231 rc->refcount++;
1234 UNLOCK(&res_conf_lock);
1236 return rc;
1241 static void
1242 put_res_conf(res_conf *rc)
1244 LOCK(&res_conf_lock);
1246 decref_res_conf(rc);
1248 UNLOCK(&res_conf_lock);
1253 static res_conf *
1254 new_res_conf(res_state res)
1256 res_conf *rc;
1257 int count = 0;
1258 char **sd, *p;
1260 rc = calloc(sizeof(res_conf), 1);
1261 if (rc) {
1262 rc->refcount = 1;
1264 sd = res->dnsrch;
1265 while (*sd) {
1266 if (searchable_domain(*sd)) {
1267 count++;
1269 sd++;
1272 rc->search_domains = calloc(sizeof(char *), count + 1);
1273 if (!(rc->search_domains)) {
1274 decref_res_conf(rc);
1275 return NULL;
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;
1288 rc->no_search++;
1290 sd++;
1293 rc->timeout = res->retrans;
1295 if (svc_flags & kDNSServiceFlagsForceMulticast) {
1296 rc->ndots = 1;
1297 if (rc->timeout > 2) {
1298 rc->timeout = 2;
1300 } else {
1301 rc->ndots = res->ndots;
1305 return rc;
1310 static short
1311 get_timeout(void)
1313 short timeout = 5;
1314 res_conf *rc;
1316 rc = get_res_conf();
1317 if (rc) {
1318 timeout = rc->timeout;
1319 put_res_conf(rc);
1322 return timeout;