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>
45 #include <notify_private.h>
46 #include <CommonCrypto/CommonDigest.h>
48 #include <CoreFoundation/CoreFoundation.h>
49 #include <SystemConfiguration/SystemConfiguration.h>
50 #include <SystemConfiguration/SCPrivate.h>
51 #include <SystemConfiguration/SCValidation.h>
52 #include "ip_plugin.h"
54 #include "dns-configuration.h"
57 #include "dnsinfo_create.h"
58 #include "dnsinfo_internal.h"
59 #include "dnsinfo_logging.h"
60 #include "dnsinfo_private.h"
61 #include "dnsinfo_server.h"
63 #include <network_information.h>
66 #include <dns_sd_private.h>
68 #define DNS_CONFIGURATION_FLAGS_KEY CFSTR("__FLAGS__")
69 #define DNS_CONFIGURATION_IF_INDEX_KEY CFSTR("__IF_INDEX__")
70 #define DNS_CONFIGURATION_ORDER_KEY CFSTR("__ORDER__")
72 /* multicast DNS resolver configurations */
73 static CFNumberRef S_mdns_timeout
= NULL
;
75 /* private DNS resolver configurations */
76 static CFNumberRef S_pdns_timeout
= NULL
;
80 #pragma mark DNS resolver flags
83 static __inline__ boolean_t
84 dns_resolver_flags_all_queries(uint32_t query_flags
)
86 return ((query_flags
& DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS
) == DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS
);
93 dns_resolver_flags_service(CFDictionaryRef service
, uint32_t resolver_flags
)
96 // check if the service has v4 configured
97 if (((resolver_flags
& DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS
) == 0) &&
98 service_is_routable(service
, AF_INET
)) {
99 resolver_flags
|= DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS
;
102 // check if the service has v6 configured
103 if (((resolver_flags
& DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS
) == 0) &&
104 service_is_routable(service
, AF_INET6
)) {
105 resolver_flags
|= DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS
;
108 return resolver_flags
;
113 add_dns_resolver_flags(const void *key
, const void *value
, void *context
)
116 CFDictionaryRef service
= (CFDictionaryRef
)value
;
117 // CFStringRef serviceID = (CFStringRef)key;
118 uint32_t *resolver_flags
= (uint32_t *)context
;
120 if (service_is_scoped_only(service
)) {
124 // update resovler flags based on configured (and available) protocols
125 *resolver_flags
= dns_resolver_flags_service(service
, *resolver_flags
);
131 #pragma mark DNS resolver configuration
135 add_resolver(CFMutableArrayRef resolvers
, CFMutableDictionaryRef resolver
)
138 CFStringRef interface
;
141 uint32_t order_val
= 0;
143 order
= CFDictionaryGetValue(resolver
, kSCPropNetDNSSearchOrder
);
144 if (!isA_CFNumber(order
) ||
145 !CFNumberGetValue(order
, kCFNumberSInt32Type
, &order_val
)) {
150 n_resolvers
= CFArrayGetCount(resolvers
);
151 for (i
= 0; i
< n_resolvers
; i
++) {
152 CFDictionaryRef match_resolver
;
154 match_resolver
= CFArrayGetValueAtIndex(resolvers
, i
);
155 if (CFEqual(resolver
, match_resolver
)) {
161 CFMutableDictionaryRef compare
;
164 compare
= CFDictionaryCreateMutableCopy(NULL
, 0, match_resolver
);
165 CFDictionarySetValue(compare
, kSCPropNetDNSSearchOrder
, order
);
166 match
= CFEqual(resolver
, compare
);
169 CFNumberRef match_order
;
170 uint32_t match_order_val
= 0;
172 // if only the search order's are different
173 match_order
= CFDictionaryGetValue(match_resolver
, kSCPropNetDNSSearchOrder
);
174 if (!isA_CFNumber(match_order
) ||
175 !CFNumberGetValue(match_order
, kCFNumberSInt32Type
, &match_order_val
)) {
179 if (order_val
< match_order_val
) {
180 // if we should prefer this match resolver, else just skip it
181 CFArraySetValueAtIndex(resolvers
, i
, resolver
);
189 order
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &n_resolvers
);
190 CFDictionarySetValue(resolver
, DNS_CONFIGURATION_ORDER_KEY
, order
);
193 interface
= CFDictionaryGetValue(resolver
, kSCPropInterfaceName
);
194 if ((interface
!= NULL
) && !CFEqual(interface
, CFSTR("*"))) {
196 unsigned int if_index
= 0;
197 char if_name
[IF_NAMESIZE
];
201 if (_SC_cfstring_to_cstring(interface
,
204 kCFStringEncodingASCII
) != NULL
) {
205 if_index
= my_if_nametoindex(if_name
);
208 if ((if_index
!= 0) &&
210 // check if this is a "scoped" configuration
211 (CFDictionaryGetValueIfPresent(resolver
, DNS_CONFIGURATION_FLAGS_KEY
, (const void **)&num
) &&
213 CFNumberGetValue(num
, kCFNumberSInt32Type
, &flags
) &&
214 (flags
& DNS_RESOLVER_FLAGS_SCOPED
) != 0)
216 // check if we should scope all queries with this configuration
217 (CFDictionaryGetValueIfPresent(resolver
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, (const void **)&val
) &&
218 isA_CFBoolean(val
) &&
219 CFBooleanGetValue(val
))
222 // if interface index available and it should be used
223 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &if_index
);
224 CFDictionarySetValue(resolver
, DNS_CONFIGURATION_IF_INDEX_KEY
, num
);
229 CFArrayAppendValue(resolvers
, resolver
);
234 #define DNS_CONFIGURATION_CONFIGURATION_ID CFSTR("__CONFIGURATION_ID__")
238 add_resolver_signature(CFMutableDictionaryRef resolver
, const char *rType
, CFStringRef cID
, CFIndex rIndex
)
242 str
= CFStringCreateWithFormat(NULL
, NULL
,
243 CFSTR("%s:%s%@ %ld"),
245 (cID
!= NULL
) ? " " : "",
246 (cID
!= NULL
) ? cID
: CFSTR(""),
248 CFDictionarySetValue(resolver
, DNS_CONFIGURATION_CONFIGURATION_ID
, str
);
256 add_supplemental(CFMutableArrayRef resolvers
,
257 CFStringRef serviceID
,
259 uint32_t defaultOrder
,
268 domains
= CFDictionaryGetValue(dns
, kSCPropNetDNSSupplementalMatchDomains
);
269 n_domains
= isA_CFArray(domains
) ? CFArrayGetCount(domains
) : 0;
270 if (n_domains
== 0) {
271 // if no supplemental match domains
275 orders
= CFDictionaryGetValue(dns
, kSCPropNetDNSSupplementalMatchOrders
);
276 if (orders
!= NULL
) {
277 if (!isA_CFArray(orders
) || (n_domains
!= CFArrayGetCount(orders
))) {
278 // if supplemental match orders... but too many/not enough
283 servers
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerAddresses
);
284 if (!isA_CFArray(servers
) || (CFArrayGetCount(servers
) == 0)) {
285 // if no DNS server addresses
290 * yes, this is a "supplemental" resolver configuration, expand
291 * the match domains and add each to the resolvers list.
293 for (i
= 0; i
< n_domains
; i
++) {
294 CFStringRef match_domain
;
295 CFNumberRef match_order
;
296 CFMutableDictionaryRef match_resolver
;
298 match_domain
= CFArrayGetValueAtIndex(domains
, i
);
299 if (!isA_CFString(match_domain
)) {
303 match_resolver
= CFDictionaryCreateMutableCopy(NULL
, 0, dns
);
305 // set supplemental resolver "domain"
306 if (CFStringGetLength(match_domain
) > 0) {
307 CFDictionarySetValue(match_resolver
, kSCPropNetDNSDomainName
, match_domain
);
309 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSDomainName
);
312 // set supplemental resolver "search_order"
313 match_order
= (orders
!= NULL
) ? CFArrayGetValueAtIndex(orders
, i
) : NULL
;
314 if (isA_CFNumber(match_order
)) {
315 CFDictionarySetValue(match_resolver
, kSCPropNetDNSSearchOrder
, match_order
);
316 } else if (!CFDictionaryContainsKey(match_resolver
, kSCPropNetDNSSearchOrder
)) {
319 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &defaultOrder
);
320 CFDictionarySetValue(match_resolver
, kSCPropNetDNSSearchOrder
, num
);
323 defaultOrder
++; // if multiple domains, maintain ordering
326 // remove keys we don't want in a supplemental resolver
327 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSSupplementalMatchDomains
);
328 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSSupplementalMatchOrders
);
329 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSSearchDomains
);
330 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSSortList
);
332 add_resolver_signature(match_resolver
,
333 scoped
? "Supplemental/Scoped" : "Supplemental",
336 add_resolver(resolvers
, match_resolver
);
337 CFRelease(match_resolver
);
348 merge_configuration_flags(CFMutableDictionaryRef newDNS
, uint32_t mergeFlags
)
353 if (!CFDictionaryGetValueIfPresent(newDNS
, DNS_CONFIGURATION_FLAGS_KEY
, (const void **)&num
) ||
354 !isA_CFNumber(num
) ||
355 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &flags
)) {
361 num
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &flags
);
362 CFDictionarySetValue(newDNS
, DNS_CONFIGURATION_FLAGS_KEY
, num
);
370 add_supplemental_resolvers(CFMutableArrayRef resolvers
,
371 CFDictionaryRef services
,
372 CFArrayRef service_order
,
373 CFStringRef scoped_interface
,
374 CFDictionaryRef scoped_service
)
376 const void * keys_q
[N_QUICK
];
377 const void ** keys
= keys_q
;
381 const void * vals_q
[N_QUICK
];
382 const void ** vals
= vals_q
;
384 n_services
= isA_CFDictionary(services
) ? CFDictionaryGetCount(services
) : 0;
385 if (n_services
== 0) {
386 return; // if no services
389 if (n_services
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
390 keys
= CFAllocatorAllocate(NULL
, n_services
* sizeof(CFTypeRef
), 0);
391 vals
= CFAllocatorAllocate(NULL
, n_services
* sizeof(CFTypeRef
), 0);
394 n_order
= isA_CFArray(service_order
) ? CFArrayGetCount(service_order
) : 0;
396 CFDictionaryGetKeysAndValues(services
, keys
, vals
);
397 for (i
= 0; i
< n_services
; i
++) {
398 uint32_t defaultOrder
;
400 uint32_t dns_resolver_flags
;
401 CFStringRef interface
;
402 CFMutableDictionaryRef newDNS
= NULL
;
404 CFDictionaryRef service
= (CFDictionaryRef
)vals
[i
];
405 CFStringRef serviceID
= (CFStringRef
)keys
[i
];
406 Boolean trusted
= FALSE
; // trusted config w/interface
408 if (!isA_CFDictionary(service
)) {
412 dns
= CFDictionaryGetValue(service
, kSCEntNetDNS
);
413 dns
= isA_CFDictionary(dns
);
418 interface
= CFDictionaryGetValue(dns
, kSCPropInterfaceName
);
420 if (scoped_interface
!= NULL
) {
422 // we only want to add split/supplemental configurations
423 // for queries scoped to an interface if they are NOT
424 // associated with a "real" service
426 if (CFDictionaryContainsKey(service
, kSCEntNetIPv4
) ||
427 CFDictionaryContainsKey(service
, kSCEntNetIPv6
)) {
432 // in addition, we don't want to add split/supplemental
433 // configurations for queries scoped to an interface if
434 // the configuration does not apply to all interfaces and
435 // the configuration is explicitly NOT for this interface
437 if (!_SC_CFEqual(interface
, CFSTR("*")) &&
438 !_SC_CFEqual(interface
, scoped_interface
)) {
443 // lastly, check if A/AAAA queries should be issued (based
444 // on the IP[v6] addresses). If we would not be issuing a
445 // query then don't bother adding the configuration.
447 dns_resolver_flags
= dns_resolver_flags_service(scoped_service
, 0);
448 if (dns_resolver_flags
== 0) {
453 defaultOrder
= DEFAULT_SEARCH_ORDER
454 - (DEFAULT_SEARCH_ORDER
/ 2)
455 + ((DEFAULT_SEARCH_ORDER
/ 1000) * (uint32_t)i
);
457 !CFArrayContainsValue(service_order
, CFRangeMake(0, n_order
), keys
[i
])) {
458 // push out services not specified in service order
459 defaultOrder
+= (DEFAULT_SEARCH_ORDER
/ 1000) * n_services
;
463 * Ensure that we have the correct InterfaceName in the DNS configuration
465 * scoped_interface [supplemental] interface Trusted config DNS interface
466 * ================ ======================== ============== =================
467 * NULL NULL No NULL (No change)
470 * NULL NULL Yes NULL (No change)
471 * NULL en0 Yes en0 (trusted config w/interface)
473 * en0 NULL N/A en0 (scoped interface)
474 * en0 en0 N/A en0 (scoped interface)
475 * en0 * N/A en0 (scoped interface)
477 if ((scoped_interface
== NULL
) && (interface
== NULL
)) {
478 newDNS
= (CFMutableDictionaryRef
)CFRetain(dns
);
482 newDNS
= CFDictionaryCreateMutableCopy(NULL
, 0, dns
);
483 if (scoped_interface
!= NULL
) {
484 CFDictionarySetValue(newDNS
, kSCPropInterfaceName
, scoped_interface
);
485 } else if ((interface
!= NULL
) &&
486 CFDictionaryGetValueIfPresent(dns
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, (const void **)&val
) &&
487 isA_CFBoolean(val
) &&
488 CFBooleanGetValue(val
)) {
489 // leave the [trusted configuration] InterfaceName in place
492 CFDictionaryRemoveValue(newDNS
, kSCPropInterfaceName
);
496 // set "supplemental" flag
497 newFlags
= DNS_RESOLVER_FLAGS_SUPPLEMENTAL
;
499 if (scoped_interface
!= NULL
) {
500 // set "scoped" configuration flag
501 newFlags
|= DNS_RESOLVER_FLAGS_SCOPED
;
503 // add "Request A/AAAA query" flag(s)
504 newFlags
|= dns_resolver_flags
;
505 } else if (trusted
) {
506 // use the DNS query flags from the supplemental match service
507 newFlags
|= dns_resolver_flags_service(service
, 0);
510 merge_configuration_flags(newDNS
, newFlags
);
512 // add [scoped] resolver entry
513 add_supplemental(resolvers
, serviceID
, newDNS
, defaultOrder
, (scoped_interface
!= NULL
));
517 if (keys
!= keys_q
) {
518 CFAllocatorDeallocate(NULL
, keys
);
519 CFAllocatorDeallocate(NULL
, vals
);
527 add_multicast_resolvers(CFMutableArrayRef resolvers
, CFArrayRef multicastResolvers
)
532 n
= isA_CFArray(multicastResolvers
) ? CFArrayGetCount(multicastResolvers
) : 0;
533 for (i
= 0; i
< n
; i
++) {
534 uint32_t defaultOrder
;
537 CFMutableDictionaryRef resolver
;
539 domain
= CFArrayGetValueAtIndex(multicastResolvers
, i
);
540 domain
= _SC_trimDomain(domain
);
541 if (domain
== NULL
) {
545 defaultOrder
= DEFAULT_SEARCH_ORDER
546 + (DEFAULT_SEARCH_ORDER
/ 2)
547 + ((DEFAULT_SEARCH_ORDER
/ 1000) * (uint32_t)i
);
549 resolver
= CFDictionaryCreateMutable(NULL
,
551 &kCFTypeDictionaryKeyCallBacks
,
552 &kCFTypeDictionaryValueCallBacks
);
553 CFDictionarySetValue(resolver
, kSCPropNetDNSDomainName
, domain
);
554 CFDictionarySetValue(resolver
, kSCPropNetDNSOptions
, CFSTR("mdns"));
555 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &defaultOrder
);
556 CFDictionarySetValue(resolver
, kSCPropNetDNSSearchOrder
, num
);
558 if (S_mdns_timeout
!= NULL
) {
559 CFDictionarySetValue(resolver
, kSCPropNetDNSServerTimeout
, S_mdns_timeout
);
561 add_resolver_signature(resolver
, "Multicast DNS", NULL
, i
);
562 add_resolver(resolvers
, resolver
);
572 add_private_resolvers(CFMutableArrayRef resolvers
, CFArrayRef privateResolvers
)
577 n
= isA_CFArray(privateResolvers
) ? CFArrayGetCount(privateResolvers
) : 0;
578 for (i
= 0; i
< n
; i
++) {
579 uint32_t defaultOrder
;
582 CFMutableDictionaryRef resolver
;
584 domain
= CFArrayGetValueAtIndex(privateResolvers
, i
);
585 domain
= _SC_trimDomain(domain
);
586 if (domain
== NULL
) {
590 defaultOrder
= DEFAULT_SEARCH_ORDER
591 - (DEFAULT_SEARCH_ORDER
/ 4)
592 + ((DEFAULT_SEARCH_ORDER
/ 1000) * (uint32_t)i
);
594 resolver
= CFDictionaryCreateMutable(NULL
,
596 &kCFTypeDictionaryKeyCallBacks
,
597 &kCFTypeDictionaryValueCallBacks
);
598 CFDictionarySetValue(resolver
, kSCPropNetDNSDomainName
, domain
);
599 CFDictionarySetValue(resolver
, kSCPropNetDNSOptions
, CFSTR("pdns"));
600 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &defaultOrder
);
601 CFDictionarySetValue(resolver
, kSCPropNetDNSSearchOrder
, num
);
603 if (S_pdns_timeout
!= NULL
) {
604 CFDictionarySetValue(resolver
, kSCPropNetDNSServerTimeout
, S_pdns_timeout
);
606 add_resolver_signature(resolver
, "Private DNS", NULL
, i
);
607 add_resolver(resolvers
, resolver
);
616 static CFComparisonResult
617 compareBySearchOrder(const void *val1
, const void *val2
, void *context
)
619 #pragma unused(context)
620 CFDictionaryRef dns1
= (CFDictionaryRef
)val1
;
621 CFDictionaryRef dns2
= (CFDictionaryRef
)val2
;
624 uint32_t order1
= DEFAULT_SEARCH_ORDER
;
625 uint32_t order2
= DEFAULT_SEARCH_ORDER
;
627 num1
= CFDictionaryGetValue(dns1
, kSCPropNetDNSSearchOrder
);
628 if (!isA_CFNumber(num1
) ||
629 !CFNumberGetValue(num1
, kCFNumberSInt32Type
, &order1
)) {
630 order1
= DEFAULT_SEARCH_ORDER
;
633 num2
= CFDictionaryGetValue(dns2
, kSCPropNetDNSSearchOrder
);
634 if (!isA_CFNumber(num2
) ||
635 !CFNumberGetValue(num2
, kCFNumberSInt32Type
, &order2
)) {
636 order2
= DEFAULT_SEARCH_ORDER
;
639 if (order1
== order2
) {
640 // if same "SearchOrder", retain original orderring for configurations
641 if (CFDictionaryGetValueIfPresent(dns1
, DNS_CONFIGURATION_ORDER_KEY
, (const void **)&num1
) &&
642 CFDictionaryGetValueIfPresent(dns2
, DNS_CONFIGURATION_ORDER_KEY
, (const void **)&num2
) &&
643 isA_CFNumber(num1
) &&
644 isA_CFNumber(num2
) &&
645 CFNumberGetValue(num1
, kCFNumberSInt32Type
, &order1
) &&
646 CFNumberGetValue(num2
, kCFNumberSInt32Type
, &order2
)) {
647 if (order1
== order2
) {
648 return kCFCompareEqualTo
;
650 return (order1
< order2
) ? kCFCompareLessThan
: kCFCompareGreaterThan
;
654 return kCFCompareEqualTo
;
657 return (order1
< order2
) ? kCFCompareLessThan
: kCFCompareGreaterThan
;
661 static CF_RETURNS_RETAINED CFArrayRef
662 extract_search_domains(CFMutableDictionaryRef defaultDomain
, CFArrayRef supplemental
)
664 CFStringRef defaultDomainName
= NULL
;
665 uint32_t defaultOrder
= DEFAULT_SEARCH_ORDER
;
666 CFArrayRef defaultSearchDomains
= NULL
;
667 CFIndex defaultSearchIndex
= 0;
668 CFMutableArrayRef mySearchDomains
;
669 CFMutableArrayRef mySupplemental
= NULL
;
670 CFIndex n_supplemental
;
671 CFStringRef trimmedDomainName
;
673 mySearchDomains
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
675 if (defaultDomain
!= NULL
) {
678 num
= CFDictionaryGetValue(defaultDomain
, kSCPropNetDNSSearchOrder
);
679 if (!isA_CFNumber(num
) ||
680 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &defaultOrder
)) {
681 defaultOrder
= DEFAULT_SEARCH_ORDER
;
684 defaultDomainName
= CFDictionaryGetValue(defaultDomain
, kSCPropNetDNSDomainName
);
685 defaultSearchDomains
= CFDictionaryGetValue(defaultDomain
, kSCPropNetDNSSearchDomains
);
688 // validate the provided "search" domains or move/expand/promote the "domain" name
689 if (isA_CFArray(defaultSearchDomains
)) {
692 n_search
= CFArrayGetCount(defaultSearchDomains
);
693 for (int i
= 0; i
< n_search
; i
++) {
696 search
= CFArrayGetValueAtIndex(defaultSearchDomains
, i
);
697 search
= _SC_trimDomain(search
);
698 if (search
!= NULL
) {
699 CFArrayAppendValue(mySearchDomains
, search
);
704 trimmedDomainName
= _SC_trimDomain(defaultDomainName
);
705 #ifdef PERFORM_DOMAIN_EXPANSION
707 * With BIND 4.8.3 (and earlier) resolvers, the default search list included
708 * the default domain and each of its parent domains with two or more labels.
710 if ((trimmedDomainName
!= NULL
) &&
711 CFStringHasSuffix(defaultDomainName
, CFSTR("."))) {
712 // if "domain" name is fully qualified
713 CFArrayAppendValue(mySearchDomains
, trimmedDomainName
);
714 CFRelease(trimmedDomainName
);
715 } else if (trimmedDomainName
!= NULL
) {
717 int domain_parts
= 1;
721 domain
= _SC_cfstring_to_cstring(trimmedDomainName
,
724 kCFStringEncodingUTF8
);
725 CFRelease(trimmedDomainName
);
727 // count domain parts
728 for (dp
= domain
; *dp
!= '\0'; dp
++) {
734 // move "domain" to "search" list (and expand as needed)
740 str
= CFStringCreateWithCString(NULL
,
742 kCFStringEncodingUTF8
);
743 search
= _SC_trimDomain(str
);
745 if (search
!= NULL
) {
746 CFArrayAppendValue(mySearchDomains
, search
);
750 dp
= strchr(dp
, '.') + 1;
751 } while (domain_parts
-- > 2);
752 CFAllocatorDeallocate(NULL
, domain
);
754 #else // PERFORM_DOMAIN_EXPANSION
756 * With BIND 4.9.3 (and later) resolvers, the default search list included
757 * just the default domain.
759 if (trimmedDomainName
!= NULL
) {
760 CFArrayAppendValue(mySearchDomains
, trimmedDomainName
);
761 CFRelease(trimmedDomainName
);
763 #endif // PERFORM_DOMAIN_EXPANSION
766 // add any supplemental "domain" names to the search list
767 n_supplemental
= (supplemental
!= NULL
) ? CFArrayGetCount(supplemental
) : 0;
768 if (n_supplemental
> 1) {
769 mySupplemental
= CFArrayCreateMutableCopy(NULL
, 0, supplemental
);
770 CFArraySortValues(mySupplemental
,
771 CFRangeMake(0, n_supplemental
),
772 compareBySearchOrder
,
774 supplemental
= mySupplemental
;
776 for (int i
= 0; i
< n_supplemental
; i
++) {
782 CFStringRef supplementalDomain
;
783 uint32_t supplementalOrder
;
785 dns
= CFArrayGetValueAtIndex(supplemental
, i
);
787 options
= CFDictionaryGetValue(dns
, kSCPropNetDNSOptions
);
788 if (isA_CFString(options
)) {
791 if (CFEqual(options
, CFSTR("pdns"))) {
792 // don't add private resolver domains to the search list
796 range
= CFStringFind(options
, CFSTR("interface="), 0);
797 if (range
.location
!= kCFNotFound
) {
798 // don't add scoped resolver domains to the search list
803 supplementalDomain
= CFDictionaryGetValue(dns
, kSCPropNetDNSDomainName
);
804 supplementalDomain
= _SC_trimDomain(supplementalDomain
);
805 if (supplementalDomain
== NULL
) {
809 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSSupplementalMatchDomainsNoSearch
);
810 if (isA_CFNumber(num
) &&
811 CFNumberGetValue(num
, kCFNumberIntType
, &noSearch
) &&
813 CFRelease(supplementalDomain
);
817 if (CFStringHasSuffix(supplementalDomain
, CFSTR(".in-addr.arpa")) ||
818 CFStringHasSuffix(supplementalDomain
, CFSTR(".ip6.arpa" ))) {
819 CFRelease(supplementalDomain
);
823 domainIndex
= CFArrayGetFirstIndexOfValue(mySearchDomains
,
824 CFRangeMake(0, CFArrayGetCount(mySearchDomains
)),
827 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSSearchOrder
);
828 if (!isA_CFNumber(num
) ||
829 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &supplementalOrder
)) {
830 supplementalOrder
= DEFAULT_SEARCH_ORDER
;
833 if (supplementalOrder
< defaultOrder
) {
834 if (domainIndex
!= kCFNotFound
) {
835 // if supplemental domain is already in the search list
836 CFArrayRemoveValueAtIndex(mySearchDomains
, domainIndex
);
837 if (domainIndex
< defaultSearchIndex
) {
838 defaultSearchIndex
--;
841 CFArrayInsertValueAtIndex(mySearchDomains
,
844 defaultSearchIndex
++;
846 if (domainIndex
== kCFNotFound
) {
847 // add to the (end of the) search list
848 CFArrayAppendValue(mySearchDomains
, supplementalDomain
);
852 CFRelease(supplementalDomain
);
854 if (mySupplemental
!= NULL
) CFRelease(mySupplemental
);
856 // update the "search" domains
857 if (CFArrayGetCount(mySearchDomains
) == 0) {
858 CFRelease(mySearchDomains
);
859 mySearchDomains
= NULL
;
862 // remove the "domain" name and "search" list
863 CFDictionaryRemoveValue(defaultDomain
, kSCPropNetDNSDomainName
);
864 CFDictionaryRemoveValue(defaultDomain
, kSCPropNetDNSSearchDomains
);
866 return mySearchDomains
;
871 add_scoped_resolvers(CFMutableArrayRef scoped
,
872 CFDictionaryRef services
,
873 CFArrayRef service_order
)
875 const void * keys_q
[N_QUICK
];
876 const void ** keys
= keys_q
;
880 CFMutableArrayRef order
;
881 CFMutableSetRef seen
;
883 n_services
= isA_CFDictionary(services
) ? CFDictionaryGetCount(services
) : 0;
884 if (n_services
== 0) {
885 return; // if no services
888 // ensure that we process all services in order
890 n_order
= isA_CFArray(service_order
) ? CFArrayGetCount(service_order
) : 0;
892 order
= CFArrayCreateMutableCopy(NULL
, 0, service_order
);
894 order
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
897 if (n_services
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
898 keys
= CFAllocatorAllocate(NULL
, n_services
* sizeof(CFTypeRef
), 0);
900 CFDictionaryGetKeysAndValues(services
, keys
, NULL
);
901 for (i
= 0; i
< n_services
; i
++) {
902 CFStringRef serviceID
= (CFStringRef
)keys
[i
];
904 if (!CFArrayContainsValue(order
, CFRangeMake(0, n_order
), serviceID
)) {
905 CFArrayAppendValue(order
, serviceID
);
909 if (keys
!= keys_q
) {
910 CFAllocatorDeallocate(NULL
, keys
);
913 // iterate over services
915 seen
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
916 for (i
= 0; i
< n_order
; i
++) {
918 uint32_t dns_resolver_flags
;
919 char if_name
[IF_NAMESIZE
];
920 CFStringRef interface
;
921 CFMutableDictionaryRef newDNS
;
923 CFArrayRef searchDomains
;
924 CFDictionaryRef service
;
925 CFStringRef serviceID
;
928 serviceID
= CFArrayGetValueAtIndex(order
, i
);
929 service
= CFDictionaryGetValue(services
, serviceID
);
930 if (!isA_CFDictionary(service
)) {
935 dns
= CFDictionaryGetValue(service
, kSCEntNetDNS
);
936 if (!isA_CFDictionary(dns
)) {
941 servers
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerAddresses
);
942 if (!isA_CFArray(servers
) || (CFArrayGetCount(servers
) == 0)) {
943 // if no DNS server addresses
947 interface
= CFDictionaryGetValue(dns
, kSCPropInterfaceName
);
948 if ((interface
== NULL
) || CFEqual(interface
, CFSTR("*"))) {
949 // if no [scoped] interface or supplemental configuration w/match-all
953 if (CFDictionaryContainsKey(dns
, kSCPropNetDNSServiceIdentifier
)) {
954 // if this is a service-specific resolver
958 if (CFSetContainsValue(seen
, interface
)) {
959 // if we've already processed this [scoped] interface
962 CFSetSetValue(seen
, interface
);
964 if ((_SC_cfstring_to_cstring(interface
,
967 kCFStringEncodingASCII
) == NULL
) ||
968 (my_if_nametoindex(if_name
) == 0)) {
969 // if interface index not available
973 // add [scoped] resolver entry
974 newDNS
= CFDictionaryCreateMutableCopy(NULL
, 0, dns
);
977 searchDomains
= extract_search_domains(newDNS
, NULL
);
978 if (searchDomains
!= NULL
) {
979 CFDictionarySetValue(newDNS
, kSCPropNetDNSSearchDomains
, searchDomains
);
980 CFRelease(searchDomains
);
983 // get "Request A/AAAA query" flag(s)
984 dns_resolver_flags
= dns_resolver_flags_service(service
, 0);
985 if (dns_resolver_flags
== 0) {
989 // set "scoped" configuration flag
990 newFlags
= DNS_RESOLVER_FLAGS_SCOPED
;
992 // add "Request A/AAAA query" flag(s)
993 newFlags
|= dns_resolver_flags
;
995 merge_configuration_flags(newDNS
, newFlags
);
997 // remove keys we don't want in a [scoped] resolver
998 CFDictionaryRemoveValue(newDNS
, kSCPropNetDNSSupplementalMatchDomains
);
999 CFDictionaryRemoveValue(newDNS
, kSCPropNetDNSSupplementalMatchOrders
);
1001 // add the [scoped] resolver
1002 add_resolver_signature(newDNS
, "Scoped", serviceID
, 0);
1003 add_resolver(scoped
, newDNS
);
1005 // add any supplemental resolver configurations for this interface
1006 add_supplemental_resolvers(scoped
, services
, service_order
, interface
, service
);
1019 add_service_specific_resolvers(CFMutableArrayRef resolvers
, CFDictionaryRef services
)
1022 CFStringRef keys_q
[N_QUICK
];
1023 CFStringRef
*keys
= keys_q
;
1025 CFMutableSetRef seen
;
1026 CFDictionaryRef vals_q
[N_QUICK
];
1027 CFDictionaryRef
*vals
= vals_q
;
1029 n_services
= isA_CFDictionary(services
) ? CFDictionaryGetCount(services
) : 0;
1030 if (n_services
== 0) {
1031 return; // if no services
1034 if (n_services
> (CFIndex
)(sizeof(keys_q
) / sizeof(keys_q
[0]))) {
1035 keys
= CFAllocatorAllocate(kCFAllocatorDefault
, n_services
* sizeof(keys
[0]), 0);
1036 vals
= CFAllocatorAllocate(kCFAllocatorDefault
, n_services
* sizeof(vals
[0]), 0);
1038 CFDictionaryGetKeysAndValues(services
, (const void **)keys
, (const void **)vals
);
1040 seen
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
1041 for (i
= 0; i
< n_services
; i
++) {
1042 CFDictionaryRef dns
;
1043 CFNumberRef dns_service_identifier
;
1044 CFMutableDictionaryRef newDNS
;
1045 uint32_t newFlags
= 0;
1046 CFDictionaryRef service
= vals
[i
];
1047 CFStringRef serviceID
= keys
[i
];
1048 CFArrayRef searchDomains
;
1050 dns
= CFDictionaryGetValue(service
, kSCEntNetDNS
);
1051 if (!isA_CFDictionary(dns
)) {
1056 dns_service_identifier
= CFDictionaryGetValue(dns
, kSCPropNetDNSServiceIdentifier
);
1057 if (!isA_CFNumber(dns_service_identifier
)) {
1058 // if no DNS [vpn] Service Identifier
1062 if (CFSetContainsValue(seen
, dns_service_identifier
)) {
1063 my_log(LOG_ERR
, "add_service_specific_resolvers: got a resolver with a duplicate service identifier, skipping");
1066 CFSetSetValue(seen
, dns_service_identifier
);
1068 newDNS
= CFDictionaryCreateMutableCopy(NULL
, 0, dns
);
1070 // add "Request A/AAAA query" flag(s)
1071 newFlags
|= DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS
;
1074 searchDomains
= extract_search_domains(newDNS
, NULL
);
1075 if (searchDomains
!= NULL
) {
1076 CFDictionarySetValue(newDNS
, kSCPropNetDNSSearchDomains
, searchDomains
);
1077 CFRelease(searchDomains
);
1078 searchDomains
= NULL
;
1081 CFDictionaryRemoveValue(newDNS
, kSCPropNetDNSSupplementalMatchDomains
);
1082 CFDictionaryRemoveValue(newDNS
, kSCPropNetDNSSupplementalMatchOrders
);
1084 if (CFDictionaryContainsKey(newDNS
, kSCPropInterfaceName
)) {
1085 CFMutableDictionaryRef interfaceScopedDNS
;
1086 uint32_t interfaceScopedFlags
;
1088 // The dictionary has an interface, so add a interface-scoped resolver
1090 CFDictionarySetValue(newDNS
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, kCFBooleanTrue
);
1092 interfaceScopedDNS
= CFDictionaryCreateMutableCopy(NULL
, 0, newDNS
);
1093 interfaceScopedFlags
= newFlags
;
1095 // set "scoped" configuration flag
1096 interfaceScopedFlags
|= DNS_RESOLVER_FLAGS_SCOPED
;
1097 merge_configuration_flags(interfaceScopedDNS
, interfaceScopedFlags
);
1099 CFDictionaryRemoveValue(interfaceScopedDNS
, kSCPropNetDNSServiceIdentifier
);
1101 add_resolver_signature(interfaceScopedDNS
, "Service", serviceID
, 0);
1102 add_resolver(resolvers
, interfaceScopedDNS
);
1103 CFRelease(interfaceScopedDNS
);
1106 // set "service specific" configuration flag
1107 newFlags
|= DNS_RESOLVER_FLAGS_SERVICE_SPECIFIC
;
1108 merge_configuration_flags(newDNS
, newFlags
);
1110 add_resolver_signature(newDNS
, "Service", serviceID
, 0);
1111 add_resolver(resolvers
, newDNS
);
1116 if (keys
!= keys_q
) {
1117 CFAllocatorDeallocate(kCFAllocatorDefault
, keys
);
1118 CFAllocatorDeallocate(kCFAllocatorDefault
, vals
);
1126 add_default_resolver(CFMutableArrayRef resolvers
,
1127 CFDictionaryRef defaultResolver
,
1128 Boolean
*orderAdded
,
1129 CFArrayRef
*searchDomains
)
1131 CFMutableDictionaryRef myDefault
;
1132 uint32_t myOrder
= DEFAULT_SEARCH_ORDER
;
1135 if (defaultResolver
== NULL
) {
1136 myDefault
= CFDictionaryCreateMutable(NULL
,
1138 &kCFTypeDictionaryKeyCallBacks
,
1139 &kCFTypeDictionaryValueCallBacks
);
1141 myDefault
= CFDictionaryCreateMutableCopy(NULL
, 0, defaultResolver
);
1143 assert(myDefault
!= NULL
);
1145 // ensure that the default resolver has a search order
1147 order
= CFDictionaryGetValue(myDefault
, kSCPropNetDNSSearchOrder
);
1148 if (!isA_CFNumber(order
) ||
1149 !CFNumberGetValue(order
, kCFNumberSInt32Type
, &myOrder
)) {
1150 myOrder
= DEFAULT_SEARCH_ORDER
;
1151 order
= CFNumberCreate(NULL
, kCFNumberIntType
, &myOrder
);
1152 CFDictionarySetValue(myDefault
, kSCPropNetDNSSearchOrder
, order
);
1157 // extract the "search" domain list for the default resolver (and
1158 // any supplemental resolvers)
1160 *searchDomains
= extract_search_domains(myDefault
, resolvers
);
1162 // add the default resolver
1164 add_resolver_signature(myDefault
, "Default", NULL
, 0);
1165 add_resolver(resolvers
, myDefault
);
1166 CFRelease(myDefault
);
1171 static dns_create_resolver_t
1172 create_resolver(CFDictionaryRef dns
)
1176 dns_create_resolver_t _resolver
;
1178 CFStringRef targetInterface
= NULL
;
1179 unsigned int targetInterfaceIndex
= 0;
1181 _resolver
= _dns_resolver_create();
1184 str
= CFDictionaryGetValue(dns
, kSCPropNetDNSDomainName
);
1185 if (isA_CFString(str
) && (CFStringGetLength(str
) > 0)) {
1186 char domain
[NS_MAXDNAME
];
1188 if (_SC_cfstring_to_cstring(str
, domain
, sizeof(domain
), kCFStringEncodingUTF8
) != NULL
) {
1189 _dns_resolver_set_domain(&_resolver
, domain
);
1193 // process search domains
1194 list
= CFDictionaryGetValue(dns
, kSCPropNetDNSSearchDomains
);
1195 if (isA_CFArray(list
)) {
1197 CFIndex n
= CFArrayGetCount(list
);
1199 // add "search" domains
1200 for (i
= 0; i
< n
; i
++) {
1201 str
= CFArrayGetValueAtIndex(list
, i
);
1202 if (isA_CFString(str
) && (CFStringGetLength(str
) > 0)) {
1203 char search
[NS_MAXDNAME
];
1205 if (_SC_cfstring_to_cstring(str
, search
, sizeof(search
), kCFStringEncodingUTF8
) != NULL
) {
1206 _dns_resolver_add_search(&_resolver
, search
);
1212 // process interface index
1213 num
= CFDictionaryGetValue(dns
, DNS_CONFIGURATION_IF_INDEX_KEY
);
1214 if (isA_CFNumber(num
)) {
1217 if (CFNumberGetValue(num
, kCFNumberIntType
, &if_index
)) {
1219 const char *if_name
= NULL
;
1221 if (if_index
!= 0) {
1222 if_name
= my_if_indextoname(if_index
, buf
);
1223 if (if_name
!= NULL
) {
1224 targetInterface
= CFStringCreateWithCString(NULL
,
1226 kCFStringEncodingASCII
);
1227 targetInterfaceIndex
= if_index
;
1231 _dns_resolver_set_if_index(&_resolver
, if_index
, if_name
);
1236 num
= CFDictionaryGetValue(dns
, DNS_CONFIGURATION_FLAGS_KEY
);
1237 if (isA_CFNumber(num
)) {
1240 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &flags
)) {
1241 _dns_resolver_set_flags(&_resolver
, flags
);
1245 // process nameserver addresses
1246 // Note: the flags may be overwritten if the resolver has LOOPBACK addresses
1247 list
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerAddresses
);
1248 if (isA_CFArray(list
)) {
1250 CFIndex n
= CFArrayGetCount(list
);
1252 for (i
= 0; i
< n
; i
++) {
1255 struct sockaddr_in sin
;
1256 struct sockaddr_in6 sin6
;
1260 str
= CFArrayGetValueAtIndex(list
, i
);
1261 if (!isA_CFString(str
)) {
1265 if (_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
) == NULL
) {
1269 if (_SC_string_to_sockaddr(buf
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) == NULL
) {
1273 if ((addr
.sa
.sa_family
== AF_INET6
) &&
1274 IN6_IS_ADDR_LINKLOCAL(&addr
.sin6
.sin6_addr
) &&
1275 (addr
.sin6
.sin6_scope_id
== 0) &&
1276 (targetInterfaceIndex
!= 0)) {
1277 // for link local [IPv6] addresses, if the scope id is not
1278 // set then we should use the interface associated with the
1279 // resolver configuration
1280 addr
.sin6
.sin6_scope_id
= targetInterfaceIndex
;
1283 _dns_resolver_add_nameserver(&_resolver
, &addr
.sa
);
1287 // process search order
1288 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSSearchOrder
);
1289 if (isA_CFNumber(num
)) {
1292 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &order
)) {
1293 _dns_resolver_set_order(&_resolver
, order
);
1298 list
= CFDictionaryGetValue(dns
, kSCPropNetDNSSortList
);
1299 if (isA_CFArray(list
)) {
1301 CFIndex n
= CFArrayGetCount(list
);
1303 for (i
= 0; i
< n
; i
++) {
1306 dns_sortaddr_t sortaddr
;
1308 str
= CFArrayGetValueAtIndex(list
, i
);
1309 if (!isA_CFString(str
)) {
1313 if (_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
) == NULL
) {
1317 slash
= strchr(buf
, '/');
1318 if (slash
!= NULL
) {
1322 memset(&sortaddr
, 0, sizeof(sortaddr
));
1323 if (inet_aton(buf
, &sortaddr
.address
) != 1) {
1324 /* if address not valid */
1328 if (slash
!= NULL
) {
1329 if (inet_aton(slash
+ 1, &sortaddr
.mask
) != 1) {
1330 /* if subnet mask not valid */
1337 a
= ntohl(sortaddr
.address
.s_addr
);
1340 } else if (IN_CLASSB(a
)) {
1342 } else if (IN_CLASSC(a
)) {
1348 sortaddr
.mask
.s_addr
= htonl(m
);
1351 _dns_resolver_add_sortaddr(&_resolver
, &sortaddr
);
1356 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerPort
);
1357 if (isA_CFNumber(num
)) {
1360 if (CFNumberGetValue(num
, kCFNumberIntType
, &port
)) {
1361 _dns_resolver_set_port(&_resolver
, (uint16_t)port
);
1366 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerTimeout
);
1367 if (isA_CFNumber(num
)) {
1370 if (CFNumberGetValue(num
, kCFNumberIntType
, &timeout
)) {
1371 _dns_resolver_set_timeout(&_resolver
, (uint32_t)timeout
);
1376 str
= CFDictionaryGetValue(dns
, kSCPropNetDNSOptions
);
1377 if (isA_CFString(str
)) {
1380 options
= _SC_cfstring_to_cstring(str
, NULL
, 0, kCFStringEncodingUTF8
);
1381 if (options
!= NULL
) {
1382 _dns_resolver_set_options(&_resolver
, options
);
1383 CFAllocatorDeallocate(NULL
, options
);
1387 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSServiceIdentifier
);
1388 if (isA_CFNumber(num
)) {
1389 int dns_service_identifier
;
1391 if (CFNumberGetValue(num
, kCFNumberIntType
, &dns_service_identifier
)) {
1392 _dns_resolver_set_service_identifier(&_resolver
, (uint32_t)dns_service_identifier
);
1396 // process configuration ID
1397 str
= CFDictionaryGetValue(dns
, DNS_CONFIGURATION_CONFIGURATION_ID
);
1398 if (isA_CFString(str
) && (CFStringGetLength(str
) > 0)) {
1401 cID
= _SC_cfstring_to_cstring(str
, NULL
, 0, kCFStringEncodingUTF8
);
1403 _dns_resolver_set_configuration_identifier(&_resolver
, cID
);
1404 CFAllocatorDeallocate(NULL
, cID
);
1408 if (targetInterface
!= NULL
) {
1409 CFRelease(targetInterface
);
1416 static __inline__ Boolean
1417 isScopedConfiguration(CFDictionaryRef dns
)
1422 if ((dns
!= NULL
) &&
1423 CFDictionaryGetValueIfPresent(dns
, DNS_CONFIGURATION_FLAGS_KEY
, (const void **)&num
) &&
1425 CFNumberGetValue(num
, kCFNumberSInt32Type
, &flags
) &&
1426 ((flags
& DNS_RESOLVER_FLAGS_SCOPED
) != 0)) {
1435 static CFComparisonResult
1436 compareDomain(const void *val1
, const void *val2
, void *context
)
1438 CFDictionaryRef dns1
= (CFDictionaryRef
)val1
;
1439 CFDictionaryRef dns2
= (CFDictionaryRef
)val2
;
1440 CFStringRef domain1
;
1441 CFStringRef domain2
;
1442 CFArrayRef labels1
= NULL
;
1443 CFArrayRef labels2
= NULL
;
1446 CFComparisonResult result
;
1452 // "default" domains sort before "supplemental" domains
1453 domain1
= CFDictionaryGetValue(dns1
, kSCPropNetDNSDomainName
);
1454 domain2
= CFDictionaryGetValue(dns2
, kSCPropNetDNSDomainName
);
1455 if (domain1
== NULL
) {
1456 return kCFCompareLessThan
;
1457 } else if (domain2
== NULL
) {
1458 return kCFCompareGreaterThan
;
1461 // sort non-scoped before scoped
1462 scoped1
= isScopedConfiguration(dns1
);
1463 scoped2
= isScopedConfiguration(dns2
);
1464 if (scoped1
!= scoped2
) {
1466 return kCFCompareLessThan
;
1468 return kCFCompareGreaterThan
;
1472 // forward (A, AAAA) domains sort before reverse (PTR) domains
1473 rev1
= CFStringHasSuffix(domain1
, CFSTR(".arpa"));
1474 rev2
= CFStringHasSuffix(domain2
, CFSTR(".arpa"));
1477 return kCFCompareGreaterThan
;
1479 return kCFCompareLessThan
;
1483 labels1
= CFStringCreateArrayBySeparatingStrings(NULL
, domain1
, CFSTR("."));
1484 n1
= CFArrayGetCount(labels1
);
1486 labels2
= CFStringCreateArrayBySeparatingStrings(NULL
, domain2
, CFSTR("."));
1487 n2
= CFArrayGetCount(labels2
);
1489 while ((n1
> 0) && (n2
> 0)) {
1490 CFStringRef label1
= CFArrayGetValueAtIndex(labels1
, --n1
);
1491 CFStringRef label2
= CFArrayGetValueAtIndex(labels2
, --n2
);
1493 // compare domain labels
1494 result
= CFStringCompare(label1
, label2
, kCFCompareCaseInsensitive
);
1495 if (result
!= kCFCompareEqualTo
) {
1500 // longer labels (corp.apple.com) sort before shorter labels (apple.com)
1502 result
= kCFCompareLessThan
;
1504 } else if (n1
< n2
) {
1505 result
= kCFCompareGreaterThan
;
1509 // sort by search order
1510 result
= compareBySearchOrder(val1
, val2
, context
);
1514 if (labels1
!= NULL
) CFRelease(labels1
);
1515 if (labels2
!= NULL
) CFRelease(labels2
);
1520 static __inline__ Boolean
1521 needsMergeWithDefaultConfiguration(CFDictionaryRef dns
)
1526 if ((dns
!= NULL
) &&
1527 CFDictionaryGetValueIfPresent(dns
, DNS_CONFIGURATION_FLAGS_KEY
, (const void **)&num
) &&
1529 CFNumberGetValue(num
, kCFNumberSInt32Type
, &flags
)) {
1531 // check if merge needed (at all)
1532 if (dns_resolver_flags_all_queries(flags
)) {
1533 // if we are already querying for both A/AAAA
1537 // check if scoped or service-specific
1538 if (((flags
& DNS_RESOLVER_FLAGS_SCOPED
) != 0) ||
1539 ((flags
& DNS_RESOLVER_FLAGS_SERVICE_SPECIFIC
) != 0)) {
1551 dns_configuration_set(CFDictionaryRef defaultResolver
,
1552 CFDictionaryRef services
,
1553 CFArrayRef serviceOrder
,
1554 CFArrayRef multicastResolvers
,
1555 CFArrayRef privateResolvers
)
1557 dns_create_config_t dns_create_config
;
1558 Boolean changed
= FALSE
;
1560 CFMutableDictionaryRef myDefault
;
1561 Boolean myOrderAdded
= FALSE
;
1562 CFArrayRef mySearchDomains
= NULL
;
1563 CFIndex n_resolvers
;
1564 CFMutableArrayRef resolvers
;
1565 unsigned char signature
[CC_SHA256_DIGEST_LENGTH
];
1566 static unsigned char signature_last
[CC_SHA256_DIGEST_LENGTH
];
1568 // establish list of resolvers
1570 resolvers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1571 assert(resolvers
!= NULL
);
1573 // collect (and add) any "supplemental" resolver configurations
1575 add_supplemental_resolvers(resolvers
, services
, serviceOrder
, NULL
, NULL
);
1577 // collect (and add) any "private" resolver configurations
1579 add_private_resolvers(resolvers
, privateResolvers
);
1581 // add the "default" resolver
1583 if (defaultResolver
!= NULL
) {
1586 servers
= CFDictionaryGetValue(defaultResolver
, kSCPropNetDNSServerAddresses
);
1587 if (!isA_CFArray(servers
) || (CFArrayGetCount(servers
) == 0)) {
1588 // if no DNS server addresses
1589 defaultResolver
= NULL
;
1593 add_default_resolver(resolvers
, defaultResolver
, &myOrderAdded
, &mySearchDomains
);
1595 // collect (and add) any "multicast" resolver configurations
1597 add_multicast_resolvers(resolvers
, multicastResolvers
);
1599 // collect (and add) any "scoped" resolver configurations
1601 add_scoped_resolvers(resolvers
, services
, serviceOrder
);
1603 // collect (and add) any "service-specific" resolver configurations
1605 add_service_specific_resolvers(resolvers
, services
);
1609 n_resolvers
= CFArrayGetCount(resolvers
);
1610 if (n_resolvers
> 1) {
1611 CFArraySortValues(resolvers
, CFRangeMake(0, n_resolvers
), compareDomain
, NULL
);
1616 for (i
= n_resolvers
; --i
> 0; ) {
1617 CFDictionaryRef resolver
;
1619 resolver
= CFArrayGetValueAtIndex(resolvers
, i
);
1620 if (!CFDictionaryContainsKey(resolver
, kSCPropNetDNSDomainName
) &&
1621 !CFDictionaryContainsKey(resolver
, kSCPropNetDNSSearchDomains
) &&
1622 !CFDictionaryContainsKey(resolver
, kSCPropNetDNSServerAddresses
)) {
1623 // remove empty resolver
1624 CFArrayRemoveValueAtIndex(resolvers
, i
);
1629 // update the default resolver
1631 myDefault
= CFDictionaryCreateMutableCopy(NULL
,
1633 CFArrayGetValueAtIndex(resolvers
, 0));
1634 if (mySearchDomains
!= NULL
) {
1635 // add search domains to the default resolver
1636 CFDictionarySetValue(myDefault
, kSCPropNetDNSSearchDomains
, mySearchDomains
);
1637 CFRelease(mySearchDomains
);
1639 if (myOrderAdded
&& (n_resolvers
> 1)) {
1640 CFDictionaryRef resolver
;
1642 resolver
= CFArrayGetValueAtIndex(resolvers
, 1);
1643 if (CFDictionaryContainsKey(resolver
, kSCPropNetDNSDomainName
) ||
1644 isScopedConfiguration(resolver
)) {
1645 // if not a supplemental "default" resolver (a domain name is
1646 // present) or if it's a scoped configuration
1647 CFDictionaryRemoveValue(myDefault
, kSCPropNetDNSSearchOrder
);
1650 CFArraySetValueAtIndex(resolvers
, 0, myDefault
);
1651 CFRelease(myDefault
);
1653 // establish resolver configuration
1655 if ((defaultResolver
== NULL
) && (n_resolvers
<= 1)) {
1657 * if no default and no supplemental/scoped resolvers
1659 dns_create_config
= NULL
;
1661 uint32_t default_resolver_flags
= 0;
1662 Boolean have_default_flags
= FALSE
;
1665 * if default and/or supplemental/scoped resolvers are defined
1667 dns_create_config
= _dns_configuration_create();
1669 for (i
= 0; i
< n_resolvers
; i
++) {
1670 Boolean merge_default_flags
;
1671 CFDictionaryRef resolver
;
1672 dns_create_resolver_t _resolver
;
1674 resolver
= CFArrayGetValueAtIndex(resolvers
, i
);
1676 merge_default_flags
= needsMergeWithDefaultConfiguration(resolver
);
1677 if (merge_default_flags
) {
1678 CFMutableDictionaryRef new_resolver
;
1680 if (!have_default_flags
) {
1681 CFDictionaryApplyFunction(services
,
1682 add_dns_resolver_flags
,
1683 &default_resolver_flags
);
1684 have_default_flags
= TRUE
;
1687 new_resolver
= CFDictionaryCreateMutableCopy(NULL
, 0, resolver
);
1688 merge_configuration_flags(new_resolver
, default_resolver_flags
);
1689 resolver
= new_resolver
;
1692 _resolver
= create_resolver(resolver
);
1693 _dns_configuration_add_resolver(&dns_create_config
, _resolver
);
1694 _dns_resolver_free(&_resolver
);
1696 if (merge_default_flags
) {
1697 CFRelease(resolver
);
1701 // add flatfile resolvers
1703 _dnsinfo_flatfile_set_flags(default_resolver_flags
);
1704 _dnsinfo_flatfile_add_resolvers(&dns_create_config
);
1707 // check if the configuration changed
1708 _dns_configuration_signature(&dns_create_config
, signature
, sizeof(signature
));
1709 if (bcmp(signature
, signature_last
, sizeof(signature
)) != 0) {
1710 // save [new] signature
1711 memcpy(signature_last
, signature
, sizeof(signature
));
1713 my_log(LOG_INFO
, "Updating DNS configuration");
1714 if (dns_create_config
!= NULL
) {
1715 dns_config_t
*dns_config
= NULL
;
1716 _dns_config_buf_t
*dns_config_buf
;
1719 n
= sizeof(_dns_config_buf_t
);
1720 n
+= ntohl(((_dns_config_buf_t
*)dns_create_config
)->n_attribute
);
1721 dns_config_buf
= _dns_configuration_buffer_create((void *)dns_create_config
, n
);
1722 if (dns_config_buf
!= NULL
) {
1723 dns_config
= _dns_configuration_buffer_expand(dns_config_buf
);
1724 if (dns_config
== NULL
) {
1725 // if we were unable to expand the configuration
1726 _dns_configuration_buffer_free(&dns_config_buf
);
1730 if (dns_config
!= NULL
) {
1731 _dns_configuration_log(dns_config
, TRUE
, NULL
);
1735 my_log(LOG_INFO
, "*** No DNS configuration");
1738 // save [new] configuration
1739 if (!_dns_configuration_store(&dns_create_config
)) {
1740 my_log(LOG_ERR
, "could not store configuration");
1747 if (dns_create_config
!= NULL
) {
1748 _dns_configuration_free(&dns_create_config
);
1751 CFRelease(resolvers
);
1756 static SCDynamicStoreRef dns_configuration_store
;
1757 static SCDynamicStoreCallBack dns_configuration_callout
;
1760 dns_configuration_changed(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1762 #pragma unused(port)
1764 #pragma unused(size)
1765 #pragma unused(info)
1766 static const CFStringRef key
= CFSTR(_PATH_RESOLVER_DIR
);
1768 Boolean resolvers_now
;
1769 static Boolean resolvers_save
= FALSE
;
1770 struct stat statbuf
;
1772 resolvers_now
= (stat(_PATH_RESOLVER_DIR
, &statbuf
) == 0);
1773 if (!resolvers_save
&& (resolvers_save
== resolvers_now
)) {
1774 // if we did not (and still do not) have an "/etc/resolvers"
1775 // directory than this notification is the result of a change
1776 // to the "/etc" directory.
1779 resolvers_save
= resolvers_now
;
1781 my_log(LOG_INFO
, _PATH_RESOLVER_DIR
" changed");
1783 // fake a "DNS" change
1784 keys
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
1785 (*dns_configuration_callout
)(dns_configuration_store
, keys
, NULL
);
1793 normalize_path(const char *file_name
, char resolved_name
[PATH_MAX
])
1796 char path
[PATH_MAX
];
1798 strlcpy(path
, file_name
, sizeof(path
));
1799 if (realpath(path
, resolved_name
) != NULL
) {
1800 // if the path exists
1804 ch
= strrchr(path
, '/');
1807 if (realpath(path
, resolved_name
) != NULL
) {
1808 // if a parent path exists
1809 strlcat(resolved_name
, "/", PATH_MAX
);
1810 strlcat(resolved_name
, ch
+1, PATH_MAX
);
1821 dns_configuration_monitor(SCDynamicStoreRef store
, SCDynamicStoreCallBack callout
)
1824 mach_port_t notify_port
;
1826 char resolver_directory_path
[PATH_MAX
];
1827 CFRunLoopSourceRef rls
;
1830 if (!normalize_path(_PATH_RESOLVER_DIR
, resolver_directory_path
)) {
1831 my_log(LOG_ERR
, "Not monitoring \"%s\", could not resolve directory path", _PATH_RESOLVER_DIR
);
1835 dns_configuration_store
= store
;
1836 dns_configuration_callout
= callout
;
1838 status
= notify_register_mach_port(_PATH_RESOLVER_DIR
, ¬ify_port
, 0, ¬ify_token
);
1839 if (status
!= NOTIFY_STATUS_OK
) {
1840 my_log(LOG_ERR
, "notify_register_mach_port() failed");
1844 #pragma GCC diagnostic push
1845 #pragma GCC diagnostic ignored "-Wdeprecated"
1846 status
= notify_monitor_file(notify_token
, resolver_directory_path
, 0);
1847 #pragma GCC diagnostic pop
1848 if (status
!= NOTIFY_STATUS_OK
) {
1849 my_log(LOG_ERR
, "notify_monitor_file() failed");
1850 (void)notify_cancel(notify_token
);
1854 mp
= _SC_CFMachPortCreateWithPort("IPMonitor/dns_configuration",
1856 dns_configuration_changed
,
1859 rls
= CFMachPortCreateRunLoopSource(NULL
, mp
, -1);
1861 my_log(LOG_ERR
, "SCDynamicStoreCreateRunLoopSource() failed");
1863 (void)notify_cancel(notify_token
);
1866 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
1876 dns_configuration_init(CFBundleRef bundle
)
1878 CFDictionaryRef dict
;
1880 dict
= CFBundleGetInfoDictionary(bundle
);
1881 if (isA_CFDictionary(dict
)) {
1882 S_mdns_timeout
= CFDictionaryGetValue(dict
, CFSTR("mdns_timeout"));
1883 S_mdns_timeout
= isA_CFNumber(S_mdns_timeout
);
1885 S_pdns_timeout
= CFDictionaryGetValue(dict
, CFSTR("pdns_timeout"));
1886 S_pdns_timeout
= isA_CFNumber(S_pdns_timeout
);
1894 #pragma mark Standalone test code
1900 split(const void * key
, const void * value
, void * context
)
1902 CFArrayRef components
;
1903 CFStringRef entity_id
;
1904 CFStringRef service_id
;
1905 CFMutableDictionaryRef state_dict
;
1907 components
= CFStringCreateArrayBySeparatingStrings(NULL
, (CFStringRef
)key
, CFSTR("/"));
1908 service_id
= CFArrayGetValueAtIndex(components
, 3);
1909 entity_id
= CFArrayGetValueAtIndex(components
, 4);
1910 state_dict
= (CFMutableDictionaryRef
)CFDictionaryGetValue(context
, service_id
);
1911 if (state_dict
!= NULL
) {
1912 state_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
1914 state_dict
= CFDictionaryCreateMutable(NULL
,
1916 &kCFTypeDictionaryKeyCallBacks
,
1917 &kCFTypeDictionaryValueCallBacks
);
1920 if (CFEqual(entity_id
, kSCEntNetIPv4
) ||
1921 CFEqual(entity_id
, kSCEntNetIPv6
)) {
1922 CFDictionaryRef dict
;
1923 CFStringRef interface
;
1925 if (CFEqual(entity_id
, kSCEntNetIPv4
)) {
1926 dict
= ipv4_dict_create(value
);
1929 dict
= ipv6_dict_create(value
);
1932 CFDictionarySetValue(state_dict
, entity_id
, dict
);
1935 interface
= CFDictionaryGetValue((CFDictionaryRef
)value
, kSCPropInterfaceName
);
1936 if (interface
!= NULL
) {
1937 CFDictionaryRef dns
;
1938 CFMutableDictionaryRef new_dns
;
1940 dns
= CFDictionaryGetValue(state_dict
, kSCEntNetDNS
);
1942 new_dns
= CFDictionaryCreateMutableCopy(NULL
, 0, dns
);
1944 new_dns
= CFDictionaryCreateMutable(NULL
,
1946 &kCFTypeDictionaryKeyCallBacks
,
1947 &kCFTypeDictionaryValueCallBacks
);
1949 CFDictionarySetValue(new_dns
, kSCPropInterfaceName
, interface
);
1950 CFDictionarySetValue(state_dict
, kSCEntNetDNS
, new_dns
);
1953 } else if (CFEqual(entity_id
, kSCEntNetDNS
)) {
1954 CFDictionaryRef dns
;
1956 dns
= CFDictionaryGetValue(state_dict
, kSCEntNetDNS
);
1958 CFStringRef interface
;
1960 interface
= CFDictionaryGetValue(dns
, kSCPropInterfaceName
);
1961 if (interface
!= NULL
) {
1962 CFMutableDictionaryRef new_dns
;
1964 new_dns
= CFDictionaryCreateMutableCopy(NULL
, 0, (CFDictionaryRef
)value
);
1965 CFDictionarySetValue(new_dns
, kSCPropInterfaceName
, interface
);
1966 CFDictionarySetValue(state_dict
, kSCEntNetDNS
, new_dns
);
1969 CFDictionarySetValue(state_dict
, kSCEntNetDNS
, (CFDictionaryRef
)value
);
1972 CFDictionarySetValue(state_dict
, kSCEntNetDNS
, (CFDictionaryRef
)value
);
1975 CFDictionarySetValue(state_dict
, entity_id
, (CFDictionaryRef
)value
);
1978 CFDictionarySetValue((CFMutableDictionaryRef
)context
, service_id
, state_dict
);
1979 CFRelease(state_dict
);
1980 CFRelease(components
);
1986 main(int argc
, char **argv
)
1988 CFDictionaryRef entities
;
1990 CFArrayRef multicast_resolvers
;
1991 CFStringRef pattern
;
1992 CFMutableArrayRef patterns
;
1993 CFStringRef primary
= NULL
;
1994 CFDictionaryRef primaryDNS
= NULL
;
1995 CFArrayRef private_resolvers
;
1996 CFArrayRef service_order
= NULL
;
1997 CFMutableDictionaryRef service_state_dict
;
1998 CFDictionaryRef setup_global_ipv4
;
1999 CFDictionaryRef state_global_ipv4
;
2000 SCDynamicStoreRef store
;
2003 _sc_log
= kSCLogDestinationFile
;
2004 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
2006 store
= SCDynamicStoreCreate(NULL
, CFSTR("TEST"), NULL
, NULL
);
2008 // get IPv4, IPv6, and DNS entities
2009 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2010 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2011 kSCDynamicStoreDomainState
,
2014 CFArrayAppendValue(patterns
, pattern
);
2016 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2017 kSCDynamicStoreDomainState
,
2020 CFArrayAppendValue(patterns
, pattern
);
2022 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2023 kSCDynamicStoreDomainState
,
2026 CFArrayAppendValue(patterns
, pattern
);
2028 entities
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
2029 CFRelease(patterns
);
2031 service_state_dict
= CFDictionaryCreateMutable(NULL
,
2033 &kCFTypeDictionaryKeyCallBacks
,
2034 &kCFTypeDictionaryValueCallBacks
);
2035 CFDictionaryApplyFunction(entities
, split
, service_state_dict
);
2036 CFRelease(entities
);
2038 // get primary service ID
2039 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2040 kSCDynamicStoreDomainState
,
2042 state_global_ipv4
= SCDynamicStoreCopyValue(store
, key
);
2044 if (state_global_ipv4
!= NULL
) {
2045 primary
= CFDictionaryGetValue(state_global_ipv4
, kSCDynamicStorePropNetPrimaryService
);
2046 if (primary
!= NULL
) {
2047 CFDictionaryRef service_dict
;
2049 // get DNS configuration for primary service
2050 service_dict
= CFDictionaryGetValue(service_state_dict
, primary
);
2051 if (service_dict
!= NULL
) {
2052 primaryDNS
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
2058 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2059 kSCDynamicStoreDomainSetup
,
2061 setup_global_ipv4
= SCDynamicStoreCopyValue(store
, key
);
2063 if (setup_global_ipv4
!= NULL
) {
2064 service_order
= CFDictionaryGetValue(setup_global_ipv4
, kSCPropNetServiceOrder
);
2067 // get multicast resolvers
2068 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
2069 kSCDynamicStoreDomainState
,
2071 CFSTR(kDNSServiceCompMulticastDNS
));
2072 multicast_resolvers
= SCDynamicStoreCopyValue(store
, key
);
2075 // get private resolvers
2076 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
2077 kSCDynamicStoreDomainState
,
2079 CFSTR(kDNSServiceCompPrivateDNS
));
2080 private_resolvers
= SCDynamicStoreCopyValue(store
, key
);
2083 // update DNS configuration
2084 dns_configuration_init(CFBundleGetMainBundle());
2085 (void)dns_configuration_set(primaryDNS
,
2088 multicast_resolvers
,
2092 if (setup_global_ipv4
!= NULL
) CFRelease(setup_global_ipv4
);
2093 if (state_global_ipv4
!= NULL
) CFRelease(state_global_ipv4
);
2094 if (multicast_resolvers
!= NULL
) CFRelease(multicast_resolvers
);
2095 if (private_resolvers
!= NULL
) CFRelease(private_resolvers
);
2096 CFRelease(service_state_dict
);