2 * Copyright (c) 2008 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@
33 #include <si_module.h>
35 #include <dns_private.h>
38 #define DNS_MAX_RECEIVE_SIZE 65536
40 #define MDNS_HANDLE_NAME "*MDNS*"
42 #define DNS_HANDLE_BUSY 0x00000001
43 #define MDNS_HANDLE_BUSY 0x00000002
48 static pthread_mutex_t dns_plugin_lock
= PTHREAD_MUTEX_INITIALIZER
;
56 } dns_build_hostent_t
;
63 } dns_plugin_private_t
;
65 #define DNS_FLAGS_RCODE_MASK 0x000f
66 static const char hexchar
[] = "0123456789abcdef";
69 dns_checkout_handle(si_mod_t
*si
)
71 dns_plugin_private_t
*pp
;
74 if (si
== NULL
) return NULL
;
76 pthread_mutex_lock(&dns_plugin_lock
);
77 if (si
->private == NULL
)
79 pp
= (dns_plugin_private_t
*)calloc(1, sizeof(dns_plugin_private_t
));
84 pp
= (dns_plugin_private_t
*)si
->private;
88 /* shouldn't happen */
90 pthread_mutex_unlock(&dns_plugin_lock
);
94 if ((pp
->flags
& DNS_HANDLE_BUSY
) == 0)
96 if (pp
->dns
== NULL
) pp
->dns
= dns_open(NULL
);
97 pp
->flags
|= DNS_HANDLE_BUSY
;
98 pthread_mutex_unlock(&dns_plugin_lock
);
102 /* main dns handle is busy - create a temporary one */
103 dns
= dns_open(NULL
);
104 pthread_mutex_unlock(&dns_plugin_lock
);
109 mdns_checkout_handle(si_mod_t
*si
)
111 dns_plugin_private_t
*pp
;
114 if (si
== NULL
) return NULL
;
116 pthread_mutex_lock(&dns_plugin_lock
);
117 if (si
->private == NULL
)
119 pp
= (dns_plugin_private_t
*)calloc(1, sizeof(dns_plugin_private_t
));
124 pp
= (dns_plugin_private_t
*)si
->private;
128 /* shouldn't happen */
129 dns
= dns_open(MDNS_HANDLE_NAME
);
130 pthread_mutex_unlock(&dns_plugin_lock
);
134 if ((pp
->flags
& MDNS_HANDLE_BUSY
) == 0)
136 if (pp
->mdns
== NULL
) pp
->mdns
= dns_open(MDNS_HANDLE_NAME
);
137 pp
->flags
|= MDNS_HANDLE_BUSY
;
138 pthread_mutex_unlock(&dns_plugin_lock
);
142 /* main mdns handle is busy - create a temporary one */
143 dns
= dns_open(MDNS_HANDLE_NAME
);
144 pthread_mutex_unlock(&dns_plugin_lock
);
149 dns_checkin_handle(si_mod_t
*si
, dns_handle_t dns
)
151 dns_plugin_private_t
*pp
;
153 if (si
== NULL
) return;
154 if (dns
== NULL
) return;
156 pthread_mutex_lock(&dns_plugin_lock
);
157 if (si
->private == NULL
)
159 /* shouldn't happen */
160 pp
= (dns_plugin_private_t
*)calloc(1, sizeof(dns_plugin_private_t
));
164 pp
= (dns_plugin_private_t
*)si
->private;
168 /* shouldn't happen */
170 pthread_mutex_unlock(&dns_plugin_lock
);
176 pp
->flags
&= ~DNS_HANDLE_BUSY
;
177 pthread_mutex_unlock(&dns_plugin_lock
);
180 else if (pp
->mdns
== dns
)
182 pp
->flags
&= ~MDNS_HANDLE_BUSY
;
183 pthread_mutex_unlock(&dns_plugin_lock
);
188 pthread_mutex_unlock(&dns_plugin_lock
);
192 dns_reverse_ipv4(const char *addr
)
201 if (addr
== NULL
) return NULL
;
203 memcpy(&(ab
.a
), addr
, 4);
205 asprintf(&p
, "%u.%u.%u.%u.in-addr.arpa.", ab
.b
[3], ab
.b
[2], ab
.b
[1], ab
.b
[0]);
210 dns_reverse_ipv6(const char *addr
)
216 if (addr
== NULL
) return NULL
;
220 for (i
= 0; i
< 16; i
++)
226 x
[j
--] = hexchar
[hi
];
228 x
[j
--] = hexchar
[lo
];
232 if (p
== NULL
) return NULL
;
235 strcat(p
, "ip6.arpa.");
241 dns_lower_case(const char *s
)
246 if (s
== NULL
) return NULL
;
247 t
= malloc(strlen(s
) + 1);
249 for (i
= 0; s
[i
] != '\0'; i
++)
251 if ((s
[i
] >= 'A') && (s
[i
] <= 'Z')) t
[i
] = s
[i
] + 32;
259 dns_host_merge_alias(const char *name
, dns_build_hostent_t
*h
)
263 if (name
== NULL
) return 0;
264 if (h
== NULL
) return 0;
266 if ((h
->host
.h_name
!= NULL
) && (!strcmp(name
, h
->host
.h_name
))) return 0;
267 for (i
= 0; i
< h
->alias_count
; i
++) if (!strcmp(name
, h
->host
.h_aliases
[i
])) return 0;
269 h
->host
.h_aliases
= (char **)reallocf(h
->host
.h_aliases
, (h
->alias_count
+ 2) * sizeof(char *));
270 if (h
->host
.h_aliases
== NULL
)
276 h
->host
.h_aliases
[h
->alias_count
] = dns_lower_case(name
);
278 h
->host
.h_aliases
[h
->alias_count
] = NULL
;
284 dns_host_append_addr(const char *addr
, uint32_t len
, dns_build_hostent_t
*h
)
286 if (addr
== NULL
) return 0;
287 if (h
== NULL
) return 0;
289 if (h
->addr_count
== 0) h
->host
.h_addr_list
= (char **)calloc(2, sizeof(char *));
290 else h
->host
.h_addr_list
= (char **)reallocf(h
->host
.h_addr_list
, (h
->addr_count
+ 2) * sizeof(char *));
292 if (h
->host
.h_addr_list
== NULL
)
298 h
->host
.h_addr_list
[h
->addr_count
] = malloc(len
);
299 if (h
->host
.h_addr_list
[h
->addr_count
] == NULL
) return -1;
301 memcpy(h
->host
.h_addr_list
[h
->addr_count
], addr
, len
);
303 h
->host
.h_addr_list
[h
->addr_count
] = NULL
;
309 dns_plugin_clear_host(dns_build_hostent_t
*h
)
314 if (h
== NULL
) return;
316 if (h
->host
.h_name
!= NULL
) free(h
->host
.h_name
);
317 h
->host
.h_name
= NULL
;
319 aliases
= h
->host
.h_aliases
;
322 while (*aliases
!= NULL
) free(*aliases
++);
323 free(h
->host
.h_aliases
);
326 h
->host
.h_aliases
= NULL
;
328 if (h
->host
.h_addr_list
!= NULL
)
330 for (i
= 0; h
->host
.h_addr_list
[i
] != NULL
; i
++) free(h
->host
.h_addr_list
[i
]);
331 free(h
->host
.h_addr_list
);
334 h
->host
.h_addr_list
= NULL
;
338 dns_reply_to_hostent(dns_reply_t
*r
, int af
, const char *addr
, dns_build_hostent_t
*h
)
340 int i
, got_data
, got_addr
;
343 if (r
== NULL
) return -1;
344 if (r
->status
!= DNS_STATUS_OK
) return -1;
345 if ((r
->header
->flags
& DNS_FLAGS_RCODE_MASK
) != ns_r_noerror
) return -1;
347 if (r
== NULL
) return -1;
348 if (r
->header
== NULL
) return -1;
349 if (r
->header
->ancount
== 0) return -1;
356 for (i
= 0; i
< r
->header
->ancount
; i
++)
358 if ((af
== AF_INET
) && (r
->answer
[i
]->dnstype
== ns_t_a
))
362 dns_host_append_addr((const char *)&(r
->answer
[i
]->data
.A
->addr
), 4, h
);
363 if (h
->ttl
== 0) h
->ttl
= r
->answer
[i
]->ttl
;
364 else if (r
->answer
[i
]->ttl
< h
->ttl
) h
->ttl
= r
->answer
[i
]->ttl
;
367 else if ((af
== AF_INET6
) && (r
->answer
[i
]->dnstype
== ns_t_aaaa
))
371 dns_host_append_addr((const char *)&(r
->answer
[i
]->data
.AAAA
->addr
), 16, h
);
372 if (h
->ttl
== 0) h
->ttl
= r
->answer
[i
]->ttl
;
373 else if (r
->answer
[i
]->ttl
< h
->ttl
) h
->ttl
= r
->answer
[i
]->ttl
;
376 else if (r
->answer
[i
]->dnstype
== ns_t_cname
)
379 if (cnamex
== -1) cnamex
= i
;
380 if (h
->ttl
== 0) h
->ttl
= r
->answer
[i
]->ttl
;
381 else if (r
->answer
[i
]->ttl
< h
->ttl
) h
->ttl
= r
->answer
[i
]->ttl
;
384 else if (r
->answer
[i
]->dnstype
== ns_t_ptr
)
387 if (ptrx
== -1) ptrx
= i
;
388 if (h
->ttl
== 0) h
->ttl
= r
->answer
[i
]->ttl
;
389 else if (r
->answer
[i
]->ttl
< h
->ttl
) h
->ttl
= r
->answer
[i
]->ttl
;
398 dns_host_append_addr(addr
, 4, h
);
400 else if (af
== AF_INET6
)
403 dns_host_append_addr(addr
, 16, h
);
407 if (got_data
== 0) return -1;
408 if (got_addr
== 0) return -1;
410 h
->host
.h_addrtype
= af
;
411 if (af
== AF_INET
) h
->host
.h_length
= 4;
412 else h
->host
.h_length
= 16;
416 /* use name from PTR record */
417 h
->host
.h_name
= dns_lower_case(r
->answer
[ptrx
]->data
.PTR
->name
);
419 else if (cnamex
!= -1)
421 /* use name from CNAME record */
422 h
->host
.h_name
= dns_lower_case(r
->answer
[cnamex
]->data
.CNAME
->name
);
426 /* use name in first answer */
427 h
->host
.h_name
= dns_lower_case(r
->answer
[0]->name
);
430 for (i
= 0; i
< r
->header
->ancount
; i
++)
432 if (r
->answer
[i
]->dnstype
== ns_t_cname
)
434 dns_host_merge_alias(r
->answer
[cnamex
]->data
.CNAME
->name
, h
);
435 dns_host_merge_alias(r
->answer
[cnamex
]->name
, h
);
439 if (h
->alias_count
== 0) h
->host
.h_aliases
= (char **)calloc(1, sizeof(char *));
445 _internal_host_byname(si_mod_t
*si
, const char *name
, int af
, const char *ignored
, uint32_t *err
, int which
)
449 dns_build_hostent_t h
;
455 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
459 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
463 if (af
== AF_INET
) type
= ns_t_a
;
464 else if (af
== AF_INET6
) type
= ns_t_aaaa
;
467 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
474 if (which
== MODULE_DNS
) dns
= dns_checkout_handle(si
);
475 else if (which
== MODULE_MDNS
) dns
= mdns_checkout_handle(si
);
479 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
483 r
= dns_lookup(dns
, name
, ns_c_in
, type
);
484 dns_checkin_handle(si
, dns
);
488 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
492 memset(&h
, 0, sizeof(dns_build_hostent_t
));
494 status
= dns_reply_to_hostent(r
, af
, NULL
, &h
);
499 dns_plugin_clear_host(&h
);
500 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
504 bb
= h
.ttl
+ time(NULL
);
508 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
, af
, h
.host
.h_length
, h
.host
.h_addr_list
);
512 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
, af
, h
.host
.h_length
, h
.host
.h_addr_list
);
515 dns_plugin_clear_host(&h
);
517 if ((out
== NULL
) && (err
!= NULL
)) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
523 dns_host_byname(si_mod_t
*si
, const char *name
, int af
, const char *ignored
, uint32_t *err
)
525 return _internal_host_byname(si
, name
, af
, NULL
, err
, MODULE_DNS
);
529 mdns_host_byname(si_mod_t
*si
, const char *name
, int af
, const char *ignored
, uint32_t *err
)
531 return _internal_host_byname(si
, name
, af
, NULL
, err
, MODULE_MDNS
);
535 _internal_host_byaddr(si_mod_t
*si
, const void *addr
, int af
, const char *ignored
, uint32_t *err
, int which
)
539 dns_build_hostent_t h
;
546 struct sockaddr_storage from
;
549 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
553 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
559 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
563 fromlen
= sizeof(struct sockaddr_storage
);
564 memset(&from
, 0, fromlen
);
575 name
= dns_reverse_ipv4(addr
);
576 cat
= CATEGORY_HOST_IPV4
;
578 else if (af
== AF_INET6
)
581 name
= dns_reverse_ipv6(addr
);
582 cat
= CATEGORY_HOST_IPV6
;
586 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
593 if (which
== MODULE_DNS
) dns
= dns_checkout_handle(si
);
594 else if (which
== MODULE_MDNS
) dns
= mdns_checkout_handle(si
);
598 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
602 r
= dns_lookup(dns
, name
, ns_c_in
, type
);
603 dns_checkin_handle(si
, dns
);
608 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
612 memset(&h
, 0, sizeof(dns_build_hostent_t
));
614 if (dns_reply_to_hostent(r
, af
, addr
, &h
) < 0)
616 dns_plugin_clear_host(&h
);
617 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
621 bb
= h
.ttl
+ time(NULL
);
622 out
= (si_item_t
*)LI_ils_create("L4488s*44a", (unsigned long)si
, cat
, 1, bb
, 0LL, h
.host
.h_name
, h
.host
.h_aliases
, af
, h
.host
.h_length
, h
.host
.h_addr_list
);
624 dns_plugin_clear_host(&h
);
626 if ((out
== NULL
) && (err
!= NULL
)) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
632 dns_host_byaddr(si_mod_t
*si
, const void *addr
, int af
, const char *ignored
, uint32_t *err
)
634 return _internal_host_byaddr(si
, addr
, af
, NULL
, err
, MODULE_DNS
);
638 mdns_host_byaddr(si_mod_t
*si
, const void *addr
, int af
, const char *ignored
, uint32_t *err
)
640 return _internal_host_byaddr(si
, addr
, af
, NULL
, err
, MODULE_MDNS
);
644 * We support dns_async_start / cancel / handle_reply using dns_item_call
647 _internal_item_call(si_mod_t
*si
, int call
, const char *name
, const char *ignored1
, const char *ignored2
, uint32_t class, uint32_t type
, uint32_t *err
, int which
)
650 char buf
[DNS_MAX_RECEIVE_SIZE
];
652 struct sockaddr_storage from
;
656 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
657 if ((call
!= SI_CALL_DNS_QUERY
) && (call
!= SI_CALL_DNS_SEARCH
))
659 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
665 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
671 if (which
== MODULE_DNS
) dns
= dns_checkout_handle(si
);
672 else if (which
== MODULE_MDNS
) dns
= mdns_checkout_handle(si
);
676 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
680 fromlen
= sizeof(struct sockaddr_storage
);
681 memset(&from
, 0, fromlen
);
684 if (call
== SI_CALL_DNS_QUERY
) len
= dns_query(dns
, name
, class, type
, buf
, sizeof(buf
), (struct sockaddr
*)&from
, &fromlen
);
685 else if (call
== SI_CALL_DNS_SEARCH
) len
= dns_search(dns
, name
, class, type
, buf
, sizeof(buf
), (struct sockaddr
*)&from
, &fromlen
);
687 dns_checkin_handle(si
, dns
);
689 if ((len
<= 0) || (len
> DNS_MAX_RECEIVE_SIZE
))
691 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
695 out
= (si_item_t
*)LI_ils_create("L4488@@", (unsigned long)si
, CATEGORY_DNSPACKET
, 1, 0LL, 0LL, len
, buf
, fromlen
, &from
);
696 if ((out
== NULL
) && (err
!= NULL
)) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
702 dns_item_call(si_mod_t
*si
, int call
, const char *name
, const char *ignored1
, const char *ignored2
, uint32_t class, uint32_t type
, uint32_t *err
)
704 return _internal_item_call(si
, call
, name
, ignored1
, ignored2
, class, type
, err
, MODULE_DNS
);
708 mdns_item_call(si_mod_t
*si
, int call
, const char *name
, const char *ignored1
, const char *ignored2
, uint32_t class, uint32_t type
, uint32_t *err
)
710 return _internal_item_call(si
, call
, name
, ignored1
, ignored2
, class, type
, err
, MODULE_MDNS
);
714 dns_is_valid(si_mod_t
*si
, si_item_t
*item
)
719 if (si
== NULL
) return 0;
720 if (item
== NULL
) return 0;
721 if (si
->name
== NULL
) return 0;
722 if (item
->src
== NULL
) return 0;
724 src
= (si_mod_t
*)item
->src
;
726 if (src
->name
== NULL
) return 0;
727 if (string_not_equal(si
->name
, src
->name
)) return 0;
730 if (item
->validation_a
< now
) return 0;
735 mdns_is_valid(si_mod_t
*si
, si_item_t
*item
)
741 dns_close(si_mod_t
*si
)
743 dns_plugin_private_t
*pp
;
745 if (si
== NULL
) return;
747 pp
= (dns_plugin_private_t
*)si
->private;
748 if (pp
== NULL
) return;
750 if (pp
->dns
!= NULL
) dns_free(pp
->dns
);
755 dns_init(si_mod_t
*si
)
757 if (si
== NULL
) return 1;
761 si
->sim_close
= dns_close
;
762 si
->sim_is_valid
= dns_is_valid
;
763 si
->sim_host_byname
= dns_host_byname
;
764 si
->sim_host_byaddr
= dns_host_byaddr
;
765 si
->sim_item_call
= dns_item_call
;
771 mdns_close(si_mod_t
*si
)
777 mdns_init(si_mod_t
*si
)
779 if (si
== NULL
) return 1;
783 si
->sim_close
= dns_close
;
784 si
->sim_is_valid
= mdns_is_valid
;
785 si
->sim_host_byname
= mdns_host_byname
;
786 si
->sim_host_byaddr
= mdns_host_byaddr
;
787 si
->sim_item_call
= mdns_item_call
;