2 * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
26 * Permission to use, copy, modify, and distribute this software for any
27 * purpose with or without fee is hereby granted, provided that the above
28 * copyright notice and this permission notice appear in all copies.
30 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
31 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
32 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
33 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
34 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
35 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
36 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
40 * Copyright (c) 1988, 1993
41 * The Regents of the University of California. All rights reserved.
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 4. Neither the name of the University nor the names of its contributors
52 * may be used to endorse or promote products derived from this software
53 * without specific prior written permission.
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
68 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
70 * Permission to use, copy, modify, and distribute this software for any
71 * purpose with or without fee is hereby granted, provided that the above
72 * copyright notice and this permission notice appear in all copies, and that
73 * the name of Digital Equipment Corporation not be used in advertising or
74 * publicity pertaining to distribution of the document or software without
75 * specific, written prior permission.
77 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
78 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
79 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
80 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
81 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
82 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
83 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
89 #include "si_module.h"
92 #include <arpa/inet.h>
93 #include <arpa/nameser.h>
94 #include <arpa/nameser_compat.h>
95 #include <libkern/OSAtomic.h>
96 #include <netinet/in.h>
108 #include <sys/event.h>
109 #include <sys/param.h>
110 #include <sys/time.h>
111 #include <sys/types.h>
112 #include <sys/socket.h>
118 #include <dns_util.h>
119 #include <TargetConditionals.h>
121 /* from dns_util.c */
122 #define DNS_MAX_RECEIVE_SIZE 65536
124 #define INET_NTOP_AF_INET_OFFSET 4
125 #define INET_NTOP_AF_INET6_OFFSET 8
127 #define IPPROTO_UNSPEC 0
129 static int _mdns_debug
= 0;
131 // mutex protects DNSServiceProcessResult and DNSServiceRefDeallocate
132 static pthread_mutex_t _mdns_mutex
= PTHREAD_MUTEX_INITIALIZER
;
135 // Options: timeout:n total_timeout attempts
137 /* _dns_config_token: notify token indicating dns config needs refresh
139 static int _dns_config_token
= -1;
144 dns_resolver_t
*primary
;
146 dns_resolver_t
**defaults
;
158 typedef struct mdns_srv_t mdns_srv_t
;
178 static uint32_t _mdns_generation
= 0;
179 DNSServiceRef _mdns_sdref
;
180 DNSServiceRef _mdns_old_sdref
;
182 static int _mdns_query_mDNSResponder(const char *name
, int class, int type
,
183 const char *interface
, DNSServiceFlags flags
,
184 uint8_t *answer
, uint32_t *anslen
,
185 mdns_reply_t
*reply
, uint32_t timeout
);
187 static int _mdns_resolver_get_option(dns_resolver_t
*resolver
, const char* option
);
188 static void _mdns_hostent_clear(mdns_hostent_t
*h
);
189 static void _mdns_reply_clear(mdns_reply_t
*r
);
191 static const char hexchar
[] = "0123456789abcdef";
193 #define BILLION 1000000000
195 /* length of a reverse DNS IPv6 address query name, e.g. "9.4.a.f.c.e.e.f.e.e.1.5.4.1.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa" */
196 #define IPv6_REVERSE_LEN 72
198 /* index of the trailing char that must be "8", "9", "A", "a", "b", or "B" */
199 #define IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR 58
201 /* index of low-order nibble of embedded scope id */
202 #define IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW 48
204 const static uint8_t hexval
[128] = {
205 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */
206 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */
207 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 - 47 */
208 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 48 - 63 */
209 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 - 79 */
210 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 95 */
211 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 - 111 */
212 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 112 - 127 */
216 * _mdns_create_search_list
217 * Creates a NULL terminated array of strings from the specied resolver's
218 * search list, or from the components of the specified resolver's domain
219 * if search list is empty.
220 * Free the list and elements with free(3) when done.
223 _mdns_create_search_list(dns_resolver_t
*resolver
)
229 if (resolver
== NULL
) return NULL
;
231 // return the search list if present
232 if (resolver
->n_search
> 0) {
233 list
= (char **)calloc(resolver
->n_search
+1, sizeof(char *));
234 if (list
== NULL
) return NULL
;
235 for (n
= 0; n
< resolver
->n_search
; ++n
) {
236 list
[n
] = strdup(resolver
->search
[n
]);
241 if (resolver
->domain
== NULL
) return NULL
;
242 domain
= strdup(resolver
->domain
);
243 if (domain
== NULL
) return NULL
;
247 for (p
= domain
; *p
!= '\0'; p
++) {
251 // trim trailing dots
252 for (p
--; (p
>= domain
) && (*p
== '.'); p
--) {
257 // make sure the resulting string is not empty
263 // dots are separators, so number of components is one larger
267 list
= (char **)calloc(n
+1, sizeof(char *));
268 if (list
== NULL
) return NULL
;
269 // first item in list is domain itself
272 // include parent domains with at least LOCALDOMAINPARTS components
274 while (n
> LOCALDOMAINPARTS
) {
275 // find next component
276 while ((*p
!= '.') && (*p
!= '\0')) p
++;
277 if (*p
== '\0') break;
281 list
[m
++] = strdup(p
);
286 /* _mdns_resolver_get_option
287 * Determines whether the specified option is present in the resolver.
290 _mdns_resolver_get_option(dns_resolver_t
*resolver
, const char* option
)
292 if (resolver
== NULL
) return 0;
293 int len
= strlen(option
);
294 char *options
= resolver
->options
;
295 if (options
== NULL
) return 0;
296 // look for "(^| )option( |:|$)"
297 char *ptr
= strstr(options
, option
);
299 if (ptr
== options
|| ptr
[-1] == ' ') {
300 if (ptr
[len
] == ' ' || ptr
[len
] == 0) {
302 } else if (ptr
[len
] == ':') {
303 return strtol(&ptr
[len
+1], NULL
, 10);
306 ptr
= strstr(ptr
, option
);
311 /* _mdns_compare_resolvers
312 * Compares two dns_resolver_t pointers by search order ascending.
315 _mdns_compare_resolvers(const void *a
, const void *b
)
317 dns_resolver_t
**x
= (dns_resolver_t
**)a
, **y
= (dns_resolver_t
**)b
;
318 return ((*x
)->search_order
- (*y
)->search_order
);
321 /* _mdns_create_default_resolvers_list
322 * Returns an array of dns_resolver_t containing only default resolvers.
323 * A resolver is a default resolver if it is the primary resolver or if it
324 * contains the "default" configuration option.
327 _mdns_config_init_default_resolvers(mdns_config_t
*config
)
329 uint32_t count
= config
->dns
->n_resolver
;
330 if (count
== 0) return;
331 config
->defaults
= calloc(count
, sizeof(dns_resolver_t
*));
332 if (config
->defaults
== NULL
) return;
334 if (config
->primary
) config
->defaults
[m
++] = config
->primary
;
335 // iterate the resolvers, add any default resolvers that are not
336 // already in the list.
337 for (i
= 0; i
< count
; ++i
) {
338 dns_resolver_t
*resolver
= config
->dns
->resolver
[i
];
339 if (_mdns_resolver_get_option(resolver
, "default")) {
341 for (j
= 0; j
< m
; ++j
) {
342 if (config
->defaults
[j
] == resolver
) {
348 config
->defaults
[m
++] = resolver
;
352 config
->n_defaults
= m
;
353 // sort list by search order ascending
354 qsort(config
->defaults
, config
->n_defaults
, sizeof(dns_resolver_t
*), _mdns_compare_resolvers
);
359 _mdns_print_dns_resolver(dns_resolver_t
*resolver
)
361 printf("resolver = {\n");
362 printf("\tdomain = %s\n", resolver
->domain
);
364 for (j
= 0; j
< resolver
->n_nameserver
; ++j
) {
366 char host
[255], serv
[255];
367 res
= getnameinfo(resolver
->nameserver
[j
], resolver
->nameserver
[j
]->sa_len
, host
, sizeof(host
), serv
, sizeof(serv
), NI_NUMERICHOST
| NI_NUMERICSERV
);
369 printf("\tnameserver[%d] = %s:%s\n", j
, host
, serv
);
371 printf("\tnameserver[%d] = %s\n", j
, gai_strerror(res
));
374 printf("\tport = %d\n", resolver
->port
);
375 for (j
= 0; j
< resolver
->n_search
; ++j
) {
376 printf("\tsearch[%d] = %s\n", j
, resolver
->search
[j
]);
379 printf("\tn_sortaddr = %d\n", resolver
->n_sortaddr
);
381 printf("\toptions = %s\n", resolver
->options
);
382 printf("\ttimeout = %d\n", resolver
->timeout
);
383 printf("\tsearch_order = %d\n", resolver
->search_order
);
388 _mdns_print_dns_config(dns_config_t
*config
)
391 dns_resolver_t
**list
= _mdns_create_sorted_resolver_list(config
);
392 dns_resolver_t
**ptr
= list
;
394 _mdns_print_dns_resolver(*ptr
);
401 _mdns_print_hostent(mdns_hostent_t
* h
)
403 if (h
== NULL
) return;
404 printf("hostent[%p] = {\n", h
);
405 printf("\thost = {\n");
406 printf("\t\th_name = %s\n", h
->host
.h_name
);
407 printf("\t\th_length = %d\n", h
->host
.h_length
);
408 printf("\t\th_addrtype = %d\n", h
->host
.h_addrtype
);
409 char **alias
= h
->host
.h_aliases
;
410 while (alias
&& *alias
) {
411 printf("\t\th_aliases = %s\n", *alias
++);
413 char **addr
= h
->host
.h_addr_list
;
414 while (addr
&& *addr
) {
415 printf("\t\th_addr_list = %x\n", ntohl(*(uint32_t*)*addr
++));
418 printf("\talias_count = %d\n", h
->alias_count
);
419 printf("\taddr_count = %d\n", h
->addr_count
);
425 /* _mdns_config_retain
426 * Retain the mdns configuration.
428 static mdns_config_t
*
429 _mdns_config_retain(mdns_config_t
*config
)
433 if (config
== NULL
) return NULL
;
434 rc
= OSAtomicIncrement32Barrier(&config
->rc
);
439 /* _mdns_config_release
440 * Releases the mdns configuration structure and
441 * frees the data if no references remain.
444 _mdns_config_release(mdns_config_t
*config
)
448 if (config
== NULL
) return;
449 rc
= OSAtomicDecrement32Barrier(&config
->rc
);
452 if (config
->dns
) dns_configuration_free(config
->dns
);
453 free(config
->defaults
);
454 char **p
= config
->search_list
;
455 while (p
&& *p
) { free(*p
++); }
456 free(config
->search_list
);
461 /* _mdns_copy_system_config
462 * Retrieves DNS configuration from SystemConfiguration.framework.
463 * Checks notify notification to determine whether configuration is in need
466 static mdns_config_t
*
467 _mdns_copy_system_config(void)
469 // first call needs refresh
470 static mdns_config_t
*current_config
;
471 mdns_config_t
*config
= NULL
;
475 pthread_mutex_lock(&_mdns_mutex
);
477 // check whether the global configuration has changed
478 if (_dns_config_token
== -1) {
479 res
= notify_register_check(dns_configuration_notify_key(), &_dns_config_token
);
480 if (res
!= NOTIFY_STATUS_OK
) _dns_config_token
= -1;
483 if (_dns_config_token
!= -1) {
484 res
= notify_check(_dns_config_token
, &refresh
);
485 if (res
!= NOTIFY_STATUS_OK
) refresh
= 1;
488 // return the current configuration if still valid
490 mdns_config_t
*config
= _mdns_config_retain(current_config
);
491 pthread_mutex_unlock(&_mdns_mutex
);
495 // need to allocate a new configuration
497 config
= calloc(1, sizeof(mdns_config_t
));
498 if (config
!= NULL
) config
->dns
= dns_configuration_copy();
500 // failed to get new config, return previous config
501 if (config
== NULL
|| config
->dns
== NULL
) {
503 config
= _mdns_config_retain(current_config
);
504 pthread_mutex_unlock(&_mdns_mutex
);
509 if (config
->dns
->n_resolver
> 0) {
510 // primary resolver is always index 0 and contains the
512 config
->primary
= config
->dns
->resolver
[0];
513 config
->search_list
= _mdns_create_search_list(config
->primary
);
514 _mdns_config_init_default_resolvers(config
);
516 config
->ndots
= _mdns_resolver_get_option(config
->primary
, "ndots");
518 // promote the new configuration to current
519 _mdns_config_release(current_config
);
520 current_config
= config
;
522 // return the new configuration
523 config
= _mdns_config_retain(config
);
524 pthread_mutex_unlock(&_mdns_mutex
);
528 /* _mdns_timeout_for_name
529 * Returns the appropriate timeout for the specified name based on the
530 * sum of the timeouts of all resolvers that match the name.
533 _mdns_timeout_for_name(mdns_config_t
*config
, const char *name
)
536 uint32_t timeout
= 0;
538 if (name
== NULL
) return 0;
540 // use strncasecmp to ignore a trailing '.' in name
541 int len
= strlen(name
);
542 if ((len
- 1) >= 0 && name
[len
-1] == '.') --len
;
544 const char *p
= name
;
546 uint32_t count
= config
->dns
->n_resolver
;
547 for (i
= 0; i
< count
; ++i
) {
548 dns_resolver_t
*resolver
= config
->dns
->resolver
[i
];
549 if (resolver
->domain
== NULL
) continue;
550 if (strncasecmp(resolver
->domain
, p
, len
) == 0) {
551 timeout
+= resolver
->timeout
;
554 // discard the current label
558 if (p
[-1] == '.') break;
564 /* _mdns_query_unqualified
565 * Performs a query for the name as an unqualified name (appends each
566 * of the default resolver's domains).
569 _mdns_query_unqualified(mdns_config_t
*config
, const char *name
, uint32_t class, uint32_t type
, const char *interface
, DNSServiceFlags flags
, uint8_t *buf
, uint32_t *len
, mdns_reply_t
*reply
)
573 for (i
= 0; i
< config
->n_defaults
; ++i
) {
574 dns_resolver_t
*resolver
= config
->defaults
[i
];
577 asprintf(&qname
, "%s.%s", name
, resolver
->domain
? resolver
->domain
: "");
578 res
= _mdns_query_mDNSResponder(qname
, class, type
, interface
, flags
, buf
, len
, reply
, resolver
->timeout
);
582 else _mdns_reply_clear(reply
);
587 /* _mdns_query_absolute
588 * Performs a query for the name as an absolute name (does not qualify with any
589 * additional domains).
592 _mdns_query_absolute(mdns_config_t
*config
, const char *name
, uint32_t class, uint32_t type
, const char *interface
, DNSServiceFlags flags
, uint32_t fqdn
, uint8_t *buf
, uint32_t *len
, mdns_reply_t
*reply
)
595 char *qname
= (char *)name
;
597 uint32_t timeout
= _mdns_timeout_for_name(config
, name
);
599 if (fqdn
== 0) asprintf(&qname
, "%s.", name
);
600 res
= _mdns_query_mDNSResponder(qname
, class, type
, interface
, flags
, buf
, len
, reply
, timeout
);
601 if (fqdn
== 0) free(qname
);
602 if (res
!= 0) _mdns_reply_clear(reply
);
607 _mdns_search(const char *name
, uint32_t class, uint32_t type
, const char *interface
, DNSServiceFlags flags
, uint32_t fqdn
, uint32_t recurse
, uint8_t *buf
, uint32_t *len
, mdns_reply_t
*reply
)
613 if (name
== NULL
) return -1;
615 mdns_config_t
*config
= _mdns_copy_system_config();
616 if (config
== NULL
) return -1;
618 // NDOTS is the threshold for trying a qualified name "as is"
619 ndots
= config
->ndots
;
620 if (ndots
== 0) ndots
= 1;
622 // count the dots, and remember position of the last one
625 for (i
= 0; name
[i
] != '\0'; i
++) {
626 if (name
[i
] == '.') {
628 dot
= (char *)(name
+ i
);
631 // FQDN has dot for last character
632 if (fqdn
== 0 && dot
!= NULL
&& dot
[1] == '\0') fqdn
= 1;
634 // if the name has at least ndots, try first as an absolute query.
635 // FQDN and PTR queries are always absolute.
636 if (n
>= ndots
|| fqdn
== 1 || type
== ns_t_ptr
) {
637 res
= _mdns_query_absolute(config
, name
, class, type
, interface
, flags
, fqdn
, buf
, len
, reply
);
639 _mdns_config_release(config
);
644 // stop if FQDN, PTR, or no recursion requested
645 if (fqdn
== 1 || type
== ns_t_ptr
|| recurse
== 0) {
646 _mdns_config_release(config
);
650 // Qualify the name with each of the search domains looking for a match.
651 char **search
= config
->search_list
;
652 if (search
!= NULL
) {
654 for (i
= 0; i
< MAXDNSRCH
&& search
[i
] != NULL
; ++i
) {
656 asprintf(&qname
, "%s.%s", name
, search
[i
]);
657 res
= _mdns_search(qname
, class, type
, interface
, flags
, 0, 0, buf
, len
, reply
);
662 // The name is not fully qualified and there is no search list.
663 // Try each default resolver, qualifying the name with that
664 // resolver's domain.
665 res
= _mdns_query_unqualified(config
, name
, class, type
, interface
, flags
, buf
, len
, reply
);
667 _mdns_config_release(config
);
672 _mdns_reverse_ipv4(const char *addr
)
681 if (addr
== NULL
) return NULL
;
683 memcpy(&(ab
.a
), addr
, 4);
685 asprintf(&p
, "%u.%u.%u.%u.in-addr.arpa.", ab
.b
[3], ab
.b
[2], ab
.b
[1], ab
.b
[0]);
690 _mdns_reverse_ipv6(const char *addr
)
696 if (addr
== NULL
) return NULL
;
700 for (i
= 0; i
< 16; i
++)
706 x
[j
--] = hexchar
[hi
];
708 x
[j
--] = hexchar
[lo
];
711 asprintf(&p
, "%sip6.arpa.", x
);
716 /* _mdns_canonicalize
717 * Canonicalize the domain name by converting to lower case and removing the
718 * trailing '.' if present.
721 _mdns_canonicalize(const char *s
)
725 if (s
== NULL
) return NULL
;
727 if (t
== NULL
) return NULL
;
728 if (t
[0] == '\0') return t
;
729 for (i
= 0; t
[i
] != '\0'; i
++) {
730 if (t
[i
] >= 'A' && t
[i
] <= 'Z') t
[i
] += 32;
732 if (t
[i
-1] == '.') t
[i
-1] = '\0';
736 /* _mdns_hostent_append_alias
737 * Appends an alias to the mdns_hostent_t structure.
740 _mdns_hostent_append_alias(mdns_hostent_t
*h
, const char *alias
)
744 if (h
== NULL
|| alias
== NULL
) return 0;
745 name
= _mdns_canonicalize(alias
);
746 if (name
== NULL
) return -1;
748 // don't add the name if it matches an existing name
749 if (h
->host
.h_name
&& string_equal(h
->host
.h_name
, name
)) {
753 for (i
= 0; i
< h
->alias_count
; ++i
) {
754 if (string_equal(h
->host
.h_aliases
[i
], name
)) {
760 // add the alias and NULL terminate the list
761 h
->host
.h_aliases
= (char **)reallocf(h
->host
.h_aliases
, (h
->alias_count
+2) * sizeof(char *));
762 if (h
->host
.h_aliases
== NULL
) {
767 h
->host
.h_aliases
[h
->alias_count
] = name
;
769 h
->host
.h_aliases
[h
->alias_count
] = NULL
;
773 /* _mdns_hostent_append_addr
774 * Appends an alias to the mdns_hostent_t structure.
777 _mdns_hostent_append_addr(mdns_hostent_t
*h
, const uint8_t *addr
, uint32_t len
)
779 if (h
== NULL
|| addr
== NULL
|| len
== 0) return 0;
781 // copy the address buffer
782 uint8_t *buf
= malloc(len
);
783 if (buf
== NULL
) return -1;
784 memcpy(buf
, addr
, len
);
786 // add the address and NULL terminate the list
787 h
->host
.h_addr_list
= (char **)reallocf(h
->host
.h_addr_list
, (h
->addr_count
+2) * sizeof(char *));
788 if (h
->host
.h_addr_list
== NULL
) {
792 h
->host
.h_addr_list
[h
->addr_count
] = (char*)buf
;
794 h
->host
.h_addr_list
[h
->addr_count
] = NULL
;
799 _mdns_hostent_clear(mdns_hostent_t
*h
)
801 if (h
== NULL
) return;
802 free(h
->host
.h_name
);
803 h
->host
.h_name
= NULL
;
805 char **aliases
= h
->host
.h_aliases
;
806 while (aliases
&& *aliases
) {
809 free(h
->host
.h_aliases
);
810 h
->host
.h_aliases
= NULL
;
813 char **addrs
= h
->host
.h_addr_list
;
814 while (addrs
&& *addrs
) {
817 free(h
->host
.h_addr_list
);
818 h
->host
.h_addr_list
= NULL
;
824 _mdns_reply_clear(mdns_reply_t
*r
)
826 if (r
== NULL
) return;
828 _mdns_hostent_clear(r
->h4
);
829 _mdns_hostent_clear(r
->h6
);
830 mdns_srv_t
*srv
= r
->srv
;
833 mdns_srv_t
*next
= srv
->next
;
834 free(srv
->srv
.target
);
841 _mdns_hostbyname(si_mod_t
*si
, const char *name
, int af
, const char *interface
, uint32_t *err
)
846 si_item_t
*out
= NULL
;
849 DNSServiceFlags flags
= 0;
851 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
854 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
858 memset(&h
, 0, sizeof(h
));
859 memset(&reply
, 0, sizeof(reply
));
869 h
.host
.h_length
= 16;
873 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
876 h
.host
.h_addrtype
= af
;
878 status
= _mdns_search(name
, ns_c_in
, type
, interface
, flags
, 0, 1, NULL
, NULL
, &reply
);
879 if (status
!= 0 || h
.addr_count
== 0) {
880 _mdns_reply_clear(&reply
);
881 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
885 bb
= reply
.ttl
+ time(NULL
);
889 out
= (si_item_t
*)LI_ils_create("L4488s*44a", (unsigned long)si
, CATEGORY_HOST_IPV4
, 1, bb
, 0LL, h
.host
.h_name
, h
.host
.h_aliases
, h
.host
.h_addrtype
, h
.host
.h_length
, h
.host
.h_addr_list
);
892 out
= (si_item_t
*)LI_ils_create("L4488s*44c", (unsigned long)si
, CATEGORY_HOST_IPV6
, 1, bb
, 0LL, h
.host
.h_name
, h
.host
.h_aliases
, h
.host
.h_addrtype
, h
.host
.h_length
, h
.host
.h_addr_list
);
896 _mdns_reply_clear(&reply
);
898 if (out
== NULL
&& err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
904 _mdns_hostbyaddr(si_mod_t
*si
, const void *addr
, int af
, const char *interface
, uint32_t *err
)
913 DNSServiceFlags flags
= 0;
915 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
917 if (addr
== NULL
|| si
== NULL
) {
918 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
922 memset(&h
, 0, sizeof(h
));
923 memset(&reply
, 0, sizeof(reply
));
929 name
= _mdns_reverse_ipv4(addr
);
930 cat
= CATEGORY_HOST_IPV4
;
933 h
.host
.h_length
= 16;
935 name
= _mdns_reverse_ipv6(addr
);
936 cat
= CATEGORY_HOST_IPV6
;
939 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
942 h
.host
.h_addrtype
= af
;
944 status
= _mdns_search(name
, ns_c_in
, ns_t_ptr
, interface
, flags
, 0, 1, NULL
, NULL
, &reply
);
947 _mdns_reply_clear(&reply
);
948 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
952 status
= _mdns_hostent_append_addr(&h
, addr
, h
.host
.h_length
);
954 _mdns_hostent_clear(&h
);
955 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
959 bb
= reply
.ttl
+ time(NULL
);
960 out
= (si_item_t
*)LI_ils_create("L4488s*44a", (unsigned long)si
, cat
, 1, bb
, 0LL, h
.host
.h_name
, h
.host
.h_aliases
, h
.host
.h_addrtype
, h
.host
.h_length
, h
.host
.h_addr_list
);
962 _mdns_hostent_clear(&h
);
964 if (out
== NULL
&& err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
969 _mdns_addrinfo(si_mod_t
*si
, const void *node
, const void *serv
, uint32_t family
, uint32_t socktype
, uint32_t proto
, uint32_t flags
, const char *interface
, uint32_t *err
)
973 if (family
== AF_INET6
) wantv4
= 0;
974 else if (family
== AF_INET
) wantv6
= 0;
975 else if (family
!= AF_UNSPEC
) return NULL
;
985 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
987 si_list_t
*out
= NULL
;
989 memset(&h4
, 0, sizeof(h4
));
990 memset(&h6
, 0, sizeof(h6
));
991 memset(&reply
, 0, sizeof(reply
));
993 h4
.host
.h_addrtype
= AF_INET
;
994 h4
.host
.h_length
= 4;
995 h6
.host
.h_addrtype
= AF_INET6
;
996 h6
.host
.h_length
= 16;
998 if (wantv4
&& wantv6
) {
1002 } else if (wantv4
) {
1005 } else if (wantv6
) {
1009 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1014 if ((flags
& AI_NUMERICSERV
) != 0) {
1015 port
= *(uint16_t *)serv
;
1017 if (_gai_serv_to_port(serv
, proto
, &port
) != 0) {
1018 if (err
) *err
= SI_STATUS_EAI_NONAME
;
1024 if ((flags
& AI_NUMERICHOST
) != 0) {
1026 struct in_addr
*p4
= NULL
;
1027 struct in6_addr
*p6
= NULL
;
1028 if (family
== AF_INET
) {
1030 memcpy(p4
, node
, sizeof(a4
));
1031 } else if (family
== AF_INET6
) {
1033 memcpy(p6
, node
, sizeof(a6
));
1035 out
= si_addrinfo_list(si
, socktype
, proto
, p4
, p6
, port
, 0, cname
, cname
);
1037 DNSServiceFlags dns_flags
= 0;
1038 if (flags
& AI_ADDRCONFIG
) {
1039 dns_flags
|= kDNSServiceFlagsSuppressUnusable
;
1042 res
= _mdns_search(node
, ns_c_in
, type
, interface
, dns_flags
, 0, 1, NULL
, NULL
, &reply
);
1043 if (res
== 0 && (h4
.addr_count
> 0 || h6
.addr_count
> 0)) {
1044 out
= si_addrinfo_list_from_hostent(si
, socktype
, proto
,
1046 (wantv4
? &h4
.host
: NULL
),
1047 (wantv6
? &h6
.host
: NULL
));
1048 } else if (err
!= NULL
) {
1049 *err
= SI_STATUS_EAI_NONAME
;
1051 _mdns_reply_clear(&reply
);
1057 _mdns_srv_byname(si_mod_t
* si
, const char *qname
, const char *interface
, uint32_t *err
)
1059 si_list_t
*out
= NULL
;
1063 const uint64_t unused
= 0;
1064 DNSServiceFlags flags
= 0;
1066 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
1068 memset(&reply
, 0, sizeof(reply
));
1069 res
= _mdns_search(qname
, ns_c_in
, ns_t_srv
, interface
, flags
, 0, 1, NULL
, NULL
, &reply
);
1074 item
= (si_item_t
*)LI_ils_create("L4488222s", (unsigned long)si
, CATEGORY_SRV
, 1, unused
, unused
, srv
->srv
.priority
, srv
->srv
.weight
, srv
->srv
.port
, srv
->srv
.target
);
1075 out
= si_list_add(out
, item
);
1076 si_item_release(item
);
1080 _mdns_reply_clear(&reply
);
1085 * We support dns_async_start / cancel / handle_reply using dns_item_call
1088 _mdns_item_call(si_mod_t
*si
, int call
, const char *name
, const char *ignored
, const char *interface
, uint32_t class, uint32_t type
, uint32_t *err
)
1091 uint8_t buf
[DNS_MAX_RECEIVE_SIZE
];
1092 uint32_t len
= sizeof(buf
);
1098 DNSServiceFlags flags
= 0;
1100 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
1103 case SI_CALL_DNS_QUERY
:
1106 case SI_CALL_DNS_SEARCH
:
1109 if (err
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1115 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1119 memset(&h4
, 0, sizeof(h4
));
1120 memset(&h6
, 0, sizeof(h6
));
1121 memset(&reply
, 0, sizeof(reply
));
1123 h4
.host
.h_addrtype
= AF_INET
;
1124 h4
.host
.h_length
= 4;
1125 h6
.host
.h_addrtype
= AF_INET6
;
1126 h6
.host
.h_length
= 16;
1130 res
= _mdns_search(name
, class, type
, interface
, flags
, norecurse
, 1, buf
, &len
, &reply
);
1131 if (res
!= 0 || len
<= 0 || len
> DNS_MAX_RECEIVE_SIZE
) {
1132 _mdns_reply_clear(&reply
);
1133 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
1137 struct sockaddr_in6 from
;
1138 uint32_t fromlen
= sizeof(from
);
1139 memset(&from
, 0, fromlen
);
1140 from
.sin6_len
= fromlen
;
1141 from
.sin6_family
= AF_INET6
;
1142 from
.sin6_addr
.__u6_addr
.__u6_addr8
[15] = 1;
1143 if (reply
.ifnum
!= 0) {
1144 from
.sin6_addr
.__u6_addr
.__u6_addr16
[0] = htons(0xfe80);
1145 from
.sin6_scope_id
= reply
.ifnum
;
1148 out
= (si_item_t
*)LI_ils_create("L4488@@", (unsigned long)si
, CATEGORY_DNSPACKET
, 1, 0LL, 0LL, len
, buf
, fromlen
, &from
);
1149 if (out
== NULL
&& err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1151 _mdns_reply_clear(&reply
);
1157 _mdns_is_valid(si_mod_t
*si
, si_item_t
*item
)
1163 _mdns_close(si_mod_t
*si
)
1165 if (_dns_config_token
!= -1) notify_cancel(_dns_config_token
);
1170 _mdns_atfork_prepare(void)
1172 // acquire our lock so that we know all other threads have "drained"
1173 pthread_mutex_lock(&_mdns_mutex
);
1177 _mdns_atfork_parent(void)
1179 // parent can simply resume
1180 pthread_mutex_unlock(&_mdns_mutex
);
1184 _mdns_atfork_child(void)
1186 // child needs to force re-initialization
1187 _mdns_old_sdref
= _mdns_sdref
; // for later deallocation
1189 _dns_config_token
= -1;
1190 pthread_mutex_unlock(&_mdns_mutex
);
1193 __private_extern__ si_mod_t
*
1194 si_module_static_mdns(void)
1196 si_mod_t
*out
= (si_mod_t
*)calloc(1, sizeof(si_mod_t
));
1197 char *outname
= strdup("mdns");
1199 if ((out
== NULL
) || (outname
== NULL
))
1206 out
->name
= outname
;
1209 out
->private = NULL
;
1211 out
->sim_close
= _mdns_close
;
1212 out
->sim_is_valid
= _mdns_is_valid
;
1213 out
->sim_host_byname
= _mdns_hostbyname
;
1214 out
->sim_host_byaddr
= _mdns_hostbyaddr
;
1215 out
->sim_item_call
= _mdns_item_call
;
1216 out
->sim_addrinfo
= _mdns_addrinfo
;
1217 out
->sim_srv_byname
= _mdns_srv_byname
;
1221 res
= notify_register_check(dns_configuration_notify_key(), &_dns_config_token
);
1222 if (res
!= NOTIFY_STATUS_OK
) _dns_config_token
= -1;
1224 pthread_atfork(_mdns_atfork_prepare
, _mdns_atfork_parent
, _mdns_atfork_child
);
1226 _mdns_debug
= getenv("RES_DEBUG") != NULL
;
1232 * _mdns_parse_domain_name
1233 * Combine DNS labels to form a string.
1234 * DNSService API does not return compressed names.
1237 _mdns_parse_domain_name(const uint8_t *data
, uint32_t datalen
)
1241 uint32_t domainlen
= 0;
1242 char *domain
= NULL
;
1244 if ((data
== NULL
) || (datalen
== 0)) return NULL
;
1246 // i: index into input data
1247 // j: index into output string
1248 while (datalen
-- > 0) {
1250 domainlen
+= (len
+ 1);
1251 domain
= reallocf(domain
, domainlen
);
1252 if (domain
== NULL
) return NULL
;
1253 if (len
== 0) break; // DNS root (NUL)
1255 domain
[j
++] = datalen
? '.' : '\0';
1258 while ((len
-- > 0) && (datalen
--)) {
1259 if (data
[i
] == '.') {
1260 // special case: escape the '.' with a '\'
1261 domain
= reallocf(domain
, ++domainlen
);
1262 if (domain
== NULL
) return NULL
;
1265 domain
[j
++] = data
[i
++];
1274 * _mdns_pack_domain_name
1275 * Format the string as packed DNS labels.
1276 * Only used for one string at a time, therefore no need for compression.
1279 _mdns_pack_domain_name(const char* str
, uint8_t *buf
, size_t buflen
) {
1281 while (i
< buflen
) {
1283 // calculate length to next '.' or '\0'
1284 char *dot
= strchr(str
, '.');
1285 if (dot
== NULL
) dot
= strchr(str
, '\0');
1287 if (len
> NS_MAXLABEL
) return -1;
1288 // copy data for label
1293 // skip past '.', break if '\0'
1294 if (*str
++ == '\0') break;
1296 if (i
>= buflen
) return -1;
1302 _is_rev_link_local(const char *name
)
1306 if (name
== NULL
) return 0;
1309 if (len
== 0) return 0;
1311 /* check for trailing '.' */
1312 if (name
[len
- 1] == '.') len
--;
1314 if (len
!= IPv6_REVERSE_LEN
) return 0;
1316 i
= IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR
;
1317 if ((name
[i
] != '8') && (name
[i
] != '9') && (name
[i
] != 'A') && (name
[i
] != 'a') && (name
[i
] != 'B') && (name
[i
] != 'b')) return 0;
1319 i
= IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR
+ 1;
1320 if (strncasecmp(name
+ i
, ".e.f.ip6.arpa", 13)) return 0;
1322 for (i
= 0; i
< IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR
; i
+= 2)
1324 if (name
[i
] < '0') return 0;
1325 if ((name
[i
] > '9') && (name
[i
] < 'A')) return 0;
1326 if ((name
[i
] > 'F') && (name
[i
] < 'a')) return 0;
1327 if (name
[i
] > 'f') return 0;
1328 if (name
[i
+ 1] != '.') return 0;
1334 /* _mdns_ipv6_extract_scope_id
1335 * If the input string is a link local IPv6 address with an encoded scope id,
1336 * the scope id is extracted and a new string is constructed with the scope id removed.
1339 _mdns_ipv6_extract_scope_id(const char *name
, uint32_t *out_ifnum
)
1346 if (out_ifnum
!= NULL
) *out_ifnum
= 0;
1348 /* examine the address, extract the scope id if present */
1349 if ((name
!= NULL
) && (_is_rev_link_local(name
)))
1351 /* _is_rev_link_local rejects chars > 127 so it's safe to index into hexval */
1352 i
= IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW
;
1353 nibble
= hexval
[(uint32_t)name
[i
]];
1357 nibble
= hexval
[(uint32_t)name
[i
]];
1358 iface
+= (nibble
<< 4);
1361 nibble
= hexval
[(uint32_t)name
[i
]];
1362 iface
+= (nibble
<< 8);
1365 nibble
= hexval
[(uint32_t)name
[i
]];
1366 iface
+= (nibble
<< 12);
1370 qname
= strdup(name
);
1371 if (qname
== NULL
) return NULL
;
1373 i
= IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW
;
1379 if (out_ifnum
) *out_ifnum
= iface
;
1387 _mdns_make_query(const char* name
, int class, int type
, uint8_t *buf
, uint32_t buflen
)
1391 if (buf
== NULL
|| buflen
< (NS_HFIXEDSZ
+ NS_QFIXEDSZ
)) return -1;
1392 memset(buf
, 0, NS_HFIXEDSZ
);
1393 HEADER
*hp
= (HEADER
*)buf
;
1396 hp
->id
= arc4random();
1398 hp
->opcode
= ns_o_query
;
1400 hp
->rcode
= ns_r_noerror
;
1401 hp
->qdcount
= htons(1);
1403 int n
= _mdns_pack_domain_name(name
, &buf
[len
], buflen
- len
);
1404 if (n
< 0) return -1;
1409 memcpy(&buf
[len
], &word
, sizeof(word
));
1410 len
+= sizeof(word
);
1411 word
= htons(class);
1412 memcpy(&buf
[len
], &word
, sizeof(word
));
1413 len
+= sizeof(word
);
1418 mdns_reply_t
*reply
;
1419 mdns_hostent_t
*host
;
1420 uint8_t *answer
; // DNS packet buffer
1421 size_t anslen
; // DNS packet buffer current length
1422 size_t ansmaxlen
; // DNS packet buffer maximum length
1423 int type
; // type of query: A, AAAA, PTR, SRV...
1424 uint16_t last_type
; // last type received
1427 DNSServiceFlags flags
;
1428 DNSServiceErrorType error
;
1429 int kq
; // kqueue to notify when callback received
1430 } mdns_query_context_t
;
1433 _mdns_query_callback(DNSServiceRef
, DNSServiceFlags
, uint32_t, DNSServiceErrorType
, const char *, uint16_t, uint16_t, uint16_t, const void *, uint32_t, void *);
1435 /* _mdns_query_start
1436 * initializes the context and starts a DNS-SD query.
1438 static DNSServiceErrorType
1439 _mdns_query_start(mdns_query_context_t
*ctx
, mdns_reply_t
*reply
, uint8_t *answer
, uint32_t *anslen
, const char* name
, int class, int type
, const char *interface
, DNSServiceFlags flags
, int kq
)
1441 DNSServiceErrorType status
;
1443 flags
|= kDNSServiceFlagsShareConnection
;
1444 flags
|= kDNSServiceFlagsReturnIntermediates
;
1446 memset(ctx
, 0, sizeof(mdns_query_context_t
));
1448 if (answer
&& anslen
) {
1449 // build a dummy DNS header to return to the caller
1450 ctx
->answer
= answer
;
1451 ctx
->ansmaxlen
= *anslen
;
1452 ctx
->anslen
= _mdns_make_query(name
, class, type
, answer
, ctx
->ansmaxlen
);
1453 if (ctx
->anslen
<= 0) return -1;
1457 ctx
->sd
= _mdns_sdref
;
1458 ctx
->sd_gen
= _mdns_generation
;
1462 if (type
== ns_t_a
) ctx
->host
= reply
->h4
;
1463 else if (type
== ns_t_aaaa
) ctx
->host
= reply
->h6
;
1464 else if (type
== ns_t_ptr
&& reply
->h4
) ctx
->host
= reply
->h4
;
1465 else if (type
== ns_t_ptr
&& reply
->h6
) ctx
->host
= reply
->h6
;
1466 else if (type
!= ns_t_srv
&& type
!= ns_t_cname
) abort();
1470 char *qname
= _mdns_ipv6_extract_scope_id(name
, &iface
);
1471 if (qname
== NULL
) qname
= (char *)name
;
1473 if (interface
!= NULL
)
1475 /* get interface number from name */
1476 int iface2
= if_nametoindex(interface
);
1478 /* balk if interface name lookup failed */
1479 if (iface2
== 0) return -1;
1481 /* balk if scope id is set AND interface is given AND they don't match */
1482 if ((iface
!= 0) && (iface2
!= 0) && (iface
!= iface2
)) return -1;
1483 if (iface2
!= 0) iface
= iface2
;
1486 if (_mdns_debug
) printf(";; mdns query %s %d %d\n", qname
, type
, class);
1487 status
= DNSServiceQueryRecord(&ctx
->sd
, flags
, iface
, qname
, type
, class, _mdns_query_callback
, ctx
);
1488 if (qname
!= name
) free(qname
);
1492 /* _mdns_query_is_complete
1493 * Determines whether the specified query has sufficient information to be
1494 * considered complete.
1497 _mdns_query_is_complete(mdns_query_context_t
*ctx
)
1499 if (ctx
== NULL
) return 1;
1500 //if (ctx->flags & kDNSServiceFlagsMoreComing) return 0;
1501 if (ctx
->last_type
!= ctx
->type
) return 0;
1502 switch (ctx
->type
) {
1505 if (ctx
->host
!= NULL
&& ctx
->host
->addr_count
> 0) {
1510 if (ctx
->host
!= NULL
&& ctx
->host
->host
.h_name
!= NULL
) {
1515 if (ctx
->reply
!= NULL
&& ctx
->reply
->srv
!= NULL
) {
1525 /* _mdns_query_clear
1526 * Clear out the temporary fields of the context, and clear any result
1527 * structures that are incomplete. Retrns 1 if the query was complete.
1530 _mdns_query_clear(mdns_query_context_t
*ctx
)
1532 int complete
= _mdns_query_is_complete(ctx
);
1533 if (ctx
== NULL
) return complete
;
1535 /* only dealloc this DNSServiceRef if the "main" _mdns_sdref has not been deallocated */
1536 if (ctx
->sd
!= NULL
&& ctx
->sd_gen
== _mdns_generation
) {
1537 DNSServiceRefDeallocate(ctx
->sd
);
1546 _mdns_hostent_clear(ctx
->host
);
1553 _mdns_query_callback(DNSServiceRef sdRef
, DNSServiceFlags flags
, uint32_t ifIndex
, DNSServiceErrorType errorCode
, const char *fullname
, uint16_t rrtype
, uint16_t rrclass
, uint16_t rdlen
, const void *rdata
, uint32_t ttl
, void *ctx
)
1555 mdns_query_context_t
*context
;
1558 context
= (mdns_query_context_t
*)ctx
;
1560 context
->flags
= flags
;
1561 context
->error
= errorCode
;
1562 context
->last_type
= rrtype
;
1564 if (errorCode
!= kDNSServiceErr_NoError
) {
1565 if (_mdns_debug
) printf(";; [%s %hu %hu]: error %d\n", fullname
, rrtype
, rrclass
, errorCode
);
1569 // embed the scope ID into link-local IPv6 addresses
1570 if (rrtype
== ns_t_aaaa
&& rdlen
== sizeof(struct in6_addr
) &&
1571 IN6_IS_ADDR_LINKLOCAL((struct in6_addr
*)rdata
)) {
1572 memcpy(&a6
, rdata
, rdlen
);
1573 a6
.__u6_addr
.__u6_addr16
[1] = htons(ifIndex
);
1577 if (context
->reply
) {
1580 mdns_reply_t
*reply
= context
->reply
;
1582 if (reply
->ifnum
== 0) {
1583 reply
->ifnum
= ifIndex
;
1586 _mdns_hostent_append_alias(context
->host
, fullname
);
1587 if (reply
->ttl
== 0 || ttl
< reply
->ttl
) reply
->ttl
= ttl
;
1592 if (((rrtype
== ns_t_a
&& context
->host
->host
.h_addrtype
== AF_INET
) ||
1593 (rrtype
== ns_t_aaaa
&& context
->host
->host
.h_addrtype
== AF_INET6
)) &&
1594 rdlen
>= context
->host
->host
.h_length
) {
1595 if (context
->host
->host
.h_name
== NULL
) {
1597 mdns_hostent_t
*h
= context
->host
;
1598 char *h_name
= _mdns_canonicalize(fullname
);
1599 context
->host
->host
.h_name
= h_name
;
1601 // 6863416 remove h_name from h_aliases
1602 for (i
= 0; i
< h
->alias_count
; ++i
) {
1603 if (h_name
== NULL
) break;
1604 if (string_equal(h
->host
.h_aliases
[i
], h_name
)) {
1605 // includes trailing NULL pointer
1606 int sz
= sizeof(char *) * (h
->alias_count
- i
);
1607 free(h
->host
.h_aliases
[i
]);
1608 memmove(&h
->host
.h_aliases
[i
], &h
->host
.h_aliases
[i
+1], sz
);
1609 h
->alias_count
-= 1;
1614 _mdns_hostent_append_addr(context
->host
, rdata
, context
->host
->host
.h_length
);
1620 name
= _mdns_parse_domain_name(rdata
, rdlen
);
1621 if (!name
) malformed
= 1;
1622 _mdns_hostent_append_alias(context
->host
, name
);
1626 name
= _mdns_parse_domain_name(rdata
, rdlen
);
1627 if (!name
) malformed
= 1;
1628 if (context
->host
&& context
->host
->host
.h_name
== NULL
) {
1629 context
->host
->host
.h_name
= _mdns_canonicalize(name
);
1631 _mdns_hostent_append_alias(context
->host
, name
);
1635 mdns_rr_srv_t
*p
= (mdns_rr_srv_t
*)rdata
;
1636 mdns_srv_t
*srv
= calloc(1, sizeof(mdns_srv_t
));
1637 if (srv
== NULL
) break;
1638 if (rdlen
< sizeof(mdns_rr_srv_t
)) {
1642 srv
->srv
.priority
= ntohs(p
->priority
);
1643 srv
->srv
.weight
= ntohs(p
->weight
);
1644 srv
->srv
.port
= ntohs(p
->port
);
1645 srv
->srv
.target
= _mdns_parse_domain_name(&p
->target
[0], rdlen
- 3*sizeof(uint16_t));
1646 if (srv
->srv
.target
== NULL
) {
1650 // append to the end of the list
1651 if (reply
->srv
== NULL
) {
1654 mdns_srv_t
*iter
= reply
->srv
;
1655 while (iter
->next
) iter
= iter
->next
;
1661 malformed
= _mdns_debug
;
1664 if (malformed
&& _mdns_debug
) {
1665 printf(";; [%s %hu %hu]: malformed reply\n", fullname
, rrtype
, rrclass
);
1670 if (context
->answer
) {
1674 size_t buflen
= context
->ansmaxlen
- context
->anslen
;
1675 if (buflen
< NS_HFIXEDSZ
)
1677 if (_mdns_debug
) printf(";; [%s %hu %hu]: malformed reply\n", fullname
, rrtype
, rrclass
);
1681 cp
= context
->answer
+ context
->anslen
;
1683 n
= _mdns_pack_domain_name(fullname
, cp
, buflen
);
1685 if (_mdns_debug
) printf(";; [%s %hu %hu]: name mismatch\n", fullname
, rrtype
, rrclass
);
1689 // check that there is enough space in the buffer for the
1690 // resource name (n), the resource record data (rdlen) and
1691 // the resource record header (10).
1692 if (buflen
< n
+ rdlen
+ 10) {
1693 if (_mdns_debug
) printf(";; [%s %hu %hu]: insufficient buffer space for reply\n", fullname
, rrtype
, rrclass
);
1703 word
= htons(rrtype
);
1704 memcpy(cp
, &word
, sizeof(word
));
1707 word
= htons(rrclass
);
1708 memcpy(cp
, &word
, sizeof(word
));
1711 longword
= htonl(ttl
);
1712 memcpy(cp
, &longword
, sizeof(longword
));
1713 cp
+= sizeof(longword
);
1715 word
= htons(rdlen
);
1716 memcpy(cp
, &word
, sizeof(word
));
1719 memcpy(cp
, rdata
, rdlen
);
1722 ans
= (HEADER
*)context
->answer
;
1723 ans
->ancount
= htons(ntohs(ans
->ancount
) + 1);
1725 context
->anslen
= (size_t)(cp
- context
->answer
);
1728 if (_mdns_debug
) printf(";; [%s %hu %hu]\n", fullname
, rrtype
, rrclass
);
1731 // Ping the waiting thread in case this callback was invoked on another
1732 if (context
->kq
!= -1) {
1734 EV_SET(&ev
, 1, EVFILT_USER
, 0, NOTE_TRIGGER
, 0, 0);
1735 int res
= kevent(context
->kq
, &ev
, 1, NULL
, 0, NULL
);
1736 if (res
&& _mdns_debug
) printf(";; kevent EV_TRIGGER: %s\n", strerror(errno
));
1741 _mdns_now(struct timespec
*now
) {
1743 gettimeofday(&tv
, NULL
);
1744 now
->tv_sec
= tv
.tv_sec
;
1745 now
->tv_nsec
= tv
.tv_usec
* 1000;
1749 _mdns_add_time(struct timespec
*sum
, const struct timespec
*a
, const struct timespec
*b
)
1751 sum
->tv_sec
= a
->tv_sec
+ b
->tv_sec
;
1752 sum
->tv_nsec
= a
->tv_nsec
+ b
->tv_nsec
;
1753 if (sum
->tv_nsec
> 1000000000) {
1754 sum
->tv_sec
+= (sum
->tv_nsec
/ 1000000000);
1755 sum
->tv_nsec
%= 1000000000;
1759 // calculate a deadline from the current time based on the desired timeout
1761 _mdns_deadline(struct timespec
*deadline
, const struct timespec
*delta
)
1763 struct timespec now
;
1765 _mdns_add_time(deadline
, &now
, delta
);
1769 _mdns_sub_time(struct timespec
*delta
, const struct timespec
*a
, const struct timespec
*b
)
1771 delta
->tv_sec
= a
->tv_sec
- b
->tv_sec
;
1772 delta
->tv_nsec
= a
->tv_nsec
- b
->tv_nsec
;
1773 if (delta
->tv_nsec
< 0) {
1774 delta
->tv_nsec
+= 1000000000;
1779 // calculate a timeout remaining before the given deadline
1781 _mdns_timeout(struct timespec
*timeout
, const struct timespec
*deadline
)
1783 struct timespec now
;
1785 _mdns_sub_time(timeout
, deadline
, &now
);
1789 _mdns_query_mDNSResponder(const char *name
, int class, int type
, const char *interface
, DNSServiceFlags flags
, uint8_t *answer
, uint32_t *anslen
, mdns_reply_t
*reply
, uint32_t timeout_sec
)
1791 DNSServiceErrorType err
= 0;
1792 int kq
, n
, wait
= 1;
1794 struct timespec start
, finish
, delta
, timeout
;
1796 int i
, complete
, got_response
= 0;
1799 // 2 for A and AAAA parallel queries
1801 mdns_query_context_t ctx
[2];
1803 if (name
== NULL
) return -1;
1805 #if TARGET_OS_EMBEDDED
1806 // log a warning for queries from the main thread
1807 if (pthread_main_np()) asl_log(NULL
, NULL
, ASL_LEVEL_WARNING
, "Warning: Libinfo call to mDNSResponder on main thread");
1808 #endif // TARGET_OS_EMBEDDED
1811 // The kevent(2) API timeout parameter is used to enforce the total
1812 // timeout of the DNS query. Each iteraion recalculates the relative
1813 // timeout based on the desired end time (total timeout from origin).
1815 // In order to workaround some DNS configurations that do not return
1816 // responses for AAAA queries, parallel queries modify the total
1817 // timeout upon receipt of the first response. The new total timeout is
1818 // set to an effective value of 2N where N is the time taken to receive
1819 // the A response (the original total timeout is preserved if 2N would
1820 // have exceeded it). However, since mDNSResponder caches values, a
1821 // minimum value of 50ms for N is enforced in order to give some time
1822 // for the receipt of a AAAA response.
1824 // determine the maximum time to wait for a result
1825 if (timeout_sec
== 0) timeout_sec
= RES_MAXRETRANS
;
1826 delta
.tv_sec
= timeout_sec
;
1828 _mdns_deadline(&finish
, &delta
);
1832 for (i
= 0; i
< 2; ++i
) {
1833 memset(&ctx
[i
], 0 , sizeof(mdns_query_context_t
));
1836 // set up the kqueue
1838 EV_SET(&ev
, 1, EVFILT_USER
, EV_ADD
| EV_CLEAR
, 0, 0, 0);
1839 n
= kevent(kq
, &ev
, 1, NULL
, 0, NULL
);
1840 if (n
!= 0) wait
= 0;
1845 pthread_mutex_lock(&_mdns_mutex
);
1846 // clear any stale contexts
1847 for (i
= 0; i
< n_ctx
; ++i
) {
1848 _mdns_query_clear(&ctx
[i
]);
1852 if (_mdns_sdref
== NULL
) {
1853 if (_mdns_old_sdref
!= NULL
) {
1855 DNSServiceRefDeallocate(_mdns_old_sdref
);
1856 _mdns_old_sdref
= NULL
;
1858 // (re)initialize the shared connection
1859 err
= DNSServiceCreateConnection(&_mdns_sdref
);
1862 pthread_mutex_unlock(&_mdns_mutex
);
1867 // issue (or reissue) the queries
1868 // unspecified type: do parallel A and AAAA
1870 err
= _mdns_query_start(&ctx
[n_ctx
++], reply
,
1873 (type
== 0) ? ns_t_a
: type
, interface
, flags
, kq
);
1875 if (err
== 0 && type
== 0) {
1876 err
= _mdns_query_start(&ctx
[n_ctx
++], reply
,
1878 name
, class, ns_t_aaaa
, interface
, flags
, kq
);
1880 if (err
&& _mdns_debug
) printf(";; initialization error %d\n", err
);
1881 // try to reinitialize
1882 if (err
== kDNSServiceErr_Unknown
||
1883 err
== kDNSServiceErr_ServiceNotRunning
||
1884 err
== kDNSServiceErr_BadReference
) {
1887 DNSServiceRefDeallocate(_mdns_sdref
);
1892 pthread_mutex_unlock(&_mdns_mutex
);
1894 } else if (err
!= 0) {
1895 pthread_mutex_unlock(&_mdns_mutex
);
1899 // (re)register the fd with kqueue
1900 int fd
= DNSServiceRefSockFD(_mdns_sdref
);
1901 EV_SET(&ev
, fd
, EVFILT_READ
, EV_ADD
, 0, 0, 0);
1902 n
= kevent(kq
, &ev
, 1, NULL
, 0, NULL
);
1903 pthread_mutex_unlock(&_mdns_mutex
);
1904 if (err
!= 0 || n
!= 0) break;
1907 if (_mdns_debug
) printf(";; kevent timeout %ld.%ld\n", timeout
.tv_sec
, timeout
.tv_nsec
);
1908 n
= kevent(kq
, NULL
, 0, &ev
, 1, &timeout
);
1909 if (n
< 0 && errno
!= EINTR
) {
1914 pthread_mutex_lock(&_mdns_mutex
);
1915 // DNSServiceProcessResult() is a blocking API
1916 // confirm that there is still data on the socket
1917 const struct timespec notimeout
= { 0, 0 };
1918 int m
= kevent(kq
, NULL
, 0, &ev
, 1, ¬imeout
);
1919 if (_mdns_sdref
== NULL
) {
1921 } else if (m
> 0 && ev
.filter
== EVFILT_READ
) {
1922 err
= DNSServiceProcessResult(_mdns_sdref
);
1923 if (err
== kDNSServiceErr_ServiceNotRunning
||
1924 err
== kDNSServiceErr_BadReference
) {
1925 if (_mdns_debug
) printf(";; DNSServiceProcessResult status %d\n", err
);
1927 // re-initialize the shared connection
1929 DNSServiceRefDeallocate(_mdns_sdref
);
1935 // Check if all queries are complete (including errors)
1937 for (i
= 0; i
< n_ctx
; ++i
) {
1938 if (_mdns_query_is_complete(&ctx
[i
]) || ctx
[i
].error
!= 0) {
1939 if (ctx
[i
].type
== ns_t_a
) {
1946 pthread_mutex_unlock(&_mdns_mutex
);
1949 if (_mdns_debug
) printf(";; DNSServiceProcessResult status %d\n", err
);
1951 } else if (complete
== 1) {
1952 if (_mdns_debug
) printf(";; done\n");
1954 } else if (got_response
== 1) {
1955 // got A, adjust deadline for AAAA
1956 struct timespec now
, tn
, ms100
;
1958 // delta = now - start
1960 _mdns_sub_time(&delta
, &now
, &start
);
1963 _mdns_add_time(&tn
, &delta
, &delta
);
1965 // delta = tn + 100ms
1967 ms100
.tv_nsec
= 100000000;
1968 _mdns_add_time(&delta
, &tn
, &ms100
);
1970 // check that delta doesn't exceed our total timeout
1971 _mdns_sub_time(&tn
, &timeout
, &delta
);
1972 if (tn
.tv_sec
>= 0) {
1973 if (_mdns_debug
) printf(";; new timeout (waiting for AAAA) %ld.%ld\n", delta
.tv_sec
, delta
.tv_nsec
);
1974 _mdns_deadline(&finish
, &delta
);
1978 // calculate remaining timeout
1979 _mdns_timeout(&timeout
, &finish
);
1981 // check for time remaining
1982 if (timeout
.tv_sec
< 0) {
1983 if (_mdns_debug
) printf(";; timeout\n");
1989 pthread_mutex_lock(&_mdns_mutex
);
1990 for (i
= 0; i
< n_ctx
; ++i
) {
1991 if (err
== 0) err
= ctx
[i
].error
;
1992 // Only clears hostents if result is incomplete.
1993 complete
= _mdns_query_clear(&ctx
[i
]) || complete
;
1995 pthread_mutex_unlock(&_mdns_mutex
);
1996 // Everything should be done with the kq by now.
1999 // Return error if everything is incomplete
2000 if (complete
== 0) {
2004 if (anslen
) *anslen
= ctx
[0].anslen
;