2 * Copyright (c) 2008-2018 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>
109 #include <sys/event.h>
110 #include <sys/param.h>
111 #include <sys/time.h>
112 #include <sys/types.h>
113 #include <sys/socket.h>
119 #include <dns_util.h>
120 #include <TargetConditionals.h>
121 #include <dispatch/dispatch.h>
123 /* from dns_util.c */
124 #define DNS_MAX_RECEIVE_SIZE 65536
126 #define INET_NTOP_AF_INET_OFFSET 4
127 #define INET_NTOP_AF_INET6_OFFSET 8
129 #define IPPROTO_UNSPEC 0
133 #define SHORT_AAAA_EXTRA 2
134 #define MEDIUM_AAAA_EXTRA 5
135 #define LONG_AAAA_EXTRA 10
137 #define MDNS_DEBUG_FILE "/etc/.mdns_debug"
138 #define MDNS_DEBUG_STDOUT 0x00000001
139 #define MDNS_DEBUG_STDERR 0x00000002
140 #define MDNS_DEBUG_ASL 0x00000004
141 #define MDNS_DEBUG_OUT 0x00000007
143 static int _mdns_debug
= 0;
145 /* mutex protects DNSServiceProcessResult and DNSServiceRefDeallocate */
146 static pthread_mutex_t _mdns_mutex
= PTHREAD_MUTEX_INITIALIZER
;
156 typedef struct mdns_srv_t mdns_srv_t
;
179 static uint32_t _mdns_generation
= 0;
180 static DNSServiceRef _mdns_sdref
;
181 static DNSServiceRef _mdns_old_sdref
;
183 static void _mdns_hostent_clear(mdns_hostent_t
*h
);
184 static void _mdns_reply_clear(mdns_reply_t
*r
);
185 static int _mdns_search(const char *name
, int class, int type
, const char *interface
, DNSServiceFlags flags
, uint8_t *answer
, uint32_t *anslen
, mdns_reply_t
*reply
);
186 static int _mdns_search_ex(const char *name
, int class, int type
, uint32_t ifindex
, DNSServiceFlags flags
, uint8_t *answer
, uint32_t *anslen
, mdns_reply_t
*reply
);
188 static const char hexchar
[] = "0123456789abcdef";
190 #define BILLION 1000000000
192 /* 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" */
193 #define IPv6_REVERSE_LEN 72
195 /* index of the trailing char that must be "8", "9", "A", "a", "b", or "B" */
196 #define IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR 58
198 /* index of low-order nibble of embedded scope id */
199 #define IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW 48
201 const static uint8_t hexval
[128] = {
202 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */
203 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */
204 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 - 47 */
205 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 48 - 63 */
206 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 - 79 */
207 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 95 */
208 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 - 111 */
209 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 112 - 127 */
213 _mdns_debug_message(const char *str
, ...)
218 if (str
== NULL
) return;
219 if ((_mdns_debug
& MDNS_DEBUG_OUT
) == 0) return;
222 vasprintf(&out
, str
, v
);
223 if (out
== NULL
) return;
225 if (_mdns_debug
& MDNS_DEBUG_STDOUT
) fprintf(stdout
, "%s", out
);
226 if (_mdns_debug
& MDNS_DEBUG_STDERR
) fprintf(stderr
, "%s", out
);
227 if (_mdns_debug
& MDNS_DEBUG_ASL
) os_log(OS_LOG_DEFAULT
, "%s", out
);
234 _mdns_reverse_ipv4(const uint8_t *addr
)
243 if (addr
== NULL
) return NULL
;
245 memcpy(&(ab
.a
), addr
, 4);
247 asprintf(&p
, "%u.%u.%u.%u.in-addr.arpa.", ab
.b
[3], ab
.b
[2], ab
.b
[1], ab
.b
[0]);
252 _mdns_reverse_ipv6(const uint8_t *addr
)
258 if (addr
== NULL
) return NULL
;
263 for (i
= 0; i
< 16; i
++)
269 x
[j
--] = hexchar
[hi
];
271 x
[j
--] = hexchar
[lo
];
274 asprintf(&p
, "%sip6.arpa.", x
);
281 * Canonicalize the domain name by converting to lower case and removing the
282 * trailing '.' if present.
285 _mdns_canonicalize(const char *s
)
290 if (s
== NULL
) return NULL
;
293 if (t
== NULL
) return NULL
;
295 if (t
[0] == '\0') return t
;
297 for (i
= 0; t
[i
] != '\0'; i
++)
299 if (t
[i
] >= 'A' && t
[i
] <= 'Z') t
[i
] += 32;
302 if (t
[i
-1] == '.') t
[i
-1] = '\0';
308 * _mdns_hostent_append_alias
309 * Appends an alias to the mdns_hostent_t structure.
312 _mdns_hostent_append_alias(mdns_hostent_t
*h
, const char *alias
)
317 _mdns_debug_message(";; _mdns_hostent_append_alias(%p, %s)\n", h
, alias
);
318 if ((h
== NULL
) || (alias
== NULL
)) return 0;
320 name
= _mdns_canonicalize(alias
);
321 if (name
== NULL
) return -1;
323 /* don't add the name if it matches an existing name */
324 if ((h
->host
.h_name
!= NULL
) && string_equal(h
->host
.h_name
, name
))
330 for (i
= 0; i
< h
->alias_count
; ++i
)
332 if (string_equal(h
->host
.h_aliases
[i
], name
))
339 /* add the alias and NULL terminate the list if it is new */
340 h
->host
.h_aliases
= (char **)reallocf(h
->host
.h_aliases
, (h
->alias_count
+ 2) * sizeof(char *));
341 if (h
->host
.h_aliases
== NULL
)
348 h
->host
.h_aliases
[h
->alias_count
] = name
;
351 h
->host
.h_aliases
[h
->alias_count
] = NULL
;
356 * _mdns_hostent_append_addr
357 * Appends an alias to the mdns_hostent_t structure.
360 _mdns_hostent_append_addr(mdns_hostent_t
*h
, const uint8_t *addr
, uint32_t len
)
362 _mdns_debug_message(";; _mdns_hostent_append_addr(%p, %p, %u)\n", h
, addr
, len
);
363 if ((h
== NULL
) || (addr
== NULL
) || (len
== 0)) return 0;
365 /* copy the address buffer */
366 uint8_t *buf
= malloc(len
);
367 if (buf
== NULL
) return -1;
369 memcpy(buf
, addr
, len
);
371 /* add the address and NULL terminate the list if it is new */
372 h
->host
.h_addr_list
= (char **)reallocf(h
->host
.h_addr_list
, (h
->addr_count
+ 2) * sizeof(char *));
374 if (h
->host
.h_addr_list
== NULL
)
381 h
->host
.h_addr_list
[h
->addr_count
] = (char*)buf
;
384 h
->host
.h_addr_list
[h
->addr_count
] = NULL
;
389 _mdns_hostent_clear(mdns_hostent_t
*h
)
391 if (h
== NULL
) return;
393 free(h
->host
.h_name
);
394 h
->host
.h_name
= NULL
;
396 char **aliases
= h
->host
.h_aliases
;
397 while (aliases
&& *aliases
) free(*aliases
++);
399 free(h
->host
.h_aliases
);
400 h
->host
.h_aliases
= NULL
;
403 char **addrs
= h
->host
.h_addr_list
;
404 while (addrs
&& *addrs
) free(*addrs
++);
406 free(h
->host
.h_addr_list
);
407 h
->host
.h_addr_list
= NULL
;
412 _mdns_reply_clear(mdns_reply_t
*r
)
414 if (r
== NULL
) return;
417 _mdns_hostent_clear(r
->h4
);
418 _mdns_hostent_clear(r
->h6
);
419 mdns_srv_t
*srv
= r
->srv
;
424 mdns_srv_t
*next
= srv
->next
;
425 free(srv
->srv
.target
);
432 mdns_hostbyname(si_mod_t
*si
, const char *name
, int af
, const char *interface
, uint32_t *err
)
437 si_item_t
*out
= NULL
;
440 DNSServiceFlags flags
= 0;
442 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
444 if ((name
== NULL
) || (si
== NULL
))
446 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
450 memset(&h
, 0, sizeof(h
));
451 memset(&reply
, 0, sizeof(reply
));
462 h
.host
.h_length
= 16;
466 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
470 _mdns_debug_message(";; mdns_hostbyname %s type %u class %u\n", name
, type
, ns_c_in
);
472 h
.host
.h_addrtype
= af
;
474 status
= _mdns_search(name
, ns_c_in
, type
, interface
, flags
, NULL
, NULL
, &reply
);
475 if ((status
!= 0) || (h
.addr_count
== 0))
477 _mdns_reply_clear(&reply
);
478 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
482 bb
= reply
.ttl
+ time(NULL
);
487 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
);
490 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
);
494 _mdns_reply_clear(&reply
);
496 if ((out
== NULL
) && (err
!= NULL
)) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
501 static bool _is_v4addr_ifaddr(const uint8_t addrBytes
[4])
504 struct ifaddrs
*ifaddrs
;
505 const struct ifaddrs
*ifa
;
506 const struct sockaddr_in
*sa4
;
510 err
= getifaddrs(&ifaddrs
);
511 if (err
!= 0) goto exit
;
513 memcpy(&addr
, addrBytes
, 4);
514 for (ifa
= ifaddrs
; ifa
; ifa
= ifa
->ifa_next
)
516 if ((ifa
->ifa_flags
& IFF_UP
) == 0) continue;
517 if (!ifa
->ifa_addr
|| (ifa
->ifa_addr
->sa_family
!= AF_INET
)) continue;
518 sa4
= (const struct sockaddr_in
*)ifa
->ifa_addr
;
519 if (sa4
->sin_addr
.s_addr
== addr
)
525 freeifaddrs(ifaddrs
);
531 static bool _is_v6addr_ifaddr(const uint8_t addrBytes
[16], uint32_t ifindex
)
534 struct ifaddrs
*ifaddrs
;
535 const struct ifaddrs
*ifa
;
536 const struct sockaddr_in6
*sa6
;
539 err
= getifaddrs(&ifaddrs
);
540 if (err
!= 0) goto exit
;
542 for (ifa
= ifaddrs
; ifa
; ifa
= ifa
->ifa_next
)
544 if ((ifa
->ifa_flags
& IFF_UP
) == 0) continue;
545 if (!ifa
->ifa_addr
|| (ifa
->ifa_addr
->sa_family
!= AF_INET6
)) continue;
546 sa6
= (const struct sockaddr_in6
*)ifa
->ifa_addr
;
547 if ((sa6
->sin6_scope_id
== ifindex
) && (memcmp(&sa6
->sin6_addr
.s6_addr
, addrBytes
, 16) == 0))
553 freeifaddrs(ifaddrs
);
560 mdns_hostbyaddr(si_mod_t
*si
, const void *addr
, int af
, const char *interface
, uint32_t *err
)
569 DNSServiceFlags flags
= 0;
570 uint32_t ifindex
= 0;
572 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
574 if ((addr
== NULL
) || (si
== NULL
))
576 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
580 memset(&h
, 0, sizeof(h
));
581 memset(&reply
, 0, sizeof(reply
));
587 const uint8_t * const target_addr
= (const uint8_t *)addr
;
589 // If no interface is specified; the IPv4 address is a link-local address, i.e., it's in 169.254.0.0/16; and the
590 // IPv4 address belongs to one of the local host's interfaces, then use kDNSServiceInterfaceIndexLocalOnly as
591 // the interface index to pass to DNSServiceQueryRecord(). This is done to get a response directly from
592 // mDNSResponder's authoritative resource records as opposed to issuing an mDNS query, which does not work if
593 // the interface is currently not participating in mDNS. See <rdar://problem/40702045> for more details.
595 if (!interface
&& (target_addr
[0] == 169) && (target_addr
[1] == 254) && _is_v4addr_ifaddr(target_addr
))
597 ifindex
= kDNSServiceInterfaceIndexLocalOnly
;
601 name
= _mdns_reverse_ipv4(target_addr
);
602 cat
= CATEGORY_HOST_IPV4
;
607 const uint8_t *target_addr
= (const uint8_t *)addr
;
608 uint8_t fixed_addr
[16];
610 // If no interface is specified; the IPv6 address is a link-local address, i.e., it's in fe80::/10; and the IPv6
611 // address belongs to one of the local host's interfaces, then use kDNSServiceInterfaceIndexLocalOnly as the
612 // interface index to pass to DNSServiceQueryRecord(). For rationale, see the comment for the AF_INET case.
614 if (!interface
&& (target_addr
[0] == 0xFE) && ((target_addr
[1] & 0xC0) == 0x80))
616 // Note: si_nameinfo() embeds the scope ID (interface index) in bytes 2 and 3 in network byte order.
618 const uint32_t embedded_index
= (target_addr
[2] << 8) | target_addr
[3];
620 memcpy(fixed_addr
, target_addr
, 16);
624 if ((embedded_index
!= 0) && _is_v6addr_ifaddr(fixed_addr
, embedded_index
))
626 target_addr
= fixed_addr
;
627 ifindex
= kDNSServiceInterfaceIndexLocalOnly
;
631 ifindex
= embedded_index
;
635 h
.host
.h_length
= 16;
637 name
= _mdns_reverse_ipv6(target_addr
);
638 cat
= CATEGORY_HOST_IPV6
;
642 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
646 h
.host
.h_addrtype
= af
;
648 _mdns_debug_message(";; mdns_hostbyaddr %s type %u class %u\n", name
, ns_t_ptr
, ns_c_in
);
652 status
= _mdns_search(name
, ns_c_in
, ns_t_ptr
, interface
, flags
, NULL
, NULL
, &reply
);
656 status
= _mdns_search_ex(name
, ns_c_in
, ns_t_ptr
, ifindex
, flags
, NULL
, NULL
, &reply
);
661 _mdns_reply_clear(&reply
);
662 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
666 status
= _mdns_hostent_append_addr(&h
, addr
, h
.host
.h_length
);
669 _mdns_hostent_clear(&h
);
670 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
674 bb
= reply
.ttl
+ time(NULL
);
679 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
);
682 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
);
686 _mdns_hostent_clear(&h
);
688 if ((out
== NULL
) && (err
!= NULL
)) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
693 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
)
707 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
711 if (family
== AF_INET6
)
713 if ((flags
& AI_V4MAPPED
) == 0) wantv4
= false;
715 else if (family
== AF_INET
)
719 else if (family
!= AF_UNSPEC
)
724 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
726 _mdns_debug_message(";; mdns_addrinfo node %s serv %s\n", (const char *)node
, (const char *)serv
);
728 si_list_t
*out
= NULL
;
730 memset(&h4
, 0, sizeof(h4
));
731 memset(&h6
, 0, sizeof(h6
));
732 memset(&reply
, 0, sizeof(reply
));
734 h4
.host
.h_addrtype
= AF_INET
;
735 h4
.host
.h_length
= 4;
736 h6
.host
.h_addrtype
= AF_INET6
;
737 h6
.host
.h_length
= 16;
739 if (wantv4
&& wantv6
)
757 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
762 if ((flags
& AI_NUMERICSERV
) != 0)
764 if (serv
== NULL
) port
= 0;
765 else port
= *(uint16_t *)serv
;
769 if (_gai_serv_to_port(serv
, proto
, &port
) != 0)
771 if (err
) *err
= SI_STATUS_EAI_NONAME
;
777 if ((flags
& AI_NUMERICHOST
) != 0)
780 struct in_addr
*p4
= NULL
;
781 struct in6_addr
*p6
= NULL
;
783 if (node
== NULL
) return NULL
;
785 if (family
== AF_INET
)
788 memcpy(p4
, node
, sizeof(a4
));
790 else if (family
== AF_INET6
)
793 memcpy(p6
, node
, sizeof(a6
));
796 out
= si_addrinfo_list(si
, flags
, socktype
, proto
, p4
, p6
, port
, 0, cname
, cname
);
801 DNSServiceFlags dns_flags
= 0;
803 if (node
== NULL
) return NULL
;
805 if (flags
& AI_ADDRCONFIG
)
807 dns_flags
|= kDNSServiceFlagsSuppressUnusable
;
810 res
= _mdns_search(node
, ns_c_in
, type
, interface
, dns_flags
, NULL
, NULL
, &reply
);
811 if ((res
== 0) && ((h4
.addr_count
> 0) || (h6
.addr_count
> 0)))
813 out
= si_addrinfo_list_from_hostent(si
, flags
, socktype
, proto
, port
, 0, (wantv4 ?
&h4
.host
: NULL
), (wantv6 ?
&h6
.host
: NULL
));
815 else if (err
!= NULL
)
817 *err
= SI_STATUS_EAI_NONAME
;
820 _mdns_reply_clear(&reply
);
827 mdns_srv_byname(si_mod_t
* si
, const char *qname
, const char *interface
, uint32_t *err
)
829 si_list_t
*out
= NULL
;
833 const uint64_t unused
= 0;
834 DNSServiceFlags flags
= 0;
838 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
842 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
844 _mdns_debug_message(";; mdns_srv_byname %s type %u class %u\n", qname
, ns_t_srv
, ns_c_in
);
846 memset(&reply
, 0, sizeof(reply
));
847 res
= _mdns_search(qname
, ns_c_in
, ns_t_srv
, interface
, flags
, NULL
, NULL
, &reply
);
854 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
);
855 out
= si_list_add(out
, item
);
856 si_item_release(item
);
861 _mdns_reply_clear(&reply
);
866 * We support dns_async_start / cancel / handle_reply using dns_item_call
869 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
)
872 uint8_t buf
[DNS_MAX_RECEIVE_SIZE
];
873 uint32_t len
= sizeof(buf
);
878 DNSServiceFlags flags
= 0;
880 if ((si
== NULL
) || (name
== NULL
))
882 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
886 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
888 _mdns_debug_message(";; mdns_item_call %s type %u class %u\n", name
, type
, class);
890 memset(&h4
, 0, sizeof(h4
));
891 memset(&h6
, 0, sizeof(h6
));
892 memset(&reply
, 0, sizeof(reply
));
894 h4
.host
.h_addrtype
= AF_INET
;
895 h4
.host
.h_length
= 4;
896 h6
.host
.h_addrtype
= AF_INET6
;
897 h6
.host
.h_length
= 16;
901 res
= _mdns_search(name
, class, type
, interface
, flags
, buf
, &len
, &reply
);
902 if ((res
!= 0) || (len
<= 0) || (len
> DNS_MAX_RECEIVE_SIZE
))
904 _mdns_reply_clear(&reply
);
905 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
909 struct sockaddr_in6 from
;
910 uint32_t fromlen
= sizeof(from
);
911 memset(&from
, 0, fromlen
);
912 from
.sin6_len
= fromlen
;
913 from
.sin6_family
= AF_INET6
;
914 from
.sin6_addr
.__u6_addr
.__u6_addr8
[15] = 1;
916 if (reply
.ifnum
!= 0)
918 from
.sin6_addr
.__u6_addr
.__u6_addr16
[0] = htons(0xfe80);
919 from
.sin6_scope_id
= reply
.ifnum
;
922 out
= (si_item_t
*)LI_ils_create("L4488@@", (unsigned long)si
, CATEGORY_DNSPACKET
, 1, 0LL, 0LL, len
, buf
, fromlen
, &from
);
923 if ((out
== NULL
) && (err
!= NULL
)) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
925 _mdns_reply_clear(&reply
);
931 mdns_is_valid(si_mod_t
*si
, si_item_t
*item
)
937 mdns_close(si_mod_t
*si
)
942 _mdns_atfork_prepare(void)
944 /* acquire our lock so that we know all other threads have "drained" */
945 pthread_mutex_lock(&_mdns_mutex
);
949 _mdns_atfork_parent(void)
951 /* parent can simply resume */
952 pthread_mutex_unlock(&_mdns_mutex
);
956 _mdns_atfork_child(void)
958 /* child needs to force re-initialization */
959 _mdns_old_sdref
= _mdns_sdref
; // for later deallocation
961 pthread_mutex_unlock(&_mdns_mutex
);
967 pthread_atfork(_mdns_atfork_prepare
, _mdns_atfork_parent
, _mdns_atfork_child
);
969 if (getenv("RES_DEBUG") != NULL
) _mdns_debug
|= MDNS_DEBUG_STDOUT
;
970 int fd
= open(MDNS_DEBUG_FILE
, O_RDONLY
, 0);
977 memset(c
, 0, sizeof(c
));
980 for (i
= 0; i
< n
; i
++)
982 if ((c
[i
] == 'o') || (c
[i
] == 'O')) _mdns_debug
|= MDNS_DEBUG_STDOUT
;
983 if ((c
[i
] == 'e') || (c
[i
] == 'E')) _mdns_debug
|= MDNS_DEBUG_STDERR
;
984 if ((c
[i
] == 'a') || (c
[i
] == 'A')) _mdns_debug
|= MDNS_DEBUG_ASL
;
990 si_module_static_mdns(void)
992 static const struct si_mod_vtable_s mdns_vtable
=
994 .sim_close
= &mdns_close
,
995 .sim_is_valid
= &mdns_is_valid
,
996 .sim_host_byname
= &mdns_hostbyname
,
997 .sim_host_byaddr
= &mdns_hostbyaddr
,
998 .sim_item_call
= &mdns_item_call
,
999 .sim_addrinfo
= &mdns_addrinfo
,
1000 .sim_srv_byname
= &mdns_srv_byname
,
1003 static si_mod_t si
=
1007 .flags
= SI_MOD_FLAG_STATIC
,
1010 .vtable
= &mdns_vtable
,
1013 static dispatch_once_t once
;
1015 dispatch_once(&once
, ^{
1016 si
.name
= strdup("mdns");
1020 return (si_mod_t
*)&si
;
1024 * _mdns_parse_domain_name
1025 * Combine DNS labels to form a string.
1026 * DNSService API does not return compressed names.
1029 _mdns_parse_domain_name(const uint8_t *data
, uint32_t datalen
)
1033 uint32_t domainlen
= 0;
1034 char *domain
= NULL
;
1036 if ((data
== NULL
) || (datalen
== 0)) return NULL
;
1039 * i: index into input data
1040 * j: index into output string
1042 while (datalen
-- > 0)
1045 domainlen
+= (len
+ 1);
1046 domain
= reallocf(domain
, domainlen
);
1048 if (domain
== NULL
) return NULL
;
1050 if (len
== 0) break; // DNS root (NUL)
1054 domain
[j
++] = datalen ?
'.' : '\0';
1057 while ((len
-- > 0) && (0 != datalen
--))
1061 /* special case: escape the '.' with a '\' */
1062 domain
= reallocf(domain
, ++domainlen
);
1063 if (domain
== NULL
) return NULL
;
1068 domain
[j
++] = data
[i
++];
1078 * _mdns_pack_domain_name
1079 * Format the string as packed DNS labels.
1080 * Only used for one string at a time, therefore no need for compression.
1083 _mdns_pack_domain_name(const char *str
, uint8_t *buf
, size_t buflen
)
1088 if ((str
== NULL
) || (buf
== NULL
)) return -1;
1092 /* calculate length to next '.' or '\0' */
1093 char *dot
= strchr(str
, '.');
1094 if (dot
== NULL
) dot
= strchr(str
, '\0');
1097 if (len
> NS_MAXLABEL
) return -1;
1099 /* copy data for label */
1101 while (str
< dot
&& i
< buflen
)
1106 /* skip past '.', break if '\0' */
1107 if (*str
++ == '\0') break;
1110 if (i
>= buflen
) return -1;
1114 /* no trailing dot - add a null label */
1116 if (i
>= buflen
) return -1;
1124 _is_rev_link_local(const char *name
)
1128 if (name
== NULL
) return 0;
1131 if (len
== 0) return 0;
1133 /* check for trailing '.' */
1134 if (name
[len
- 1] == '.') len
--;
1136 if (len
!= IPv6_REVERSE_LEN
) return 0;
1138 i
= IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR
;
1139 if ((name
[i
] != '8') && (name
[i
] != '9') && (name
[i
] != 'A') && (name
[i
] != 'a') && (name
[i
] != 'B') && (name
[i
] != 'b')) return 0;
1141 i
= IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR
+ 1;
1142 if (strncasecmp(name
+ i
, ".e.f.ip6.arpa", 13)) return 0;
1144 for (i
= 0; i
< IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR
; i
+= 2)
1146 if (name
[i
] < '0') return 0;
1147 if ((name
[i
] > '9') && (name
[i
] < 'A')) return 0;
1148 if ((name
[i
] > 'F') && (name
[i
] < 'a')) return 0;
1149 if (name
[i
] > 'f') return 0;
1150 if (name
[i
+ 1] != '.') return 0;
1157 * _mdns_ipv6_extract_scope_id
1158 * If the input string is a link local IPv6 address with an encoded scope id,
1159 * the scope id is extracted and a new string is constructed with the scope id removed.
1162 _mdns_ipv6_extract_scope_id(const char *name
, uint32_t *out_ifnum
)
1169 if (out_ifnum
!= NULL
) *out_ifnum
= 0;
1170 if (name
== NULL
) return NULL
;
1172 /* examine the address, extract the scope id if present */
1173 if (_is_rev_link_local(name
))
1175 /* _is_rev_link_local rejects chars > 127 so it's safe to index into hexval */
1176 i
= IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW
;
1177 nibble
= hexval
[(uint32_t)name
[i
]];
1181 nibble
= hexval
[(uint32_t)name
[i
]];
1182 iface
+= (nibble
<< 4);
1185 nibble
= hexval
[(uint32_t)name
[i
]];
1186 iface
+= (nibble
<< 8);
1189 nibble
= hexval
[(uint32_t)name
[i
]];
1190 iface
+= (nibble
<< 12);
1194 qname
= strdup(name
);
1195 if (qname
== NULL
) return NULL
;
1197 i
= IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW
;
1203 if (out_ifnum
) *out_ifnum
= iface
;
1211 _mdns_make_query(const char* name
, int class, int type
, uint8_t *buf
, uint32_t buflen
)
1215 if ((buf
== NULL
) || (buflen
< (NS_HFIXEDSZ
+ NS_QFIXEDSZ
))) return -1;
1217 memset(buf
, 0, NS_HFIXEDSZ
);
1218 HEADER
*hp
= (HEADER
*)buf
;
1221 hp
->id
= arc4random();
1223 hp
->opcode
= ns_o_query
;
1225 hp
->rcode
= ns_r_noerror
;
1226 hp
->qdcount
= htons(1);
1228 int n
= _mdns_pack_domain_name(name
, &buf
[len
], buflen
- len
);
1229 if (n
< 0) return -1;
1234 memcpy(&buf
[len
], &word
, sizeof(word
));
1235 len
+= sizeof(word
);
1236 word
= htons(class);
1237 memcpy(&buf
[len
], &word
, sizeof(word
));
1238 len
+= sizeof(word
);
1243 typedef struct mdns_query_context_s
1245 mdns_reply_t
*reply
;
1246 mdns_hostent_t
*host
;
1247 uint8_t *answer
; // DNS packet buffer
1248 size_t anslen
; // DNS packet buffer current length
1249 size_t ansmaxlen
; // DNS packet buffer maximum length
1250 int type
; // type of query: A, AAAA, PTR, SRV...
1251 uint16_t last_type
; // last type received
1254 DNSServiceFlags flags
;
1255 DNSServiceErrorType error
;
1256 int kq
; // kqueue to notify when callback received
1257 struct mdns_query_context_s
*next
; // linked list
1258 } mdns_query_context_t
;
1260 static mdns_query_context_t
*in_flight
;
1263 _mdns_query_callback(DNSServiceRef
, DNSServiceFlags
, uint32_t, DNSServiceErrorType
, const char *, uint16_t, uint16_t, uint16_t, const void *, uint32_t, void *);
1267 * initializes the context and starts a DNS-SD query.
1269 static DNSServiceErrorType
1270 _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
, uint32_t ifindex
, DNSServiceFlags flags
, int kq
)
1272 DNSServiceErrorType status
;
1274 flags
|= kDNSServiceFlagsShareConnection
;
1275 flags
|= kDNSServiceFlagsReturnIntermediates
;
1277 /* <rdar://problem/7428439> mDNSResponder is now responsible for timeouts */
1278 flags
|= kDNSServiceFlagsTimeout
;
1280 memset(ctx
, 0, sizeof(mdns_query_context_t
));
1282 if ((answer
!= NULL
) && (anslen
!= NULL
))
1284 /* build a dummy DNS header to return to the caller */
1285 ctx
->answer
= answer
;
1286 ctx
->ansmaxlen
= *anslen
;
1287 ctx
->anslen
= _mdns_make_query(name
, class, type
, answer
, ctx
->ansmaxlen
);
1288 if (ctx
->anslen
<= 0) return -1;
1292 ctx
->sd
= _mdns_sdref
;
1293 ctx
->sd_gen
= _mdns_generation
;
1299 if (type
== ns_t_a
) ctx
->host
= reply
->h4
;
1300 else if (type
== ns_t_aaaa
) ctx
->host
= reply
->h6
;
1301 else if (type
== ns_t_ptr
&& reply
->h4
) ctx
->host
= reply
->h4
;
1302 else if (type
== ns_t_ptr
&& reply
->h6
) ctx
->host
= reply
->h6
;
1303 else if (type
!= ns_t_srv
&& type
!= ns_t_cname
) return -1;
1307 char *qname
= _mdns_ipv6_extract_scope_id(name
, &iface
);
1308 if (qname
== NULL
) qname
= (char *)name
;
1312 /* balk if scope id is set AND interface is given AND they don't match */
1313 if ((iface
!= 0) && (iface
!= ifindex
)) return -1;
1317 _mdns_debug_message(";; mdns query %s type %d class %d ifindex %d [ctx %p]\n", qname
, type
, class, (int)iface
, ctx
);
1319 status
= DNSServiceQueryRecord(&ctx
->sd
, flags
, iface
, qname
, type
, class, _mdns_query_callback
, ctx
);
1320 if (qname
!= name
) free(qname
);
1322 /* keep a linked list of all in-flight queries */
1323 ctx
->next
= in_flight
;
1330 * _mdns_query_is_complete
1331 * Determines whether the specified query has sufficient information to be
1332 * considered complete.
1335 _mdns_query_is_complete(mdns_query_context_t
*ctx
, bool *more
)
1337 bool complete
= false;
1339 /* NULL context is an error, but we call it complete */
1340 if (ctx
== NULL
) return true;
1342 /* not complete if discoveryd says there is more coming (for some in-flight query - possibly not this one) */
1343 if (ctx
->flags
& kDNSServiceFlagsMoreComing
)
1345 if (more
!= NULL
) *more
= true;
1346 _mdns_debug_message(";; mdns is_complete type %d ctx %p more coming - incomplete\n", ctx
->type
, ctx
);
1349 if (more
!= NULL
) *more
= false;
1350 _mdns_debug_message(";; mdns is_complete type %d ctx %p clear more coming - complete\n", ctx
->type
, ctx
);
1353 if (ctx
->last_type
!= ctx
->type
)
1355 _mdns_debug_message(";; mdns is_complete ctx %p type mismatch (%d != %d) - incomplete\n", ctx
, ctx
->last_type
, ctx
->type
);
1363 if (ctx
->host
!= NULL
&& ctx
->host
->addr_count
> 0)
1365 _mdns_debug_message(";; mdns is_complete type %d ctx %p host addr count %d complete -> true\n", ctx
->type
, ctx
, ctx
->host
->addr_count
);
1370 if (ctx
->host
!= NULL
&& ctx
->host
->host
.h_name
!= NULL
)
1373 _mdns_debug_message(";; mdns is_complete type %d ctx %p host name %s complete -> true\n", ctx
->type
, ctx
, ctx
->host
->host
.h_name
);
1377 if (ctx
->reply
!= NULL
&& ctx
->reply
->srv
!= NULL
)
1379 _mdns_debug_message(";; mdns is_complete type %d ctx %p srv %s complete -> true\n", ctx
->type
, ctx
, ctx
->reply
->srv
);
1384 _mdns_debug_message(";; mdns is_complete unexpected type %d ctx %p\n", ctx
->type
, ctx
);
1387 _mdns_debug_message(";; mdns is_complete type %d ctx %p %scomplete\n", ctx
->type
, ctx
, complete ?
" - " : " - in");
1394 * Clear out the temporary fields of the context, and clear any result
1395 * structures that are incomplete. Returns true if the query was complete.
1398 _mdns_query_clear(mdns_query_context_t
*ctx
)
1400 mdns_query_context_t
*p
;
1402 if (ctx
== NULL
) return true;
1405 bool complete
= _mdns_query_is_complete(ctx
, &more
);
1407 if (ctx
->sd
!= NULL
)
1409 /* only dealloc this DNSServiceRef if the "main" _mdns_sdref has not been deallocated */
1410 if (ctx
->sd
!= NULL
&& ctx
->sd_gen
== _mdns_generation
)
1412 DNSServiceRefDeallocate(ctx
->sd
);
1421 if (in_flight
== ctx
)
1423 in_flight
= ctx
->next
;
1428 while ((p
!= NULL
) && (p
->next
!= ctx
)) p
= p
->next
;
1429 if (p
!= NULL
) p
->next
= ctx
->next
;
1434 if (!complete
&& !more
)
1436 _mdns_hostent_clear(ctx
->host
);
1440 return complete
| more
;
1444 _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
)
1446 mdns_query_context_t
*p
, *context
;
1449 context
= (mdns_query_context_t
*)ctx
;
1450 _mdns_debug_message(";; _mdns_query_callback ctx %p flags=0x%08x%s\n", context
, flags
, (flags
& kDNSServiceFlagsMoreComing
) ?
" (kDNSServiceFlagsMoreComing is set)" : "");
1452 context
->flags
= flags
;
1453 context
->error
= errorCode
;
1454 context
->last_type
= rrtype
;
1456 /* if kDNSServiceFlagsMoreComing is NOT set, there is no more data coming for ALL in-flight queries */
1457 if (!(flags
& kDNSServiceFlagsMoreComing
))
1459 for (p
= in_flight
; p
!= NULL
; p
= p
->next
)
1461 if (p
->flags
& kDNSServiceFlagsMoreComing
)
1463 _mdns_debug_message(";; cleared kDNSServiceFlagsMoreComing flag for ctx %p\n", p
);
1464 p
->flags
&= ~kDNSServiceFlagsMoreComing
;
1469 if (errorCode
!= kDNSServiceErr_NoError
)
1471 _mdns_debug_message(";; [%s type %hu class %hu]: error %d [ctx %p]\n", fullname
, rrtype
, rrclass
, errorCode
, context
);
1475 /* embed the scope ID into link-local IPv6 addresses */
1476 if ((rrtype
== ns_t_aaaa
) && (rdlen
== sizeof(struct in6_addr
)) && IN6_IS_ADDR_LINKLOCAL((struct in6_addr
*)rdata
))
1478 memcpy(&a6
, rdata
, rdlen
);
1479 a6
.__u6_addr
.__u6_addr16
[1] = htons(ifIndex
);
1483 if (context
->reply
!= NULL
)
1487 mdns_reply_t
*reply
= context
->reply
;
1489 if (reply
->ifnum
== 0) reply
->ifnum
= ifIndex
;
1491 _mdns_hostent_append_alias(context
->host
, fullname
);
1492 if ((reply
->ttl
== 0) || (ttl
< reply
->ttl
)) reply
->ttl
= ttl
;
1499 if ((context
->host
!= NULL
) &&
1500 ((((rrtype
== ns_t_a
) && (context
->host
->host
.h_addrtype
== AF_INET
)) || ((rrtype
== ns_t_aaaa
) && (context
->host
->host
.h_addrtype
== AF_INET6
))) &&
1501 (rdlen
>= context
->host
->host
.h_length
)))
1503 if (context
->host
->host
.h_name
== NULL
)
1506 mdns_hostent_t
*h
= context
->host
;
1507 char *h_name
= _mdns_canonicalize(fullname
);
1508 context
->host
->host
.h_name
= h_name
;
1510 /* 6863416 remove h_name from h_aliases */
1511 for (i
= 0; i
< h
->alias_count
; ++i
)
1513 if (h_name
== NULL
) break;
1515 if (string_equal(h
->host
.h_aliases
[i
], h_name
))
1517 /* includes trailing NULL pointer */
1518 int sz
= sizeof(char *) * (h
->alias_count
- i
);
1519 free(h
->host
.h_aliases
[i
]);
1520 memmove(&h
->host
.h_aliases
[i
], &h
->host
.h_aliases
[i
+1], sz
);
1521 h
->alias_count
-= 1;
1527 _mdns_hostent_append_addr(context
->host
, rdata
, context
->host
->host
.h_length
);
1538 name
= _mdns_parse_domain_name(rdata
, rdlen
);
1539 if (name
== NULL
) malformed
= 1;
1541 _mdns_hostent_append_alias(context
->host
, name
);
1542 _mdns_debug_message(";; [%s type %hu class %hu] cname %s [ctx %p]\n", fullname
, rrtype
, rrclass
, name
, context
);
1548 name
= _mdns_parse_domain_name(rdata
, rdlen
);
1549 if (name
== NULL
) malformed
= 1;
1551 if ((context
->host
!= NULL
) && (context
->host
->host
.h_name
== NULL
))
1553 context
->host
->host
.h_name
= _mdns_canonicalize(name
);
1556 _mdns_hostent_append_alias(context
->host
, name
);
1562 mdns_rr_srv_t
*p
= (mdns_rr_srv_t
*)rdata
;
1563 mdns_srv_t
*srv
= calloc(1, sizeof(mdns_srv_t
));
1564 if (srv
== NULL
) break;
1566 if (rdlen
< sizeof(mdns_rr_srv_t
))
1573 srv
->srv
.priority
= ntohs(p
->priority
);
1574 srv
->srv
.weight
= ntohs(p
->weight
);
1575 srv
->srv
.port
= ntohs(p
->port
);
1576 srv
->srv
.target
= _mdns_parse_domain_name(&p
->target
[0], rdlen
- 3*sizeof(uint16_t));
1578 if (srv
->srv
.target
== NULL
)
1585 /* append to the end of the list */
1586 if (reply
->srv
== NULL
)
1592 mdns_srv_t
*iter
= reply
->srv
;
1593 while (iter
->next
) iter
= iter
->next
;
1601 malformed
= _mdns_debug
;
1608 _mdns_debug_message(";; [%s type %hu class %hu]: malformed reply [ctx %p]\n", fullname
, rrtype
, rrclass
, context
);
1613 if (context
->answer
!= NULL
)
1618 size_t buflen
= context
->ansmaxlen
- context
->anslen
;
1620 if (buflen
< NS_HFIXEDSZ
)
1622 _mdns_debug_message(";; [%s type %hu class %hu]: malformed reply (too small) [ctx %p]\n", fullname
, rrtype
, rrclass
, context
);
1626 cp
= context
->answer
+ context
->anslen
;
1628 n
= _mdns_pack_domain_name(fullname
, cp
, buflen
);
1631 _mdns_debug_message(";; [%s type %hu class %hu]: name mismatch [ctx %p]\n", fullname
, rrtype
, rrclass
, context
);
1636 * check that there is enough space in the buffer for the
1637 * resource name (n), the resource record data (rdlen) and
1638 * the resource record header (10).
1640 if (buflen
< (n
+ rdlen
+ 10))
1642 _mdns_debug_message(";; [%s type %hu class %hu]: insufficient buffer space for reply [ctx %p]\n", fullname
, rrtype
, rrclass
, context
);
1652 word
= htons(rrtype
);
1653 memcpy(cp
, &word
, sizeof(word
));
1656 word
= htons(rrclass
);
1657 memcpy(cp
, &word
, sizeof(word
));
1660 longword
= htonl(ttl
);
1661 memcpy(cp
, &longword
, sizeof(longword
));
1662 cp
+= sizeof(longword
);
1664 word
= htons(rdlen
);
1665 memcpy(cp
, &word
, sizeof(word
));
1668 memcpy(cp
, rdata
, rdlen
);
1671 ans
= (HEADER
*)context
->answer
;
1672 ans
->ancount
= htons(ntohs(ans
->ancount
) + 1);
1674 context
->anslen
= (size_t)(cp
- context
->answer
);
1677 _mdns_debug_message(";; [%s type %hu class %hu] reply [ctx %p]\n", fullname
, rrtype
, rrclass
, context
);
1681 /* Ping the waiting thread in case this callback was invoked on another */
1682 if (context
->kq
!= -1)
1684 _mdns_debug_message(";; _mdns_query_callback sending kevent wakeup\n");
1686 EV_SET(&ev
, 1, EVFILT_USER
, 0, NOTE_TRIGGER
, 0, 0);
1687 int res
= kevent(context
->kq
, &ev
, 1, NULL
, 0, NULL
);
1688 if (res
!= 0) _mdns_debug_message(";; kevent EV_TRIGGER: %s [ctx %p]\n", strerror(errno
), context
);
1693 _mdns_now(struct timespec
*now
)
1696 gettimeofday(&tv
, NULL
);
1697 now
->tv_sec
= tv
.tv_sec
;
1698 now
->tv_nsec
= tv
.tv_usec
* 1000;
1702 _mdns_add_time(struct timespec
*sum
, const struct timespec
*a
, const struct timespec
*b
)
1704 sum
->tv_sec
= a
->tv_sec
+ b
->tv_sec
;
1705 sum
->tv_nsec
= a
->tv_nsec
+ b
->tv_nsec
;
1707 if (sum
->tv_nsec
> 1000000000)
1709 sum
->tv_sec
+= (sum
->tv_nsec
/ 1000000000);
1710 sum
->tv_nsec
%= 1000000000;
1714 /* calculate a deadline from the current time based on the desired timeout */
1716 _mdns_deadline(struct timespec
*deadline
, const struct timespec
*delta
)
1718 struct timespec now
;
1720 _mdns_add_time(deadline
, &now
, delta
);
1724 _mdns_sub_time(struct timespec
*delta
, const struct timespec
*a
, const struct timespec
*b
)
1726 delta
->tv_sec
= a
->tv_sec
- b
->tv_sec
;
1727 delta
->tv_nsec
= a
->tv_nsec
- b
->tv_nsec
;
1729 if (delta
->tv_nsec
< 0)
1731 delta
->tv_nsec
+= 1000000000;
1736 /* calculate a timeout remaining before the given deadline */
1738 _mdns_timeout(struct timespec
*timeout
, const struct timespec
*deadline
)
1740 struct timespec now
;
1742 _mdns_sub_time(timeout
, deadline
, &now
);
1746 si_inet_config(uint32_t *inet4
, uint32_t *inet6
);
1749 _mdns_search(const char *name
, int class, int type
, const char *interface
, DNSServiceFlags flags
, uint8_t *answer
, uint32_t *anslen
, mdns_reply_t
*reply
)
1755 ifindex
= if_nametoindex(interface
);
1756 if (ifindex
== 0) return -1;
1763 return _mdns_search_ex(name
, class, type
, ifindex
, flags
, answer
, anslen
, reply
);
1767 _mdns_search_ex(const char *name
, int class, int type
, uint32_t ifindex
, DNSServiceFlags flags
, uint8_t *answer
, uint32_t *anslen
, mdns_reply_t
*reply
)
1769 DNSServiceErrorType err
= 0;
1772 struct timespec start
, finish
, delta
, timeout
;
1774 int i
, got_a_response
= 0;
1775 bool complete
, initialize
= true;
1777 uint32_t n_iface_4
= 0;
1779 /* determine number of IPv4 interfaces (ignore loopback) */
1780 si_inet_config(&n_iface_4
, NULL
);
1781 if (n_iface_4
> 0) n_iface_4
--;
1783 /* <rdar://problem/7732497> limit the number of initialization retries */
1784 int initialize_retries
= 3;
1786 /* 2 for A and AAAA parallel queries */
1788 mdns_query_context_t ctx
[2];
1789 bool more_coming
[2];
1791 if (name
== NULL
) return -1;
1793 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1794 /* log a warning for queries from the main thread */
1795 if (pthread_is_threaded_np() && pthread_main_np()) os_log(OS_LOG_DEFAULT
, "Warning: Libinfo call to mDNSResponder on main thread");
1796 #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
1800 * The kevent(2) API timeout parameter is used to enforce the total
1801 * timeout of the DNS query. Each iteraion recalculates the relative
1802 * timeout based on the desired end time (total timeout from origin).
1804 * In order to workaround some DNS configurations that do not return
1805 * responses for AAAA queries, parallel queries modify the total
1806 * timeout upon receipt of the first response. The new total timeout is
1807 * set to an effective value of 2N where N is the time taken to receive
1808 * the A response (the original total timeout is preserved if 2N would
1809 * have exceeded it). However, since mDNSResponder caches values, a
1810 * minimum value of 50ms for N is enforced in order to give some time
1811 * for the receipt of a AAAA response.
1814 /* determine the maximum time to wait for a result */
1815 delta
.tv_sec
= RES_MAXRETRANS
+ 5;
1817 _mdns_deadline(&finish
, &delta
);
1822 for (i
= 0; i
< 2; ++i
) {
1823 memset(&ctx
[i
], 0 , sizeof(mdns_query_context_t
));
1824 more_coming
[i
] = false;
1826 /* set up the kqueue */
1828 EV_SET(&ev
, 1, EVFILT_USER
, EV_ADD
| EV_CLEAR
, 0, 0, 0);
1829 n
= kevent(kq
, &ev
, 1, NULL
, 0, NULL
);
1830 if (n
!= 0) wait
= false;
1834 _mdns_debug_message(";; _mdns_search wait loop\n");
1839 pthread_mutex_lock(&_mdns_mutex
);
1841 /* clear any stale contexts */
1842 for (i
= 0; i
< n_ctx
; ++i
) _mdns_query_clear(&ctx
[i
]);
1845 if (_mdns_sdref
== NULL
)
1847 if (_mdns_old_sdref
!= NULL
)
1850 DNSServiceRefDeallocate(_mdns_old_sdref
);
1851 _mdns_old_sdref
= NULL
;
1854 /* (re)initialize the shared connection */
1855 err
= DNSServiceCreateConnection(&_mdns_sdref
);
1857 /* limit the number of retries */
1858 if ((initialize_retries
-- <= 0) && (err
== 0)) err
= kDNSServiceErr_Unknown
;
1862 pthread_mutex_unlock(&_mdns_mutex
);
1868 * issue (or reissue) the queries
1869 * unspecified type: do parallel A and AAAA
1873 err
= _mdns_query_start(&ctx
[n_ctx
++], reply
, answer
, anslen
, name
, class, (type
== 0) ? ns_t_a
: type
, ifindex
, flags
, kq
);
1876 if ((err
== 0) && (type
== 0))
1878 err
= _mdns_query_start(&ctx
[n_ctx
++], reply
, answer
, anslen
, name
, class, ns_t_aaaa
, ifindex
, flags
, kq
);
1881 if (err
!= 0) _mdns_debug_message(";; initialization error %d\n", err
);
1883 /* try to reinitialize */
1884 if ((err
== kDNSServiceErr_Unknown
) || (err
== kDNSServiceErr_ServiceNotRunning
) || (err
== kDNSServiceErr_BadReference
) || (err
== kDNSServiceErr_DefunctConnection
))
1886 if (_mdns_sdref
!= NULL
)
1889 DNSServiceRefDeallocate(_mdns_sdref
);
1895 pthread_mutex_unlock(&_mdns_mutex
);
1900 pthread_mutex_unlock(&_mdns_mutex
);
1904 /* (re)register the fd with kqueue */
1905 int fd
= DNSServiceRefSockFD(_mdns_sdref
);
1906 EV_SET(&ev
, fd
, EVFILT_READ
, EV_ADD
, 0, 0, 0);
1907 n
= kevent(kq
, &ev
, 1, NULL
, 0, NULL
);
1908 pthread_mutex_unlock(&_mdns_mutex
);
1909 if (err
!= 0 || n
!= 0) break;
1912 _mdns_debug_message(";; set kevent timeout %ld.%ld [ctx %p %p]\n", timeout
.tv_sec
, timeout
.tv_nsec
, (n_ctx
> 0) ?
&(ctx
[0]) : NULL
, (n_ctx
> 1) ?
&(ctx
[1]) : NULL
);
1915 n
= kevent(kq
, NULL
, 0, &ev
, 1, &timeout
);
1916 if ((n
< 0) && (errno
!= EINTR
))
1922 pthread_mutex_lock(&_mdns_mutex
);
1925 * DNSServiceProcessResult() is a blocking API
1926 * confirm that there is still data on the socket
1928 const struct timespec notimeout
= { 0, 0 };
1929 int m
= kevent(kq
, NULL
, 0, &ev
, 1, ¬imeout
);
1931 if (_mdns_sdref
== NULL
)
1934 _mdns_debug_message(";; _mdns_sdref is NULL, initialize = true\n");
1936 else if (m
> 0 && ev
.filter
== EVFILT_READ
)
1938 _mdns_debug_message(";; _mdns_search calling DNSServiceProcessResult\n", err
);
1939 err
= DNSServiceProcessResult(_mdns_sdref
);
1940 _mdns_debug_message(";; DNSServiceProcessResult -> %s\n", err
);
1941 if ((err
== kDNSServiceErr_ServiceNotRunning
) || (err
== kDNSServiceErr_BadReference
) || (err
== kDNSServiceErr_DefunctConnection
))
1943 _mdns_debug_message(";; DNSServiceProcessResult status %d [ctx %p %p]\n", err
, (n_ctx
> 0) ?
&(ctx
[0]) : NULL
, (n_ctx
> 1) ?
&(ctx
[1]) : NULL
);
1946 /* re-initialize the shared connection */
1948 DNSServiceRefDeallocate(_mdns_sdref
);
1953 else if (m
== 0 && ev
.filter
== EVFILT_USER
)
1955 _mdns_debug_message(";; kevent wakeup\n", m
, (int)ev
.filter
);
1959 _mdns_debug_message(";; kevent m=%d ev.filter=0x%08x\n", m
, ev
.filter
);
1962 /* Check if all queries are complete (including errors) */
1964 for (i
= 0; i
< n_ctx
; ++i
)
1966 bool qc
= _mdns_query_is_complete(&ctx
[i
], &more_coming
[i
]);
1967 _mdns_debug_message(";; ctx %d %p error=%d complete=%s\n", i
, &(ctx
[i
]), ctx
[i
].error
, qc ?
"true" : "false");
1969 if ((ctx
[i
].error
!= 0) || qc
)
1971 if (ctx
[i
].type
== ns_t_a
)
1973 got_a_response
= GOT_DATA
;
1974 if (ctx
[i
].error
!= 0) got_a_response
= GOT_ERROR
;
1975 _mdns_debug_message(";; type ns_t_a got_a_response=%s ctx %p\n", (got_a_response
== GOT_DATA
) ?
"GOT_DATA" : "GOT_ERROR", &(ctx
[i
]));
1978 _mdns_debug_message(";; [%s type %d class %d] finished processing ctx %p\n", name
, type
, class, &(ctx
[i
]));
1982 _mdns_debug_message(";; [%s type %d class %d] continuing ctx %p\n", name
, type
, class, &(ctx
[i
]));
1987 pthread_mutex_unlock(&_mdns_mutex
);
1991 _mdns_debug_message(";; DNSServiceProcessResult error status %d [ctx %p %p]\n", err
, (n_ctx
> 0) ?
&(ctx
[0]) : NULL
, (n_ctx
> 1) ?
&(ctx
[1]) : NULL
);
1996 _mdns_debug_message(";; [%s type %d class %d] done [ctx %p %p]\n", name
, type
, class, (n_ctx
> 0) ?
&(ctx
[0]) : NULL
, (n_ctx
> 1) ?
&(ctx
[1]) : NULL
);
1999 else if (more_coming
[0] || more_coming
[1])
2001 /* got partial data - probably from cache - reduce wait time */
2002 struct timespec now
, tmp
, extra
;
2004 /* tmp = now - start */
2006 _mdns_sub_time(&tmp
, &now
, &start
);
2008 extra
.tv_sec
= MEDIUM_AAAA_EXTRA
;
2011 /* delta = tmp + extra */
2012 _mdns_add_time(&delta
, &tmp
, &extra
);
2014 /* check that delta doesn't exceed our total timeout */
2015 _mdns_sub_time(&tmp
, &timeout
, &delta
);
2016 if (tmp
.tv_sec
>= 0)
2018 _mdns_debug_message(";; new timeout [%s type %d class %d] (waiting for more) %ld.%ld [ctx %p %p]\n", name
, type
, class, delta
.tv_sec
, delta
.tv_nsec
, (n_ctx
> 0) ?
&(ctx
[0]) : NULL
, (n_ctx
> 1) ?
&(ctx
[1]) : NULL
);
2019 _mdns_deadline(&finish
, &delta
);
2022 else if (got_a_response
== GOT_DATA
)
2024 /* got A, adjust deadline for AAAA */
2025 struct timespec now
, tn
, extra
;
2027 /* delta = now - start */
2029 _mdns_sub_time(&delta
, &now
, &start
);
2031 extra
.tv_sec
= SHORT_AAAA_EXTRA
;
2034 /* if delta is small (<= 20 milliseconds), we probably got a result from cache */
2035 if ((delta
.tv_sec
== 0) && (delta
.tv_nsec
<= 20000000))
2037 extra
.tv_sec
= MEDIUM_AAAA_EXTRA
;
2039 else if (n_iface_4
== 0)
2041 extra
.tv_sec
= LONG_AAAA_EXTRA
;
2043 else if (got_a_response
== GOT_ERROR
)
2045 extra
.tv_sec
= MEDIUM_AAAA_EXTRA
;
2048 /* tn = 2 * delta */
2049 _mdns_add_time(&tn
, &delta
, &delta
);
2051 /* delta = tn + extra */
2052 _mdns_add_time(&delta
, &tn
, &extra
);
2054 /* check that delta doesn't exceed our total timeout */
2055 _mdns_sub_time(&tn
, &timeout
, &delta
);
2058 _mdns_debug_message(";; new timeout [%s type %d class %d] (waiting for AAAA) %ld.%ld [ctx %p %p]\n", name
, type
, class, delta
.tv_sec
, delta
.tv_nsec
, (n_ctx
> 0) ?
&(ctx
[0]) : NULL
, (n_ctx
> 1) ?
&(ctx
[1]) : NULL
);
2059 _mdns_deadline(&finish
, &delta
);
2063 /* calculate remaining timeout */
2064 _mdns_timeout(&timeout
, &finish
);
2066 /* check for time remaining */
2067 if (timeout
.tv_sec
< 0)
2069 _mdns_debug_message(";; [%s type %d class %d] timeout [ctx %p %p]\n", name
, type
, class, (n_ctx
> 0) ?
&(ctx
[0]) : NULL
, (n_ctx
> 1) ?
&(ctx
[1]) : NULL
);
2074 _mdns_debug_message(";; finished _mdns_search loop [ctx %p %p]\n", (n_ctx
> 0) ?
&(ctx
[0]) : NULL
, (n_ctx
> 1) ?
&(ctx
[1]) : NULL
);
2077 pthread_mutex_lock(&_mdns_mutex
);
2079 for (i
= 0; i
< n_ctx
; ++i
)
2081 /* only clears hostents if result is incomplete */
2082 bool cc
= _mdns_query_clear(&ctx
[i
]);
2083 complete
= cc
| complete
;
2084 _mdns_debug_message(";; _mdns_search ctx %p %scomplete\n", &ctx
[i
], cc ?
"" : "in");
2087 if (more_coming
[0] || more_coming
[1]) complete
= false;
2089 _mdns_debug_message(";; _mdns_search overall %scomplete\n", complete ?
"" : "in");
2090 pthread_mutex_unlock(&_mdns_mutex
);
2092 /* everything should be done with the kq by now */
2095 /* return error if everything is incomplete */
2096 if (!complete
) res
= -1;
2098 if (anslen
!= NULL
) *anslen
= ctx
[0].anslen
;
2099 _mdns_debug_message(";; _mdns_search exit res %d\n", res
);