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