2 * Copyright (c) 2004-2020 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@
25 * Modification History
27 * March 22, 2004 Allan Nathanson <ajn@apple.com>
31 #include <TargetConditionals.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <arpa/nameser.h>
44 #include <CommonCrypto/CommonDigest.h>
46 #include <CoreFoundation/CoreFoundation.h>
47 #include <SystemConfiguration/SystemConfiguration.h>
48 #include <SystemConfiguration/SCPrivate.h>
49 #include <SystemConfiguration/SCValidation.h>
50 #include "ip_plugin.h"
52 #include "dns-configuration.h"
55 #include "dnsinfo_create.h"
56 #include "dnsinfo_internal.h"
57 #include "dnsinfo_logging.h"
58 #include "dnsinfo_private.h"
59 #include "dnsinfo_server.h"
61 #include <network_information.h>
64 #include <dns_sd_private.h>
67 #include <CoreServices/CoreServices.h>
68 #else // TARGET_OS_IPHONE
69 #include <FSEvents/FSEvents.h>
70 #endif // TARGET_OS_IPHONE
72 #define DNS_CONFIGURATION_FLAGS_KEY CFSTR("__FLAGS__")
73 #define DNS_CONFIGURATION_IF_INDEX_KEY CFSTR("__IF_INDEX__")
74 #define DNS_CONFIGURATION_ORDER_KEY CFSTR("__ORDER__")
76 /* multicast DNS resolver configurations */
77 static CFNumberRef S_mdns_timeout
= NULL
;
79 /* private DNS resolver configurations */
80 static CFNumberRef S_pdns_timeout
= NULL
;
84 #pragma mark DNS resolver flags
87 static __inline__ boolean_t
88 dns_resolver_flags_all_queries(uint32_t query_flags
)
90 return ((query_flags
& DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS
) == DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS
);
97 dns_resolver_flags_service(CFDictionaryRef service
, uint32_t resolver_flags
)
100 // check if the service has v4 configured
101 if (((resolver_flags
& DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS
) == 0) &&
102 service_is_routable(service
, AF_INET
)) {
103 resolver_flags
|= DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS
;
106 // check if the service has v6 configured
107 if (((resolver_flags
& DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS
) == 0) &&
108 service_is_routable(service
, AF_INET6
)) {
109 resolver_flags
|= DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS
;
112 return resolver_flags
;
117 add_dns_resolver_flags(const void *key
, const void *value
, void *context
)
120 CFDictionaryRef service
= (CFDictionaryRef
)value
;
121 // CFStringRef serviceID = (CFStringRef)key;
122 uint32_t *resolver_flags
= (uint32_t *)context
;
124 if (service_is_scoped_only(service
)) {
128 // update resovler flags based on configured (and available) protocols
129 *resolver_flags
= dns_resolver_flags_service(service
, *resolver_flags
);
135 #pragma mark DNS resolver configuration
139 add_resolver(CFMutableArrayRef resolvers
, CFMutableDictionaryRef resolver
)
142 CFStringRef interface
;
145 uint32_t order_val
= 0;
147 order
= CFDictionaryGetValue(resolver
, kSCPropNetDNSSearchOrder
);
148 if (!isA_CFNumber(order
) ||
149 !CFNumberGetValue(order
, kCFNumberSInt32Type
, &order_val
)) {
154 n_resolvers
= CFArrayGetCount(resolvers
);
155 for (i
= 0; i
< n_resolvers
; i
++) {
156 CFDictionaryRef match_resolver
;
158 match_resolver
= CFArrayGetValueAtIndex(resolvers
, i
);
159 if (CFEqual(resolver
, match_resolver
)) {
165 CFMutableDictionaryRef compare
;
168 compare
= CFDictionaryCreateMutableCopy(NULL
, 0, match_resolver
);
169 CFDictionarySetValue(compare
, kSCPropNetDNSSearchOrder
, order
);
170 match
= CFEqual(resolver
, compare
);
173 CFNumberRef match_order
;
174 uint32_t match_order_val
= 0;
176 // if only the search order's are different
177 match_order
= CFDictionaryGetValue(match_resolver
, kSCPropNetDNSSearchOrder
);
178 if (!isA_CFNumber(match_order
) ||
179 !CFNumberGetValue(match_order
, kCFNumberSInt32Type
, &match_order_val
)) {
183 if (order_val
< match_order_val
) {
184 // if we should prefer this match resolver, else just skip it
185 CFArraySetValueAtIndex(resolvers
, i
, resolver
);
193 order
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &n_resolvers
);
194 CFDictionarySetValue(resolver
, DNS_CONFIGURATION_ORDER_KEY
, order
);
197 interface
= CFDictionaryGetValue(resolver
, kSCPropInterfaceName
);
198 if ((interface
!= NULL
) && !CFEqual(interface
, CFSTR("*"))) {
200 unsigned int if_index
= 0;
201 char if_name
[IF_NAMESIZE
];
205 if (_SC_cfstring_to_cstring(interface
,
208 kCFStringEncodingASCII
) != NULL
) {
209 if_index
= my_if_nametoindex(if_name
);
212 if ((if_index
!= 0) &&
214 // check if this is a "scoped" configuration
215 (CFDictionaryGetValueIfPresent(resolver
, DNS_CONFIGURATION_FLAGS_KEY
, (const void **)&num
) &&
217 CFNumberGetValue(num
, kCFNumberSInt32Type
, &flags
) &&
218 (flags
& DNS_RESOLVER_FLAGS_SCOPED
) != 0)
220 // check if we should scope all queries with this configuration
221 (CFDictionaryGetValueIfPresent(resolver
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, (const void **)&val
) &&
222 isA_CFBoolean(val
) &&
223 CFBooleanGetValue(val
))
226 // if interface index available and it should be used
227 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &if_index
);
228 CFDictionarySetValue(resolver
, DNS_CONFIGURATION_IF_INDEX_KEY
, num
);
233 CFArrayAppendValue(resolvers
, resolver
);
238 #define DNS_CONFIGURATION_CONFIGURATION_ID CFSTR("__CONFIGURATION_ID__")
242 add_resolver_signature(CFMutableDictionaryRef resolver
, const char *rType
, CFStringRef cID
, CFIndex rIndex
)
246 str
= CFStringCreateWithFormat(NULL
, NULL
,
247 CFSTR("%s:%s%@ %ld"),
249 (cID
!= NULL
) ? " " : "",
250 (cID
!= NULL
) ? cID
: CFSTR(""),
252 CFDictionarySetValue(resolver
, DNS_CONFIGURATION_CONFIGURATION_ID
, str
);
260 add_supplemental(CFMutableArrayRef resolvers
,
261 CFStringRef serviceID
,
263 uint32_t defaultOrder
,
272 domains
= CFDictionaryGetValue(dns
, kSCPropNetDNSSupplementalMatchDomains
);
273 n_domains
= isA_CFArray(domains
) ? CFArrayGetCount(domains
) : 0;
274 if (n_domains
== 0) {
275 // if no supplemental match domains
279 orders
= CFDictionaryGetValue(dns
, kSCPropNetDNSSupplementalMatchOrders
);
280 if (orders
!= NULL
) {
281 if (!isA_CFArray(orders
) || (n_domains
!= CFArrayGetCount(orders
))) {
282 // if supplemental match orders... but too many/not enough
287 servers
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerAddresses
);
288 if (!isA_CFArray(servers
) || (CFArrayGetCount(servers
) == 0)) {
289 // if no DNS server addresses
294 * yes, this is a "supplemental" resolver configuration, expand
295 * the match domains and add each to the resolvers list.
297 for (i
= 0; i
< n_domains
; i
++) {
298 CFStringRef match_domain
;
299 CFNumberRef match_order
;
300 CFMutableDictionaryRef match_resolver
;
302 match_domain
= CFArrayGetValueAtIndex(domains
, i
);
303 if (!isA_CFString(match_domain
)) {
307 match_resolver
= CFDictionaryCreateMutableCopy(NULL
, 0, dns
);
309 // set supplemental resolver "domain"
310 if (CFStringGetLength(match_domain
) > 0) {
311 CFDictionarySetValue(match_resolver
, kSCPropNetDNSDomainName
, match_domain
);
313 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSDomainName
);
316 // set supplemental resolver "search_order"
317 match_order
= (orders
!= NULL
) ? CFArrayGetValueAtIndex(orders
, i
) : NULL
;
318 if (isA_CFNumber(match_order
)) {
319 CFDictionarySetValue(match_resolver
, kSCPropNetDNSSearchOrder
, match_order
);
320 } else if (!CFDictionaryContainsKey(match_resolver
, kSCPropNetDNSSearchOrder
)) {
323 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &defaultOrder
);
324 CFDictionarySetValue(match_resolver
, kSCPropNetDNSSearchOrder
, num
);
327 defaultOrder
++; // if multiple domains, maintain ordering
330 // remove keys we don't want in a supplemental resolver
331 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSSupplementalMatchDomains
);
332 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSSupplementalMatchOrders
);
333 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSSearchDomains
);
334 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSSortList
);
336 add_resolver_signature(match_resolver
,
337 scoped
? "Supplemental/Scoped" : "Supplemental",
340 add_resolver(resolvers
, match_resolver
);
341 CFRelease(match_resolver
);
352 merge_configuration_flags(CFMutableDictionaryRef newDNS
, uint32_t mergeFlags
)
357 if (!CFDictionaryGetValueIfPresent(newDNS
, DNS_CONFIGURATION_FLAGS_KEY
, (const void **)&num
) ||
358 !isA_CFNumber(num
) ||
359 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &flags
)) {
365 num
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &flags
);
366 CFDictionarySetValue(newDNS
, DNS_CONFIGURATION_FLAGS_KEY
, num
);
374 add_supplemental_resolvers(CFMutableArrayRef resolvers
,
375 CFDictionaryRef services
,
376 CFArrayRef service_order
,
377 CFStringRef scoped_interface
,
378 CFDictionaryRef scoped_service
)
380 const void * keys_q
[N_QUICK
];
381 const void ** keys
= keys_q
;
385 const void * vals_q
[N_QUICK
];
386 const void ** vals
= vals_q
;
388 n_services
= isA_CFDictionary(services
) ? CFDictionaryGetCount(services
) : 0;
389 if (n_services
== 0) {
390 return; // if no services
393 if (n_services
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
394 keys
= CFAllocatorAllocate(NULL
, n_services
* sizeof(CFTypeRef
), 0);
395 vals
= CFAllocatorAllocate(NULL
, n_services
* sizeof(CFTypeRef
), 0);
398 n_order
= isA_CFArray(service_order
) ? CFArrayGetCount(service_order
) : 0;
400 CFDictionaryGetKeysAndValues(services
, keys
, vals
);
401 for (i
= 0; i
< n_services
; i
++) {
402 uint32_t defaultOrder
;
404 uint32_t dns_resolver_flags
;
405 CFStringRef interface
;
406 CFMutableDictionaryRef newDNS
= NULL
;
408 CFDictionaryRef service
= (CFDictionaryRef
)vals
[i
];
409 CFStringRef serviceID
= (CFStringRef
)keys
[i
];
410 Boolean trusted
= FALSE
; // trusted config w/interface
412 if (!isA_CFDictionary(service
)) {
416 dns
= CFDictionaryGetValue(service
, kSCEntNetDNS
);
417 dns
= isA_CFDictionary(dns
);
422 interface
= CFDictionaryGetValue(dns
, kSCPropInterfaceName
);
424 if (scoped_interface
!= NULL
) {
426 // we only want to add split/supplemental configurations
427 // for queries scoped to an interface if they are NOT
428 // associated with a "real" service
430 if (CFDictionaryContainsKey(service
, kSCEntNetIPv4
) ||
431 CFDictionaryContainsKey(service
, kSCEntNetIPv6
)) {
436 // in addition, we don't want to add split/supplemental
437 // configurations for queries scoped to an interface if
438 // the configuration does not apply to all interfaces and
439 // the configuration is explicitly NOT for this interface
441 if (!_SC_CFEqual(interface
, CFSTR("*")) &&
442 !_SC_CFEqual(interface
, scoped_interface
)) {
447 // lastly, check if A/AAAA queries should be issued (based
448 // on the IP[v6] addresses). If we would not be issuing a
449 // query then don't bother adding the configuration.
451 dns_resolver_flags
= dns_resolver_flags_service(scoped_service
, 0);
452 if (dns_resolver_flags
== 0) {
457 defaultOrder
= DEFAULT_SEARCH_ORDER
458 - (DEFAULT_SEARCH_ORDER
/ 2)
459 + ((DEFAULT_SEARCH_ORDER
/ 1000) * (uint32_t)i
);
461 !CFArrayContainsValue(service_order
, CFRangeMake(0, n_order
), keys
[i
])) {
462 // push out services not specified in service order
463 defaultOrder
+= (DEFAULT_SEARCH_ORDER
/ 1000) * n_services
;
467 * Ensure that we have the correct InterfaceName in the DNS configuration
469 * scoped_interface [supplemental] interface Trusted config DNS interface
470 * ================ ======================== ============== =================
471 * NULL NULL No NULL (No change)
474 * NULL NULL Yes NULL (No change)
475 * NULL en0 Yes en0 (trusted config w/interface)
477 * en0 NULL N/A en0 (scoped interface)
478 * en0 en0 N/A en0 (scoped interface)
479 * en0 * N/A en0 (scoped interface)
481 if ((scoped_interface
== NULL
) && (interface
== NULL
)) {
482 newDNS
= (CFMutableDictionaryRef
)CFRetain(dns
);
486 newDNS
= CFDictionaryCreateMutableCopy(NULL
, 0, dns
);
487 if (scoped_interface
!= NULL
) {
488 CFDictionarySetValue(newDNS
, kSCPropInterfaceName
, scoped_interface
);
489 } else if ((interface
!= NULL
) &&
490 CFDictionaryGetValueIfPresent(dns
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, (const void **)&val
) &&
491 isA_CFBoolean(val
) &&
492 CFBooleanGetValue(val
)) {
493 // leave the [trusted configuration] InterfaceName in place
496 CFDictionaryRemoveValue(newDNS
, kSCPropInterfaceName
);
500 // set "supplemental" flag
501 newFlags
= DNS_RESOLVER_FLAGS_SUPPLEMENTAL
;
503 if (scoped_interface
!= NULL
) {
504 // set "scoped" configuration flag
505 newFlags
|= DNS_RESOLVER_FLAGS_SCOPED
;
507 // add "Request A/AAAA query" flag(s)
508 newFlags
|= dns_resolver_flags
;
509 } else if (trusted
) {
510 // use the DNS query flags from the supplemental match service
511 newFlags
|= dns_resolver_flags_service(service
, 0);
514 merge_configuration_flags(newDNS
, newFlags
);
516 // add [scoped] resolver entry
517 add_supplemental(resolvers
, serviceID
, newDNS
, defaultOrder
, (scoped_interface
!= NULL
));
521 if (keys
!= keys_q
) {
522 CFAllocatorDeallocate(NULL
, keys
);
523 CFAllocatorDeallocate(NULL
, vals
);
531 add_multicast_resolvers(CFMutableArrayRef resolvers
, CFArrayRef multicastResolvers
)
536 n
= isA_CFArray(multicastResolvers
) ? CFArrayGetCount(multicastResolvers
) : 0;
537 for (i
= 0; i
< n
; i
++) {
538 uint32_t defaultOrder
;
541 CFMutableDictionaryRef resolver
;
543 domain
= CFArrayGetValueAtIndex(multicastResolvers
, i
);
544 domain
= _SC_trimDomain(domain
);
545 if (domain
== NULL
) {
549 defaultOrder
= DEFAULT_SEARCH_ORDER
550 + (DEFAULT_SEARCH_ORDER
/ 2)
551 + ((DEFAULT_SEARCH_ORDER
/ 1000) * (uint32_t)i
);
553 resolver
= CFDictionaryCreateMutable(NULL
,
555 &kCFTypeDictionaryKeyCallBacks
,
556 &kCFTypeDictionaryValueCallBacks
);
557 CFDictionarySetValue(resolver
, kSCPropNetDNSDomainName
, domain
);
558 CFDictionarySetValue(resolver
, kSCPropNetDNSOptions
, CFSTR("mdns"));
559 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &defaultOrder
);
560 CFDictionarySetValue(resolver
, kSCPropNetDNSSearchOrder
, num
);
562 if (S_mdns_timeout
!= NULL
) {
563 CFDictionarySetValue(resolver
, kSCPropNetDNSServerTimeout
, S_mdns_timeout
);
565 add_resolver_signature(resolver
, "Multicast DNS", NULL
, i
);
566 add_resolver(resolvers
, resolver
);
576 add_private_resolvers(CFMutableArrayRef resolvers
, CFArrayRef privateResolvers
)
581 n
= isA_CFArray(privateResolvers
) ? CFArrayGetCount(privateResolvers
) : 0;
582 for (i
= 0; i
< n
; i
++) {
583 uint32_t defaultOrder
;
586 CFMutableDictionaryRef resolver
;
588 domain
= CFArrayGetValueAtIndex(privateResolvers
, i
);
589 domain
= _SC_trimDomain(domain
);
590 if (domain
== NULL
) {
594 defaultOrder
= DEFAULT_SEARCH_ORDER
595 - (DEFAULT_SEARCH_ORDER
/ 4)
596 + ((DEFAULT_SEARCH_ORDER
/ 1000) * (uint32_t)i
);
598 resolver
= CFDictionaryCreateMutable(NULL
,
600 &kCFTypeDictionaryKeyCallBacks
,
601 &kCFTypeDictionaryValueCallBacks
);
602 CFDictionarySetValue(resolver
, kSCPropNetDNSDomainName
, domain
);
603 CFDictionarySetValue(resolver
, kSCPropNetDNSOptions
, CFSTR("pdns"));
604 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &defaultOrder
);
605 CFDictionarySetValue(resolver
, kSCPropNetDNSSearchOrder
, num
);
607 if (S_pdns_timeout
!= NULL
) {
608 CFDictionarySetValue(resolver
, kSCPropNetDNSServerTimeout
, S_pdns_timeout
);
610 add_resolver_signature(resolver
, "Private DNS", NULL
, i
);
611 add_resolver(resolvers
, resolver
);
620 static CFComparisonResult
621 compareBySearchOrder(const void *val1
, const void *val2
, void *context
)
623 #pragma unused(context)
624 CFDictionaryRef dns1
= (CFDictionaryRef
)val1
;
625 CFDictionaryRef dns2
= (CFDictionaryRef
)val2
;
628 uint32_t order1
= DEFAULT_SEARCH_ORDER
;
629 uint32_t order2
= DEFAULT_SEARCH_ORDER
;
631 num1
= CFDictionaryGetValue(dns1
, kSCPropNetDNSSearchOrder
);
632 if (!isA_CFNumber(num1
) ||
633 !CFNumberGetValue(num1
, kCFNumberSInt32Type
, &order1
)) {
634 order1
= DEFAULT_SEARCH_ORDER
;
637 num2
= CFDictionaryGetValue(dns2
, kSCPropNetDNSSearchOrder
);
638 if (!isA_CFNumber(num2
) ||
639 !CFNumberGetValue(num2
, kCFNumberSInt32Type
, &order2
)) {
640 order2
= DEFAULT_SEARCH_ORDER
;
643 if (order1
== order2
) {
644 // if same "SearchOrder", retain original orderring for configurations
645 if (CFDictionaryGetValueIfPresent(dns1
, DNS_CONFIGURATION_ORDER_KEY
, (const void **)&num1
) &&
646 CFDictionaryGetValueIfPresent(dns2
, DNS_CONFIGURATION_ORDER_KEY
, (const void **)&num2
) &&
647 isA_CFNumber(num1
) &&
648 isA_CFNumber(num2
) &&
649 CFNumberGetValue(num1
, kCFNumberSInt32Type
, &order1
) &&
650 CFNumberGetValue(num2
, kCFNumberSInt32Type
, &order2
)) {
651 if (order1
== order2
) {
652 return kCFCompareEqualTo
;
654 return (order1
< order2
) ? kCFCompareLessThan
: kCFCompareGreaterThan
;
658 return kCFCompareEqualTo
;
661 return (order1
< order2
) ? kCFCompareLessThan
: kCFCompareGreaterThan
;
665 static CF_RETURNS_RETAINED CFArrayRef
666 extract_search_domains(CFMutableDictionaryRef defaultDomain
, CFArrayRef supplemental
)
668 CFStringRef defaultDomainName
= NULL
;
669 uint32_t defaultOrder
= DEFAULT_SEARCH_ORDER
;
670 CFArrayRef defaultSearchDomains
= NULL
;
671 CFIndex defaultSearchIndex
= 0;
672 CFMutableArrayRef mySearchDomains
;
673 CFMutableArrayRef mySupplemental
= NULL
;
674 CFIndex n_supplemental
;
675 CFStringRef trimmedDomainName
;
677 mySearchDomains
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
679 if (defaultDomain
!= NULL
) {
682 num
= CFDictionaryGetValue(defaultDomain
, kSCPropNetDNSSearchOrder
);
683 if (!isA_CFNumber(num
) ||
684 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &defaultOrder
)) {
685 defaultOrder
= DEFAULT_SEARCH_ORDER
;
688 defaultDomainName
= CFDictionaryGetValue(defaultDomain
, kSCPropNetDNSDomainName
);
689 defaultSearchDomains
= CFDictionaryGetValue(defaultDomain
, kSCPropNetDNSSearchDomains
);
692 // validate the provided "search" domains or move/expand/promote the "domain" name
693 if (isA_CFArray(defaultSearchDomains
)) {
696 n_search
= CFArrayGetCount(defaultSearchDomains
);
697 for (int i
= 0; i
< n_search
; i
++) {
700 search
= CFArrayGetValueAtIndex(defaultSearchDomains
, i
);
701 search
= _SC_trimDomain(search
);
702 if (search
!= NULL
) {
703 CFArrayAppendValue(mySearchDomains
, search
);
708 trimmedDomainName
= _SC_trimDomain(defaultDomainName
);
709 #ifdef PERFORM_DOMAIN_EXPANSION
711 * With BIND 4.8.3 (and earlier) resolvers, the default search list included
712 * the default domain and each of its parent domains with two or more labels.
714 if ((trimmedDomainName
!= NULL
) &&
715 CFStringHasSuffix(defaultDomainName
, CFSTR("."))) {
716 // if "domain" name is fully qualified
717 CFArrayAppendValue(mySearchDomains
, trimmedDomainName
);
718 CFRelease(trimmedDomainName
);
719 } else if (trimmedDomainName
!= NULL
) {
721 int domain_parts
= 1;
725 domain
= _SC_cfstring_to_cstring(trimmedDomainName
,
728 kCFStringEncodingUTF8
);
729 CFRelease(trimmedDomainName
);
731 // count domain parts
732 for (dp
= domain
; *dp
!= '\0'; dp
++) {
738 // move "domain" to "search" list (and expand as needed)
744 str
= CFStringCreateWithCString(NULL
,
746 kCFStringEncodingUTF8
);
747 search
= _SC_trimDomain(str
);
749 if (search
!= NULL
) {
750 CFArrayAppendValue(mySearchDomains
, search
);
754 dp
= strchr(dp
, '.') + 1;
755 } while (domain_parts
-- > 2);
756 CFAllocatorDeallocate(NULL
, domain
);
758 #else // PERFORM_DOMAIN_EXPANSION
760 * With BIND 4.9.3 (and later) resolvers, the default search list included
761 * just the default domain.
763 if (trimmedDomainName
!= NULL
) {
764 CFArrayAppendValue(mySearchDomains
, trimmedDomainName
);
765 CFRelease(trimmedDomainName
);
767 #endif // PERFORM_DOMAIN_EXPANSION
770 // add any supplemental "domain" names to the search list
771 n_supplemental
= (supplemental
!= NULL
) ? CFArrayGetCount(supplemental
) : 0;
772 if (n_supplemental
> 1) {
773 mySupplemental
= CFArrayCreateMutableCopy(NULL
, 0, supplemental
);
774 CFArraySortValues(mySupplemental
,
775 CFRangeMake(0, n_supplemental
),
776 compareBySearchOrder
,
778 supplemental
= mySupplemental
;
780 for (int i
= 0; i
< n_supplemental
; i
++) {
786 CFStringRef supplementalDomain
;
787 uint32_t supplementalOrder
;
789 dns
= CFArrayGetValueAtIndex(supplemental
, i
);
791 options
= CFDictionaryGetValue(dns
, kSCPropNetDNSOptions
);
792 if (isA_CFString(options
)) {
795 if (CFEqual(options
, CFSTR("pdns"))) {
796 // don't add private resolver domains to the search list
800 range
= CFStringFind(options
, CFSTR("interface="), 0);
801 if (range
.location
!= kCFNotFound
) {
802 // don't add scoped resolver domains to the search list
807 supplementalDomain
= CFDictionaryGetValue(dns
, kSCPropNetDNSDomainName
);
808 supplementalDomain
= _SC_trimDomain(supplementalDomain
);
809 if (supplementalDomain
== NULL
) {
813 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSSupplementalMatchDomainsNoSearch
);
814 if (isA_CFNumber(num
) &&
815 CFNumberGetValue(num
, kCFNumberIntType
, &noSearch
) &&
817 CFRelease(supplementalDomain
);
821 if (CFStringHasSuffix(supplementalDomain
, CFSTR(".in-addr.arpa")) ||
822 CFStringHasSuffix(supplementalDomain
, CFSTR(".ip6.arpa" ))) {
823 CFRelease(supplementalDomain
);
827 domainIndex
= CFArrayGetFirstIndexOfValue(mySearchDomains
,
828 CFRangeMake(0, CFArrayGetCount(mySearchDomains
)),
831 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSSearchOrder
);
832 if (!isA_CFNumber(num
) ||
833 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &supplementalOrder
)) {
834 supplementalOrder
= DEFAULT_SEARCH_ORDER
;
837 if (supplementalOrder
< defaultOrder
) {
838 if (domainIndex
!= kCFNotFound
) {
839 // if supplemental domain is already in the search list
840 CFArrayRemoveValueAtIndex(mySearchDomains
, domainIndex
);
841 if (domainIndex
< defaultSearchIndex
) {
842 defaultSearchIndex
--;
845 CFArrayInsertValueAtIndex(mySearchDomains
,
848 defaultSearchIndex
++;
850 if (domainIndex
== kCFNotFound
) {
851 // add to the (end of the) search list
852 CFArrayAppendValue(mySearchDomains
, supplementalDomain
);
856 CFRelease(supplementalDomain
);
858 if (mySupplemental
!= NULL
) CFRelease(mySupplemental
);
860 // update the "search" domains
861 if (CFArrayGetCount(mySearchDomains
) == 0) {
862 CFRelease(mySearchDomains
);
863 mySearchDomains
= NULL
;
866 // remove the "domain" name and "search" list
867 CFDictionaryRemoveValue(defaultDomain
, kSCPropNetDNSDomainName
);
868 CFDictionaryRemoveValue(defaultDomain
, kSCPropNetDNSSearchDomains
);
870 return mySearchDomains
;
875 add_scoped_resolvers(CFMutableArrayRef scoped
,
876 CFDictionaryRef services
,
877 CFArrayRef service_order
)
879 const void * keys_q
[N_QUICK
];
880 const void ** keys
= keys_q
;
884 CFMutableArrayRef order
;
885 CFMutableSetRef seen
;
887 n_services
= isA_CFDictionary(services
) ? CFDictionaryGetCount(services
) : 0;
888 if (n_services
== 0) {
889 return; // if no services
892 // ensure that we process all services in order
894 n_order
= isA_CFArray(service_order
) ? CFArrayGetCount(service_order
) : 0;
896 order
= CFArrayCreateMutableCopy(NULL
, 0, service_order
);
898 order
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
901 if (n_services
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
902 keys
= CFAllocatorAllocate(NULL
, n_services
* sizeof(CFTypeRef
), 0);
904 CFDictionaryGetKeysAndValues(services
, keys
, NULL
);
905 for (i
= 0; i
< n_services
; i
++) {
906 CFStringRef serviceID
= (CFStringRef
)keys
[i
];
908 if (!CFArrayContainsValue(order
, CFRangeMake(0, n_order
), serviceID
)) {
909 CFArrayAppendValue(order
, serviceID
);
913 if (keys
!= keys_q
) {
914 CFAllocatorDeallocate(NULL
, keys
);
917 // iterate over services
919 seen
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
920 for (i
= 0; i
< n_order
; i
++) {
922 uint32_t dns_resolver_flags
;
923 char if_name
[IF_NAMESIZE
];
924 CFStringRef interface
;
925 CFMutableDictionaryRef newDNS
;
927 CFArrayRef searchDomains
;
928 CFDictionaryRef service
;
929 CFStringRef serviceID
;
932 serviceID
= CFArrayGetValueAtIndex(order
, i
);
933 service
= CFDictionaryGetValue(services
, serviceID
);
934 if (!isA_CFDictionary(service
)) {
939 dns
= CFDictionaryGetValue(service
, kSCEntNetDNS
);
940 if (!isA_CFDictionary(dns
)) {
945 servers
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerAddresses
);
946 if (!isA_CFArray(servers
) || (CFArrayGetCount(servers
) == 0)) {
947 // if no DNS server addresses
951 interface
= CFDictionaryGetValue(dns
, kSCPropInterfaceName
);
952 if ((interface
== NULL
) || CFEqual(interface
, CFSTR("*"))) {
953 // if no [scoped] interface or supplemental configuration w/match-all
957 if (CFDictionaryContainsKey(dns
, kSCPropNetDNSServiceIdentifier
)) {
958 // if this is a service-specific resolver
962 if (CFSetContainsValue(seen
, interface
)) {
963 // if we've already processed this [scoped] interface
966 CFSetSetValue(seen
, interface
);
968 if ((_SC_cfstring_to_cstring(interface
,
971 kCFStringEncodingASCII
) == NULL
) ||
972 (my_if_nametoindex(if_name
) == 0)) {
973 // if interface index not available
977 // add [scoped] resolver entry
978 newDNS
= CFDictionaryCreateMutableCopy(NULL
, 0, dns
);
981 searchDomains
= extract_search_domains(newDNS
, NULL
);
982 if (searchDomains
!= NULL
) {
983 CFDictionarySetValue(newDNS
, kSCPropNetDNSSearchDomains
, searchDomains
);
984 CFRelease(searchDomains
);
987 // get "Request A/AAAA query" flag(s)
988 dns_resolver_flags
= dns_resolver_flags_service(service
, 0);
989 if (dns_resolver_flags
== 0) {
993 // set "scoped" configuration flag
994 newFlags
= DNS_RESOLVER_FLAGS_SCOPED
;
996 // add "Request A/AAAA query" flag(s)
997 newFlags
|= dns_resolver_flags
;
999 merge_configuration_flags(newDNS
, newFlags
);
1001 // remove keys we don't want in a [scoped] resolver
1002 CFDictionaryRemoveValue(newDNS
, kSCPropNetDNSSupplementalMatchDomains
);
1003 CFDictionaryRemoveValue(newDNS
, kSCPropNetDNSSupplementalMatchOrders
);
1005 // add the [scoped] resolver
1006 add_resolver_signature(newDNS
, "Scoped", serviceID
, 0);
1007 add_resolver(scoped
, newDNS
);
1009 // add any supplemental resolver configurations for this interface
1010 add_supplemental_resolvers(scoped
, services
, service_order
, interface
, service
);
1023 add_service_specific_resolvers(CFMutableArrayRef resolvers
, CFDictionaryRef services
)
1026 CFStringRef keys_q
[N_QUICK
];
1027 CFStringRef
*keys
= keys_q
;
1029 CFMutableSetRef seen
;
1030 CFDictionaryRef vals_q
[N_QUICK
];
1031 CFDictionaryRef
*vals
= vals_q
;
1033 n_services
= isA_CFDictionary(services
) ? CFDictionaryGetCount(services
) : 0;
1034 if (n_services
== 0) {
1035 return; // if no services
1038 if (n_services
> (CFIndex
)(sizeof(keys_q
) / sizeof(keys_q
[0]))) {
1039 keys
= CFAllocatorAllocate(kCFAllocatorDefault
, n_services
* sizeof(keys
[0]), 0);
1040 vals
= CFAllocatorAllocate(kCFAllocatorDefault
, n_services
* sizeof(vals
[0]), 0);
1042 CFDictionaryGetKeysAndValues(services
, (const void **)keys
, (const void **)vals
);
1044 seen
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
1045 for (i
= 0; i
< n_services
; i
++) {
1046 CFDictionaryRef dns
;
1047 CFNumberRef dns_service_identifier
;
1048 CFMutableDictionaryRef newDNS
;
1049 uint32_t newFlags
= 0;
1050 CFDictionaryRef service
= vals
[i
];
1051 CFStringRef serviceID
= keys
[i
];
1052 CFArrayRef searchDomains
;
1054 dns
= CFDictionaryGetValue(service
, kSCEntNetDNS
);
1055 if (!isA_CFDictionary(dns
)) {
1060 dns_service_identifier
= CFDictionaryGetValue(dns
, kSCPropNetDNSServiceIdentifier
);
1061 if (!isA_CFNumber(dns_service_identifier
)) {
1062 // if no DNS [vpn] Service Identifier
1066 if (CFSetContainsValue(seen
, dns_service_identifier
)) {
1067 my_log(LOG_ERR
, "add_service_specific_resolvers: got a resolver with a duplicate service identifier, skipping");
1070 CFSetSetValue(seen
, dns_service_identifier
);
1072 newDNS
= CFDictionaryCreateMutableCopy(NULL
, 0, dns
);
1074 // add "Request A/AAAA query" flag(s)
1075 newFlags
|= DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS
;
1078 searchDomains
= extract_search_domains(newDNS
, NULL
);
1079 if (searchDomains
!= NULL
) {
1080 CFDictionarySetValue(newDNS
, kSCPropNetDNSSearchDomains
, searchDomains
);
1081 CFRelease(searchDomains
);
1082 searchDomains
= NULL
;
1085 CFDictionaryRemoveValue(newDNS
, kSCPropNetDNSSupplementalMatchDomains
);
1086 CFDictionaryRemoveValue(newDNS
, kSCPropNetDNSSupplementalMatchOrders
);
1088 if (CFDictionaryContainsKey(newDNS
, kSCPropInterfaceName
)) {
1089 CFMutableDictionaryRef interfaceScopedDNS
;
1090 uint32_t interfaceScopedFlags
;
1092 // The dictionary has an interface, so add a interface-scoped resolver
1094 CFDictionarySetValue(newDNS
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, kCFBooleanTrue
);
1096 interfaceScopedDNS
= CFDictionaryCreateMutableCopy(NULL
, 0, newDNS
);
1097 interfaceScopedFlags
= newFlags
;
1099 // set "scoped" configuration flag
1100 interfaceScopedFlags
|= DNS_RESOLVER_FLAGS_SCOPED
;
1101 merge_configuration_flags(interfaceScopedDNS
, interfaceScopedFlags
);
1103 CFDictionaryRemoveValue(interfaceScopedDNS
, kSCPropNetDNSServiceIdentifier
);
1105 add_resolver_signature(interfaceScopedDNS
, "Service", serviceID
, 0);
1106 add_resolver(resolvers
, interfaceScopedDNS
);
1107 CFRelease(interfaceScopedDNS
);
1110 // set "service specific" configuration flag
1111 newFlags
|= DNS_RESOLVER_FLAGS_SERVICE_SPECIFIC
;
1112 merge_configuration_flags(newDNS
, newFlags
);
1114 add_resolver_signature(newDNS
, "Service", serviceID
, 0);
1115 add_resolver(resolvers
, newDNS
);
1120 if (keys
!= keys_q
) {
1121 CFAllocatorDeallocate(kCFAllocatorDefault
, keys
);
1122 CFAllocatorDeallocate(kCFAllocatorDefault
, vals
);
1130 add_default_resolver(CFMutableArrayRef resolvers
,
1131 CFDictionaryRef defaultResolver
,
1132 Boolean
*orderAdded
,
1133 CFArrayRef
*searchDomains
)
1135 CFMutableDictionaryRef myDefault
;
1136 uint32_t myOrder
= DEFAULT_SEARCH_ORDER
;
1139 if (defaultResolver
== NULL
) {
1140 myDefault
= CFDictionaryCreateMutable(NULL
,
1142 &kCFTypeDictionaryKeyCallBacks
,
1143 &kCFTypeDictionaryValueCallBacks
);
1145 myDefault
= CFDictionaryCreateMutableCopy(NULL
, 0, defaultResolver
);
1147 assert(myDefault
!= NULL
);
1149 // ensure that the default resolver has a search order
1151 order
= CFDictionaryGetValue(myDefault
, kSCPropNetDNSSearchOrder
);
1152 if (!isA_CFNumber(order
) ||
1153 !CFNumberGetValue(order
, kCFNumberSInt32Type
, &myOrder
)) {
1154 myOrder
= DEFAULT_SEARCH_ORDER
;
1155 order
= CFNumberCreate(NULL
, kCFNumberIntType
, &myOrder
);
1156 CFDictionarySetValue(myDefault
, kSCPropNetDNSSearchOrder
, order
);
1161 // extract the "search" domain list for the default resolver (and
1162 // any supplemental resolvers)
1164 *searchDomains
= extract_search_domains(myDefault
, resolvers
);
1166 // add the default resolver
1168 add_resolver_signature(myDefault
, "Default", NULL
, 0);
1169 add_resolver(resolvers
, myDefault
);
1170 CFRelease(myDefault
);
1175 static dns_create_resolver_t
1176 create_resolver(CFDictionaryRef dns
)
1180 dns_create_resolver_t _resolver
;
1182 CFStringRef targetInterface
= NULL
;
1183 unsigned int targetInterfaceIndex
= 0;
1185 _resolver
= _dns_resolver_create();
1188 str
= CFDictionaryGetValue(dns
, kSCPropNetDNSDomainName
);
1189 if (isA_CFString(str
) && (CFStringGetLength(str
) > 0)) {
1190 char domain
[NS_MAXDNAME
];
1192 if (_SC_cfstring_to_cstring(str
, domain
, sizeof(domain
), kCFStringEncodingUTF8
) != NULL
) {
1193 _dns_resolver_set_domain(&_resolver
, domain
);
1197 // process search domains
1198 list
= CFDictionaryGetValue(dns
, kSCPropNetDNSSearchDomains
);
1199 if (isA_CFArray(list
)) {
1201 CFIndex n
= CFArrayGetCount(list
);
1203 // add "search" domains
1204 for (i
= 0; i
< n
; i
++) {
1205 str
= CFArrayGetValueAtIndex(list
, i
);
1206 if (isA_CFString(str
) && (CFStringGetLength(str
) > 0)) {
1207 char search
[NS_MAXDNAME
];
1209 if (_SC_cfstring_to_cstring(str
, search
, sizeof(search
), kCFStringEncodingUTF8
) != NULL
) {
1210 _dns_resolver_add_search(&_resolver
, search
);
1216 // process interface index
1217 num
= CFDictionaryGetValue(dns
, DNS_CONFIGURATION_IF_INDEX_KEY
);
1218 if (isA_CFNumber(num
)) {
1221 if (CFNumberGetValue(num
, kCFNumberIntType
, &if_index
)) {
1223 const char *if_name
= NULL
;
1225 if (if_index
!= 0) {
1226 if_name
= my_if_indextoname(if_index
, buf
);
1227 if (if_name
!= NULL
) {
1228 targetInterface
= CFStringCreateWithCString(NULL
,
1230 kCFStringEncodingASCII
);
1231 targetInterfaceIndex
= if_index
;
1235 _dns_resolver_set_if_index(&_resolver
, if_index
, if_name
);
1240 num
= CFDictionaryGetValue(dns
, DNS_CONFIGURATION_FLAGS_KEY
);
1241 if (isA_CFNumber(num
)) {
1244 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &flags
)) {
1245 _dns_resolver_set_flags(&_resolver
, flags
);
1249 // process nameserver addresses
1250 // Note: the flags may be overwritten if the resolver has LOOPBACK addresses
1251 list
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerAddresses
);
1252 if (isA_CFArray(list
)) {
1254 CFIndex n
= CFArrayGetCount(list
);
1256 for (i
= 0; i
< n
; i
++) {
1259 struct sockaddr_in sin
;
1260 struct sockaddr_in6 sin6
;
1264 str
= CFArrayGetValueAtIndex(list
, i
);
1265 if (!isA_CFString(str
)) {
1269 if (_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
) == NULL
) {
1273 if (_SC_string_to_sockaddr(buf
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) == NULL
) {
1277 if ((addr
.sa
.sa_family
== AF_INET6
) &&
1278 IN6_IS_ADDR_LINKLOCAL(&addr
.sin6
.sin6_addr
) &&
1279 (addr
.sin6
.sin6_scope_id
== 0) &&
1280 (targetInterfaceIndex
!= 0)) {
1281 // for link local [IPv6] addresses, if the scope id is not
1282 // set then we should use the interface associated with the
1283 // resolver configuration
1284 addr
.sin6
.sin6_scope_id
= targetInterfaceIndex
;
1287 _dns_resolver_add_nameserver(&_resolver
, &addr
.sa
);
1291 // process search order
1292 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSSearchOrder
);
1293 if (isA_CFNumber(num
)) {
1296 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &order
)) {
1297 _dns_resolver_set_order(&_resolver
, order
);
1302 list
= CFDictionaryGetValue(dns
, kSCPropNetDNSSortList
);
1303 if (isA_CFArray(list
)) {
1305 CFIndex n
= CFArrayGetCount(list
);
1307 for (i
= 0; i
< n
; i
++) {
1310 dns_sortaddr_t sortaddr
;
1312 str
= CFArrayGetValueAtIndex(list
, i
);
1313 if (!isA_CFString(str
)) {
1317 if (_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
) == NULL
) {
1321 slash
= strchr(buf
, '/');
1322 if (slash
!= NULL
) {
1326 memset(&sortaddr
, 0, sizeof(sortaddr
));
1327 if (inet_aton(buf
, &sortaddr
.address
) != 1) {
1328 /* if address not valid */
1332 if (slash
!= NULL
) {
1333 if (inet_aton(slash
+ 1, &sortaddr
.mask
) != 1) {
1334 /* if subnet mask not valid */
1341 a
= ntohl(sortaddr
.address
.s_addr
);
1344 } else if (IN_CLASSB(a
)) {
1346 } else if (IN_CLASSC(a
)) {
1352 sortaddr
.mask
.s_addr
= htonl(m
);
1355 _dns_resolver_add_sortaddr(&_resolver
, &sortaddr
);
1360 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerPort
);
1361 if (isA_CFNumber(num
)) {
1364 if (CFNumberGetValue(num
, kCFNumberIntType
, &port
)) {
1365 _dns_resolver_set_port(&_resolver
, (uint16_t)port
);
1370 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerTimeout
);
1371 if (isA_CFNumber(num
)) {
1374 if (CFNumberGetValue(num
, kCFNumberIntType
, &timeout
)) {
1375 _dns_resolver_set_timeout(&_resolver
, (uint32_t)timeout
);
1380 str
= CFDictionaryGetValue(dns
, kSCPropNetDNSOptions
);
1381 if (isA_CFString(str
)) {
1384 options
= _SC_cfstring_to_cstring(str
, NULL
, 0, kCFStringEncodingUTF8
);
1385 if (options
!= NULL
) {
1386 _dns_resolver_set_options(&_resolver
, options
);
1387 CFAllocatorDeallocate(NULL
, options
);
1391 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSServiceIdentifier
);
1392 if (isA_CFNumber(num
)) {
1393 int dns_service_identifier
;
1395 if (CFNumberGetValue(num
, kCFNumberIntType
, &dns_service_identifier
)) {
1396 _dns_resolver_set_service_identifier(&_resolver
, (uint32_t)dns_service_identifier
);
1400 // process configuration ID
1401 str
= CFDictionaryGetValue(dns
, DNS_CONFIGURATION_CONFIGURATION_ID
);
1402 if (isA_CFString(str
) && (CFStringGetLength(str
) > 0)) {
1405 cID
= _SC_cfstring_to_cstring(str
, NULL
, 0, kCFStringEncodingUTF8
);
1407 _dns_resolver_set_configuration_identifier(&_resolver
, cID
);
1408 CFAllocatorDeallocate(NULL
, cID
);
1412 if (targetInterface
!= NULL
) {
1413 CFRelease(targetInterface
);
1420 static __inline__ Boolean
1421 isScopedConfiguration(CFDictionaryRef dns
)
1426 if ((dns
!= NULL
) &&
1427 CFDictionaryGetValueIfPresent(dns
, DNS_CONFIGURATION_FLAGS_KEY
, (const void **)&num
) &&
1429 CFNumberGetValue(num
, kCFNumberSInt32Type
, &flags
) &&
1430 ((flags
& DNS_RESOLVER_FLAGS_SCOPED
) != 0)) {
1439 static CFComparisonResult
1440 compareDomain(const void *val1
, const void *val2
, void *context
)
1442 CFDictionaryRef dns1
= (CFDictionaryRef
)val1
;
1443 CFDictionaryRef dns2
= (CFDictionaryRef
)val2
;
1444 CFStringRef domain1
;
1445 CFStringRef domain2
;
1446 CFArrayRef labels1
= NULL
;
1447 CFArrayRef labels2
= NULL
;
1450 CFComparisonResult result
;
1456 // "default" domains sort before "supplemental" domains
1457 domain1
= CFDictionaryGetValue(dns1
, kSCPropNetDNSDomainName
);
1458 domain2
= CFDictionaryGetValue(dns2
, kSCPropNetDNSDomainName
);
1459 if (domain1
== NULL
) {
1460 return kCFCompareLessThan
;
1461 } else if (domain2
== NULL
) {
1462 return kCFCompareGreaterThan
;
1465 // sort non-scoped before scoped
1466 scoped1
= isScopedConfiguration(dns1
);
1467 scoped2
= isScopedConfiguration(dns2
);
1468 if (scoped1
!= scoped2
) {
1470 return kCFCompareLessThan
;
1472 return kCFCompareGreaterThan
;
1476 // forward (A, AAAA) domains sort before reverse (PTR) domains
1477 rev1
= CFStringHasSuffix(domain1
, CFSTR(".arpa"));
1478 rev2
= CFStringHasSuffix(domain2
, CFSTR(".arpa"));
1481 return kCFCompareGreaterThan
;
1483 return kCFCompareLessThan
;
1487 labels1
= CFStringCreateArrayBySeparatingStrings(NULL
, domain1
, CFSTR("."));
1488 n1
= CFArrayGetCount(labels1
);
1490 labels2
= CFStringCreateArrayBySeparatingStrings(NULL
, domain2
, CFSTR("."));
1491 n2
= CFArrayGetCount(labels2
);
1493 while ((n1
> 0) && (n2
> 0)) {
1494 CFStringRef label1
= CFArrayGetValueAtIndex(labels1
, --n1
);
1495 CFStringRef label2
= CFArrayGetValueAtIndex(labels2
, --n2
);
1497 // compare domain labels
1498 result
= CFStringCompare(label1
, label2
, kCFCompareCaseInsensitive
);
1499 if (result
!= kCFCompareEqualTo
) {
1504 // longer labels (corp.apple.com) sort before shorter labels (apple.com)
1506 result
= kCFCompareLessThan
;
1508 } else if (n1
< n2
) {
1509 result
= kCFCompareGreaterThan
;
1513 // sort by search order
1514 result
= compareBySearchOrder(val1
, val2
, context
);
1518 if (labels1
!= NULL
) CFRelease(labels1
);
1519 if (labels2
!= NULL
) CFRelease(labels2
);
1524 static __inline__ Boolean
1525 needsMergeWithDefaultConfiguration(CFDictionaryRef dns
)
1530 if ((dns
!= NULL
) &&
1531 CFDictionaryGetValueIfPresent(dns
, DNS_CONFIGURATION_FLAGS_KEY
, (const void **)&num
) &&
1533 CFNumberGetValue(num
, kCFNumberSInt32Type
, &flags
)) {
1535 // check if merge needed (at all)
1536 if (dns_resolver_flags_all_queries(flags
)) {
1537 // if we are already querying for both A/AAAA
1541 // check if scoped or service-specific
1542 if (((flags
& DNS_RESOLVER_FLAGS_SCOPED
) != 0) ||
1543 ((flags
& DNS_RESOLVER_FLAGS_SERVICE_SPECIFIC
) != 0)) {
1555 dns_configuration_set(CFDictionaryRef defaultResolver
,
1556 CFDictionaryRef services
,
1557 CFArrayRef serviceOrder
,
1558 CFArrayRef multicastResolvers
,
1559 CFArrayRef privateResolvers
,
1560 CFDictionaryRef
*globalResolver
)
1562 dns_create_config_t dns_create_config
;
1563 Boolean changed
= FALSE
;
1565 CFMutableDictionaryRef myDefault
;
1566 Boolean myOrderAdded
= FALSE
;
1567 CFArrayRef mySearchDomains
= NULL
;
1568 CFIndex n_resolvers
;
1569 CFMutableArrayRef resolvers
;
1570 unsigned char signature
[CC_SHA256_DIGEST_LENGTH
];
1571 static unsigned char signature_last
[CC_SHA256_DIGEST_LENGTH
];
1573 // establish list of resolvers
1575 resolvers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1576 assert(resolvers
!= NULL
);
1578 // collect (and add) any "supplemental" resolver configurations
1580 add_supplemental_resolvers(resolvers
, services
, serviceOrder
, NULL
, NULL
);
1582 // collect (and add) any "private" resolver configurations
1584 add_private_resolvers(resolvers
, privateResolvers
);
1586 // add the "default" resolver
1588 if (defaultResolver
!= NULL
) {
1591 servers
= CFDictionaryGetValue(defaultResolver
, kSCPropNetDNSServerAddresses
);
1592 if (!isA_CFArray(servers
) || (CFArrayGetCount(servers
) == 0)) {
1593 // if no DNS server addresses
1594 defaultResolver
= NULL
;
1598 add_default_resolver(resolvers
, defaultResolver
, &myOrderAdded
, &mySearchDomains
);
1600 // collect (and add) any "multicast" resolver configurations
1602 add_multicast_resolvers(resolvers
, multicastResolvers
);
1604 // collect (and add) any "scoped" resolver configurations
1606 add_scoped_resolvers(resolvers
, services
, serviceOrder
);
1608 // collect (and add) any "service-specific" resolver configurations
1610 add_service_specific_resolvers(resolvers
, services
);
1614 n_resolvers
= CFArrayGetCount(resolvers
);
1615 if (n_resolvers
> 1) {
1616 CFArraySortValues(resolvers
, CFRangeMake(0, n_resolvers
), compareDomain
, NULL
);
1621 for (i
= n_resolvers
; --i
> 0; ) {
1622 CFDictionaryRef resolver
;
1624 resolver
= CFArrayGetValueAtIndex(resolvers
, i
);
1625 if (!CFDictionaryContainsKey(resolver
, kSCPropNetDNSDomainName
) &&
1626 !CFDictionaryContainsKey(resolver
, kSCPropNetDNSSearchDomains
) &&
1627 !CFDictionaryContainsKey(resolver
, kSCPropNetDNSServerAddresses
)) {
1628 // remove empty resolver
1629 CFArrayRemoveValueAtIndex(resolvers
, i
);
1634 // update the default resolver
1636 myDefault
= CFDictionaryCreateMutableCopy(NULL
,
1638 CFArrayGetValueAtIndex(resolvers
, 0));
1639 if (mySearchDomains
!= NULL
) {
1640 // add search domains to the default resolver
1641 CFDictionarySetValue(myDefault
, kSCPropNetDNSSearchDomains
, mySearchDomains
);
1642 CFRelease(mySearchDomains
);
1644 if (myOrderAdded
&& (n_resolvers
> 1)) {
1645 CFDictionaryRef resolver
;
1647 resolver
= CFArrayGetValueAtIndex(resolvers
, 1);
1648 if (CFDictionaryContainsKey(resolver
, kSCPropNetDNSDomainName
) ||
1649 isScopedConfiguration(resolver
)) {
1650 // if not a supplemental "default" resolver (a domain name is
1651 // present) or if it's a scoped configuration
1652 CFDictionaryRemoveValue(myDefault
, kSCPropNetDNSSearchOrder
);
1655 CFArraySetValueAtIndex(resolvers
, 0, myDefault
);
1656 CFRelease(myDefault
);
1658 // establish resolver configuration
1660 if ((defaultResolver
== NULL
) && (n_resolvers
<= 1)) {
1662 * if no default and no supplemental/scoped resolvers
1664 dns_create_config
= NULL
;
1666 uint32_t default_resolver_flags
= 0;
1667 Boolean have_default_flags
= FALSE
;
1670 * if default and/or supplemental/scoped resolvers are defined
1672 dns_create_config
= _dns_configuration_create();
1674 for (i
= 0; i
< n_resolvers
; i
++) {
1675 Boolean merge_default_flags
;
1676 CFDictionaryRef resolver
;
1677 dns_create_resolver_t _resolver
;
1679 resolver
= CFArrayGetValueAtIndex(resolvers
, i
);
1681 merge_default_flags
= needsMergeWithDefaultConfiguration(resolver
);
1682 if (merge_default_flags
) {
1683 CFMutableDictionaryRef new_resolver
;
1685 if (!have_default_flags
) {
1686 CFDictionaryApplyFunction(services
,
1687 add_dns_resolver_flags
,
1688 &default_resolver_flags
);
1689 have_default_flags
= TRUE
;
1692 new_resolver
= CFDictionaryCreateMutableCopy(NULL
, 0, resolver
);
1693 merge_configuration_flags(new_resolver
, default_resolver_flags
);
1694 resolver
= new_resolver
;
1698 *globalResolver
= CFRetain(resolver
);
1701 _resolver
= create_resolver(resolver
);
1702 _dns_configuration_add_resolver(&dns_create_config
, _resolver
);
1703 _dns_resolver_free(&_resolver
);
1705 if (merge_default_flags
) {
1706 CFRelease(resolver
);
1710 // add flatfile resolvers
1712 _dnsinfo_flatfile_set_flags(default_resolver_flags
);
1713 _dnsinfo_flatfile_add_resolvers(&dns_create_config
);
1716 // check if the configuration changed
1717 _dns_configuration_signature(&dns_create_config
, signature
, sizeof(signature
));
1718 if (bcmp(signature
, signature_last
, sizeof(signature
)) != 0) {
1719 // save [new] signature
1720 memcpy(signature_last
, signature
, sizeof(signature
));
1722 my_log(LOG_INFO
, "Updating DNS configuration");
1723 if (dns_create_config
!= NULL
) {
1724 dns_config_t
*dns_config
= NULL
;
1725 _dns_config_buf_t
*dns_config_buf
;
1728 n
= sizeof(_dns_config_buf_t
);
1729 n
+= ntohl(((_dns_config_buf_t
*)dns_create_config
)->n_attribute
);
1730 dns_config_buf
= _dns_configuration_buffer_create((void *)dns_create_config
, n
);
1731 if (dns_config_buf
!= NULL
) {
1732 dns_config
= _dns_configuration_buffer_expand(dns_config_buf
);
1733 if (dns_config
== NULL
) {
1734 // if we were unable to expand the configuration
1735 _dns_configuration_buffer_free(&dns_config_buf
);
1739 if (dns_config
!= NULL
) {
1740 _dns_configuration_log(dns_config
, TRUE
, NULL
);
1744 my_log(LOG_INFO
, "*** No DNS configuration");
1747 // save [new] configuration
1748 if (!_dns_configuration_store(&dns_create_config
)) {
1749 my_log(LOG_ERR
, "could not store configuration");
1756 if (dns_create_config
!= NULL
) {
1757 _dns_configuration_free(&dns_create_config
);
1760 CFRelease(resolvers
);
1765 static SCDynamicStoreRef dns_configuration_store
;
1766 static SCDynamicStoreCallBack dns_configuration_callout
;
1769 dns_configuration_changed(ConstFSEventStreamRef streamRef
,
1770 void *clientCallBackInfo
,
1773 const FSEventStreamEventFlags
*eventFlags
,
1774 const FSEventStreamEventId
*eventIds
)
1776 #pragma unused(streamRef)
1777 #pragma unused(clientCallBackInfo)
1778 #pragma unused(numEvents)
1779 #pragma unused(eventPaths)
1780 #pragma unused(eventFlags)
1781 #pragma unused(eventIds)
1782 static const CFStringRef key
= CFSTR(_PATH_RESOLVER_DIR
);
1784 Boolean resolvers_now
;
1785 static Boolean resolvers_save
= FALSE
;
1786 struct stat statbuf
;
1788 resolvers_now
= (stat(_PATH_RESOLVER_DIR
, &statbuf
) == 0);
1789 if (!resolvers_save
&& (resolvers_save
== resolvers_now
)) {
1790 // if we did not (and still do not) have an "/etc/resolvers"
1791 // directory than this notification is the result of a change
1792 // to the "/etc" directory.
1795 resolvers_save
= resolvers_now
;
1797 my_log(LOG_INFO
, _PATH_RESOLVER_DIR
" changed");
1799 // fake a "DNS" change
1800 keys
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
1801 (*dns_configuration_callout
)(dns_configuration_store
, keys
, NULL
);
1809 normalize_path(const char *file_name
, char resolved_name
[PATH_MAX
])
1812 char path
[PATH_MAX
];
1814 strlcpy(path
, file_name
, sizeof(path
));
1815 if (realpath(path
, resolved_name
) != NULL
) {
1816 // if the path exists
1820 ch
= strrchr(path
, '/');
1823 if (realpath(path
, resolved_name
) != NULL
) {
1824 // if a parent path exists
1825 strlcat(resolved_name
, "/", PATH_MAX
);
1826 strlcat(resolved_name
, ch
+1, PATH_MAX
);
1837 dns_configuration_monitor(SCDynamicStoreRef store
, SCDynamicStoreCallBack callout
)
1839 FSEventStreamContext context
= { 0, // version
1843 NULL
}; // copyDescription
1844 FSEventStreamCreateFlags flags
= kFSEventStreamCreateFlagUseCFTypes
1845 | kFSEventStreamCreateFlagFileEvents
1846 | kFSEventStreamCreateFlagWatchRoot
;
1847 FSEventStreamRef monitor
;
1849 CFMutableArrayRef paths
;
1850 char resolver_directory_path
[PATH_MAX
];
1852 if (!normalize_path(_PATH_RESOLVER_DIR
, resolver_directory_path
)) {
1853 my_log(LOG_ERR
, "Not monitoring \"%s\", could not resolve directory path", _PATH_RESOLVER_DIR
);
1857 dns_configuration_store
= store
;
1858 dns_configuration_callout
= callout
;
1860 paths
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1861 path
= CFStringCreateWithCString(NULL
, resolver_directory_path
, kCFStringEncodingUTF8
);
1862 CFArrayAppendValue(paths
, path
);
1865 monitor
= FSEventStreamCreate(NULL
, // allocator
1866 dns_configuration_changed
, // callback
1867 &context
, // context
1868 paths
, // pathsToWatch (CFArray)
1869 kFSEventStreamEventIdSinceNow
, // sinceWhen
1875 FSEventStreamScheduleWithRunLoop(monitor
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
);
1876 FSEventStreamStart(monitor
);
1884 dns_configuration_init(CFBundleRef bundle
)
1886 CFDictionaryRef dict
;
1888 dict
= CFBundleGetInfoDictionary(bundle
);
1889 if (isA_CFDictionary(dict
)) {
1890 S_mdns_timeout
= CFDictionaryGetValue(dict
, CFSTR("mdns_timeout"));
1891 S_mdns_timeout
= isA_CFNumber(S_mdns_timeout
);
1893 S_pdns_timeout
= CFDictionaryGetValue(dict
, CFSTR("pdns_timeout"));
1894 S_pdns_timeout
= isA_CFNumber(S_pdns_timeout
);
1902 #pragma mark Standalone test code
1908 split(const void * key
, const void * value
, void * context
)
1910 CFArrayRef components
;
1911 CFStringRef entity_id
;
1912 CFStringRef service_id
;
1913 CFMutableDictionaryRef state_dict
;
1915 components
= CFStringCreateArrayBySeparatingStrings(NULL
, (CFStringRef
)key
, CFSTR("/"));
1916 service_id
= CFArrayGetValueAtIndex(components
, 3);
1917 entity_id
= CFArrayGetValueAtIndex(components
, 4);
1918 state_dict
= (CFMutableDictionaryRef
)CFDictionaryGetValue(context
, service_id
);
1919 if (state_dict
!= NULL
) {
1920 state_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
1922 state_dict
= CFDictionaryCreateMutable(NULL
,
1924 &kCFTypeDictionaryKeyCallBacks
,
1925 &kCFTypeDictionaryValueCallBacks
);
1928 if (CFEqual(entity_id
, kSCEntNetIPv4
) ||
1929 CFEqual(entity_id
, kSCEntNetIPv6
)) {
1930 CFDictionaryRef dict
;
1931 CFStringRef interface
;
1933 if (CFEqual(entity_id
, kSCEntNetIPv4
)) {
1934 dict
= ipv4_dict_create(value
);
1937 dict
= ipv6_dict_create(value
);
1940 CFDictionarySetValue(state_dict
, entity_id
, dict
);
1943 interface
= CFDictionaryGetValue((CFDictionaryRef
)value
, kSCPropInterfaceName
);
1944 if (interface
!= NULL
) {
1945 CFDictionaryRef dns
;
1946 CFMutableDictionaryRef new_dns
;
1948 dns
= CFDictionaryGetValue(state_dict
, kSCEntNetDNS
);
1950 new_dns
= CFDictionaryCreateMutableCopy(NULL
, 0, dns
);
1952 new_dns
= CFDictionaryCreateMutable(NULL
,
1954 &kCFTypeDictionaryKeyCallBacks
,
1955 &kCFTypeDictionaryValueCallBacks
);
1957 CFDictionarySetValue(new_dns
, kSCPropInterfaceName
, interface
);
1958 CFDictionarySetValue(state_dict
, kSCEntNetDNS
, new_dns
);
1961 } else if (CFEqual(entity_id
, kSCEntNetDNS
)) {
1962 CFDictionaryRef dns
;
1964 dns
= CFDictionaryGetValue(state_dict
, kSCEntNetDNS
);
1966 CFStringRef interface
;
1968 interface
= CFDictionaryGetValue(dns
, kSCPropInterfaceName
);
1969 if (interface
!= NULL
) {
1970 CFMutableDictionaryRef new_dns
;
1972 new_dns
= CFDictionaryCreateMutableCopy(NULL
, 0, (CFDictionaryRef
)value
);
1973 CFDictionarySetValue(new_dns
, kSCPropInterfaceName
, interface
);
1974 CFDictionarySetValue(state_dict
, kSCEntNetDNS
, new_dns
);
1977 CFDictionarySetValue(state_dict
, kSCEntNetDNS
, (CFDictionaryRef
)value
);
1980 CFDictionarySetValue(state_dict
, kSCEntNetDNS
, (CFDictionaryRef
)value
);
1983 CFDictionarySetValue(state_dict
, entity_id
, (CFDictionaryRef
)value
);
1986 CFDictionarySetValue((CFMutableDictionaryRef
)context
, service_id
, state_dict
);
1987 CFRelease(state_dict
);
1988 CFRelease(components
);
1994 main(int argc
, char **argv
)
1996 CFDictionaryRef entities
;
1997 CFDictionaryRef globalResolver
= NULL
;
1999 CFArrayRef multicast_resolvers
;
2000 CFStringRef pattern
;
2001 CFMutableArrayRef patterns
;
2002 CFStringRef primary
= NULL
;
2003 CFDictionaryRef primaryDNS
= NULL
;
2004 CFArrayRef private_resolvers
;
2005 CFArrayRef service_order
= NULL
;
2006 CFMutableDictionaryRef service_state_dict
;
2007 CFDictionaryRef setup_global_ipv4
;
2008 CFDictionaryRef state_global_ipv4
;
2009 SCDynamicStoreRef store
;
2012 _sc_log
= kSCLogDestinationFile
;
2013 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
2015 store
= SCDynamicStoreCreate(NULL
, CFSTR("TEST"), NULL
, NULL
);
2017 // get IPv4, IPv6, and DNS entities
2018 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2019 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2020 kSCDynamicStoreDomainState
,
2023 CFArrayAppendValue(patterns
, pattern
);
2025 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2026 kSCDynamicStoreDomainState
,
2029 CFArrayAppendValue(patterns
, pattern
);
2031 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2032 kSCDynamicStoreDomainState
,
2035 CFArrayAppendValue(patterns
, pattern
);
2037 entities
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
2038 CFRelease(patterns
);
2040 service_state_dict
= CFDictionaryCreateMutable(NULL
,
2042 &kCFTypeDictionaryKeyCallBacks
,
2043 &kCFTypeDictionaryValueCallBacks
);
2044 CFDictionaryApplyFunction(entities
, split
, service_state_dict
);
2045 CFRelease(entities
);
2047 // get primary service ID
2048 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2049 kSCDynamicStoreDomainState
,
2051 state_global_ipv4
= SCDynamicStoreCopyValue(store
, key
);
2053 if (state_global_ipv4
!= NULL
) {
2054 primary
= CFDictionaryGetValue(state_global_ipv4
, kSCDynamicStorePropNetPrimaryService
);
2055 if (primary
!= NULL
) {
2056 CFDictionaryRef service_dict
;
2058 // get DNS configuration for primary service
2059 service_dict
= CFDictionaryGetValue(service_state_dict
, primary
);
2060 if (service_dict
!= NULL
) {
2061 primaryDNS
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
2067 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2068 kSCDynamicStoreDomainSetup
,
2070 setup_global_ipv4
= SCDynamicStoreCopyValue(store
, key
);
2072 if (setup_global_ipv4
!= NULL
) {
2073 service_order
= CFDictionaryGetValue(setup_global_ipv4
, kSCPropNetServiceOrder
);
2076 // get multicast resolvers
2077 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
2078 kSCDynamicStoreDomainState
,
2080 CFSTR(kDNSServiceCompMulticastDNS
));
2081 multicast_resolvers
= SCDynamicStoreCopyValue(store
, key
);
2084 // get private resolvers
2085 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
2086 kSCDynamicStoreDomainState
,
2088 CFSTR(kDNSServiceCompPrivateDNS
));
2089 private_resolvers
= SCDynamicStoreCopyValue(store
, key
);
2092 // update DNS configuration
2093 dns_configuration_init(CFBundleGetMainBundle());
2094 (void)dns_configuration_set(primaryDNS
,
2097 multicast_resolvers
,
2102 if (setup_global_ipv4
!= NULL
) CFRelease(setup_global_ipv4
);
2103 if (state_global_ipv4
!= NULL
) CFRelease(state_global_ipv4
);
2104 if (multicast_resolvers
!= NULL
) CFRelease(multicast_resolvers
);
2105 if (private_resolvers
!= NULL
) CFRelease(private_resolvers
);
2106 if (globalResolver
!= NULL
) CFRelease(globalResolver
);
2107 CFRelease(service_state_dict
);