1 /* $NetBSD: nss_mdnsd.c,v 1.2 2009/10/26 00:46:19 tsarna Exp $ */
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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/
46 #include <sys/socket.h>
47 #include <sys/param.h>
48 #include <sys/queue.h>
50 #include <netinet/in.h>
51 #include <arpa/nameser.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;
70 struct timespec last_config
;
72 typedef struct svc_ref
{
73 SLIST_ENTRY(svc_ref
) entries
;
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)
105 #define UNUSED(a) (void)&a
107 #define UNUSED(a) a = a
110 #define MAXALIASES 35
113 typedef struct callback_ctx
{
117 typedef struct hostent_ctx
{
118 callback_ctx cb_ctx
; /* must come first */
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
;
127 typedef struct addrinfo_ctx
{
128 callback_ctx cb_ctx
; /* must come first */
129 struct addrinfo start
, *last
;
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
;
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
{
154 char buf
[MAXHOSTNAMELEN
];
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
*,
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,
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
},
215 nss_module_register(const char *source
, u_int
*nelems
,
216 nss_module_unregister_fn
*unreg
)
218 *nelems
= sizeof(mtab
) / sizeof(mtab
[0]);
223 if (!strcmp(source
, "multicast_dns")) {
224 svc_flags
= kDNSServiceFlagsForceMulticast
;
233 _mdns_getaddrinfo(void *cbrv
, void *cbdata
, va_list ap
)
235 const struct addrinfo
*pai
;
236 const char *name
, *sname
;
237 DNSServiceProtocol proto
;
247 name
= va_arg(ap
, char *);
248 pai
= va_arg(ap
, struct addrinfo
*);
250 switch (pai
->ai_family
) {
252 proto
= kDNSServiceProtocol_IPv6
| kDNSServiceProtocol_IPv4
;
256 proto
= kDNSServiceProtocol_IPv6
;
260 proto
= kDNSServiceProtocol_IPv4
;
264 h_errno
= NO_RECOVERY
;
270 h_errno
= NETDB_INTERNAL
;
274 if ((err
= search_init(&iter
, name
, &sname
)) != NS_SUCCESS
) {
279 _mdns_addrinfo_init(&ctx
, pai
);
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
);
292 if (err
== NS_SUCCESS
) {
293 *(struct addrinfo
**)cbrv
= _mdns_addrinfo_done(&ctx
);
302 _mdns_getaddrinfo_abs(const char *name
, DNSServiceProtocol proto
,
303 svc_ref
**sr
, addrinfo_ctx
*ctx
, short timeout
)
305 DNSServiceErrorType err
= kDNSServiceErr_ServiceNotRunning
;
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(
318 | kDNSServiceFlagsShareConnection
319 | kDNSServiceFlagsReturnIntermediates
,
320 kDNSServiceInterfaceIndexAny
,
327 retry
= retry_query(sr
, err
);
331 h_errno
= NETDB_INTERNAL
;
335 _mdns_eventloop(*sr
, (void *)ctx
, timeout
);
337 DNSServiceRefDeallocate(sdRef
);
339 if (ctx
->start
.ai_next
) {
342 h_errno
= HOST_NOT_FOUND
;
350 _mdns_gethtbyaddr(void *cbrv
, void *cbdata
, va_list ap
)
352 const unsigned char *addr
;
354 char qbuf
[NS_MAXDNAME
+ 1], *qp
, *ep
;
356 DNSServiceErrorType err
;
363 addr
= va_arg(ap
, unsigned char *);
364 addrlen
= va_arg(ap
, int);
365 af
= va_arg(ap
, int);
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
;
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));
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
;
392 ep
= qbuf
+ sizeof(qbuf
) - 1;
393 for (n
= IN6ADDRSZ
- 1; n
>= 0; n
--) {
394 advance
= snprintf(qp
, (size_t)(ep
- qp
), "%x.%x.",
396 ((unsigned int)addr
[n
] >> 4) & 0xf);
397 if (advance
> 0 && qp
+ advance
< ep
)
400 h_errno
= NETDB_INTERNAL
;
404 if (strlcat(qbuf
, "ip6.arpa", sizeof(qbuf
)) >= sizeof(qbuf
)) {
405 h_errno
= NETDB_INTERNAL
;
411 h_errno
= NO_RECOVERY
;
415 _mdns_hostent_init(&h_ctx
, af
, addrlen
);
416 _mdns_hostent_add_addr(&h_ctx
, addr
, addrlen
);
420 h_errno
= NETDB_INTERNAL
;
424 while (sr
&& retry
) {
425 /* We must always use a copy of the ref when using a shared
426 connection, per kDNSServiceFlagsShareConnection docs */
429 err
= DNSServiceQueryRecord(
432 | kDNSServiceFlagsShareConnection
433 | kDNSServiceFlagsReturnIntermediates
,
434 kDNSServiceInterfaceIndexAny
,
442 retry
= retry_query(&sr
, err
);
447 h_errno
= NETDB_INTERNAL
;
451 _mdns_eventloop(sr
, (void *)&h_ctx
, get_timeout());
453 DNSServiceRefDeallocate(sdRef
);
456 if (h_ctx
.naliases
) {
457 *(struct hostent
**)cbrv
= _mdns_hostent_done(&h_ctx
);
461 h_errno
= HOST_NOT_FOUND
;
469 _mdns_gethtbyname(void *cbrv
, void *cbdata
, va_list ap
)
471 int namelen
, af
, addrlen
, rrtype
, err
;
472 const char *name
, *sname
;
479 name
= va_arg(ap
, char *);
480 namelen
= va_arg(ap
, int);
481 af
= va_arg(ap
, int);
487 rrtype
= kDNSServiceType_A
;
492 rrtype
= kDNSServiceType_AAAA
;
497 h_errno
= NO_RECOVERY
;
503 h_errno
= NETDB_INTERNAL
;
507 if ((err
= search_init(&iter
, name
, &sname
)) != NS_SUCCESS
) {
512 _mdns_hostent_init(&h_ctx
, af
, addrlen
);
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
);
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
);
537 _mdns_gethtbyname_abs(const char *name
, int rrtype
, svc_ref
**sr
, short timeout
)
539 DNSServiceErrorType err
= kDNSServiceErr_ServiceNotRunning
;
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(
551 | kDNSServiceFlagsShareConnection
552 | kDNSServiceFlagsReturnIntermediates
,
553 kDNSServiceInterfaceIndexAny
,
561 retry
= retry_query(sr
, err
);
565 h_errno
= NETDB_INTERNAL
;
569 _mdns_eventloop(*sr
, (void *)&h_ctx
, timeout
);
571 DNSServiceRefDeallocate(sdRef
);
576 h_errno
= HOST_NOT_FOUND
;
584 _mdns_addrinfo_init(addrinfo_ctx
*ctx
, const struct addrinfo
*ai
)
586 ctx
->cb_ctx
.done
= false;
588 ctx
->start
.ai_next
= NULL
;
589 ctx
->start
.ai_canonname
= NULL
;
590 ctx
->last
= &(ctx
->start
);
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
;
613 p
= ctx
->start
.ai_next
;
616 if (p
->ai_next
->ai_family
== AF_INET6
) {
617 t
->ai_next
= p
->ai_next
;
619 p
->ai_next
= p
->ai_next
->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
;
636 _mdns_hostent_init(hostent_ctx
*ctx
, int af
, int addrlen
)
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]);
658 _mdns_hostent_add_host(hostent_ctx
*ctx
, const char *name
)
663 if (name
&& (len
= strlen(name
))
664 && (HCTX_BUFLEFT(ctx
) > len
) && (ctx
->naliases
< MAXALIASES
)) {
665 if (len
&& (name
[len
- 1] == '.')) {
669 /* skip dupe names */
671 if ((ctx
->host
.h_name
) && !strncmp(ctx
->host
.h_name
, name
, len
)
672 && (strlen(ctx
->host
.h_name
) == len
)) {
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
)) {
683 strncpy(ctx
->next
, name
, len
);
686 if (ctx
->naliases
== 0) {
687 ctx
->host
.h_name
= ctx
->next
;
689 ctx
->host
.h_aliases
[ctx
->naliases
- 1] = ctx
->next
;
692 ctx
->next
+= (len
+ 1);
694 } /* else silently ignore */
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
)
713 /* terminate array */
714 ctx
->host
.h_aliases
[ctx
->naliases
- 1] = NULL
;
715 ctx
->host
.h_addr_list
[ctx
->naddrs
] = NULL
;
726 DNSServiceFlags flags
,
727 uint32_t interfaceIndex
,
728 DNSServiceErrorType errorCode
,
729 const char *hostname
,
730 const struct sockaddr
*address
,
734 addrinfo_ctx
*ctx
= context
;
738 UNUSED(interfaceIndex
);
741 if (errorCode
== kDNSServiceErr_NoError
) {
742 if (! (flags
& kDNSServiceFlagsMoreComing
)) {
743 ctx
->cb_ctx
.done
= true;
746 ai
= allocaddrinfo((socklen_t
)(address
->sa_len
));
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
);
771 DNSServiceFlags flags
,
772 uint32_t interfaceIndex
,
773 DNSServiceErrorType errorCode
,
774 const char *fullname
,
782 hostent_ctx
*ctx
= (hostent_ctx
*)context
;
783 char buf
[NS_MAXDNAME
+1];
786 UNUSED(interfaceIndex
);
790 if (! (flags
& kDNSServiceFlagsMoreComing
)) {
791 ctx
->cb_ctx
.done
= true;
794 if (errorCode
== kDNSServiceErr_NoError
) {
796 case kDNSServiceType_PTR
:
797 if (!_mdns_rdata2name(rdata
, rdlen
, buf
, sizeof(buf
))) {
798 /* corrupt response -- skip */
802 _mdns_hostent_add_host(ctx
, buf
);
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
);
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
);
819 } else if (errorCode
== kDNSServiceErr_NoSuchRecord
) {
820 ctx
->cb_ctx
.done
= true;
827 _mdns_eventloop(svc_ref
*sr
, callback_ctx
*ctx
, short timeout
)
832 fd
= DNSServiceRefSockFD(sr
->sdRef
);
834 fds
.events
= POLLRDNORM
;
837 ret
= poll(&fds
, 1, timeout
* 1000);
839 DNSServiceProcessResult(sr
->sdRef
);
849 _mdns_rdata2name(const unsigned char *rdata
, uint16_t rdlen
, char *buf
, size_t buflen
)
854 /* illegal 0-size answer or not enough room for even "." */
855 if ((!rdlen
) || (rdlen
< 2)) {
859 buflen
--; /* reserve space for terminating NUL now */
861 /* special case empty as "." */
862 if ((rdlen
== 1) && (!*rdata
)) {
868 while (rdlen
&& *rdata
) {
869 /* label length byte */
870 l
= *rdata
++; rdlen
--;
873 /* compression or bitstrings -- shouldn't happen */
875 } else if (l
> buflen
) {
876 /* not enough space */
878 } else if (l
> rdlen
) {
879 /* label shouldn't be longer than remaining rdata */
882 /* empty label -- should be done */
884 /* but more left!? */
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
) {
901 *buf
++ = '.'; buflen
--;
905 /* we reserved space above, so we know we have space
906 to add this termination */
916 search_init(search_iter
*iter
, const char *name
, const char **first
)
918 const char *c
= name
, *cmp
;
919 int dots
= 0, enddot
= 0;
922 iter
->conf
= get_res_conf();
924 h_errno
= NETDB_INTERNAL
;
930 iter
->abs_first
= iter
->abs_last
= false;
931 iter
->next_search
= iter
->conf
->search_domains
;
943 if (svc_flags
& kDNSServiceFlagsForceMulticast
) {
945 iter
->next_search
= iter
->conf
->no_search
;
946 if ((dots
- enddot
) == 1) {
947 len
= strlen(iter
->name
);
948 cl
= strlen(".local") + enddot
;
950 cmp
= enddot
? ".local." : ".local";
951 c
= iter
->name
+ len
- cl
;
953 if (!strcasecmp(c
, cmp
)) {
954 iter
->abs_first
= true;
960 if (dots
>= iter
->conf
->ndots
) {
961 iter
->abs_first
= true;
963 iter
->abs_last
= true;
967 /* absolute; don't search */
968 iter
->next_search
= iter
->conf
->no_search
;
972 *first
= search_next(iter
);
975 h_errno
= HOST_NOT_FOUND
;
985 search_next(search_iter
*iter
)
987 const char *a
= NULL
;
991 if (iter
->abs_first
) {
992 iter
->abs_first
= false;
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
;
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 */
1022 if (iter
->abs_last
) {
1023 iter
->abs_last
= false;
1033 search_done(search_iter
*iter
)
1036 put_res_conf(iter
->conf
);
1044 * Is domain appropriate to be in the domain search list?
1045 * For mdnsd, take everything. For multicast_dns, only "local"
1049 searchable_domain(char *d
)
1051 if (!(svc_flags
& kDNSServiceFlagsForceMulticast
)) {
1055 if (!strcasecmp(d
, "local") || !strcasecmp(d
, "local.")) {
1065 destroy_svc_ref(svc_ref
*sr
)
1067 /* assumes not on conn list */
1070 DNSServiceRefDeallocate(sr
->sdRef
);
1082 LOCK(&conn_list_lock
);
1084 if (getpid() != my_pid
) {
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
);
1099 /* try to recycle a connection */
1100 sr
= SLIST_FIRST(&conn_list
);
1102 SLIST_REMOVE_HEAD(&conn_list
, entries
);
1107 UNLOCK(&conn_list_lock
);
1110 /* none available, we need a new one */
1112 sr
= calloc(sizeof(svc_ref
), 1);
1114 if (DNSServiceCreateConnection(&(sr
->sdRef
))) {
1119 if (fcntl(DNSServiceRefSockFD(sr
->sdRef
), F_SETFD
, FD_CLOEXEC
) < 0) {
1120 destroy_svc_ref(sr
);
1132 put_svc_ref(svc_ref
*sr
)
1135 LOCK(&conn_list_lock
);
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
);
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.
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 */
1168 /* this was an old socket, so kill it and get another */
1169 destroy_svc_ref(*sr
);
1170 *sr
= get_svc_ref();
1184 decref_res_conf(res_conf
*rc
)
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
);
1206 struct timespec last_change
;
1210 LOCK(&res_conf_lock
);
1212 /* check if resolver config changed */
1214 res
= __res_get_state();
1216 res_check(res
, &last_change
);
1217 if (timespeccmp(&last_config
, &last_change
, <)) {
1219 decref_res_conf(cur_res_conf
);
1221 cur_res_conf
= new_res_conf(res
);
1223 last_config
= last_change
;
1226 __res_put_state(res
);
1234 UNLOCK(&res_conf_lock
);
1242 put_res_conf(res_conf
*rc
)
1244 LOCK(&res_conf_lock
);
1246 decref_res_conf(rc
);
1248 UNLOCK(&res_conf_lock
);
1254 new_res_conf(res_state res
)
1260 rc
= calloc(sizeof(res_conf
), 1);
1266 if (searchable_domain(*sd
)) {
1272 rc
->search_domains
= calloc(sizeof(char *), count
+ 1);
1273 if (!(rc
->search_domains
)) {
1274 decref_res_conf(rc
);
1279 rc
->no_search
= rc
->search_domains
;
1281 if (searchable_domain(*sd
)) {
1282 *(rc
->no_search
) = p
= strdup(*sd
);
1284 decref_res_conf(rc
);
1293 rc
->timeout
= res
->retrans
;
1295 if (svc_flags
& kDNSServiceFlagsForceMulticast
) {
1297 if (rc
->timeout
> 2) {
1301 rc
->ndots
= res
->ndots
;
1316 rc
= get_res_conf();
1318 timeout
= rc
->timeout
;