]> git.saurik.com Git - apple/libinfo.git/blob - lookup.subproj/mdns_module.c
978469325c15552775d04cba4ddc520dbd2cdc03
[apple/libinfo.git] / lookup.subproj / mdns_module.c
1 /*
2 * Copyright (c) 2008-2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
25 *
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.
29 *
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
37 * SOFTWARE.
38 */
39 /*
40 * Copyright (c) 1988, 1993
41 * The Regents of the University of California. All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
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.
54 *
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
65 * SUCH DAMAGE.
66 */
67 /*
68 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
69 *
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.
76 *
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
84 * SOFTWARE.
85 */
86
87 #include "ils.h"
88 #include "netdb.h"
89 #include "si_module.h"
90
91 #include <assert.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>
97 #include <ctype.h>
98 #include <dns_sd.h>
99 #include <dnsinfo.h>
100 #include <errno.h>
101 #include <nameser.h>
102 #include <notify.h>
103 #include <pthread.h>
104 #include <resolv.h>
105 #include <stdio.h>
106 #include <stdlib.h>
107 #include <string.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>
113 #include <net/if.h>
114 #include <time.h>
115 #include <unistd.h>
116 #include <asl.h>
117 #include <dns.h>
118 #include <dns_util.h>
119 #include <TargetConditionals.h>
120 #include <dispatch/dispatch.h>
121
122 /* from dns_util.c */
123 #define DNS_MAX_RECEIVE_SIZE 65536
124
125 #define INET_NTOP_AF_INET_OFFSET 4
126 #define INET_NTOP_AF_INET6_OFFSET 8
127
128 #define IPPROTO_UNSPEC 0
129
130 #define GOT_DATA 1
131 #define GOT_ERROR 2
132 #define SHORT_AAAA_EXTRA 2
133 #define MEDIUM_AAAA_EXTRA 5
134 #define LONG_AAAA_EXTRA 10
135
136 #define MDNS_DEBUG_FILE "/etc/.mdns_debug"
137 #define MDNS_DEBUG_STDOUT 0x00000001
138 #define MDNS_DEBUG_STDERR 0x00000002
139 #define MDNS_DEBUG_ASL 0x00000004
140 #define MDNS_DEBUG_OUT 0x00000007
141 #define MDNS_DEBUG_MORE 0x00000010
142
143 static int _mdns_debug = 0;
144
145 /* mutex protects DNSServiceProcessResult and DNSServiceRefDeallocate */
146 static pthread_mutex_t _mdns_mutex = PTHREAD_MUTEX_INITIALIZER;
147
148 typedef struct
149 {
150 uint16_t priority;
151 uint16_t weight;
152 uint16_t port;
153 uint8_t target[0];
154 } mdns_rr_srv_t;
155
156 typedef struct mdns_srv_t mdns_srv_t;
157 struct mdns_srv_t
158 {
159 si_srv_t srv;
160 mdns_srv_t *next;
161 };
162
163 typedef struct
164 {
165 struct hostent host;
166 int alias_count;
167 int addr_count;
168 } mdns_hostent_t;
169
170 typedef struct
171 {
172 mdns_hostent_t *h4;
173 mdns_hostent_t *h6;
174 mdns_srv_t *srv;
175 uint64_t ttl;
176 uint32_t ifnum;
177 } mdns_reply_t;
178
179 static uint32_t _mdns_generation = 0;
180 static DNSServiceRef _mdns_sdref;
181 static DNSServiceRef _mdns_old_sdref;
182
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
187 static const char hexchar[] = "0123456789abcdef";
188
189 #define BILLION 1000000000
190
191 /* 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" */
192 #define IPv6_REVERSE_LEN 72
193
194 /* index of the trailing char that must be "8", "9", "A", "a", "b", or "B" */
195 #define IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR 58
196
197 /* index of low-order nibble of embedded scope id */
198 #define IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW 48
199
200 const static uint8_t hexval[128] = {
201 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */
202 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */
203 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 - 47 */
204 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 48 - 63 */
205 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 - 79 */
206 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 95 */
207 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 - 111 */
208 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 112 - 127 */
209 };
210
211 static void
212 _mdns_debug_message(const char *str, ...)
213 {
214 va_list v;
215 char *out = NULL;
216
217 if (str == NULL) return;
218 if ((_mdns_debug & MDNS_DEBUG_OUT) == 0) return;
219
220 va_start(v, str);
221 vasprintf(&out, str, v);
222 if (out == NULL) return;
223
224 if (_mdns_debug & MDNS_DEBUG_STDOUT) fprintf(stdout, "%s", out);
225 if (_mdns_debug & MDNS_DEBUG_STDERR) fprintf(stderr, "%s", out);
226 if (_mdns_debug & MDNS_DEBUG_ASL) asl_log_message(ASL_LEVEL_NOTICE, "%s", out);
227 free(out);
228
229 va_end(v);
230 }
231
232 static char *
233 _mdns_reverse_ipv4(const char *addr)
234 {
235 union
236 {
237 uint32_t a;
238 unsigned char b[4];
239 } ab;
240 char *p;
241
242 if (addr == NULL) return NULL;
243
244 memcpy(&(ab.a), addr, 4);
245
246 asprintf(&p, "%u.%u.%u.%u.in-addr.arpa.", ab.b[3], ab.b[2], ab.b[1], ab.b[0]);
247 return p;
248 }
249
250 static char *
251 _mdns_reverse_ipv6(const char *addr)
252 {
253 char x[65], *p;
254 int i, j;
255 u_int8_t d, hi, lo;
256
257 if (addr == NULL) return NULL;
258
259 x[64] = '\0';
260 j = 63;
261
262 for (i = 0; i < 16; i++)
263 {
264 d = addr[i];
265 lo = d & 0x0f;
266 hi = d >> 4;
267 x[j--] = '.';
268 x[j--] = hexchar[hi];
269 x[j--] = '.';
270 x[j--] = hexchar[lo];
271 }
272
273 asprintf(&p, "%sip6.arpa.", x);
274
275 return p;
276 }
277
278 /*
279 * _mdns_canonicalize
280 * Canonicalize the domain name by converting to lower case and removing the
281 * trailing '.' if present.
282 */
283 static char *
284 _mdns_canonicalize(const char *s)
285 {
286 int i;
287 char *t;
288
289 if (s == NULL) return NULL;
290
291 t = strdup(s);
292 if (t == NULL) return NULL;
293
294 if (t[0] == '\0') return t;
295
296 for (i = 0; t[i] != '\0'; i++)
297 {
298 if (t[i] >= 'A' && t[i] <= 'Z') t[i] += 32;
299 }
300
301 if (t[i-1] == '.') t[i-1] = '\0';
302
303 return t;
304 }
305
306 /*
307 * _mdns_hostent_append_alias
308 * Appends an alias to the mdns_hostent_t structure.
309 */
310 static int
311 _mdns_hostent_append_alias(mdns_hostent_t *h, const char *alias)
312 {
313 int i;
314 char *name;
315
316 if ((h == NULL) || (alias == NULL)) return 0;
317
318 name = _mdns_canonicalize(alias);
319 if (name == NULL) return -1;
320
321 /* don't add the name if it matches an existing name */
322 if ((h->host.h_name != NULL) && string_equal(h->host.h_name, name))
323 {
324 free(name);
325 return 0;
326 }
327
328 for (i = 0; i < h->alias_count; ++i)
329 {
330 if (string_equal(h->host.h_aliases[i], name))
331 {
332 free(name);
333 return 0;
334 }
335 }
336
337 /* add the alias and NULL terminate the list if it is new */
338 h->host.h_aliases = (char **)reallocf(h->host.h_aliases, (h->alias_count + 2) * sizeof(char *));
339 if (h->host.h_aliases == NULL)
340 {
341 h->alias_count = 0;
342 free(name);
343 return -1;
344 }
345
346 h->host.h_aliases[h->alias_count] = name;
347 h->alias_count++;
348
349 h->host.h_aliases[h->alias_count] = NULL;
350 return 0;
351 }
352
353 /*
354 * _mdns_hostent_append_addr
355 * Appends an alias to the mdns_hostent_t structure.
356 */
357 static int
358 _mdns_hostent_append_addr(mdns_hostent_t *h, const uint8_t *addr, uint32_t len)
359 {
360 if ((h == NULL) || (addr == NULL) || (len == 0)) return 0;
361
362 /* copy the address buffer */
363 uint8_t *buf = malloc(len);
364 if (buf == NULL) return -1;
365
366 memcpy(buf, addr, len);
367
368 /* add the address and NULL terminate the list if it is new */
369 h->host.h_addr_list = (char **)reallocf(h->host.h_addr_list, (h->addr_count + 2) * sizeof(char *));
370
371 if (h->host.h_addr_list == NULL)
372 {
373 h->addr_count = 0;
374 return -1;
375 }
376
377 h->host.h_addr_list[h->addr_count] = (char*)buf;
378 h->addr_count++;
379
380 h->host.h_addr_list[h->addr_count] = NULL;
381 return 0;
382 }
383
384 static void
385 _mdns_hostent_clear(mdns_hostent_t *h)
386 {
387 if (h == NULL) return;
388
389 free(h->host.h_name);
390 h->host.h_name = NULL;
391
392 char **aliases = h->host.h_aliases;
393 while (aliases && *aliases) free(*aliases++);
394
395 free(h->host.h_aliases);
396 h->host.h_aliases = NULL;
397 h->alias_count = 0;
398
399 char **addrs = h->host.h_addr_list;
400 while (addrs && *addrs) free(*addrs++);
401
402 free(h->host.h_addr_list);
403 h->host.h_addr_list = NULL;
404 h->addr_count = 0;
405 }
406
407 static void
408 _mdns_reply_clear(mdns_reply_t *r)
409 {
410 if (r == NULL) return;
411
412 r->ifnum = 0;
413 _mdns_hostent_clear(r->h4);
414 _mdns_hostent_clear(r->h6);
415 mdns_srv_t *srv = r->srv;
416 r->srv = NULL;
417
418 while (srv != NULL)
419 {
420 mdns_srv_t *next = srv->next;
421 free(srv->srv.target);
422 free(srv);
423 srv = next;
424 }
425 }
426
427 static si_item_t *
428 mdns_hostbyname(si_mod_t *si, const char *name, int af, const char *interface, uint32_t *err)
429 {
430 uint32_t type;
431 mdns_hostent_t h;
432 mdns_reply_t reply;
433 si_item_t *out = NULL;
434 uint64_t bb;
435 int status;
436 DNSServiceFlags flags = 0;
437
438 if (err != NULL) *err = SI_STATUS_NO_ERROR;
439
440 if ((name == NULL) || (si == NULL))
441 {
442 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
443 return NULL;
444 }
445
446 memset(&h, 0, sizeof(h));
447 memset(&reply, 0, sizeof(reply));
448
449 switch (af)
450 {
451 case AF_INET:
452 type = ns_t_a;
453 h.host.h_length = 4;
454 reply.h4 = &h;
455 break;
456 case AF_INET6:
457 type = ns_t_aaaa;
458 h.host.h_length = 16;
459 reply.h6 = &h;
460 break;
461 default:
462 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
463 return NULL;
464 }
465
466 _mdns_debug_message(";; mdns_hostbyname %s type %u class %u\n", name, type, ns_c_in);
467
468 h.host.h_addrtype = af;
469
470 status = _mdns_search(name, ns_c_in, type, interface, flags, NULL, NULL, &reply);
471 if ((status != 0) || (h.addr_count == 0))
472 {
473 _mdns_reply_clear(&reply);
474 if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
475 return NULL;
476 }
477
478 bb = reply.ttl + time(NULL);
479
480 switch (af)
481 {
482 case AF_INET:
483 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);
484 break;
485 case AF_INET6:
486 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);
487 break;
488 }
489
490 _mdns_reply_clear(&reply);
491
492 if ((out == NULL) && (err != NULL)) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
493
494 return out;
495 }
496
497 static si_item_t *
498 mdns_hostbyaddr(si_mod_t *si, const void *addr, int af, const char *interface, uint32_t *err)
499 {
500 mdns_hostent_t h;
501 mdns_reply_t reply;
502 char *name;
503 si_item_t *out;
504 uint64_t bb;
505 int cat;
506 int status;
507 DNSServiceFlags flags = 0;
508
509 if (err != NULL) *err = SI_STATUS_NO_ERROR;
510
511 if ((addr == NULL) || (si == NULL))
512 {
513 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
514 return NULL;
515 }
516
517 memset(&h, 0, sizeof(h));
518 memset(&reply, 0, sizeof(reply));
519
520 switch (af)
521 {
522 case AF_INET:
523 h.host.h_length = 4;
524 reply.h4 = &h;
525 name = _mdns_reverse_ipv4(addr);
526 cat = CATEGORY_HOST_IPV4;
527 break;
528 case AF_INET6:
529 h.host.h_length = 16;
530 reply.h6 = &h;
531 name = _mdns_reverse_ipv6(addr);
532 cat = CATEGORY_HOST_IPV6;
533 break;
534 default:
535 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
536 return NULL;
537 }
538
539 h.host.h_addrtype = af;
540
541 _mdns_debug_message(";; mdns_hostbyaddr %s type %u class %u\n", name, ns_t_ptr, ns_c_in);
542
543 status = _mdns_search(name, ns_c_in, ns_t_ptr, interface, flags, NULL, NULL, &reply);
544 free(name);
545 if (status != 0)
546 {
547 _mdns_reply_clear(&reply);
548 if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
549 return NULL;
550 }
551
552 status = _mdns_hostent_append_addr(&h, addr, h.host.h_length);
553 if (status != 0)
554 {
555 _mdns_hostent_clear(&h);
556 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
557 return NULL;
558 }
559
560 bb = reply.ttl + time(NULL);
561 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);
562
563 _mdns_hostent_clear(&h);
564
565 if ((out == NULL) && (err != NULL)) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
566 return out;
567 }
568
569 static si_list_t *
570 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)
571 {
572 bool wantv4 = true;
573 bool wantv6 = true;
574 struct in_addr a4;
575 struct in6_addr a6;
576 mdns_hostent_t h4;
577 mdns_hostent_t h6;
578 mdns_reply_t reply;
579 uint32_t type;
580 uint16_t port;
581
582 if (si == NULL)
583 {
584 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
585 return NULL;
586 }
587
588 if (family == AF_INET6)
589 {
590 if ((flags & AI_V4MAPPED) == 0) wantv4 = false;
591 }
592 else if (family == AF_INET)
593 {
594 wantv6 = false;
595 }
596 else if (family != AF_UNSPEC)
597 {
598 return NULL;
599 }
600
601 if (err != NULL) *err = SI_STATUS_NO_ERROR;
602
603 _mdns_debug_message(";; mdns_addrinfo node %s serv %s\n", (const char *)node, (const char *)serv);
604
605 si_list_t *out = NULL;
606
607 memset(&h4, 0, sizeof(h4));
608 memset(&h6, 0, sizeof(h6));
609 memset(&reply, 0, sizeof(reply));
610
611 h4.host.h_addrtype = AF_INET;
612 h4.host.h_length = 4;
613 h6.host.h_addrtype = AF_INET6;
614 h6.host.h_length = 16;
615
616 if (wantv4 && wantv6)
617 {
618 type = 0;
619 reply.h4 = &h4;
620 reply.h6 = &h6;
621 }
622 else if (wantv4)
623 {
624 reply.h4 = &h4;
625 type = ns_t_a;
626 }
627 else if (wantv6)
628 {
629 type = ns_t_aaaa;
630 reply.h6 = &h6;
631 }
632 else
633 {
634 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
635 return NULL;
636 }
637
638 /* service lookup */
639 if ((flags & AI_NUMERICSERV) != 0)
640 {
641 if (serv == NULL) port = 0;
642 else port = *(uint16_t *)serv;
643 }
644 else
645 {
646 if (_gai_serv_to_port(serv, proto, &port) != 0)
647 {
648 if (err) *err = SI_STATUS_EAI_NONAME;
649 return NULL;
650 }
651 }
652
653 /* host lookup */
654 if ((flags & AI_NUMERICHOST) != 0)
655 {
656 char *cname = NULL;
657 struct in_addr *p4 = NULL;
658 struct in6_addr *p6 = NULL;
659
660 if (node == NULL) return NULL;
661
662 if (family == AF_INET)
663 {
664 p4 = &a4;
665 memcpy(p4, node, sizeof(a4));
666 }
667 else if (family == AF_INET6)
668 {
669 p6 = &a6;
670 memcpy(p6, node, sizeof(a6));
671 }
672
673 out = si_addrinfo_list(si, flags, socktype, proto, p4, p6, port, 0, cname, cname);
674 }
675 else
676 {
677 int res;
678 DNSServiceFlags dns_flags = 0;
679
680 if (node == NULL) return NULL;
681
682 if (flags & AI_ADDRCONFIG)
683 {
684 dns_flags |= kDNSServiceFlagsSuppressUnusable;
685 }
686
687 res = _mdns_search(node, ns_c_in, type, interface, dns_flags, NULL, NULL, &reply);
688 if ((res == 0) && ((h4.addr_count > 0) || (h6.addr_count > 0)))
689 {
690 out = si_addrinfo_list_from_hostent(si, flags, socktype, proto, port, 0, (wantv4 ? &h4.host : NULL), (wantv6 ? &h6.host : NULL));
691 }
692 else if (err != NULL)
693 {
694 *err = SI_STATUS_EAI_NONAME;
695 }
696
697 _mdns_reply_clear(&reply);
698 }
699
700 return out;
701 }
702
703 static si_list_t *
704 mdns_srv_byname(si_mod_t* si, const char *qname, const char *interface, uint32_t *err)
705 {
706 si_list_t *out = NULL;
707 mdns_reply_t reply;
708 mdns_srv_t *srv;
709 int res;
710 const uint64_t unused = 0;
711 DNSServiceFlags flags = 0;
712
713 if (si == NULL)
714 {
715 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
716 return NULL;
717 }
718
719 if (err != NULL) *err = SI_STATUS_NO_ERROR;
720
721 _mdns_debug_message(";; mdns_srv_byname %s type %u class %u\n", qname, ns_t_srv, ns_c_in);
722
723 memset(&reply, 0, sizeof(reply));
724 res = _mdns_search(qname, ns_c_in, ns_t_srv, interface, flags, NULL, NULL, &reply);
725 if (res == 0)
726 {
727 srv = reply.srv;
728 while (srv != NULL)
729 {
730 si_item_t *item;
731 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);
732 out = si_list_add(out, item);
733 si_item_release(item);
734 srv = srv->next;
735 }
736 }
737
738 _mdns_reply_clear(&reply);
739 return out;
740 }
741
742 /*
743 * We support dns_async_start / cancel / handle_reply using dns_item_call
744 */
745 static si_item_t *
746 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)
747 {
748 int res;
749 uint8_t buf[DNS_MAX_RECEIVE_SIZE];
750 uint32_t len = sizeof(buf);
751 mdns_reply_t reply;
752 mdns_hostent_t h4;
753 mdns_hostent_t h6;
754 si_item_t *out;
755 DNSServiceFlags flags = 0;
756
757 if ((si == NULL) || (name == NULL))
758 {
759 if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
760 return NULL;
761 }
762
763 if (err != NULL) *err = SI_STATUS_NO_ERROR;
764
765 _mdns_debug_message(";; mdns_item_call %s type %u class %u\n", name, type, class);
766
767 memset(&h4, 0, sizeof(h4));
768 memset(&h6, 0, sizeof(h6));
769 memset(&reply, 0, sizeof(reply));
770
771 h4.host.h_addrtype = AF_INET;
772 h4.host.h_length = 4;
773 h6.host.h_addrtype = AF_INET6;
774 h6.host.h_length = 16;
775 reply.h4 = &h4;
776 reply.h6 = &h6;
777
778 res = _mdns_search(name, class, type, interface, flags, buf, &len, &reply);
779 if ((res != 0) || (len <= 0) || (len > DNS_MAX_RECEIVE_SIZE))
780 {
781 _mdns_reply_clear(&reply);
782 if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
783 return NULL;
784 }
785
786 struct sockaddr_in6 from;
787 uint32_t fromlen = sizeof(from);
788 memset(&from, 0, fromlen);
789 from.sin6_len = fromlen;
790 from.sin6_family = AF_INET6;
791 from.sin6_addr.__u6_addr.__u6_addr8[15] = 1;
792
793 if (reply.ifnum != 0)
794 {
795 from.sin6_addr.__u6_addr.__u6_addr16[0] = htons(0xfe80);
796 from.sin6_scope_id = reply.ifnum;
797 }
798
799 out = (si_item_t *)LI_ils_create("L4488@@", (unsigned long)si, CATEGORY_DNSPACKET, 1, 0LL, 0LL, len, buf, fromlen, &from);
800 if ((out == NULL) && (err != NULL)) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
801
802 _mdns_reply_clear(&reply);
803
804 return out;
805 }
806
807 static int
808 mdns_is_valid(si_mod_t *si, si_item_t *item)
809 {
810 return 0;
811 }
812
813 static void
814 mdns_close(si_mod_t *si)
815 {
816 }
817
818 static void
819 _mdns_atfork_prepare(void)
820 {
821 /* acquire our lock so that we know all other threads have "drained" */
822 pthread_mutex_lock(&_mdns_mutex);
823 }
824
825 static void
826 _mdns_atfork_parent(void)
827 {
828 /* parent can simply resume */
829 pthread_mutex_unlock(&_mdns_mutex);
830 }
831
832 static void
833 _mdns_atfork_child(void)
834 {
835 /* child needs to force re-initialization */
836 _mdns_old_sdref = _mdns_sdref; // for later deallocation
837 _mdns_sdref = NULL;
838 pthread_mutex_unlock(&_mdns_mutex);
839 }
840
841 static void
842 _mdns_init(void)
843 {
844 pthread_atfork(_mdns_atfork_prepare, _mdns_atfork_parent, _mdns_atfork_child);
845
846 if (getenv("RES_DEBUG") != NULL) _mdns_debug |= MDNS_DEBUG_STDOUT;
847 int fd = open(MDNS_DEBUG_FILE, O_RDONLY, 0);
848 errno = 0;
849
850 if (fd >= 0)
851 {
852 int i, n;
853 char c[5];
854 memset(c, 0, sizeof(c));
855 n = read(fd, c, 4);
856
857 for (i = 0; i < n; i++)
858 {
859 if ((c[i] == 'o') || (c[i] == 'O')) _mdns_debug |= MDNS_DEBUG_STDOUT;
860 if ((c[i] == 'e') || (c[i] == 'E')) _mdns_debug |= MDNS_DEBUG_STDERR;
861 if ((c[i] == 'a') || (c[i] == 'A')) _mdns_debug |= MDNS_DEBUG_ASL;
862 if ((c[i] == 'm') || (c[i] == 'M')) _mdns_debug |= MDNS_DEBUG_MORE;
863 }
864 }
865 }
866
867 si_mod_t *
868 si_module_static_mdns(void)
869 {
870 static const struct si_mod_vtable_s mdns_vtable =
871 {
872 .sim_close = &mdns_close,
873 .sim_is_valid = &mdns_is_valid,
874 .sim_host_byname = &mdns_hostbyname,
875 .sim_host_byaddr = &mdns_hostbyaddr,
876 .sim_item_call = &mdns_item_call,
877 .sim_addrinfo = &mdns_addrinfo,
878 .sim_srv_byname = &mdns_srv_byname,
879 };
880
881 static si_mod_t si =
882 {
883 .vers = 1,
884 .refcount = 1,
885 .flags = SI_MOD_FLAG_STATIC,
886
887 .private = NULL,
888 .vtable = &mdns_vtable,
889 };
890
891 static dispatch_once_t once;
892
893 dispatch_once(&once, ^{
894 si.name = strdup("mdns");
895 _mdns_init();
896 });
897
898 return (si_mod_t*)&si;
899 }
900
901 /*
902 * _mdns_parse_domain_name
903 * Combine DNS labels to form a string.
904 * DNSService API does not return compressed names.
905 */
906 static char *
907 _mdns_parse_domain_name(const uint8_t *data, uint32_t datalen)
908 {
909 int i = 0, j = 0;
910 uint32_t len;
911 uint32_t domainlen = 0;
912 char *domain = NULL;
913
914 if ((data == NULL) || (datalen == 0)) return NULL;
915
916 /*
917 * i: index into input data
918 * j: index into output string
919 */
920 while (datalen-- > 0)
921 {
922 len = data[i++];
923 domainlen += (len + 1);
924 domain = reallocf(domain, domainlen);
925
926 if (domain == NULL) return NULL;
927
928 if (len == 0) break; // DNS root (NUL)
929
930 if (j > 0)
931 {
932 domain[j++] = datalen ? '.' : '\0';
933 }
934
935 while ((len-- > 0) && (0 != datalen--))
936 {
937 if (data[i] == '.')
938 {
939 /* special case: escape the '.' with a '\' */
940 domain = reallocf(domain, ++domainlen);
941 if (domain == NULL) return NULL;
942
943 domain[j++] = '\\';
944 }
945
946 domain[j++] = data[i++];
947 }
948 }
949
950 domain[j] = '\0';
951
952 return domain;
953 }
954
955 /*
956 * _mdns_pack_domain_name
957 * Format the string as packed DNS labels.
958 * Only used for one string at a time, therefore no need for compression.
959 */
960 static int
961 _mdns_pack_domain_name(const char *str, uint8_t *buf, size_t buflen)
962 {
963 int i = 0;
964 uintptr_t len = 0;
965
966 if ((str == NULL) || (buf == NULL)) return -1;
967
968 while (i < buflen)
969 {
970 /* calculate length to next '.' or '\0' */
971 char *dot = strchr(str, '.');
972 if (dot == NULL) dot = strchr(str, '\0');
973
974 len = (dot - str);
975 if (len > NS_MAXLABEL) return -1;
976
977 /* copy data for label */
978 buf[i++] = len;
979 while (str < dot && i < buflen)
980 {
981 buf[i++] = *str++;
982 }
983
984 /* skip past '.', break if '\0' */
985 if (*str++ == '\0') break;
986 }
987
988 if (i >= buflen) return -1;
989
990 if (len > 0)
991 {
992 /* no trailing dot - add a null label */
993 buf[i++] = 0;
994 if (i >= buflen) return -1;
995 }
996
997 buf[i] = '\0';
998 return i;
999 }
1000
1001 static int
1002 _is_rev_link_local(const char *name)
1003 {
1004 int len, i;
1005
1006 if (name == NULL) return 0;
1007
1008 len = strlen(name);
1009 if (len == 0) return 0;
1010
1011 /* check for trailing '.' */
1012 if (name[len - 1] == '.') len--;
1013
1014 if (len != IPv6_REVERSE_LEN) return 0;
1015
1016 i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR;
1017 if ((name[i] != '8') && (name[i] != '9') && (name[i] != 'A') && (name[i] != 'a') && (name[i] != 'B') && (name[i] != 'b')) return 0;
1018
1019 i = IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR + 1;
1020 if (strncasecmp(name + i, ".e.f.ip6.arpa", 13)) return 0;
1021
1022 for (i = 0; i < IPv6_REVERSE_LINK_LOCAL_TRAILING_CHAR; i += 2)
1023 {
1024 if (name[i] < '0') return 0;
1025 if ((name[i] > '9') && (name[i] < 'A')) return 0;
1026 if ((name[i] > 'F') && (name[i] < 'a')) return 0;
1027 if (name[i] > 'f') return 0;
1028 if (name[i + 1] != '.') return 0;
1029 }
1030
1031 return 1;
1032 }
1033
1034 /*
1035 * _mdns_ipv6_extract_scope_id
1036 * If the input string is a link local IPv6 address with an encoded scope id,
1037 * the scope id is extracted and a new string is constructed with the scope id removed.
1038 */
1039 static char *
1040 _mdns_ipv6_extract_scope_id(const char *name, uint32_t *out_ifnum)
1041 {
1042 char *qname = NULL;
1043 uint16_t nibble;
1044 uint32_t iface;
1045 int i;
1046
1047 if (out_ifnum != NULL) *out_ifnum = 0;
1048 if (name == NULL) return NULL;
1049
1050 /* examine the address, extract the scope id if present */
1051 if (_is_rev_link_local(name))
1052 {
1053 /* _is_rev_link_local rejects chars > 127 so it's safe to index into hexval */
1054 i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW;
1055 nibble = hexval[(uint32_t)name[i]];
1056 iface = nibble;
1057
1058 i += 2;
1059 nibble = hexval[(uint32_t)name[i]];
1060 iface += (nibble << 4);
1061
1062 i += 2;
1063 nibble = hexval[(uint32_t)name[i]];
1064 iface += (nibble << 8);
1065
1066 i += 2;
1067 nibble = hexval[(uint32_t)name[i]];
1068 iface += (nibble << 12);
1069
1070 if (iface != 0)
1071 {
1072 qname = strdup(name);
1073 if (qname == NULL) return NULL;
1074
1075 i = IPv6_REVERSE_LINK_LOCAL_SCOPE_ID_LOW;
1076 qname[i] = '0';
1077 qname[i + 2] = '0';
1078 qname[i + 4] = '0';
1079 qname[i + 6] = '0';
1080
1081 if (out_ifnum) *out_ifnum = iface;
1082 }
1083 }
1084
1085 return qname;
1086 }
1087
1088 static int
1089 _mdns_make_query(const char* name, int class, int type, uint8_t *buf, uint32_t buflen)
1090 {
1091 uint32_t len = 0;
1092
1093 if ((buf == NULL) || (buflen < (NS_HFIXEDSZ + NS_QFIXEDSZ))) return -1;
1094
1095 memset(buf, 0, NS_HFIXEDSZ);
1096 HEADER *hp = (HEADER *)buf;
1097
1098 len += NS_HFIXEDSZ;
1099 hp->id = arc4random();
1100 hp->qr = 1;
1101 hp->opcode = ns_o_query;
1102 hp->rd = 1;
1103 hp->rcode = ns_r_noerror;
1104 hp->qdcount = htons(1);
1105
1106 int n = _mdns_pack_domain_name(name, &buf[len], buflen - len);
1107 if (n < 0) return -1;
1108
1109 len += n;
1110 uint16_t word;
1111 word = htons(type);
1112 memcpy(&buf[len], &word, sizeof(word));
1113 len += sizeof(word);
1114 word = htons(class);
1115 memcpy(&buf[len], &word, sizeof(word));
1116 len += sizeof(word);
1117
1118 return len;
1119 }
1120
1121 typedef struct
1122 {
1123 mdns_reply_t *reply;
1124 mdns_hostent_t *host;
1125 uint8_t *answer; // DNS packet buffer
1126 size_t anslen; // DNS packet buffer current length
1127 size_t ansmaxlen; // DNS packet buffer maximum length
1128 int type; // type of query: A, AAAA, PTR, SRV...
1129 uint16_t last_type; // last type received
1130 uint32_t sd_gen;
1131 DNSServiceRef sd;
1132 DNSServiceFlags flags;
1133 DNSServiceErrorType error;
1134 int kq; // kqueue to notify when callback received
1135 } mdns_query_context_t;
1136
1137 static void
1138 _mdns_query_callback(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType, const char *, uint16_t, uint16_t, uint16_t, const void *, uint32_t, void *);
1139
1140 /*
1141 * _mdns_query_start
1142 * initializes the context and starts a DNS-SD query.
1143 */
1144 static DNSServiceErrorType
1145 _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)
1146 {
1147 DNSServiceErrorType status;
1148
1149 flags |= kDNSServiceFlagsShareConnection;
1150 flags |= kDNSServiceFlagsReturnIntermediates;
1151
1152 /* <rdar://problem/7428439> mDNSResponder is now responsible for timeouts */
1153 flags |= kDNSServiceFlagsTimeout;
1154
1155 memset(ctx, 0, sizeof(mdns_query_context_t));
1156
1157 if ((answer != NULL) && (anslen != NULL))
1158 {
1159 /* build a dummy DNS header to return to the caller */
1160 ctx->answer = answer;
1161 ctx->ansmaxlen = *anslen;
1162 ctx->anslen = _mdns_make_query(name, class, type, answer, ctx->ansmaxlen);
1163 if (ctx->anslen <= 0) return -1;
1164 }
1165
1166 ctx->type = type;
1167 ctx->sd = _mdns_sdref;
1168 ctx->sd_gen = _mdns_generation;
1169 ctx->kq = kq;
1170
1171 if (reply != NULL)
1172 {
1173 ctx->reply = reply;
1174 if (type == ns_t_a) ctx->host = reply->h4;
1175 else if (type == ns_t_aaaa) ctx->host = reply->h6;
1176 else if (type == ns_t_ptr && reply->h4) ctx->host = reply->h4;
1177 else if (type == ns_t_ptr && reply->h6) ctx->host = reply->h6;
1178 else if (type != ns_t_srv && type != ns_t_cname) return -1;
1179 }
1180
1181 uint32_t iface = 0;
1182 char *qname = _mdns_ipv6_extract_scope_id(name, &iface);
1183 if (qname == NULL) qname = (char *)name;
1184
1185 if (interface != NULL)
1186 {
1187 /* get interface number from name */
1188 int iface2 = if_nametoindex(interface);
1189
1190 /* balk if interface name lookup failed */
1191 if (iface2 == 0) return -1;
1192
1193 /* balk if scope id is set AND interface is given AND they don't match */
1194 if ((iface != 0) && (iface2 != 0) && (iface != iface2)) return -1;
1195 if (iface2 != 0) iface = iface2;
1196 }
1197
1198 _mdns_debug_message(";; mdns query %s type %d class %d [ctx %p]\n", qname, type, class, ctx);
1199
1200 status = DNSServiceQueryRecord(&ctx->sd, flags, iface, qname, type, class, _mdns_query_callback, ctx);
1201 if (qname != name) free(qname);
1202 return status;
1203 }
1204
1205 /*
1206 * _mdns_query_is_complete
1207 * Determines whether the specified query has sufficient information to be
1208 * considered complete.
1209 */
1210 static bool
1211 _mdns_query_is_complete(mdns_query_context_t *ctx)
1212 {
1213 bool complete = false;
1214
1215 /* NULL context is an error, but we call it complete */
1216 if (ctx == NULL) return true;
1217
1218 /*
1219 * The default is to ignore kDNSServiceFlagsMoreComing, since it has either
1220 * never been supported or worked correctly. MDNS_DEBUG_MORE makes us honor it.
1221 */
1222 if (ctx->flags & kDNSServiceFlagsMoreComing)
1223 {
1224 if (_mdns_debug & MDNS_DEBUG_MORE)
1225 {
1226 _mdns_debug_message(";; mdns is_complete type %d ctx %p more coming - incomplete\n", ctx->type, ctx);
1227 return false;
1228 }
1229 }
1230
1231 if (ctx->last_type != ctx->type)
1232 {
1233 _mdns_debug_message(";; mdns is_complete ctx %p type mismatch (%d != %d) - incomplete\n", ctx, ctx->last_type, ctx->type);
1234 return false;
1235 }
1236
1237 switch (ctx->type)
1238 {
1239 case ns_t_a:
1240 case ns_t_aaaa:
1241 if (ctx->host != NULL && ctx->host->addr_count > 0) complete = true;
1242 break;
1243 case ns_t_ptr:
1244 if (ctx->host != NULL && ctx->host->host.h_name != NULL) complete = true;
1245 break;
1246 case ns_t_srv:
1247 if (ctx->reply != NULL && ctx->reply->srv != NULL) complete = true;
1248 break;
1249 default:
1250 _mdns_debug_message(";; mdns is_complete unexpected type %d ctx %p\n", ctx->type, ctx);
1251 }
1252
1253 _mdns_debug_message(";; mdns is_complete type %d ctx %p %s%scomplete\n", ctx->type, ctx, (ctx->flags & kDNSServiceFlagsMoreComing) ? "(more coming flag ignored)" : "", complete ? " - " : " - in");
1254
1255 return complete;
1256 }
1257
1258 /*
1259 * _mdns_query_clear
1260 * Clear out the temporary fields of the context, and clear any result
1261 * structures that are incomplete. Retrns 1 if the query was complete.
1262 */
1263 static bool
1264 _mdns_query_clear(mdns_query_context_t *ctx)
1265 {
1266 bool complete = _mdns_query_is_complete(ctx);
1267
1268 if (ctx == NULL) return complete;
1269
1270 if (ctx->sd != NULL)
1271 {
1272 /* only dealloc this DNSServiceRef if the "main" _mdns_sdref has not been deallocated */
1273 if (ctx->sd != NULL && ctx->sd_gen == _mdns_generation)
1274 {
1275 DNSServiceRefDeallocate(ctx->sd);
1276 }
1277 }
1278
1279 ctx->sd = NULL;
1280 ctx->sd_gen = 0;
1281 ctx->flags = 0;
1282 ctx->kq = -1;
1283
1284 if (!complete)
1285 {
1286 _mdns_hostent_clear(ctx->host);
1287 ctx->anslen = -1;
1288 }
1289
1290 return complete;
1291 }
1292
1293 static void
1294 _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)
1295 {
1296 mdns_query_context_t *context;
1297 struct in6_addr a6;
1298
1299 context = (mdns_query_context_t *)ctx;
1300
1301 context->flags = flags;
1302 context->error = errorCode;
1303 context->last_type = rrtype;
1304
1305 if (errorCode != kDNSServiceErr_NoError)
1306 {
1307 _mdns_debug_message(";; [%s type %hu class %hu]: error %d [ctx %p]\n", fullname, rrtype, rrclass, errorCode, context);
1308 goto wakeup_kevent;
1309 }
1310
1311 /* embed the scope ID into link-local IPv6 addresses */
1312 if ((rrtype == ns_t_aaaa) && (rdlen == sizeof(struct in6_addr)) && IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)rdata))
1313 {
1314 memcpy(&a6, rdata, rdlen);
1315 a6.__u6_addr.__u6_addr16[1] = htons(ifIndex);
1316 rdata = &a6;
1317 }
1318
1319 if (context->reply != NULL)
1320 {
1321 char *name;
1322 int malformed = 0;
1323 mdns_reply_t *reply = context->reply;
1324
1325 if (reply->ifnum == 0) reply->ifnum = ifIndex;
1326
1327 _mdns_hostent_append_alias(context->host, fullname);
1328 if ((reply->ttl == 0) || (ttl < reply->ttl)) reply->ttl = ttl;
1329
1330 switch (rrtype)
1331 {
1332 case ns_t_a:
1333 case ns_t_aaaa:
1334 {
1335 if ((context->host != NULL) &&
1336 ((((rrtype == ns_t_a) && (context->host->host.h_addrtype == AF_INET)) || ((rrtype == ns_t_aaaa) && (context->host->host.h_addrtype == AF_INET6))) &&
1337 (rdlen >= context->host->host.h_length)))
1338 {
1339 if (context->host->host.h_name == NULL)
1340 {
1341 int i;
1342 mdns_hostent_t *h = context->host;
1343 char *h_name = _mdns_canonicalize(fullname);
1344 context->host->host.h_name = h_name;
1345
1346 /* 6863416 remove h_name from h_aliases */
1347 for (i = 0; i < h->alias_count; ++i)
1348 {
1349 if (h_name == NULL) break;
1350
1351 if (string_equal(h->host.h_aliases[i], h_name))
1352 {
1353 /* includes trailing NULL pointer */
1354 int sz = sizeof(char *) * (h->alias_count - i);
1355 free(h->host.h_aliases[i]);
1356 memmove(&h->host.h_aliases[i], &h->host.h_aliases[i+1], sz);
1357 h->alias_count -= 1;
1358 break;
1359 }
1360 }
1361 }
1362
1363 _mdns_hostent_append_addr(context->host, rdata, context->host->host.h_length);
1364 }
1365 else
1366 {
1367 malformed = 1;
1368 }
1369
1370 break;
1371 }
1372 case ns_t_cname:
1373 {
1374 name = _mdns_parse_domain_name(rdata, rdlen);
1375 if (name == NULL) malformed = 1;
1376
1377 _mdns_hostent_append_alias(context->host, name);
1378 _mdns_debug_message(";; [%s type %hu class %hu] cname %s [ctx %p]\n", fullname, rrtype, rrclass, name, context);
1379 free(name);
1380 break;
1381 }
1382 case ns_t_ptr:
1383 {
1384 name = _mdns_parse_domain_name(rdata, rdlen);
1385 if (name == NULL) malformed = 1;
1386
1387 if ((context->host != NULL) && (context->host->host.h_name == NULL))
1388 {
1389 context->host->host.h_name = _mdns_canonicalize(name);
1390 }
1391
1392 _mdns_hostent_append_alias(context->host, name);
1393 free(name);
1394 break;
1395 }
1396 case ns_t_srv:
1397 {
1398 mdns_rr_srv_t *p = (mdns_rr_srv_t *)rdata;
1399 mdns_srv_t *srv = calloc(1, sizeof(mdns_srv_t));
1400 if (srv == NULL) break;
1401
1402 if (rdlen < sizeof(mdns_rr_srv_t))
1403 {
1404 malformed = 1;
1405 break;
1406 }
1407
1408 srv->srv.priority = ntohs(p->priority);
1409 srv->srv.weight = ntohs(p->weight);
1410 srv->srv.port = ntohs(p->port);
1411 srv->srv.target = _mdns_parse_domain_name(&p->target[0], rdlen - 3*sizeof(uint16_t));
1412
1413 if (srv->srv.target == NULL)
1414 {
1415 malformed = 1;
1416 break;
1417 }
1418
1419 /* append to the end of the list */
1420 if (reply->srv == NULL)
1421 {
1422 reply->srv = srv;
1423 }
1424 else
1425 {
1426 mdns_srv_t *iter = reply->srv;
1427 while (iter->next) iter = iter->next;
1428 iter->next = srv;
1429 }
1430
1431 break;
1432 }
1433 default:
1434 {
1435 malformed = _mdns_debug;
1436 break;
1437 }
1438 }
1439
1440 if (malformed != 0)
1441 {
1442 _mdns_debug_message(";; [%s type %hu class %hu]: malformed reply [ctx %p]\n", fullname, rrtype, rrclass, context);
1443 goto wakeup_kevent;
1444 }
1445 }
1446
1447 if (context->answer != NULL)
1448 {
1449 int n;
1450 uint8_t *cp;
1451 HEADER *ans;
1452 size_t buflen = context->ansmaxlen - context->anslen;
1453
1454 if (buflen < NS_HFIXEDSZ)
1455 {
1456 _mdns_debug_message(";; [%s type %hu class %hu]: malformed reply (too small) [ctx %p]\n", fullname, rrtype, rrclass, context);
1457 goto wakeup_kevent;
1458 }
1459
1460 cp = context->answer + context->anslen;
1461
1462 n = _mdns_pack_domain_name(fullname, cp, buflen);
1463 if (n < 0)
1464 {
1465 _mdns_debug_message(";; [%s type %hu class %hu]: name mismatch [ctx %p]\n", fullname, rrtype, rrclass, context);
1466 goto wakeup_kevent;
1467 }
1468
1469 /*
1470 * check that there is enough space in the buffer for the
1471 * resource name (n), the resource record data (rdlen) and
1472 * the resource record header (10).
1473 */
1474 if (buflen < (n + rdlen + 10))
1475 {
1476 _mdns_debug_message(";; [%s type %hu class %hu]: insufficient buffer space for reply [ctx %p]\n", fullname, rrtype, rrclass, context);
1477 goto wakeup_kevent;
1478 }
1479
1480 cp += n;
1481 buflen -= n;
1482
1483 uint16_t word;
1484 uint32_t longword;
1485
1486 word = htons(rrtype);
1487 memcpy(cp, &word, sizeof(word));
1488 cp += sizeof(word);
1489
1490 word = htons(rrclass);
1491 memcpy(cp, &word, sizeof(word));
1492 cp += sizeof(word);
1493
1494 longword = htonl(ttl);
1495 memcpy(cp, &longword, sizeof(longword));
1496 cp += sizeof(longword);
1497
1498 word = htons(rdlen);
1499 memcpy(cp, &word, sizeof(word));
1500 cp += sizeof(word);
1501
1502 memcpy(cp, rdata, rdlen);
1503 cp += rdlen;
1504
1505 ans = (HEADER *)context->answer;
1506 ans->ancount = htons(ntohs(ans->ancount) + 1);
1507
1508 context->anslen = (size_t)(cp - context->answer);
1509 }
1510
1511 _mdns_debug_message(";; [%s type %hu class %hu] reply [ctx %p]\n", fullname, rrtype, rrclass, context);
1512
1513 wakeup_kevent:
1514
1515 /* Ping the waiting thread in case this callback was invoked on another */
1516 if (context->kq != -1)
1517 {
1518 struct kevent ev;
1519 EV_SET(&ev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0);
1520 int res = kevent(context->kq, &ev, 1, NULL, 0, NULL);
1521 if (res != 0) _mdns_debug_message(";; kevent EV_TRIGGER: %s [ctx %p]\n", strerror(errno), context);
1522 }
1523 }
1524
1525 static void
1526 _mdns_now(struct timespec *now)
1527 {
1528 struct timeval tv;
1529 gettimeofday(&tv, NULL);
1530 now->tv_sec = tv.tv_sec;
1531 now->tv_nsec = tv.tv_usec * 1000;
1532 }
1533
1534 static void
1535 _mdns_add_time(struct timespec *sum, const struct timespec *a, const struct timespec *b)
1536 {
1537 sum->tv_sec = a->tv_sec + b->tv_sec;
1538 sum->tv_nsec = a->tv_nsec + b->tv_nsec;
1539
1540 if (sum->tv_nsec > 1000000000)
1541 {
1542 sum->tv_sec += (sum->tv_nsec / 1000000000);
1543 sum->tv_nsec %= 1000000000;
1544 }
1545 }
1546
1547 /* calculate a deadline from the current time based on the desired timeout */
1548 static void
1549 _mdns_deadline(struct timespec *deadline, const struct timespec *delta)
1550 {
1551 struct timespec now;
1552 _mdns_now(&now);
1553 _mdns_add_time(deadline, &now, delta);
1554 }
1555
1556 static void
1557 _mdns_sub_time(struct timespec *delta, const struct timespec *a, const struct timespec *b)
1558 {
1559 delta->tv_sec = a->tv_sec - b->tv_sec;
1560 delta->tv_nsec = a->tv_nsec - b->tv_nsec;
1561
1562 if (delta->tv_nsec < 0)
1563 {
1564 delta->tv_nsec += 1000000000;
1565 delta->tv_sec -= 1;
1566 }
1567 }
1568
1569 /* calculate a timeout remaining before the given deadline */
1570 static void
1571 _mdns_timeout(struct timespec *timeout, const struct timespec *deadline)
1572 {
1573 struct timespec now;
1574 _mdns_now(&now);
1575 _mdns_sub_time(timeout, deadline, &now);
1576 }
1577
1578 int
1579 _mdns_search(const char *name, int class, int type, const char *interface, DNSServiceFlags flags, uint8_t *answer, uint32_t *anslen, mdns_reply_t *reply)
1580 {
1581 DNSServiceErrorType err = 0;
1582 int kq, n;
1583 struct kevent ev;
1584 struct timespec start, finish, delta, timeout;
1585 int res = 0;
1586 int i, got_a_response = 0;
1587 bool complete, initialize = true;
1588 bool wait = true;
1589 uint32_t n_iface_4 = 0;
1590
1591 /* determine number of IPv4 interfaces (ignore loopback) */
1592 si_inet_config(&n_iface_4, NULL);
1593 if (n_iface_4 > 0) n_iface_4--;
1594
1595 /* <rdar://problem/7732497> limit the number of initialization retries */
1596 int initialize_retries = 3;
1597
1598 /* 2 for A and AAAA parallel queries */
1599 int n_ctx = 0;
1600 mdns_query_context_t ctx[2];
1601
1602 if (name == NULL) return -1;
1603
1604 #if TARGET_OS_EMBEDDED
1605 /* log a warning for queries from the main thread */
1606 if (pthread_is_threaded_np() && pthread_main_np()) asl_log(NULL, NULL, ASL_LEVEL_WARNING, "Warning: Libinfo call to mDNSResponder on main thread");
1607 #endif /* TARGET_OS_EMBEDDED */
1608
1609 /*
1610 * Timeout Logic
1611 * The kevent(2) API timeout parameter is used to enforce the total
1612 * timeout of the DNS query. Each iteraion recalculates the relative
1613 * timeout based on the desired end time (total timeout from origin).
1614 *
1615 * In order to workaround some DNS configurations that do not return
1616 * responses for AAAA queries, parallel queries modify the total
1617 * timeout upon receipt of the first response. The new total timeout is
1618 * set to an effective value of 2N where N is the time taken to receive
1619 * the A response (the original total timeout is preserved if 2N would
1620 * have exceeded it). However, since mDNSResponder caches values, a
1621 * minimum value of 50ms for N is enforced in order to give some time
1622 * for the receipt of a AAAA response.
1623 */
1624
1625 /* determine the maximum time to wait for a result */
1626 delta.tv_sec = RES_MAXRETRANS + 5;
1627 delta.tv_nsec = 0;
1628 _mdns_deadline(&finish, &delta);
1629 timeout = delta;
1630 _mdns_now(&start);
1631
1632 for (i = 0; i < 2; ++i) memset(&ctx[i], 0 , sizeof(mdns_query_context_t));
1633
1634 /* set up the kqueue */
1635 kq = kqueue();
1636 EV_SET(&ev, 1, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0);
1637 n = kevent(kq, &ev, 1, NULL, 0, NULL);
1638 if (n != 0) wait = false;
1639
1640 while (wait)
1641 {
1642 if (initialize)
1643 {
1644 initialize = false;
1645 pthread_mutex_lock(&_mdns_mutex);
1646
1647 /* clear any stale contexts */
1648 for (i = 0; i < n_ctx; ++i) _mdns_query_clear(&ctx[i]);
1649 n_ctx = 0;
1650
1651 if (_mdns_sdref == NULL)
1652 {
1653 if (_mdns_old_sdref != NULL)
1654 {
1655 _mdns_generation++;
1656 DNSServiceRefDeallocate(_mdns_old_sdref);
1657 _mdns_old_sdref = NULL;
1658 }
1659
1660 /* (re)initialize the shared connection */
1661 err = DNSServiceCreateConnection(&_mdns_sdref);
1662
1663 /* limit the number of retries */
1664 if ((initialize_retries-- <= 0) && (err == 0)) err = kDNSServiceErr_Unknown;
1665 if (err != 0)
1666 {
1667 wait = false;
1668 pthread_mutex_unlock(&_mdns_mutex);
1669 break;
1670 }
1671 }
1672
1673 /*
1674 * issue (or reissue) the queries
1675 * unspecified type: do parallel A and AAAA
1676 */
1677 if (err == 0)
1678 {
1679 err = _mdns_query_start(&ctx[n_ctx++], reply, answer, anslen, name, class, (type == 0) ? ns_t_a : type, interface, flags, kq);
1680 }
1681
1682 if ((err == 0) && (type == 0))
1683 {
1684 err = _mdns_query_start(&ctx[n_ctx++], reply, answer, anslen, name, class, ns_t_aaaa, interface, flags, kq);
1685 }
1686
1687 if (err != 0) _mdns_debug_message(";; initialization error %d\n", err);
1688
1689 /* try to reinitialize */
1690 if ((err == kDNSServiceErr_Unknown) || (err == kDNSServiceErr_ServiceNotRunning) || (err == kDNSServiceErr_BadReference))
1691 {
1692 if (_mdns_sdref != NULL)
1693 {
1694 _mdns_generation++;
1695 DNSServiceRefDeallocate(_mdns_sdref);
1696 _mdns_sdref = NULL;
1697 }
1698
1699 err = 0;
1700 initialize = true;
1701 pthread_mutex_unlock(&_mdns_mutex);
1702 continue;
1703 }
1704 else if (err != 0)
1705 {
1706 pthread_mutex_unlock(&_mdns_mutex);
1707 break;
1708 }
1709
1710 /* (re)register the fd with kqueue */
1711 int fd = DNSServiceRefSockFD(_mdns_sdref);
1712 EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
1713 n = kevent(kq, &ev, 1, NULL, 0, NULL);
1714 pthread_mutex_unlock(&_mdns_mutex);
1715 if (err != 0 || n != 0) break;
1716 }
1717
1718 _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);
1719
1720 n = kevent(kq, NULL, 0, &ev, 1, &timeout);
1721 if ((n < 0) && (errno != EINTR))
1722 {
1723 res = -1;
1724 break;
1725 }
1726
1727 pthread_mutex_lock(&_mdns_mutex);
1728
1729 /*
1730 * DNSServiceProcessResult() is a blocking API
1731 * confirm that there is still data on the socket
1732 */
1733 const struct timespec notimeout = { 0, 0 };
1734 int m = kevent(kq, NULL, 0, &ev, 1, &notimeout);
1735
1736 if (_mdns_sdref == NULL)
1737 {
1738 initialize = true;
1739 }
1740 else if (m > 0 && ev.filter == EVFILT_READ)
1741 {
1742 err = DNSServiceProcessResult(_mdns_sdref);
1743 if ((err == kDNSServiceErr_ServiceNotRunning) || (err == kDNSServiceErr_BadReference))
1744 {
1745 _mdns_debug_message(";; DNSServiceProcessResult status %d [ctx %p %p]\n", err, (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
1746 err = 0;
1747
1748 /* re-initialize the shared connection */
1749 _mdns_generation++;
1750 DNSServiceRefDeallocate(_mdns_sdref);
1751 _mdns_sdref = NULL;
1752 initialize = true;
1753 }
1754 }
1755
1756 /* Check if all queries are complete (including errors) */
1757 complete = true;
1758 for (i = 0; i < n_ctx; ++i)
1759 {
1760 if ((ctx[i].error != 0) || _mdns_query_is_complete(&ctx[i]))
1761 {
1762 if (ctx[i].type == ns_t_a)
1763 {
1764 got_a_response = GOT_DATA;
1765 if (ctx[i].error != 0) got_a_response = GOT_ERROR;
1766 }
1767
1768 _mdns_debug_message(";; [%s type %d class %d] finished processing ctx %p\n", name, type, class, &(ctx[i]));
1769 }
1770 else
1771 {
1772 _mdns_debug_message(";; [%s type %d class %d] continuing ctx %p\n", name, type, class, &(ctx[i]));
1773 complete = false;
1774 }
1775 }
1776
1777 pthread_mutex_unlock(&_mdns_mutex);
1778
1779 if (err != 0)
1780 {
1781 _mdns_debug_message(";; DNSServiceProcessResult error status %d [ctx %p %p]\n", err, (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
1782 break;
1783 }
1784 else if (complete)
1785 {
1786 _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);
1787 break;
1788 }
1789 else if (got_a_response != 0)
1790 {
1791 /* got A, adjust deadline for AAAA */
1792 struct timespec now, tn, extra;
1793
1794 /* delta = now - start */
1795 _mdns_now(&now);
1796 _mdns_sub_time(&delta, &now, &start);
1797
1798 extra.tv_sec = SHORT_AAAA_EXTRA;
1799 extra.tv_nsec = 0;
1800
1801 /* if delta is small (<= 20 milliseconds), we probably got a result from mDNSResponder's cache */
1802 if ((delta.tv_sec == 0) && (delta.tv_nsec <= 20000000))
1803 {
1804 extra.tv_sec = MEDIUM_AAAA_EXTRA;
1805 }
1806 else if (n_iface_4 == 0)
1807 {
1808 extra.tv_sec = LONG_AAAA_EXTRA;
1809 }
1810 else if (got_a_response == GOT_ERROR)
1811 {
1812 extra.tv_sec = MEDIUM_AAAA_EXTRA;
1813 }
1814
1815 /* tn = 2 * delta */
1816 _mdns_add_time(&tn, &delta, &delta);
1817
1818 /* delta = tn + extra */
1819 _mdns_add_time(&delta, &tn, &extra);
1820
1821 /* check that delta doesn't exceed our total timeout */
1822 _mdns_sub_time(&tn, &timeout, &delta);
1823 if (tn.tv_sec >= 0)
1824 {
1825 _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);
1826 _mdns_deadline(&finish, &delta);
1827 }
1828 }
1829
1830 /* calculate remaining timeout */
1831 _mdns_timeout(&timeout, &finish);
1832
1833 /* check for time remaining */
1834 if (timeout.tv_sec < 0)
1835 {
1836 _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);
1837 break;
1838 }
1839 }
1840
1841 complete = false;
1842 pthread_mutex_lock(&_mdns_mutex);
1843
1844 for (i = 0; i < n_ctx; ++i)
1845 {
1846 if (err == 0) err = ctx[i].error;
1847 /* only clears hostents if result is incomplete */
1848 complete = _mdns_query_clear(&ctx[i]) | complete;
1849 }
1850
1851 pthread_mutex_unlock(&_mdns_mutex);
1852
1853 /* everything should be done with the kq by now */
1854 close(kq);
1855
1856 /* return error if everything is incomplete */
1857 if (!complete) res = -1;
1858
1859 if (anslen != NULL) *anslen = ctx[0].anslen;
1860 return res;
1861 }