xref: /netbsd-src/external/apache2/mDNSResponder/nss/nss_mdnsd.c (revision ca453df649ce9db45b64d73678ba06cbccf9aa11)
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