2 * Copyright (c) 2008-2011 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>
120 #include <dispatch/dispatch.h>
122 /* from dns_util.c */
123 #define DNS_MAX_RECEIVE_SIZE 65536
125 #define INET_NTOP_AF_INET_OFFSET 4
126 #define INET_NTOP_AF_INET6_OFFSET 8
128 #define IPPROTO_UNSPEC 0
132 #define SHORT_AAAA_EXTRA 2
133 #define MEDIUM_AAAA_EXTRA 5
134 #define LONG_AAAA_EXTRA 10
136 static int _mdns_debug
= 0;
138 // mutex protects DNSServiceProcessResult and DNSServiceRefDeallocate
139 static pthread_mutex_t _mdns_mutex
= PTHREAD_MUTEX_INITIALIZER
;
148 typedef struct mdns_srv_t mdns_srv_t
;
168 static uint32_t _mdns_generation
= 0;
169 static DNSServiceRef _mdns_sdref
;
170 static DNSServiceRef _mdns_old_sdref
;
172 static void _mdns_hostent_clear(mdns_hostent_t
*h
);
173 static void _mdns_reply_clear(mdns_reply_t
*r
);
174 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
);
176 static const char hexchar
[] = "0123456789abcdef";
178 #define BILLION 1000000000
180 /* 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" */
181 #define IPv6_REVERSE_LEN 72
183 /* index of the trailing char that must be "8", "9", "A", "a", "b", or "B" */
184 #define IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR 58
186 /* index of low-order nibble of embedded scope id */
187 #define IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW 48
189 const static uint8_t hexval
[128] = {
190 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */
191 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */
192 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 - 47 */
193 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 48 - 63 */
194 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 - 79 */
195 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 95 */
196 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 - 111 */
197 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 112 - 127 */
201 _mdns_reverse_ipv4(const char *addr
)
210 if (addr
== NULL
) return NULL
;
212 memcpy(&(ab
.a
), addr
, 4);
214 asprintf(&p
, "%u.%u.%u.%u.in-addr.arpa.", ab
.b
[3], ab
.b
[2], ab
.b
[1], ab
.b
[0]);
219 _mdns_reverse_ipv6(const char *addr
)
225 if (addr
== NULL
) return NULL
;
229 for (i
= 0; i
< 16; i
++)
235 x
[j
--] = hexchar
[hi
];
237 x
[j
--] = hexchar
[lo
];
240 asprintf(&p
, "%sip6.arpa.", x
);
245 /* _mdns_canonicalize
246 * Canonicalize the domain name by converting to lower case and removing the
247 * trailing '.' if present.
250 _mdns_canonicalize(const char *s
)
254 if (s
== NULL
) return NULL
;
256 if (t
== NULL
) return NULL
;
257 if (t
[0] == '\0') return t
;
258 for (i
= 0; t
[i
] != '\0'; i
++) {
259 if (t
[i
] >= 'A' && t
[i
] <= 'Z') t
[i
] += 32;
261 if (t
[i
-1] == '.') t
[i
-1] = '\0';
265 /* _mdns_hostent_append_alias
266 * Appends an alias to the mdns_hostent_t structure.
269 _mdns_hostent_append_alias(mdns_hostent_t
*h
, const char *alias
)
273 if (h
== NULL
|| alias
== NULL
) return 0;
274 name
= _mdns_canonicalize(alias
);
275 if (name
== NULL
) return -1;
277 // don't add the name if it matches an existing name
278 if (h
->host
.h_name
&& string_equal(h
->host
.h_name
, name
)) {
282 for (i
= 0; i
< h
->alias_count
; ++i
) {
283 if (string_equal(h
->host
.h_aliases
[i
], name
)) {
289 // add the alias and NULL terminate the list
290 h
->host
.h_aliases
= (char **)reallocf(h
->host
.h_aliases
, (h
->alias_count
+2) * sizeof(char *));
291 if (h
->host
.h_aliases
== NULL
) {
296 h
->host
.h_aliases
[h
->alias_count
] = name
;
298 h
->host
.h_aliases
[h
->alias_count
] = NULL
;
302 /* _mdns_hostent_append_addr
303 * Appends an alias to the mdns_hostent_t structure.
306 _mdns_hostent_append_addr(mdns_hostent_t
*h
, const uint8_t *addr
, uint32_t len
)
308 if (h
== NULL
|| addr
== NULL
|| len
== 0) return 0;
310 // copy the address buffer
311 uint8_t *buf
= malloc(len
);
312 if (buf
== NULL
) return -1;
313 memcpy(buf
, addr
, len
);
315 // add the address and NULL terminate the list
316 h
->host
.h_addr_list
= (char **)reallocf(h
->host
.h_addr_list
, (h
->addr_count
+2) * sizeof(char *));
317 if (h
->host
.h_addr_list
== NULL
) {
321 h
->host
.h_addr_list
[h
->addr_count
] = (char*)buf
;
323 h
->host
.h_addr_list
[h
->addr_count
] = NULL
;
328 _mdns_hostent_clear(mdns_hostent_t
*h
)
330 if (h
== NULL
) return;
331 free(h
->host
.h_name
);
332 h
->host
.h_name
= NULL
;
334 char **aliases
= h
->host
.h_aliases
;
335 while (aliases
&& *aliases
) {
338 free(h
->host
.h_aliases
);
339 h
->host
.h_aliases
= NULL
;
342 char **addrs
= h
->host
.h_addr_list
;
343 while (addrs
&& *addrs
) {
346 free(h
->host
.h_addr_list
);
347 h
->host
.h_addr_list
= NULL
;
353 _mdns_reply_clear(mdns_reply_t
*r
)
355 if (r
== NULL
) return;
357 _mdns_hostent_clear(r
->h4
);
358 _mdns_hostent_clear(r
->h6
);
359 mdns_srv_t
*srv
= r
->srv
;
362 mdns_srv_t
*next
= srv
->next
;
363 free(srv
->srv
.target
);
370 mdns_hostbyname(si_mod_t
*si
, const char *name
, int af
, const char *interface
, uint32_t *err
)
375 si_item_t
*out
= NULL
;
378 DNSServiceFlags flags
= 0;
380 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
383 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
387 memset(&h
, 0, sizeof(h
));
388 memset(&reply
, 0, sizeof(reply
));
398 h
.host
.h_length
= 16;
402 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
405 h
.host
.h_addrtype
= af
;
407 status
= _mdns_search(name
, ns_c_in
, type
, interface
, flags
, NULL
, NULL
, &reply
);
408 if (status
!= 0 || h
.addr_count
== 0) {
409 _mdns_reply_clear(&reply
);
410 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
414 bb
= reply
.ttl
+ time(NULL
);
418 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
);
421 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
);
425 _mdns_reply_clear(&reply
);
427 if (out
== NULL
&& err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
433 mdns_hostbyaddr(si_mod_t
*si
, const void *addr
, int af
, const char *interface
, uint32_t *err
)
442 DNSServiceFlags flags
= 0;
444 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
446 if (addr
== NULL
|| si
== NULL
) {
447 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
451 memset(&h
, 0, sizeof(h
));
452 memset(&reply
, 0, sizeof(reply
));
458 name
= _mdns_reverse_ipv4(addr
);
459 cat
= CATEGORY_HOST_IPV4
;
462 h
.host
.h_length
= 16;
464 name
= _mdns_reverse_ipv6(addr
);
465 cat
= CATEGORY_HOST_IPV6
;
468 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
471 h
.host
.h_addrtype
= af
;
473 status
= _mdns_search(name
, ns_c_in
, ns_t_ptr
, interface
, flags
, NULL
, NULL
, &reply
);
476 _mdns_reply_clear(&reply
);
477 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
481 status
= _mdns_hostent_append_addr(&h
, addr
, h
.host
.h_length
);
483 _mdns_hostent_clear(&h
);
484 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
488 bb
= reply
.ttl
+ time(NULL
);
489 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
);
491 _mdns_hostent_clear(&h
);
493 if (out
== NULL
&& err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
498 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
)
510 if (family
== AF_INET6
)
512 if ((flags
& AI_V4MAPPED
) == 0) wantv4
= 0;
514 else if (family
== AF_INET
)
518 else if (family
!= AF_UNSPEC
)
523 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
525 si_list_t
*out
= NULL
;
527 memset(&h4
, 0, sizeof(h4
));
528 memset(&h6
, 0, sizeof(h6
));
529 memset(&reply
, 0, sizeof(reply
));
531 h4
.host
.h_addrtype
= AF_INET
;
532 h4
.host
.h_length
= 4;
533 h6
.host
.h_addrtype
= AF_INET6
;
534 h6
.host
.h_length
= 16;
536 if (wantv4
&& wantv6
) {
547 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
552 if ((flags
& AI_NUMERICSERV
) != 0) {
553 port
= *(uint16_t *)serv
;
555 if (_gai_serv_to_port(serv
, proto
, &port
) != 0) {
556 if (err
) *err
= SI_STATUS_EAI_NONAME
;
562 if ((flags
& AI_NUMERICHOST
) != 0) {
564 struct in_addr
*p4
= NULL
;
565 struct in6_addr
*p6
= NULL
;
566 if (family
== AF_INET
) {
568 memcpy(p4
, node
, sizeof(a4
));
569 } else if (family
== AF_INET6
) {
571 memcpy(p6
, node
, sizeof(a6
));
573 out
= si_addrinfo_list(si
, flags
, socktype
, proto
, p4
, p6
, port
, 0, cname
, cname
);
575 DNSServiceFlags dns_flags
= 0;
576 if (flags
& AI_ADDRCONFIG
) {
577 dns_flags
|= kDNSServiceFlagsSuppressUnusable
;
580 res
= _mdns_search(node
, ns_c_in
, type
, interface
, dns_flags
, NULL
, NULL
, &reply
);
581 if (res
== 0 && (h4
.addr_count
> 0 || h6
.addr_count
> 0)) {
582 out
= si_addrinfo_list_from_hostent(si
, flags
, socktype
, proto
,
584 (wantv4
? &h4
.host
: NULL
),
585 (wantv6
? &h6
.host
: NULL
));
586 } else if (err
!= NULL
) {
587 *err
= SI_STATUS_EAI_NONAME
;
589 _mdns_reply_clear(&reply
);
595 mdns_srv_byname(si_mod_t
* si
, const char *qname
, const char *interface
, uint32_t *err
)
597 si_list_t
*out
= NULL
;
601 const uint64_t unused
= 0;
602 DNSServiceFlags flags
= 0;
604 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
606 memset(&reply
, 0, sizeof(reply
));
607 res
= _mdns_search(qname
, ns_c_in
, ns_t_srv
, interface
, flags
, NULL
, NULL
, &reply
);
612 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
);
613 out
= si_list_add(out
, item
);
614 si_item_release(item
);
618 _mdns_reply_clear(&reply
);
623 * We support dns_async_start / cancel / handle_reply using dns_item_call
626 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
)
629 uint8_t buf
[DNS_MAX_RECEIVE_SIZE
];
630 uint32_t len
= sizeof(buf
);
635 DNSServiceFlags flags
= 0;
637 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
640 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
644 memset(&h4
, 0, sizeof(h4
));
645 memset(&h6
, 0, sizeof(h6
));
646 memset(&reply
, 0, sizeof(reply
));
648 h4
.host
.h_addrtype
= AF_INET
;
649 h4
.host
.h_length
= 4;
650 h6
.host
.h_addrtype
= AF_INET6
;
651 h6
.host
.h_length
= 16;
655 res
= _mdns_search(name
, class, type
, interface
, flags
, buf
, &len
, &reply
);
656 if (res
!= 0 || len
<= 0 || len
> DNS_MAX_RECEIVE_SIZE
) {
657 _mdns_reply_clear(&reply
);
658 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
662 struct sockaddr_in6 from
;
663 uint32_t fromlen
= sizeof(from
);
664 memset(&from
, 0, fromlen
);
665 from
.sin6_len
= fromlen
;
666 from
.sin6_family
= AF_INET6
;
667 from
.sin6_addr
.__u6_addr
.__u6_addr8
[15] = 1;
668 if (reply
.ifnum
!= 0) {
669 from
.sin6_addr
.__u6_addr
.__u6_addr16
[0] = htons(0xfe80);
670 from
.sin6_scope_id
= reply
.ifnum
;
673 out
= (si_item_t
*)LI_ils_create("L4488@@", (unsigned long)si
, CATEGORY_DNSPACKET
, 1, 0LL, 0LL, len
, buf
, fromlen
, &from
);
674 if (out
== NULL
&& err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
676 _mdns_reply_clear(&reply
);
682 mdns_is_valid(si_mod_t
*si
, si_item_t
*item
)
688 mdns_close(si_mod_t
*si
)
693 _mdns_atfork_prepare(void)
695 // acquire our lock so that we know all other threads have "drained"
696 pthread_mutex_lock(&_mdns_mutex
);
700 _mdns_atfork_parent(void)
702 // parent can simply resume
703 pthread_mutex_unlock(&_mdns_mutex
);
707 _mdns_atfork_child(void)
709 // child needs to force re-initialization
710 _mdns_old_sdref
= _mdns_sdref
; // for later deallocation
712 pthread_mutex_unlock(&_mdns_mutex
);
718 pthread_atfork(_mdns_atfork_prepare
, _mdns_atfork_parent
, _mdns_atfork_child
);
720 _mdns_debug
= getenv("RES_DEBUG") != NULL
;
724 si_module_static_mdns(void)
726 static const struct si_mod_vtable_s mdns_vtable
=
728 .sim_close
= &mdns_close
,
729 .sim_is_valid
= &mdns_is_valid
,
730 .sim_host_byname
= &mdns_hostbyname
,
731 .sim_host_byaddr
= &mdns_hostbyaddr
,
732 .sim_item_call
= &mdns_item_call
,
733 .sim_addrinfo
= &mdns_addrinfo
,
734 .sim_srv_byname
= &mdns_srv_byname
,
741 .flags
= SI_MOD_FLAG_STATIC
,
744 .vtable
= &mdns_vtable
,
747 static dispatch_once_t once
;
749 dispatch_once(&once
, ^{
750 si
.name
= strdup("mdns");
754 return (si_mod_t
*)&si
;
758 * _mdns_parse_domain_name
759 * Combine DNS labels to form a string.
760 * DNSService API does not return compressed names.
763 _mdns_parse_domain_name(const uint8_t *data
, uint32_t datalen
)
767 uint32_t domainlen
= 0;
770 if ((data
== NULL
) || (datalen
== 0)) return NULL
;
772 // i: index into input data
773 // j: index into output string
774 while (datalen
-- > 0) {
776 domainlen
+= (len
+ 1);
777 domain
= reallocf(domain
, domainlen
);
778 if (domain
== NULL
) return NULL
;
779 if (len
== 0) break; // DNS root (NUL)
781 domain
[j
++] = datalen
? '.' : '\0';
784 while ((len
-- > 0) && (datalen
--)) {
785 if (data
[i
] == '.') {
786 // special case: escape the '.' with a '\'
787 domain
= reallocf(domain
, ++domainlen
);
788 if (domain
== NULL
) return NULL
;
791 domain
[j
++] = data
[i
++];
800 * _mdns_pack_domain_name
801 * Format the string as packed DNS labels.
802 * Only used for one string at a time, therefore no need for compression.
805 _mdns_pack_domain_name(const char* str
, uint8_t *buf
, size_t buflen
) {
810 // calculate length to next '.' or '\0'
811 char *dot
= strchr(str
, '.');
812 if (dot
== NULL
) dot
= strchr(str
, '\0');
814 if (len
> NS_MAXLABEL
) return -1;
815 // copy data for label
817 while (str
< dot
&& i
< buflen
) {
820 // skip past '.', break if '\0'
821 if (*str
++ == '\0') break;
824 if (i
>= buflen
) return -1;
827 // no trailing dot - add a null label
829 if (i
>= buflen
) return -1;
837 _is_rev_link_local(const char *name
)
841 if (name
== NULL
) return 0;
844 if (len
== 0) return 0;
846 /* check for trailing '.' */
847 if (name
[len
- 1] == '.') len
--;
849 if (len
!= IPv6_REVERSE_LEN
) return 0;
851 i
= IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR
;
852 if ((name
[i
] != '8') && (name
[i
] != '9') && (name
[i
] != 'A') && (name
[i
] != 'a') && (name
[i
] != 'B') && (name
[i
] != 'b')) return 0;
854 i
= IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR
+ 1;
855 if (strncasecmp(name
+ i
, ".e.f.ip6.arpa", 13)) return 0;
857 for (i
= 0; i
< IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR
; i
+= 2)
859 if (name
[i
] < '0') return 0;
860 if ((name
[i
] > '9') && (name
[i
] < 'A')) return 0;
861 if ((name
[i
] > 'F') && (name
[i
] < 'a')) return 0;
862 if (name
[i
] > 'f') return 0;
863 if (name
[i
+ 1] != '.') return 0;
869 /* _mdns_ipv6_extract_scope_id
870 * If the input string is a link local IPv6 address with an encoded scope id,
871 * the scope id is extracted and a new string is constructed with the scope id removed.
874 _mdns_ipv6_extract_scope_id(const char *name
, uint32_t *out_ifnum
)
881 if (out_ifnum
!= NULL
) *out_ifnum
= 0;
883 /* examine the address, extract the scope id if present */
884 if ((name
!= NULL
) && (_is_rev_link_local(name
)))
886 /* _is_rev_link_local rejects chars > 127 so it's safe to index into hexval */
887 i
= IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW
;
888 nibble
= hexval
[(uint32_t)name
[i
]];
892 nibble
= hexval
[(uint32_t)name
[i
]];
893 iface
+= (nibble
<< 4);
896 nibble
= hexval
[(uint32_t)name
[i
]];
897 iface
+= (nibble
<< 8);
900 nibble
= hexval
[(uint32_t)name
[i
]];
901 iface
+= (nibble
<< 12);
905 qname
= strdup(name
);
906 if (qname
== NULL
) return NULL
;
908 i
= IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW
;
914 if (out_ifnum
) *out_ifnum
= iface
;
922 _mdns_make_query(const char* name
, int class, int type
, uint8_t *buf
, uint32_t buflen
)
926 if (buf
== NULL
|| buflen
< (NS_HFIXEDSZ
+ NS_QFIXEDSZ
)) return -1;
927 memset(buf
, 0, NS_HFIXEDSZ
);
928 HEADER
*hp
= (HEADER
*)buf
;
931 hp
->id
= arc4random();
933 hp
->opcode
= ns_o_query
;
935 hp
->rcode
= ns_r_noerror
;
936 hp
->qdcount
= htons(1);
938 int n
= _mdns_pack_domain_name(name
, &buf
[len
], buflen
- len
);
939 if (n
< 0) return -1;
944 memcpy(&buf
[len
], &word
, sizeof(word
));
947 memcpy(&buf
[len
], &word
, sizeof(word
));
954 mdns_hostent_t
*host
;
955 uint8_t *answer
; // DNS packet buffer
956 size_t anslen
; // DNS packet buffer current length
957 size_t ansmaxlen
; // DNS packet buffer maximum length
958 int type
; // type of query: A, AAAA, PTR, SRV...
959 uint16_t last_type
; // last type received
962 DNSServiceFlags flags
;
963 DNSServiceErrorType error
;
964 int kq
; // kqueue to notify when callback received
965 } mdns_query_context_t
;
968 _mdns_query_callback(DNSServiceRef
, DNSServiceFlags
, uint32_t, DNSServiceErrorType
, const char *, uint16_t, uint16_t, uint16_t, const void *, uint32_t, void *);
971 * initializes the context and starts a DNS-SD query.
973 static DNSServiceErrorType
974 _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
)
976 DNSServiceErrorType status
;
978 flags
|= kDNSServiceFlagsShareConnection
;
979 flags
|= kDNSServiceFlagsReturnIntermediates
;
981 /* <rdar://problem/7428439> mDNSResponder is now responsible for timeouts */
982 flags
|= kDNSServiceFlagsTimeout
;
984 memset(ctx
, 0, sizeof(mdns_query_context_t
));
986 if (answer
&& anslen
) {
987 // build a dummy DNS header to return to the caller
988 ctx
->answer
= answer
;
989 ctx
->ansmaxlen
= *anslen
;
990 ctx
->anslen
= _mdns_make_query(name
, class, type
, answer
, ctx
->ansmaxlen
);
991 if (ctx
->anslen
<= 0) return -1;
995 ctx
->sd
= _mdns_sdref
;
996 ctx
->sd_gen
= _mdns_generation
;
1000 if (type
== ns_t_a
) ctx
->host
= reply
->h4
;
1001 else if (type
== ns_t_aaaa
) ctx
->host
= reply
->h6
;
1002 else if (type
== ns_t_ptr
&& reply
->h4
) ctx
->host
= reply
->h4
;
1003 else if (type
== ns_t_ptr
&& reply
->h6
) ctx
->host
= reply
->h6
;
1004 else if (type
!= ns_t_srv
&& type
!= ns_t_cname
) abort();
1008 char *qname
= _mdns_ipv6_extract_scope_id(name
, &iface
);
1009 if (qname
== NULL
) qname
= (char *)name
;
1011 if (interface
!= NULL
)
1013 /* get interface number from name */
1014 int iface2
= if_nametoindex(interface
);
1016 /* balk if interface name lookup failed */
1017 if (iface2
== 0) return -1;
1019 /* balk if scope id is set AND interface is given AND they don't match */
1020 if ((iface
!= 0) && (iface2
!= 0) && (iface
!= iface2
)) return -1;
1021 if (iface2
!= 0) iface
= iface2
;
1024 if (_mdns_debug
) printf(";; mdns query %s %d %d\n", qname
, type
, class);
1025 status
= DNSServiceQueryRecord(&ctx
->sd
, flags
, iface
, qname
, type
, class, _mdns_query_callback
, ctx
);
1026 if (qname
!= name
) free(qname
);
1030 /* _mdns_query_is_complete
1031 * Determines whether the specified query has sufficient information to be
1032 * considered complete.
1035 _mdns_query_is_complete(mdns_query_context_t
*ctx
)
1037 if (ctx
== NULL
) return 1;
1038 //if (ctx->flags & kDNSServiceFlagsMoreComing) return 0;
1039 if (ctx
->last_type
!= ctx
->type
) return 0;
1040 switch (ctx
->type
) {
1043 if (ctx
->host
!= NULL
&& ctx
->host
->addr_count
> 0) {
1048 if (ctx
->host
!= NULL
&& ctx
->host
->host
.h_name
!= NULL
) {
1053 if (ctx
->reply
!= NULL
&& ctx
->reply
->srv
!= NULL
) {
1063 /* _mdns_query_clear
1064 * Clear out the temporary fields of the context, and clear any result
1065 * structures that are incomplete. Retrns 1 if the query was complete.
1068 _mdns_query_clear(mdns_query_context_t
*ctx
)
1070 int complete
= _mdns_query_is_complete(ctx
);
1071 if (ctx
== NULL
) return complete
;
1073 if (ctx
->sd
!= NULL
) {
1074 /* only dealloc this DNSServiceRef if the "main" _mdns_sdref has not been deallocated */
1075 if (ctx
->sd
!= NULL
&& ctx
->sd_gen
== _mdns_generation
) {
1076 DNSServiceRefDeallocate(ctx
->sd
);
1086 _mdns_hostent_clear(ctx
->host
);
1093 _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
)
1095 mdns_query_context_t
*context
;
1098 context
= (mdns_query_context_t
*)ctx
;
1100 context
->flags
= flags
;
1101 context
->error
= errorCode
;
1102 context
->last_type
= rrtype
;
1104 if (errorCode
!= kDNSServiceErr_NoError
) {
1105 if (_mdns_debug
) printf(";; [%s %hu %hu]: error %d\n", fullname
, rrtype
, rrclass
, errorCode
);
1109 // embed the scope ID into link-local IPv6 addresses
1110 if (rrtype
== ns_t_aaaa
&& rdlen
== sizeof(struct in6_addr
) &&
1111 IN6_IS_ADDR_LINKLOCAL((struct in6_addr
*)rdata
)) {
1112 memcpy(&a6
, rdata
, rdlen
);
1113 a6
.__u6_addr
.__u6_addr16
[1] = htons(ifIndex
);
1117 if (context
->reply
) {
1120 mdns_reply_t
*reply
= context
->reply
;
1122 if (reply
->ifnum
== 0) {
1123 reply
->ifnum
= ifIndex
;
1126 _mdns_hostent_append_alias(context
->host
, fullname
);
1127 if (reply
->ttl
== 0 || ttl
< reply
->ttl
) reply
->ttl
= ttl
;
1132 if (((rrtype
== ns_t_a
&& context
->host
->host
.h_addrtype
== AF_INET
) ||
1133 (rrtype
== ns_t_aaaa
&& context
->host
->host
.h_addrtype
== AF_INET6
)) &&
1134 rdlen
>= context
->host
->host
.h_length
) {
1135 if (context
->host
->host
.h_name
== NULL
) {
1137 mdns_hostent_t
*h
= context
->host
;
1138 char *h_name
= _mdns_canonicalize(fullname
);
1139 context
->host
->host
.h_name
= h_name
;
1141 // 6863416 remove h_name from h_aliases
1142 for (i
= 0; i
< h
->alias_count
; ++i
) {
1143 if (h_name
== NULL
) break;
1144 if (string_equal(h
->host
.h_aliases
[i
], h_name
)) {
1145 // includes trailing NULL pointer
1146 int sz
= sizeof(char *) * (h
->alias_count
- i
);
1147 free(h
->host
.h_aliases
[i
]);
1148 memmove(&h
->host
.h_aliases
[i
], &h
->host
.h_aliases
[i
+1], sz
);
1149 h
->alias_count
-= 1;
1154 _mdns_hostent_append_addr(context
->host
, rdata
, context
->host
->host
.h_length
);
1160 name
= _mdns_parse_domain_name(rdata
, rdlen
);
1161 if (!name
) malformed
= 1;
1162 _mdns_hostent_append_alias(context
->host
, name
);
1166 name
= _mdns_parse_domain_name(rdata
, rdlen
);
1167 if (!name
) malformed
= 1;
1168 if (context
->host
&& context
->host
->host
.h_name
== NULL
) {
1169 context
->host
->host
.h_name
= _mdns_canonicalize(name
);
1171 _mdns_hostent_append_alias(context
->host
, name
);
1175 mdns_rr_srv_t
*p
= (mdns_rr_srv_t
*)rdata
;
1176 mdns_srv_t
*srv
= calloc(1, sizeof(mdns_srv_t
));
1177 if (srv
== NULL
) break;
1178 if (rdlen
< sizeof(mdns_rr_srv_t
)) {
1182 srv
->srv
.priority
= ntohs(p
->priority
);
1183 srv
->srv
.weight
= ntohs(p
->weight
);
1184 srv
->srv
.port
= ntohs(p
->port
);
1185 srv
->srv
.target
= _mdns_parse_domain_name(&p
->target
[0], rdlen
- 3*sizeof(uint16_t));
1186 if (srv
->srv
.target
== NULL
) {
1190 // append to the end of the list
1191 if (reply
->srv
== NULL
) {
1194 mdns_srv_t
*iter
= reply
->srv
;
1195 while (iter
->next
) iter
= iter
->next
;
1201 malformed
= _mdns_debug
;
1204 if (malformed
&& _mdns_debug
) {
1205 printf(";; [%s %hu %hu]: malformed reply\n", fullname
, rrtype
, rrclass
);
1210 if (context
->answer
) {
1214 size_t buflen
= context
->ansmaxlen
- context
->anslen
;
1215 if (buflen
< NS_HFIXEDSZ
)
1217 if (_mdns_debug
) printf(";; [%s %hu %hu]: malformed reply\n", fullname
, rrtype
, rrclass
);
1221 cp
= context
->answer
+ context
->anslen
;
1223 n
= _mdns_pack_domain_name(fullname
, cp
, buflen
);
1225 if (_mdns_debug
) printf(";; [%s %hu %hu]: name mismatch\n", fullname
, rrtype
, rrclass
);
1229 // check that there is enough space in the buffer for the
1230 // resource name (n), the resource record data (rdlen) and
1231 // the resource record header (10).
1232 if (buflen
< n
+ rdlen
+ 10) {
1233 if (_mdns_debug
) printf(";; [%s %hu %hu]: insufficient buffer space for reply\n", fullname
, rrtype
, rrclass
);
1243 word
= htons(rrtype
);
1244 memcpy(cp
, &word
, sizeof(word
));
1247 word
= htons(rrclass
);
1248 memcpy(cp
, &word
, sizeof(word
));
1251 longword
= htonl(ttl
);
1252 memcpy(cp
, &longword
, sizeof(longword
));
1253 cp
+= sizeof(longword
);
1255 word
= htons(rdlen
);
1256 memcpy(cp
, &word
, sizeof(word
));
1259 memcpy(cp
, rdata
, rdlen
);
1262 ans
= (HEADER
*)context
->answer
;
1263 ans
->ancount
= htons(ntohs(ans
->ancount
) + 1);
1265 context
->anslen
= (size_t)(cp
- context
->answer
);
1268 if (_mdns_debug
) printf(";; [%s %hu %hu]\n", fullname
, rrtype
, rrclass
);
1271 // Ping the waiting thread in case this callback was invoked on another
1272 if (context
->kq
!= -1) {
1274 EV_SET(&ev
, 1, EVFILT_USER
, 0, NOTE_TRIGGER
, 0, 0);
1275 int res
= kevent(context
->kq
, &ev
, 1, NULL
, 0, NULL
);
1276 if (res
&& _mdns_debug
) printf(";; kevent EV_TRIGGER: %s\n", strerror(errno
));
1281 _mdns_now(struct timespec
*now
) {
1283 gettimeofday(&tv
, NULL
);
1284 now
->tv_sec
= tv
.tv_sec
;
1285 now
->tv_nsec
= tv
.tv_usec
* 1000;
1289 _mdns_add_time(struct timespec
*sum
, const struct timespec
*a
, const struct timespec
*b
)
1291 sum
->tv_sec
= a
->tv_sec
+ b
->tv_sec
;
1292 sum
->tv_nsec
= a
->tv_nsec
+ b
->tv_nsec
;
1293 if (sum
->tv_nsec
> 1000000000) {
1294 sum
->tv_sec
+= (sum
->tv_nsec
/ 1000000000);
1295 sum
->tv_nsec
%= 1000000000;
1299 // calculate a deadline from the current time based on the desired timeout
1301 _mdns_deadline(struct timespec
*deadline
, const struct timespec
*delta
)
1303 struct timespec now
;
1305 _mdns_add_time(deadline
, &now
, delta
);
1309 _mdns_sub_time(struct timespec
*delta
, const struct timespec
*a
, const struct timespec
*b
)
1311 delta
->tv_sec
= a
->tv_sec
- b
->tv_sec
;
1312 delta
->tv_nsec
= a
->tv_nsec
- b
->tv_nsec
;
1313 if (delta
->tv_nsec
< 0) {
1314 delta
->tv_nsec
+= 1000000000;
1319 // calculate a timeout remaining before the given deadline
1321 _mdns_timeout(struct timespec
*timeout
, const struct timespec
*deadline
)
1323 struct timespec now
;
1325 _mdns_sub_time(timeout
, deadline
, &now
);
1329 _mdns_search(const char *name
, int class, int type
, const char *interface
, DNSServiceFlags flags
, uint8_t *answer
, uint32_t *anslen
, mdns_reply_t
*reply
)
1331 DNSServiceErrorType err
= 0;
1332 int kq
, n
, wait
= 1;
1334 struct timespec start
, finish
, delta
, timeout
;
1336 int i
, complete
, got_a_response
= 0;
1338 uint32_t n_iface_4
= 0;
1340 // determine number of IPv4 interfaces (ignore loopback)
1341 si_inet_config(&n_iface_4
, NULL
);
1342 if (n_iface_4
> 0) n_iface_4
--;
1344 // <rdar://problem/7732497> limit the number of initialization retries
1345 int initialize_retries
= 3;
1347 // 2 for A and AAAA parallel queries
1349 mdns_query_context_t ctx
[2];
1351 if (name
== NULL
) return -1;
1353 #if TARGET_OS_EMBEDDED
1354 // log a warning for queries from the main thread
1355 if (pthread_is_threaded_np() && pthread_main_np()) asl_log(NULL
, NULL
, ASL_LEVEL_WARNING
, "Warning: Libinfo call to mDNSResponder on main thread");
1356 #endif // TARGET_OS_EMBEDDED
1359 // The kevent(2) API timeout parameter is used to enforce the total
1360 // timeout of the DNS query. Each iteraion recalculates the relative
1361 // timeout based on the desired end time (total timeout from origin).
1363 // In order to workaround some DNS configurations that do not return
1364 // responses for AAAA queries, parallel queries modify the total
1365 // timeout upon receipt of the first response. The new total timeout is
1366 // set to an effective value of 2N where N is the time taken to receive
1367 // the A response (the original total timeout is preserved if 2N would
1368 // have exceeded it). However, since mDNSResponder caches values, a
1369 // minimum value of 50ms for N is enforced in order to give some time
1370 // for the receipt of a AAAA response.
1372 // determine the maximum time to wait for a result
1373 delta
.tv_sec
= RES_MAXRETRANS
+ 5;
1375 _mdns_deadline(&finish
, &delta
);
1379 for (i
= 0; i
< 2; ++i
) {
1380 memset(&ctx
[i
], 0 , sizeof(mdns_query_context_t
));
1383 // set up the kqueue
1385 EV_SET(&ev
, 1, EVFILT_USER
, EV_ADD
| EV_CLEAR
, 0, 0, 0);
1386 n
= kevent(kq
, &ev
, 1, NULL
, 0, NULL
);
1387 if (n
!= 0) wait
= 0;
1392 pthread_mutex_lock(&_mdns_mutex
);
1393 // clear any stale contexts
1394 for (i
= 0; i
< n_ctx
; ++i
) {
1395 _mdns_query_clear(&ctx
[i
]);
1399 if (_mdns_sdref
== NULL
) {
1400 if (_mdns_old_sdref
!= NULL
) {
1402 DNSServiceRefDeallocate(_mdns_old_sdref
);
1403 _mdns_old_sdref
= NULL
;
1405 // (re)initialize the shared connection
1406 err
= DNSServiceCreateConnection(&_mdns_sdref
);
1408 // limit the number of retries
1409 if (initialize_retries
-- <= 0 && err
== 0) {
1410 err
= kDNSServiceErr_Unknown
;
1414 pthread_mutex_unlock(&_mdns_mutex
);
1419 // issue (or reissue) the queries
1420 // unspecified type: do parallel A and AAAA
1422 err
= _mdns_query_start(&ctx
[n_ctx
++], reply
,
1425 (type
== 0) ? ns_t_a
: type
, interface
, flags
, kq
);
1427 if (err
== 0 && type
== 0) {
1428 err
= _mdns_query_start(&ctx
[n_ctx
++], reply
,
1430 name
, class, ns_t_aaaa
, interface
, flags
, kq
);
1432 if (err
&& _mdns_debug
) printf(";; initialization error %d\n", err
);
1433 // try to reinitialize
1434 if (err
== kDNSServiceErr_Unknown
||
1435 err
== kDNSServiceErr_ServiceNotRunning
||
1436 err
== kDNSServiceErr_BadReference
) {
1439 DNSServiceRefDeallocate(_mdns_sdref
);
1444 pthread_mutex_unlock(&_mdns_mutex
);
1446 } else if (err
!= 0) {
1447 pthread_mutex_unlock(&_mdns_mutex
);
1451 // (re)register the fd with kqueue
1452 int fd
= DNSServiceRefSockFD(_mdns_sdref
);
1453 EV_SET(&ev
, fd
, EVFILT_READ
, EV_ADD
, 0, 0, 0);
1454 n
= kevent(kq
, &ev
, 1, NULL
, 0, NULL
);
1455 pthread_mutex_unlock(&_mdns_mutex
);
1456 if (err
!= 0 || n
!= 0) break;
1459 if (_mdns_debug
) printf(";; kevent timeout %ld.%ld\n", timeout
.tv_sec
, timeout
.tv_nsec
);
1460 n
= kevent(kq
, NULL
, 0, &ev
, 1, &timeout
);
1461 if (n
< 0 && errno
!= EINTR
) {
1466 pthread_mutex_lock(&_mdns_mutex
);
1467 // DNSServiceProcessResult() is a blocking API
1468 // confirm that there is still data on the socket
1469 const struct timespec notimeout
= { 0, 0 };
1470 int m
= kevent(kq
, NULL
, 0, &ev
, 1, ¬imeout
);
1471 if (_mdns_sdref
== NULL
) {
1473 } else if (m
> 0 && ev
.filter
== EVFILT_READ
) {
1474 err
= DNSServiceProcessResult(_mdns_sdref
);
1475 if (err
== kDNSServiceErr_ServiceNotRunning
||
1476 err
== kDNSServiceErr_BadReference
) {
1477 if (_mdns_debug
) printf(";; DNSServiceProcessResult status %d\n", err
);
1479 // re-initialize the shared connection
1481 DNSServiceRefDeallocate(_mdns_sdref
);
1487 // Check if all queries are complete (including errors)
1489 for (i
= 0; i
< n_ctx
; ++i
) {
1490 if (_mdns_query_is_complete(&ctx
[i
]) || ctx
[i
].error
!= 0) {
1491 if (ctx
[i
].type
== ns_t_a
) {
1492 got_a_response
= GOT_DATA
;
1493 if (ctx
[i
].error
!= 0) got_a_response
= GOT_ERROR
;
1499 pthread_mutex_unlock(&_mdns_mutex
);
1502 if (_mdns_debug
) printf(";; DNSServiceProcessResult status %d\n", err
);
1504 } else if (complete
== 1) {
1505 if (_mdns_debug
) printf(";; done\n");
1507 } else if (got_a_response
!= 0) {
1508 // got A, adjust deadline for AAAA
1509 struct timespec now
, tn
, extra
;
1511 // delta = now - start
1513 _mdns_sub_time(&delta
, &now
, &start
);
1515 extra
.tv_sec
= SHORT_AAAA_EXTRA
;
1518 // if delta is really small, we probably got a result from mDNSResponder's cache
1519 if ((delta
.tv_sec
== 0) && (delta
.tv_nsec
<= 200000000)) {
1520 extra
.tv_sec
= LONG_AAAA_EXTRA
;
1522 else if (n_iface_4
== 0) {
1523 extra
.tv_sec
= LONG_AAAA_EXTRA
;
1524 } else if (got_a_response
== GOT_ERROR
) {
1525 extra
.tv_sec
= MEDIUM_AAAA_EXTRA
;
1529 _mdns_add_time(&tn
, &delta
, &delta
);
1531 // delta = tn + extra
1532 _mdns_add_time(&delta
, &tn
, &extra
);
1534 // check that delta doesn't exceed our total timeout
1535 _mdns_sub_time(&tn
, &timeout
, &delta
);
1536 if (tn
.tv_sec
>= 0) {
1537 if (_mdns_debug
) printf(";; new timeout (waiting for AAAA) %ld.%ld\n", delta
.tv_sec
, delta
.tv_nsec
);
1538 _mdns_deadline(&finish
, &delta
);
1542 // calculate remaining timeout
1543 _mdns_timeout(&timeout
, &finish
);
1545 // check for time remaining
1546 if (timeout
.tv_sec
< 0) {
1547 if (_mdns_debug
) printf(";; timeout\n");
1553 pthread_mutex_lock(&_mdns_mutex
);
1554 for (i
= 0; i
< n_ctx
; ++i
) {
1555 if (err
== 0) err
= ctx
[i
].error
;
1556 // Only clears hostents if result is incomplete.
1557 complete
= _mdns_query_clear(&ctx
[i
]) || complete
;
1559 pthread_mutex_unlock(&_mdns_mutex
);
1560 // Everything should be done with the kq by now.
1563 // Return error if everything is incomplete
1564 if (complete
== 0) {
1568 if (anslen
) *anslen
= ctx
[0].anslen
;