1/*
2 * Copyright (c) 2004-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * Modification History
26 *
27 * March 22, 2004	Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31#include <TargetConditionals.h>
32#include <fcntl.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <unistd.h>
36#include <sys/types.h>
37#include <sys/socket.h>
38#include <sys/stat.h>
39#include <net/if.h>
40#include <netinet/in.h>
41#include <arpa/inet.h>
42#include <arpa/nameser.h>
43#include <resolv.h>
44#if	!TARGET_OS_IPHONE
45#include <notify.h>
46extern uint32_t notify_monitor_file(int token, const char *name, int flags);
47#endif	// !TARGET_OS_IPHONE
48#include <CommonCrypto/CommonDigest.h>
49
50#include <CoreFoundation/CoreFoundation.h>
51#include <SystemConfiguration/SystemConfiguration.h>
52#include <SystemConfiguration/SCPrivate.h>
53#include <SystemConfiguration/SCValidation.h>
54#include "ip_plugin.h"
55
56#include "dns-configuration.h"
57
58#include <dnsinfo.h>
59#include "dnsinfo_create.h"
60#include "dnsinfo_server.h"
61
62#ifdef	MAIN
63#undef	MAIN
64#include "dnsinfo_copy.c"
65#include "dnsinfo_internal.h"
66#define	MAIN
67#define	DNS_CONFIGURATION_DEBUG
68#endif	// MAIN
69
70#include <dns_sd.h>
71#ifndef	kDNSServiceCompMulticastDNS
72#define	kDNSServiceCompMulticastDNS	"MulticastDNS"
73#endif
74#ifndef	kDNSServiceCompPrivateDNS
75#define	kDNSServiceCompPrivateDNS	"PrivateDNS"
76#endif
77
78#define DNS_CONFIGURATION_FLAGS_KEY	CFSTR("__FLAGS__")
79#define DNS_CONFIGURATION_IF_INDEX_KEY	CFSTR("__IF_INDEX__")
80#define DNS_CONFIGURATION_ORDER_KEY	CFSTR("__ORDER__")
81
82/* multicast DNS resolver configurations */
83static	CFNumberRef	S_mdns_timeout	= NULL;
84
85/* private DNS resolver configurations */
86static	CFNumberRef	S_pdns_timeout	= NULL;
87
88
89#pragma mark -
90#pragma mark DNS resolver flags
91
92
93static __inline__ boolean_t
94dns_resolver_flags_all_queries(uint32_t query_flags)
95{
96	return ((query_flags & DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS) == DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS);
97}
98
99
100
101
102static void
103add_dns_query_flags(const void *key, const void *value, void *context)
104{
105	CFDictionaryRef	service		= value;
106	uint32_t	*query_flags	= context;
107
108
109	// check if the service has v4 or v6 configured
110
111	if ((*query_flags & DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS) == 0) {
112		CFDictionaryRef	v4_dict;
113
114		v4_dict = CFDictionaryGetValue(service, kSCEntNetIPv4);
115		if (v4_dict != NULL) {
116			CFDictionaryRef	v4_service;
117
118			v4_service = CFDictionaryGetValue(v4_dict, kIPv4DictService);
119			if (isA_CFDictionary(v4_service)) {
120				CFArrayRef	if_addrs;
121				CFBooleanRef	is_null;
122
123				is_null = CFDictionaryGetValue(v4_service, kIsNULL);
124				if_addrs = CFDictionaryGetValue(v4_service, kSCPropNetIPv4Addresses);
125				if (isA_CFBoolean(is_null) != NULL && CFBooleanGetValue(is_null)) {
126					// ignore this service
127				}
128				else if (isA_CFArray(if_addrs) != NULL) {
129					int	i;
130					int	count;
131
132					count = CFArrayGetCount(if_addrs);
133					for (i = 0; i < count; i++) {
134						CFStringRef	if_addr;
135						struct in_addr	v4_addr;
136
137						if_addr = CFArrayGetValueAtIndex(if_addrs, i);
138						if (isA_CFString(if_addr) == NULL) {
139							continue;
140						}
141
142						cfstring_to_ip(if_addr, &v4_addr);
143						if (!IN_LINKLOCAL(ntohl(v4_addr.s_addr))) {
144							// set the request v4 dns record bit
145							*query_flags |= DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS;
146							break;
147						}
148					}
149				}
150			}
151		}
152	}
153
154	if ((*query_flags & DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS) == 0) {
155		CFDictionaryRef	v6_dict;
156
157		v6_dict = CFDictionaryGetValue(service, kSCEntNetIPv6);
158		if (isA_CFDictionary(v6_dict) != NULL) {
159			CFArrayRef	if_addrs6;
160			CFBooleanRef	is_null;
161
162			is_null = CFDictionaryGetValue(v6_dict, kIsNULL);
163			if_addrs6 = CFDictionaryGetValue(v6_dict, kSCPropNetIPv6Addresses);
164			if (isA_CFBoolean(is_null) != NULL && CFBooleanGetValue(is_null)) {
165				// ignore this service
166			}
167			else if (isA_CFArray(if_addrs6) != NULL) {
168				int	i;
169				int	count;
170
171				count = CFArrayGetCount(if_addrs6);
172				for (i = 0; i < count; i++) {
173					CFStringRef	if_addr6;
174					struct in6_addr	v6_addr;
175
176					if_addr6 = CFArrayGetValueAtIndex(if_addrs6, i);
177					if (isA_CFString(if_addr6) == NULL) {
178						continue;
179					}
180
181					cfstring_to_ip6(if_addr6, &v6_addr);
182					if (!IN6_IS_ADDR_LINKLOCAL(&v6_addr)
183					    && !IN6_IS_ADDR_MC_LINKLOCAL(&v6_addr)) {
184						// set the request v6 dns record bit
185						*query_flags |= DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS;
186						break;
187					}
188				}
189			}
190		}
191	}
192
193
194	return;
195}
196
197
198#pragma mark -
199#pragma mark DNS resolver configuration
200
201
202static void
203add_resolver(CFMutableArrayRef resolvers, CFMutableDictionaryRef resolver)
204{
205	CFIndex		i;
206	CFStringRef	interface;
207	CFIndex		n_resolvers;
208	CFNumberRef	order;
209	uint32_t	order_val	= 0;
210
211	order = CFDictionaryGetValue(resolver, kSCPropNetDNSSearchOrder);
212	if (!isA_CFNumber(order) ||
213	    !CFNumberGetValue(order, kCFNumberSInt32Type, &order_val)) {
214		order     = NULL;
215		order_val = 0;
216	}
217
218	n_resolvers = CFArrayGetCount(resolvers);
219	for (i = 0; i < n_resolvers; i++) {
220		CFDictionaryRef		match_resolver;
221
222		match_resolver = CFArrayGetValueAtIndex(resolvers, i);
223		if (CFEqual(resolver, match_resolver)) {
224			// a real duplicate
225			return;
226		}
227
228		if (order != NULL) {
229			CFMutableDictionaryRef	compare;
230			Boolean			match;
231
232			compare = CFDictionaryCreateMutableCopy(NULL, 0, match_resolver);
233			CFDictionarySetValue(compare, kSCPropNetDNSSearchOrder, order);
234			match = CFEqual(resolver, compare);
235			CFRelease(compare);
236			if (match) {
237				CFNumberRef	match_order;
238				uint32_t	match_order_val	= 0;
239
240				// if only the search order's are different
241				match_order = CFDictionaryGetValue(match_resolver, kSCPropNetDNSSearchOrder);
242				if (!isA_CFNumber(match_order) ||
243				    !CFNumberGetValue(match_order, kCFNumberSInt32Type, &match_order_val)) {
244					match_order_val = 0;
245				}
246
247				if (order_val < match_order_val ) {
248					// if we should prefer this match resolver, else just skip it
249					CFArraySetValueAtIndex(resolvers, i, resolver);
250				}
251
252				return;
253			}
254		}
255	}
256
257	order = CFNumberCreate(NULL, kCFNumberCFIndexType, &n_resolvers);
258	CFDictionarySetValue(resolver, DNS_CONFIGURATION_ORDER_KEY, order);
259	CFRelease(order);
260
261	interface = CFDictionaryGetValue(resolver, kSCPropInterfaceName);
262	if ((interface != NULL) && !CFEqual(interface, CFSTR("*"))) {
263		uint32_t	flags;
264		unsigned int	if_index		= 0;
265		char		if_name[IF_NAMESIZE];
266		CFNumberRef	num;
267		CFBooleanRef	val;
268
269		if (_SC_cfstring_to_cstring(interface,
270					    if_name,
271					    sizeof(if_name),
272					    kCFStringEncodingASCII) != NULL) {
273			if_index = if_nametoindex(if_name);
274		}
275
276		if ((if_index != 0) &&
277		    (
278		     // check if this is a "scoped" configuration
279		     (CFDictionaryGetValueIfPresent(resolver, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) &&
280		      isA_CFNumber(num) &&
281		      CFNumberGetValue(num, kCFNumberSInt32Type, &flags) &&
282		      (flags & DNS_RESOLVER_FLAGS_SCOPED) != 0)
283		     ||
284		     // check if we should scope all queries with this configuration
285		     (CFDictionaryGetValueIfPresent(resolver, DNS_CONFIGURATION_SCOPED_QUERY_KEY, (const void **)&val) &&
286		      isA_CFBoolean(val) &&
287		      CFBooleanGetValue(val))
288		    )
289		   ) {
290			// if interface index available and it should be used
291			num = CFNumberCreate(NULL, kCFNumberIntType, &if_index);
292			CFDictionarySetValue(resolver, DNS_CONFIGURATION_IF_INDEX_KEY, num);
293			CFRelease(num);
294		}
295	}
296
297	CFArrayAppendValue(resolvers, resolver);
298	return;
299}
300
301
302static void
303add_supplemental(CFMutableArrayRef resolvers, CFDictionaryRef dns, uint32_t defaultOrder)
304{
305	CFArrayRef	domains;
306	CFIndex		i;
307	CFIndex		n_domains;
308	CFArrayRef	orders;
309
310	domains = CFDictionaryGetValue(dns, kSCPropNetDNSSupplementalMatchDomains);
311	n_domains = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0;
312	if (n_domains == 0) {
313		return;
314	}
315
316	orders = CFDictionaryGetValue(dns, kSCPropNetDNSSupplementalMatchOrders);
317	if (orders != NULL) {
318		if (!isA_CFArray(orders) || (n_domains != CFArrayGetCount(orders))) {
319			return;
320		}
321	}
322
323	/*
324	 * yes, this is a "supplemental" resolver configuration, expand
325	 * the match domains and add each to the resolvers list.
326	 */
327	for (i = 0; i < n_domains; i++) {
328		CFStringRef		match_domain;
329		CFNumberRef		match_order;
330		CFMutableDictionaryRef	match_resolver;
331
332		match_domain = CFArrayGetValueAtIndex(domains, i);
333		if (!isA_CFString(match_domain)) {
334			continue;
335		}
336
337		match_resolver = CFDictionaryCreateMutableCopy(NULL, 0, dns);
338
339		// set supplemental resolver "domain"
340		if (CFStringGetLength(match_domain) > 0) {
341			CFDictionarySetValue(match_resolver, kSCPropNetDNSDomainName, match_domain);
342		} else {
343			CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSDomainName);
344		}
345
346		// set supplemental resolver "search_order"
347		match_order = (orders != NULL) ? CFArrayGetValueAtIndex(orders, i) : NULL;
348		if (isA_CFNumber(match_order)) {
349			CFDictionarySetValue(match_resolver, kSCPropNetDNSSearchOrder, match_order);
350		} else if (!CFDictionaryContainsKey(match_resolver, kSCPropNetDNSSearchOrder)) {
351			CFNumberRef     num;
352
353			num = CFNumberCreate(NULL, kCFNumberIntType, &defaultOrder);
354			CFDictionarySetValue(match_resolver, kSCPropNetDNSSearchOrder, num);
355			CFRelease(num);
356
357			defaultOrder++;		// if multiple domains, maintain ordering
358		}
359
360		// remove keys we don't want in a supplemental resolver
361		CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSupplementalMatchDomains);
362		CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSupplementalMatchOrders);
363		CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSearchDomains);
364		CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSortList);
365
366		add_resolver(resolvers, match_resolver);
367		CFRelease(match_resolver);
368	}
369
370	return;
371}
372
373
374#define	N_QUICK	32
375
376
377static void
378add_supplemental_resolvers(CFMutableArrayRef	resolvers,
379			   CFDictionaryRef	services,
380			   CFArrayRef		service_order,
381			   CFStringRef		scoped_interface,
382			   CFDictionaryRef	scoped_service)
383{
384	const void *		keys_q[N_QUICK];
385	const void **		keys	= keys_q;
386	CFIndex			i;
387	CFIndex			n_order;
388	CFIndex			n_services;
389	const void *		vals_q[N_QUICK];
390	const void **		vals	= vals_q;
391
392	n_services = isA_CFDictionary(services) ? CFDictionaryGetCount(services) : 0;
393	if (n_services == 0) {
394		return;		// if no services
395	}
396
397	if (n_services > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
398		keys = CFAllocatorAllocate(NULL, n_services * sizeof(CFTypeRef), 0);
399		vals = CFAllocatorAllocate(NULL, n_services * sizeof(CFTypeRef), 0);
400	}
401
402	n_order = isA_CFArray(service_order) ? CFArrayGetCount(service_order) : 0;
403
404	CFDictionaryGetKeysAndValues(services, keys, vals);
405	for (i = 0; i < n_services; i++) {
406		uint32_t		defaultOrder;
407		CFDictionaryRef		dns;
408		CFStringRef		interface;
409		uint32_t		interface_flags;
410		CFMutableDictionaryRef	newDNS		= NULL;
411		CFDictionaryRef		service		= (CFDictionaryRef)vals[i];
412
413		if (!isA_CFDictionary(service)) {
414			continue;
415		}
416
417		dns = CFDictionaryGetValue(service, kSCEntNetDNS);
418		dns = isA_CFDictionary(dns);
419		if (dns == NULL) {
420			continue;
421		}
422
423		interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
424
425		if (scoped_interface != NULL) {
426			//
427			// we only want to add split/supplemental configurations
428			// for queries scoped to an interface if they are NOT
429			// associated with a "real" service
430			//
431			if (CFDictionaryContainsKey(service, kSCEntNetIPv4) ||
432			    CFDictionaryContainsKey(service, kSCEntNetIPv6)) {
433				continue;
434			}
435
436			//
437			// in addition, we don't want to add split/supplemental
438			// configurations for queries scoped to an interface if
439			// the configuration does not apply to all interfaces and
440			// the configuration is explicitly NOT for this interface
441			//
442			if (!_SC_CFEqual(interface, CFSTR("*")) &&
443			    !_SC_CFEqual(interface, scoped_interface)) {
444				continue;
445			}
446
447			//
448			// lastly, check if A/AAAA queries should be issued (based
449			// on the IP[v6] addresses).  If we would not be issuing a
450			// query then don't bother adding the configuration.
451			//
452			interface_flags = 0;
453			add_dns_query_flags(NULL, scoped_service, &interface_flags);
454			if (interface_flags == 0) {
455				continue;
456			}
457		}
458
459		defaultOrder = DEFAULT_SEARCH_ORDER
460			       - (DEFAULT_SEARCH_ORDER / 2)
461			       + ((DEFAULT_SEARCH_ORDER / 1000) * i);
462		if ((n_order > 0) &&
463		    !CFArrayContainsValue(service_order, CFRangeMake(0, n_order), keys[i])) {
464			// push out services not specified in service order
465			defaultOrder += (DEFAULT_SEARCH_ORDER / 1000) * n_services;
466		}
467
468		/*
469		 * Ensure that we have the correct InterfaceName in the DNS configuration
470		 *
471		 * scoped_interface	[supplemental] interface	DNS interface
472		 * ================	========================	=================
473		 * NULL			NULL				NULL (No change)
474		 * NULL			en0				NULL
475		 * NULL			*				NULL
476		 * en0			NULL				"en0"
477		 * en0			en0				"en0" (now mutable)
478		 * en0			*				"en0"
479		 */
480		if ((scoped_interface == NULL) && (interface == NULL)) {
481			newDNS = (CFMutableDictionaryRef)CFRetain(dns);
482		} else {
483			newDNS = CFDictionaryCreateMutableCopy(NULL, 0, dns);
484			if (scoped_interface != NULL) {
485				CFDictionarySetValue(newDNS, kSCPropInterfaceName, scoped_interface);
486			} else {
487				CFDictionaryRemoveValue(newDNS, kSCPropInterfaceName);
488			}
489		}
490
491		if (scoped_interface != NULL) {
492			uint32_t	flags;
493			CFNumberRef	num;
494
495			// set "scoped" configuration flag(s)
496			if (!CFDictionaryGetValueIfPresent(newDNS, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) ||
497			    !isA_CFNumber(num) ||
498			    !CFNumberGetValue(num, kCFNumberSInt32Type, &flags)) {
499				flags = 0;
500			}
501			flags |= DNS_RESOLVER_FLAGS_SCOPED;
502
503			// add A/AAAA query flag(s)
504			flags |= interface_flags;
505
506			num = CFNumberCreate(NULL, kCFNumberSInt32Type, &flags);
507			CFDictionarySetValue(newDNS, DNS_CONFIGURATION_FLAGS_KEY, num);
508			CFRelease(num);
509		}
510
511		// add [scoped] resolver entry
512		add_supplemental(resolvers, newDNS, defaultOrder);
513		CFRelease(newDNS);
514	}
515
516	if (keys != keys_q) {
517		CFAllocatorDeallocate(NULL, keys);
518		CFAllocatorDeallocate(NULL, vals);
519	}
520
521	return;
522}
523
524
525static void
526add_multicast_resolvers(CFMutableArrayRef resolvers, CFArrayRef multicastResolvers)
527{
528	CFIndex	i;
529	CFIndex	n;
530
531	n = isA_CFArray(multicastResolvers) ? CFArrayGetCount(multicastResolvers) : 0;
532	for (i = 0; i < n; i++) {
533		uint32_t		defaultOrder;
534		CFStringRef		domain;
535		CFNumberRef		num;
536		CFMutableDictionaryRef	resolver;
537
538		domain = CFArrayGetValueAtIndex(multicastResolvers, i);
539		domain = _SC_trimDomain(domain);
540		if (domain == NULL) {
541			continue;
542		}
543
544		defaultOrder = DEFAULT_SEARCH_ORDER
545		+ (DEFAULT_SEARCH_ORDER / 2)
546		+ ((DEFAULT_SEARCH_ORDER / 1000) * i);
547
548		resolver = CFDictionaryCreateMutable(NULL,
549						     0,
550						     &kCFTypeDictionaryKeyCallBacks,
551						     &kCFTypeDictionaryValueCallBacks);
552		CFDictionarySetValue(resolver, kSCPropNetDNSDomainName, domain);
553		CFDictionarySetValue(resolver, kSCPropNetDNSOptions, CFSTR("mdns"));
554		num = CFNumberCreate(NULL, kCFNumberIntType, &defaultOrder);
555		CFDictionarySetValue(resolver, kSCPropNetDNSSearchOrder, num);
556		CFRelease(num);
557		if (S_mdns_timeout != NULL) {
558			CFDictionarySetValue(resolver, kSCPropNetDNSServerTimeout, S_mdns_timeout);
559		}
560		add_resolver(resolvers, resolver);
561		CFRelease(resolver);
562		CFRelease(domain);
563	}
564
565	return;
566}
567
568
569static void
570add_private_resolvers(CFMutableArrayRef resolvers, CFArrayRef privateResolvers)
571{
572	CFIndex	i;
573	CFIndex	n;
574
575	n = isA_CFArray(privateResolvers) ? CFArrayGetCount(privateResolvers) : 0;
576	for (i = 0; i < n; i++) {
577		uint32_t		defaultOrder;
578		CFStringRef		domain;
579		CFNumberRef		num;
580		CFMutableDictionaryRef	resolver;
581
582		domain = CFArrayGetValueAtIndex(privateResolvers, i);
583		domain = _SC_trimDomain(domain);
584		if (domain == NULL) {
585			continue;
586		}
587
588		defaultOrder = DEFAULT_SEARCH_ORDER
589			       - (DEFAULT_SEARCH_ORDER / 4)
590			       + ((DEFAULT_SEARCH_ORDER / 1000) * i);
591
592		resolver = CFDictionaryCreateMutable(NULL,
593						     0,
594						     &kCFTypeDictionaryKeyCallBacks,
595						     &kCFTypeDictionaryValueCallBacks);
596		CFDictionarySetValue(resolver, kSCPropNetDNSDomainName, domain);
597		CFDictionarySetValue(resolver, kSCPropNetDNSOptions, CFSTR("pdns"));
598		num = CFNumberCreate(NULL, kCFNumberIntType, &defaultOrder);
599		CFDictionarySetValue(resolver, kSCPropNetDNSSearchOrder, num);
600		CFRelease(num);
601		if (S_pdns_timeout != NULL) {
602			CFDictionarySetValue(resolver, kSCPropNetDNSServerTimeout, S_pdns_timeout);
603		}
604		add_resolver(resolvers, resolver);
605		CFRelease(resolver);
606		CFRelease(domain);
607	}
608
609	return;
610}
611
612
613static CFComparisonResult
614compareBySearchOrder(const void *val1, const void *val2, void *context)
615{
616	CFDictionaryRef	dns1	= (CFDictionaryRef)val1;
617	CFDictionaryRef	dns2	= (CFDictionaryRef)val2;
618	CFNumberRef	num1;
619	CFNumberRef	num2;
620	uint32_t	order1	= DEFAULT_SEARCH_ORDER;
621	uint32_t	order2	= DEFAULT_SEARCH_ORDER;
622
623	num1 = CFDictionaryGetValue(dns1, kSCPropNetDNSSearchOrder);
624	if (!isA_CFNumber(num1) ||
625	    !CFNumberGetValue(num1, kCFNumberSInt32Type, &order1)) {
626		order1 = DEFAULT_SEARCH_ORDER;
627	}
628
629	num2 = CFDictionaryGetValue(dns2, kSCPropNetDNSSearchOrder);
630	if (!isA_CFNumber(num2) ||
631	    !CFNumberGetValue(num2, kCFNumberSInt32Type, &order2)) {
632		order2 = DEFAULT_SEARCH_ORDER;
633	}
634
635	if (order1 == order2) {
636		// if same "SearchOrder", retain original orderring for configurations
637		if (CFDictionaryGetValueIfPresent(dns1, DNS_CONFIGURATION_ORDER_KEY, (const void **)&num1) &&
638		    CFDictionaryGetValueIfPresent(dns2, DNS_CONFIGURATION_ORDER_KEY, (const void **)&num2) &&
639		    isA_CFNumber(num1) &&
640		    isA_CFNumber(num2) &&
641		    CFNumberGetValue(num1, kCFNumberSInt32Type, &order1) &&
642		    CFNumberGetValue(num2, kCFNumberSInt32Type, &order2)) {
643			if (order1 == order2) {
644				return kCFCompareEqualTo;
645			} else {
646				return (order1 < order2) ? kCFCompareLessThan : kCFCompareGreaterThan;
647			}
648		}
649
650		return kCFCompareEqualTo;
651	}
652
653	return (order1 < order2) ? kCFCompareLessThan : kCFCompareGreaterThan;
654}
655
656
657static CF_RETURNS_RETAINED CFArrayRef
658extract_search_domains(CFMutableDictionaryRef defaultDomain, CFArrayRef supplemental)
659{
660	CFStringRef		defaultDomainName	= NULL;
661	uint32_t		defaultOrder		= DEFAULT_SEARCH_ORDER;
662	CFArrayRef		defaultSearchDomains	= NULL;
663	CFIndex			defaultSearchIndex	= 0;
664	CFIndex			i;
665	CFMutableArrayRef	mySearchDomains;
666	CFMutableArrayRef	mySupplemental		= NULL;
667	CFIndex			n_supplemental;
668
669	mySearchDomains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
670
671	if (defaultDomain != NULL) {
672		CFNumberRef	num;
673
674		num = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSSearchOrder);
675		if (!isA_CFNumber(num) ||
676		    !CFNumberGetValue(num, kCFNumberSInt32Type, &defaultOrder)) {
677			defaultOrder = DEFAULT_SEARCH_ORDER;
678		}
679
680		defaultDomainName    = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSDomainName);
681		defaultSearchDomains = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSSearchDomains);
682	}
683
684	// validate the provided "search" domains or move/expand/promote the "domain" name
685	if (isA_CFArray(defaultSearchDomains)) {
686		CFIndex	n_search;
687
688		n_search = CFArrayGetCount(defaultSearchDomains);
689		for (i = 0; i < n_search; i++) {
690			CFStringRef	search;
691
692			search = CFArrayGetValueAtIndex(defaultSearchDomains, i);
693			search = _SC_trimDomain(search);
694			if (search != NULL) {
695				CFArrayAppendValue(mySearchDomains, search);
696				CFRelease(search);
697			}
698		}
699	} else {
700		defaultDomainName = _SC_trimDomain(defaultDomainName);
701		if (defaultDomainName != NULL) {
702			CFStringRef	defaultOptions;
703			char		*domain;
704			int		domain_parts	= 1;
705			char		*dp;
706			int		ndots		= 1;
707
708#define	NDOTS_OPT	"ndots="
709#define	NDOTS_OPT_LEN	(sizeof("ndots=") - 1)
710
711			defaultOptions = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSOptions);
712			if (defaultOptions != NULL) {
713				char	*cp;
714				char	*options;
715
716				options = _SC_cfstring_to_cstring(defaultOptions,
717								 NULL,
718								 0,
719								 kCFStringEncodingUTF8);
720				cp = strstr(options, NDOTS_OPT);
721				if ((cp != NULL) &&
722				    ((cp == options) || isspace(cp[-1])) &&
723				    ((cp[NDOTS_OPT_LEN] != '\0') && isdigit(cp[NDOTS_OPT_LEN]))) {
724					char    *end;
725					long    val;
726
727					cp +=  NDOTS_OPT_LEN;
728					errno = 0;
729					val = strtol(cp, &end, 10);
730					if ((*cp != '\0') && (cp != end) && (errno == 0) &&
731					    ((*end == '\0') || isspace(*end)) && (val > 0)) {
732						ndots = val;
733					}
734				}
735				CFAllocatorDeallocate(NULL, options);
736			}
737
738			domain = _SC_cfstring_to_cstring(defaultDomainName,
739							 NULL,
740							 0,
741							 kCFStringEncodingUTF8);
742			CFRelease(defaultDomainName);
743
744			// count domain parts
745			for (dp = domain; *dp != '\0'; dp++) {
746				if (*dp == '.') {
747					domain_parts++;
748				}
749			}
750
751			// move "domain" to "search" list (and expand as needed)
752			i = LOCALDOMAINPARTS;
753			dp = domain;
754			do {
755				CFStringRef	search;
756				CFStringRef	str;
757
758				str = CFStringCreateWithCString(NULL,
759								dp,
760								kCFStringEncodingUTF8);
761				search = _SC_trimDomain(str);
762				CFRelease(str);
763				if (search != NULL) {
764					CFArrayAppendValue(mySearchDomains, search);
765					CFRelease(search);
766				}
767
768				dp = strchr(dp, '.') + 1;
769			} while (++i <= (domain_parts - ndots));
770			CFAllocatorDeallocate(NULL, domain);
771		}
772	}
773
774	// add any supplemental "domain" names to the search list
775	n_supplemental = (supplemental != NULL) ? CFArrayGetCount(supplemental) : 0;
776	if (n_supplemental > 1) {
777		mySupplemental = CFArrayCreateMutableCopy(NULL, 0, supplemental);
778		CFArraySortValues(mySupplemental,
779				  CFRangeMake(0, n_supplemental),
780				  compareBySearchOrder,
781				  NULL);
782		supplemental = mySupplemental;
783	}
784	for (i = 0; i < n_supplemental; i++) {
785		CFDictionaryRef dns;
786		CFIndex		domainIndex;
787		int		noSearch;
788		CFNumberRef	num;
789		CFStringRef	options;
790		CFStringRef	supplementalDomain;
791		uint32_t	supplementalOrder;
792
793		dns = CFArrayGetValueAtIndex(supplemental, i);
794
795		options = CFDictionaryGetValue(dns, kSCPropNetDNSOptions);
796		if (isA_CFString(options)) {
797			CFRange	range;
798
799			if (CFEqual(options, CFSTR("pdns"))) {
800				// don't add private resolver domains to the search list
801				continue;
802			}
803
804			range = CFStringFind(options, CFSTR("interface="), 0);
805			if (range.location != kCFNotFound) {
806				// don't add scoped resolver domains to the search list
807				continue;
808			}
809		}
810
811		supplementalDomain = CFDictionaryGetValue(dns, kSCPropNetDNSDomainName);
812		supplementalDomain = _SC_trimDomain(supplementalDomain);
813		if (supplementalDomain == NULL) {
814			continue;
815		}
816
817		num = CFDictionaryGetValue(dns, kSCPropNetDNSSupplementalMatchDomainsNoSearch);
818		if (isA_CFNumber(num) &&
819		    CFNumberGetValue(num, kCFNumberIntType, &noSearch) &&
820		    (noSearch != 0)) {
821			CFRelease(supplementalDomain);
822			continue;
823		}
824
825		if (CFStringHasSuffix(supplementalDomain, CFSTR(".in-addr.arpa")) ||
826		    CFStringHasSuffix(supplementalDomain, CFSTR(".ip6.arpa"    ))) {
827			CFRelease(supplementalDomain);
828			continue;
829		}
830
831		domainIndex = CFArrayGetFirstIndexOfValue(mySearchDomains,
832							  CFRangeMake(0, CFArrayGetCount(mySearchDomains)),
833							  supplementalDomain);
834
835		num = CFDictionaryGetValue(dns, kSCPropNetDNSSearchOrder);
836		if (!isA_CFNumber(num) ||
837		    !CFNumberGetValue(num, kCFNumberSInt32Type, &supplementalOrder)) {
838			supplementalOrder = DEFAULT_SEARCH_ORDER;
839		}
840
841		if (supplementalOrder < defaultOrder) {
842			if (domainIndex != kCFNotFound) {
843				// if supplemental domain is already in the search list
844				CFArrayRemoveValueAtIndex(mySearchDomains, domainIndex);
845				if (domainIndex < defaultSearchIndex) {
846					defaultSearchIndex--;
847				}
848			}
849			CFArrayInsertValueAtIndex(mySearchDomains,
850						  defaultSearchIndex,
851						  supplementalDomain);
852			defaultSearchIndex++;
853		} else {
854			if (domainIndex == kCFNotFound) {
855				// add to the (end of the) search list
856				CFArrayAppendValue(mySearchDomains, supplementalDomain);
857			}
858		}
859
860		CFRelease(supplementalDomain);
861	}
862	if (mySupplemental != NULL) CFRelease(mySupplemental);
863
864	// update the "search" domains
865	if (CFArrayGetCount(mySearchDomains) == 0) {
866		CFRelease(mySearchDomains);
867		mySearchDomains = NULL;
868	}
869
870	// remove the "domain" name and "search" list
871	CFDictionaryRemoveValue(defaultDomain, kSCPropNetDNSDomainName);
872	CFDictionaryRemoveValue(defaultDomain, kSCPropNetDNSSearchDomains);
873
874	return mySearchDomains;
875}
876
877
878static void
879add_scoped_resolvers(CFMutableArrayRef scoped, CFDictionaryRef services, CFArrayRef service_order)
880{
881	const void *		keys_q[N_QUICK];
882	const void **		keys	= keys_q;
883	CFIndex			i;
884	CFIndex			n_order;
885	CFIndex			n_services;
886	CFMutableArrayRef	order;
887	CFMutableSetRef		seen;
888
889	n_services = isA_CFDictionary(services) ? CFDictionaryGetCount(services) : 0;
890	if (n_services == 0) {
891		return;		// if no services
892	}
893
894	// ensure that we process all services in order
895
896	n_order = isA_CFArray(service_order) ? CFArrayGetCount(service_order) : 0;
897	if (n_order > 0) {
898		order = CFArrayCreateMutableCopy(NULL, 0, service_order);
899	} else {
900		order = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
901	}
902
903	if (n_services > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
904		keys = CFAllocatorAllocate(NULL, n_services * sizeof(CFTypeRef), 0);
905	}
906	CFDictionaryGetKeysAndValues(services, keys, NULL);
907	for (i = 0; i < n_services; i++) {
908		CFStringRef	serviceID = (CFStringRef)keys[i];
909
910		if (!CFArrayContainsValue(order, CFRangeMake(0, n_order), serviceID)) {
911			CFArrayAppendValue(order, serviceID);
912			n_order++;
913		}
914	}
915	if (keys != keys_q) {
916		CFAllocatorDeallocate(NULL, keys);
917	}
918
919	// iterate over services
920
921	seen = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
922	for (i = 0; i < n_order; i++) {
923		CFDictionaryRef		dns;
924		uint32_t		flags;
925		char			if_name[IF_NAMESIZE];
926		CFStringRef		interface;
927		CFMutableDictionaryRef	newDNS;
928		CFNumberRef		num;
929		CFArrayRef		searchDomains;
930		CFDictionaryRef		service;
931		CFStringRef		serviceID;
932		uint32_t		these_flags;
933
934		serviceID = CFArrayGetValueAtIndex(order, i);
935		service = CFDictionaryGetValue(services, serviceID);
936		if (!isA_CFDictionary(service)) {
937			// if no service
938			continue;
939		}
940
941		dns = CFDictionaryGetValue(service, kSCEntNetDNS);
942		if (!isA_CFDictionary(dns)) {
943			// if no DNS
944			continue;
945		}
946
947		interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
948		if ((interface == NULL) || CFEqual(interface, CFSTR("*"))) {
949			// if no [scoped] interface or supplemental configuration w/match-all
950			continue;
951		}
952
953		if (CFDictionaryContainsKey(dns, kSCPropNetDNSServiceIdentifier)) {
954			// if this is a service-specific resolver
955			continue;
956		}
957
958		if (CFSetContainsValue(seen, interface)) {
959			// if we've already processed this [scoped] interface
960			continue;
961		}
962		CFSetSetValue(seen, interface);
963
964		if ((_SC_cfstring_to_cstring(interface,
965					     if_name,
966					     sizeof(if_name),
967					     kCFStringEncodingASCII) == NULL) ||
968		    (if_nametoindex(if_name) == 0)) {
969			// if interface index not available
970			continue;
971		}
972
973		// add [scoped] resolver entry
974		newDNS = CFDictionaryCreateMutableCopy(NULL, 0, dns);
975
976		// set search list
977		searchDomains = extract_search_domains(newDNS, NULL);
978		if (searchDomains != NULL) {
979			CFDictionarySetValue(newDNS, kSCPropNetDNSSearchDomains, searchDomains);
980			CFRelease(searchDomains);
981		}
982
983		// set "scoped" configuration flag(s)
984		if (!CFDictionaryGetValueIfPresent(newDNS, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) ||
985		    !isA_CFNumber(num) ||
986		    !CFNumberGetValue(num, kCFNumberSInt32Type, &flags)) {
987			flags = 0;
988		}
989		flags |= DNS_RESOLVER_FLAGS_SCOPED;
990
991		these_flags = 0;
992		add_dns_query_flags(serviceID, service, &these_flags);
993		if (these_flags == 0) {
994		    goto skip;
995		}
996		flags |= these_flags;
997
998		num = CFNumberCreate(NULL, kCFNumberSInt32Type, &flags);
999		CFDictionarySetValue(newDNS, DNS_CONFIGURATION_FLAGS_KEY, num);
1000		CFRelease(num);
1001
1002		// remove keys we don't want in a [scoped] resolver
1003		CFDictionaryRemoveValue(newDNS, kSCPropNetDNSSupplementalMatchDomains);
1004		CFDictionaryRemoveValue(newDNS, kSCPropNetDNSSupplementalMatchOrders);
1005
1006		// add the [scoped] resolver
1007		add_resolver(scoped, newDNS);
1008
1009		// add any supplemental resolver configurations for this interface
1010		add_supplemental_resolvers(scoped, services, service_order, interface, service);
1011
1012	skip:
1013		CFRelease(newDNS);
1014	}
1015
1016	CFRelease(seen);
1017	CFRelease(order);
1018	return;
1019}
1020
1021
1022static void
1023add_service_specific_resolvers(CFMutableArrayRef resolvers, CFDictionaryRef services)
1024{
1025	CFIndex	services_count	= (isA_CFDictionary(services) ? CFDictionaryGetCount(services) : 0);
1026
1027	if (services_count > 0) {
1028		CFIndex			key_idx;
1029		CFStringRef		keys_q[N_QUICK];
1030		CFStringRef		*keys	= keys_q;
1031		CFMutableSetRef		seen	= CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
1032
1033		if (services_count > (CFIndex)(sizeof(keys_q) / sizeof(keys_q[0]))) {
1034			keys = CFAllocatorAllocate(kCFAllocatorDefault, services_count * sizeof(keys[0]), 0);
1035		}
1036
1037		CFDictionaryGetKeysAndValues(services, (const void **)keys, NULL);
1038
1039		for (key_idx = 0; key_idx < services_count; key_idx++) {
1040			CFDictionaryRef	service	= CFDictionaryGetValue(services, keys[key_idx]);
1041			CFDictionaryRef	dns	= CFDictionaryGetValue(service, kSCEntNetDNS);
1042
1043			if (isA_CFDictionary(dns)) {
1044				CFNumberRef	service_identifier	= CFDictionaryGetValue(dns, kSCPropNetDNSServiceIdentifier);
1045
1046				if (isA_CFNumber(service_identifier)) {
1047					if (!CFSetContainsValue(seen, service_identifier)) {
1048						CFMutableDictionaryRef	new_resolver	= CFDictionaryCreateMutableCopy(NULL, 0, dns);
1049						CFNumberRef		flags_num;
1050						int32_t			flags		= 0;
1051
1052						CFSetSetValue(seen, service_identifier);
1053
1054						if (!CFDictionaryGetValueIfPresent(new_resolver, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&flags_num) ||
1055						    !isA_CFNumber(flags_num) ||
1056						    !CFNumberGetValue(flags_num, kCFNumberSInt32Type, &flags)) {
1057							flags = 0;
1058						}
1059
1060						flags |= DNS_RESOLVER_FLAGS_SERVICE_SPECIFIC | DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS;
1061
1062						flags_num = CFNumberCreate(NULL, kCFNumberSInt32Type, &flags);
1063						CFDictionarySetValue(new_resolver, DNS_CONFIGURATION_FLAGS_KEY, flags_num);
1064						CFRelease(flags_num);
1065
1066						if (CFDictionaryContainsKey(new_resolver, kSCPropInterfaceName)) {
1067							CFDictionarySetValue(new_resolver, DNS_CONFIGURATION_SCOPED_QUERY_KEY, kCFBooleanTrue);
1068						}
1069
1070						CFDictionaryRemoveValue(new_resolver, kSCPropNetDNSSupplementalMatchDomains);
1071						CFDictionaryRemoveValue(new_resolver, kSCPropNetDNSSupplementalMatchOrders);
1072
1073						add_resolver(resolvers, new_resolver);
1074						CFRelease(new_resolver);
1075					} else {
1076						my_log(LOG_ERR, "add_service_specific_resolvers: got a resolver with a duplicate service identifier, skipping");
1077					}
1078				}
1079			}
1080		}
1081
1082		if (keys != keys_q) {
1083			CFAllocatorDeallocate(kCFAllocatorDefault, keys);
1084		}
1085		CFRelease(seen);
1086	}
1087}
1088
1089
1090static void
1091add_default_resolver(CFMutableArrayRef	resolvers,
1092		     CFDictionaryRef	defaultResolver,
1093		     Boolean		*orderAdded,
1094		     CFArrayRef		*searchDomains)
1095{
1096	CFMutableDictionaryRef	myDefault;
1097	uint32_t		myOrder	= DEFAULT_SEARCH_ORDER;
1098	CFNumberRef		order;
1099
1100	if (defaultResolver == NULL) {
1101		myDefault = CFDictionaryCreateMutable(NULL,
1102						      0,
1103						      &kCFTypeDictionaryKeyCallBacks,
1104						      &kCFTypeDictionaryValueCallBacks);
1105	} else {
1106		myDefault = CFDictionaryCreateMutableCopy(NULL, 0, defaultResolver);
1107	}
1108	assert(myDefault != NULL);
1109
1110	// ensure that the default resolver has a search order
1111
1112	order = CFDictionaryGetValue(myDefault, kSCPropNetDNSSearchOrder);
1113	if (!isA_CFNumber(order) ||
1114	    !CFNumberGetValue(order, kCFNumberSInt32Type, &myOrder)) {
1115		myOrder = DEFAULT_SEARCH_ORDER;
1116		order = CFNumberCreate(NULL, kCFNumberIntType, &myOrder);
1117		CFDictionarySetValue(myDefault, kSCPropNetDNSSearchOrder, order);
1118		CFRelease(order);
1119		*orderAdded = TRUE;
1120	}
1121
1122	// extract the "search" domain list for the default resolver (and
1123	// any supplemental resolvers)
1124
1125	*searchDomains = extract_search_domains(myDefault, resolvers);
1126
1127	// add the default resolver
1128
1129	add_resolver(resolvers, myDefault);
1130	CFRelease(myDefault);
1131	return;
1132}
1133
1134
1135static dns_create_resolver_t
1136create_resolver(CFDictionaryRef dns)
1137{
1138	CFArrayRef		list;
1139	CFNumberRef		num;
1140	dns_create_resolver_t	_resolver;
1141	CFStringRef		str;
1142	CFStringRef		targetInterface		= NULL;
1143	unsigned int		targetInterfaceIndex	= 0;
1144
1145	_resolver = _dns_resolver_create();
1146
1147	// process domain
1148	str = CFDictionaryGetValue(dns, kSCPropNetDNSDomainName);
1149	if (isA_CFString(str) && (CFStringGetLength(str) > 0)) {
1150		char	domain[NS_MAXDNAME];
1151
1152		if (_SC_cfstring_to_cstring(str, domain, sizeof(domain), kCFStringEncodingUTF8) != NULL) {
1153			_dns_resolver_set_domain(&_resolver, domain);
1154		}
1155	}
1156
1157	// process search domains
1158	list = CFDictionaryGetValue(dns, kSCPropNetDNSSearchDomains);
1159	if (isA_CFArray(list)) {
1160		CFIndex	i;
1161		CFIndex n	= CFArrayGetCount(list);
1162
1163		// add "search" domains
1164		for (i = 0; i < n; i++) {
1165			str = CFArrayGetValueAtIndex(list, i);
1166			if (isA_CFString(str) && (CFStringGetLength(str) > 0)) {
1167				char	search[NS_MAXDNAME];
1168
1169				if (_SC_cfstring_to_cstring(str, search, sizeof(search), kCFStringEncodingUTF8) != NULL) {
1170					_dns_resolver_add_search(&_resolver, search);
1171				}
1172			}
1173		}
1174	}
1175
1176	// process interface index
1177	num = CFDictionaryGetValue(dns, DNS_CONFIGURATION_IF_INDEX_KEY);
1178	if (isA_CFNumber(num)) {
1179		int	if_index;
1180
1181		if (CFNumberGetValue(num, kCFNumberIntType, &if_index)) {
1182			char	if_name[IFNAMSIZ];
1183
1184			_dns_resolver_set_if_index(&_resolver, if_index);
1185
1186			if ((if_index != 0) &&
1187			    (if_indextoname(if_index, if_name) != NULL)) {
1188				targetInterface = CFStringCreateWithCString(NULL,
1189									    if_name,
1190									    kCFStringEncodingASCII);
1191				targetInterfaceIndex = if_index;
1192			}
1193		}
1194	}
1195
1196	// process flags
1197	num = CFDictionaryGetValue(dns, DNS_CONFIGURATION_FLAGS_KEY);
1198	if (isA_CFNumber(num)) {
1199		uint32_t	flags;
1200
1201		if (CFNumberGetValue(num, kCFNumberSInt32Type, &flags)) {
1202			_dns_resolver_set_flags(&_resolver, flags);
1203		}
1204	}
1205
1206	// process nameserver addresses
1207	// Note: the flags may be overwritten if the resolver has LOOPBACK addresses
1208	list = CFDictionaryGetValue(dns, kSCPropNetDNSServerAddresses);
1209	if (isA_CFArray(list)) {
1210		CFIndex	i;
1211		CFIndex	n	= CFArrayGetCount(list);
1212
1213		for (i = 0; i < n; i++) {
1214			union {
1215				struct sockaddr         sa;
1216				struct sockaddr_in      sin;
1217				struct sockaddr_in6     sin6;
1218			} addr;
1219			char				buf[64];
1220
1221			str = CFArrayGetValueAtIndex(list, i);
1222			if (!isA_CFString(str)) {
1223				continue;
1224			}
1225
1226			if (_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII) == NULL) {
1227				continue;
1228			}
1229
1230			if (_SC_string_to_sockaddr(buf, AF_UNSPEC, (void *)&addr, sizeof(addr)) == NULL) {
1231				continue;
1232			}
1233
1234			if ((addr.sa.sa_family == AF_INET6) &&
1235			    (IN6_IS_ADDR_LINKLOCAL(&addr.sin6.sin6_addr) ||
1236			     IN6_IS_ADDR_MC_LINKLOCAL(&addr.sin6.sin6_addr)) &&
1237			    (addr.sin6.sin6_scope_id == 0) &&
1238			    (targetInterfaceIndex != 0)) {
1239				// for link local [IPv6] addresses, if the scope id is not
1240				// set then we should use the interface associated with the
1241				// resolver configuration
1242				addr.sin6.sin6_scope_id = targetInterfaceIndex;
1243			}
1244
1245			_dns_resolver_add_nameserver(&_resolver, &addr.sa);
1246		}
1247	}
1248
1249	// process search order
1250	num = CFDictionaryGetValue(dns, kSCPropNetDNSSearchOrder);
1251	if (isA_CFNumber(num)) {
1252		uint32_t	order;
1253
1254		if (CFNumberGetValue(num, kCFNumberSInt32Type, &order)) {
1255			_dns_resolver_set_order(&_resolver, order);
1256		}
1257	}
1258
1259	// process sortlist
1260	list = CFDictionaryGetValue(dns, kSCPropNetDNSSortList);
1261	if (isA_CFArray(list)) {
1262		CFIndex	i;
1263		CFIndex n	= CFArrayGetCount(list);
1264
1265		for (i = 0; i < n; i++) {
1266			char		buf[128];
1267			char		*slash;
1268			dns_sortaddr_t	sortaddr;
1269
1270			str = CFArrayGetValueAtIndex(list, i);
1271			if (!isA_CFString(str)) {
1272				continue;
1273			}
1274
1275			if (_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII) == NULL) {
1276				continue;
1277			}
1278
1279			slash = strchr(buf, '/');
1280			if (slash != NULL) {
1281				*slash = '\0';
1282			}
1283
1284			bzero(&sortaddr, sizeof(sortaddr));
1285			if (inet_aton(buf, &sortaddr.address) != 1) {
1286				/* if address not valid */
1287				continue;
1288			}
1289
1290			if (slash != NULL) {
1291				if (inet_aton(slash + 1, &sortaddr.mask) != 1) {
1292					/* if subnet mask not valid */
1293					continue;
1294				}
1295			} else {
1296				in_addr_t	a;
1297				in_addr_t	m;
1298
1299				a = ntohl(sortaddr.address.s_addr);
1300				if (IN_CLASSA(a)) {
1301					m = IN_CLASSA_NET;
1302				} else if (IN_CLASSB(a)) {
1303					m = IN_CLASSB_NET;
1304				} else if (IN_CLASSC(a)) {
1305					m = IN_CLASSC_NET;
1306				} else {
1307					continue;
1308				}
1309
1310				sortaddr.mask.s_addr = htonl(m);
1311			}
1312
1313			_dns_resolver_add_sortaddr(&_resolver, &sortaddr);
1314		}
1315	}
1316
1317	// process port
1318	num = CFDictionaryGetValue(dns, kSCPropNetDNSServerPort);
1319	if (isA_CFNumber(num)) {
1320		int	port;
1321
1322		if (CFNumberGetValue(num, kCFNumberIntType, &port)) {
1323			_dns_resolver_set_port(&_resolver, (uint16_t)port);
1324		}
1325	}
1326
1327	// process timeout
1328	num = CFDictionaryGetValue(dns, kSCPropNetDNSServerTimeout);
1329	if (isA_CFNumber(num)) {
1330		int	timeout;
1331
1332		if (CFNumberGetValue(num, kCFNumberIntType, &timeout)) {
1333			_dns_resolver_set_timeout(&_resolver, (uint32_t)timeout);
1334		}
1335	}
1336
1337	// process options
1338	str = CFDictionaryGetValue(dns, kSCPropNetDNSOptions);
1339	if (isA_CFString(str)) {
1340		char	*options;
1341
1342		options = _SC_cfstring_to_cstring(str, NULL, 0, kCFStringEncodingUTF8);
1343		if (options != NULL) {
1344			_dns_resolver_set_options(&_resolver, options);
1345			CFAllocatorDeallocate(NULL, options);
1346		}
1347	}
1348
1349	num = CFDictionaryGetValue(dns, kSCPropNetDNSServiceIdentifier);
1350	if (isA_CFNumber(num)) {
1351		int	service_identifier;
1352
1353		if (CFNumberGetValue(num, kCFNumberIntType, &service_identifier)) {
1354			_dns_resolver_set_service_identifier(&_resolver, (uint32_t)service_identifier);
1355		}
1356	}
1357
1358	if (targetInterface != NULL) {
1359		CFRelease(targetInterface);
1360	}
1361
1362	return _resolver;
1363}
1364
1365
1366static __inline__ Boolean
1367isScopedConfiguration(CFDictionaryRef dns)
1368{
1369	uint32_t	flags;
1370	CFNumberRef	num;
1371
1372	if ((dns != NULL) &&
1373	    CFDictionaryGetValueIfPresent(dns, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) &&
1374	    (num != NULL) &&
1375	    CFNumberGetValue(num, kCFNumberSInt32Type, &flags) &&
1376	    ((flags & DNS_RESOLVER_FLAGS_SCOPED) != 0)) {
1377		return TRUE;
1378	}
1379
1380	return FALSE;
1381}
1382
1383
1384static __inline__ Boolean
1385isServiceSpecificConfiguration(CFDictionaryRef dns)
1386{
1387	uint32_t	flags;
1388	CFNumberRef	num;
1389
1390	if (dns != NULL &&
1391	    CFDictionaryGetValueIfPresent(dns, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) &&
1392	    num != NULL &&
1393	    CFNumberGetValue(num, kCFNumberSInt32Type, &flags) &&
1394	    (flags & DNS_RESOLVER_FLAGS_SERVICE_SPECIFIC))
1395	{
1396		return TRUE;
1397	}
1398
1399	return FALSE;
1400}
1401
1402
1403static CFComparisonResult
1404compareDomain(const void *val1, const void *val2, void *context)
1405{
1406	CFDictionaryRef		dns1	= (CFDictionaryRef)val1;
1407	CFDictionaryRef		dns2	= (CFDictionaryRef)val2;
1408	CFStringRef		domain1;
1409	CFStringRef		domain2;
1410	CFArrayRef		labels1	= NULL;
1411	CFArrayRef		labels2	= NULL;
1412	CFIndex			n1;
1413	CFIndex			n2;
1414	CFComparisonResult	result;
1415	Boolean			rev1;
1416	Boolean			rev2;
1417	Boolean			scoped1;
1418	Boolean			scoped2;
1419
1420	// "default" domains sort before "supplemental" domains
1421	domain1 = CFDictionaryGetValue(dns1, kSCPropNetDNSDomainName);
1422	domain2 = CFDictionaryGetValue(dns2, kSCPropNetDNSDomainName);
1423	if (domain1 == NULL) {
1424		return kCFCompareLessThan;
1425	} else if (domain2 == NULL) {
1426		return kCFCompareGreaterThan;
1427	}
1428
1429	// sort non-scoped before scoped
1430	scoped1 = isScopedConfiguration(dns1);
1431	scoped2 = isScopedConfiguration(dns2);
1432	if (scoped1 != scoped2) {
1433		if (!scoped1) {
1434			return kCFCompareLessThan;
1435		} else {
1436			return kCFCompareGreaterThan;
1437		}
1438	}
1439
1440	// must have domain names for any further comparisons
1441	if ((domain1 == NULL) || (domain2 == NULL)) {
1442		return kCFCompareEqualTo;
1443	}
1444
1445	// forward (A, AAAA) domains sort before reverse (PTR) domains
1446	rev1 = CFStringHasSuffix(domain1, CFSTR(".arpa"));
1447	rev2 = CFStringHasSuffix(domain2, CFSTR(".arpa"));
1448	if (rev1 != rev2) {
1449		if (rev1) {
1450			return kCFCompareGreaterThan;
1451		} else {
1452			return kCFCompareLessThan;
1453		}
1454	}
1455
1456	labels1 = CFStringCreateArrayBySeparatingStrings(NULL, domain1, CFSTR("."));
1457	n1 = CFArrayGetCount(labels1);
1458
1459	labels2 = CFStringCreateArrayBySeparatingStrings(NULL, domain2, CFSTR("."));
1460	n2 = CFArrayGetCount(labels2);
1461
1462	while ((n1 > 0) && (n2 > 0)) {
1463		CFStringRef	label1	= CFArrayGetValueAtIndex(labels1, --n1);
1464		CFStringRef	label2	= CFArrayGetValueAtIndex(labels2, --n2);
1465
1466		// compare domain labels
1467		result = CFStringCompare(label1, label2, kCFCompareCaseInsensitive);
1468		if (result != kCFCompareEqualTo) {
1469			goto done;
1470		}
1471	}
1472
1473	// longer labels (corp.apple.com) sort before shorter labels (apple.com)
1474	if (n1 > n2) {
1475		result = kCFCompareLessThan;
1476		goto done;
1477	} else if (n1 < n2) {
1478		result = kCFCompareGreaterThan;
1479		goto done;
1480	}
1481
1482	// sort by search order
1483	result = compareBySearchOrder(val1, val2, context);
1484
1485    done :
1486
1487	if (labels1 != NULL) CFRelease(labels1);
1488	if (labels2 != NULL) CFRelease(labels2);
1489	return result;
1490}
1491
1492
1493__private_extern__
1494Boolean
1495dns_configuration_set(CFDictionaryRef   defaultResolver,
1496		      CFDictionaryRef   services,
1497		      CFArrayRef	serviceOrder,
1498		      CFArrayRef	multicastResolvers,
1499		      CFArrayRef	privateResolvers)
1500{
1501	dns_create_config_t	_config;
1502	Boolean			changed			= FALSE;
1503	uint32_t		dns_resolver_flags	= 0;
1504	CFIndex			i;
1505	CFMutableDictionaryRef	myDefault;
1506	Boolean			myOrderAdded		= FALSE;
1507	CFArrayRef		mySearchDomains		= NULL;
1508	CFIndex			n_resolvers;
1509	CFMutableArrayRef	resolvers;
1510	unsigned char		signature[CC_SHA1_DIGEST_LENGTH];
1511	static unsigned char	signature_last[CC_SHA1_DIGEST_LENGTH];
1512
1513	// establish list of resolvers
1514
1515	resolvers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1516	assert(resolvers != NULL);
1517
1518	// collect (and add) any "supplemental" resolver configurations
1519
1520	add_supplemental_resolvers(resolvers, services, serviceOrder, NULL, NULL);
1521
1522	// collect (and add) any "private" resolver configurations
1523
1524	add_private_resolvers(resolvers, privateResolvers);
1525
1526	// add the "default" resolver
1527
1528	add_default_resolver(resolvers, defaultResolver, &myOrderAdded, &mySearchDomains);
1529
1530	// collect (and add) any "multicast" resolver configurations
1531
1532	add_multicast_resolvers(resolvers, multicastResolvers);
1533
1534	// collect (and add) any "scoped" resolver configurations
1535
1536	add_scoped_resolvers(resolvers, services, serviceOrder);
1537
1538	// collect (and add) any "service-specific" resolver configurations
1539
1540	add_service_specific_resolvers(resolvers, services);
1541
1542	// sort resolvers
1543
1544	n_resolvers = CFArrayGetCount(resolvers);
1545	if (n_resolvers > 1) {
1546		CFArraySortValues(resolvers, CFRangeMake(0, n_resolvers), compareDomain, NULL);
1547	}
1548
1549	// cleanup
1550
1551	for (i = n_resolvers; --i > 0; ) {
1552		CFDictionaryRef	resolver;
1553
1554		resolver = CFArrayGetValueAtIndex(resolvers, i);
1555		if (!CFDictionaryContainsKey(resolver, kSCPropNetDNSDomainName) &&
1556		    !CFDictionaryContainsKey(resolver, kSCPropNetDNSSearchDomains) &&
1557		    !CFDictionaryContainsKey(resolver, kSCPropNetDNSServerAddresses)) {
1558			// remove empty resolver
1559			CFArrayRemoveValueAtIndex(resolvers, i);
1560			n_resolvers--;
1561		}
1562	}
1563
1564	// update the default resolver
1565
1566	myDefault = CFDictionaryCreateMutableCopy(NULL,
1567						  0,
1568						  CFArrayGetValueAtIndex(resolvers, 0));
1569	if (mySearchDomains != NULL) {
1570		// add search domains to the default resolver
1571		CFDictionarySetValue(myDefault, kSCPropNetDNSSearchDomains, mySearchDomains);
1572		CFRelease(mySearchDomains);
1573	}
1574	if (myOrderAdded && (n_resolvers > 1)) {
1575		CFDictionaryRef	resolver;
1576
1577		resolver = CFArrayGetValueAtIndex(resolvers, 1);
1578		if (CFDictionaryContainsKey(resolver, kSCPropNetDNSDomainName) ||
1579		    isScopedConfiguration(resolver)) {
1580			// if not a supplemental "default" resolver (a domain name is
1581			// present) or if it's a scoped configuration
1582			CFDictionaryRemoveValue(myDefault, kSCPropNetDNSSearchOrder);
1583		}
1584	}
1585	CFArraySetValueAtIndex(resolvers, 0, myDefault);
1586	CFRelease(myDefault);
1587
1588	// establish resolver configuration
1589
1590	if ((defaultResolver == NULL) && (n_resolvers <= 1)) {
1591		/*
1592		 * if no default and no supplemental/scoped resolvers
1593		 */
1594		_config = NULL;
1595	} else {
1596		/*
1597		 * if default and/or supplemental/scoped resolvers are defined
1598		 */
1599		_config = _dns_configuration_create();
1600
1601		CFDictionaryApplyFunction(services, add_dns_query_flags , &dns_resolver_flags);
1602
1603		for (i = 0; i < n_resolvers; i++) {
1604			boolean_t		is_default_resolver;
1605			CFDictionaryRef		resolver;
1606			dns_create_resolver_t	_resolver;
1607
1608			resolver = CFArrayGetValueAtIndex(resolvers, i);
1609
1610			is_default_resolver = (!isScopedConfiguration(resolver) && !isServiceSpecificConfiguration(resolver));
1611			if (is_default_resolver) {
1612				CFMutableDictionaryRef	new_resolver;
1613				CFNumberRef		num;
1614
1615				new_resolver = CFDictionaryCreateMutableCopy(NULL, 0, resolver);
1616
1617				num = CFNumberCreate(NULL, kCFNumberSInt32Type, &dns_resolver_flags);
1618				CFDictionarySetValue(new_resolver, DNS_CONFIGURATION_FLAGS_KEY, num);
1619				CFRelease(num);
1620
1621				resolver = new_resolver;
1622			}
1623
1624			_resolver = create_resolver(resolver);
1625			_dns_configuration_add_resolver(&_config, _resolver);
1626			_dns_resolver_free(&_resolver);
1627
1628			if (is_default_resolver) {
1629				CFRelease(resolver);
1630			}
1631		}
1632
1633#if	!TARGET_OS_IPHONE
1634		// add flatfile resolvers
1635
1636		_dnsinfo_flatfile_set_flags(dns_resolver_flags);
1637		_dnsinfo_flatfile_add_resolvers(&_config);
1638#endif	// !TARGET_OS_IPHONE
1639	}
1640
1641#ifdef	DNS_CONFIGURATION_DEBUG
1642	{
1643		uint8_t			*buf;
1644		dns_config_t		*config;
1645		_dns_config_buf_t	*config_buf;
1646		uint32_t		n_config;
1647		uint32_t		n_padding;
1648
1649		config_buf = (_dns_config_buf_t *)_config;
1650		n_config  = sizeof(_dns_config_buf_t) + ntohl(config_buf->n_attribute);
1651		n_padding = ntohl(config_buf->n_padding);
1652		buf = malloc(n_config + n_padding);
1653		bcopy((void *)config_buf, buf, n_config);
1654		bzero(&buf[n_config], n_padding);
1655		config = expand_config((_dns_config_buf_t *)buf);
1656		_dns_configuration_print(config);
1657		free(buf);
1658	}
1659#endif	// DNS_CONFIGURATION_DEBUG
1660
1661	// check if the configuration changed
1662	_dns_configuration_signature(&_config, signature, sizeof(signature));
1663	if (bcmp(signature, signature_last, sizeof(signature)) != 0) {
1664		// save [new] signature
1665		bcopy(signature, signature_last, sizeof(signature));
1666
1667		// save [new] configuration
1668		if (!_dns_configuration_store(&_config)) {
1669			my_log(LOG_ERR, "dns_configuration_set: could not store configuration");
1670		}
1671
1672		changed = TRUE;
1673	}
1674
1675	if (_config != NULL) _dns_configuration_free(&_config);
1676
1677	CFRelease(resolvers);
1678	return changed;
1679}
1680
1681
1682#if	!TARGET_OS_IPHONE
1683static SCDynamicStoreRef	dns_configuration_store;
1684static SCDynamicStoreCallBack	dns_configuration_callout;
1685
1686static void
1687dns_configuration_changed(CFMachPortRef port, void *msg, CFIndex size, void *info)
1688{
1689	CFStringRef	key		= CFSTR(_PATH_RESOLVER_DIR);
1690	CFArrayRef	keys;
1691	Boolean		resolvers_now;
1692	static Boolean	resolvers_save	= FALSE;
1693	struct stat	statbuf;
1694
1695	resolvers_now = (stat(_PATH_RESOLVER_DIR, &statbuf) == 0);
1696	if (!resolvers_save && (resolvers_save == resolvers_now)) {
1697		// if we did not (and still do not) have an "/etc/resolvers"
1698		// directory than this notification is the result of a change
1699		// to the "/etc" directory.
1700		return;
1701	}
1702	resolvers_save = resolvers_now;
1703
1704	my_log(LOG_DEBUG, _PATH_RESOLVER_DIR " changed");
1705
1706	// fake a "DNS" change
1707	keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
1708	(*dns_configuration_callout)(dns_configuration_store, keys, NULL);
1709	CFRelease(keys);
1710	return;
1711}
1712
1713
1714__private_extern__
1715void
1716dns_configuration_monitor(SCDynamicStoreRef store, SCDynamicStoreCallBack callout)
1717{
1718	CFMachPortRef		mp;
1719	mach_port_t		notify_port;
1720	int			notify_token;
1721	CFRunLoopSourceRef	rls;
1722	uint32_t		status;
1723
1724	dns_configuration_store   = store;
1725	dns_configuration_callout = callout;
1726
1727	status = notify_register_mach_port(_PATH_RESOLVER_DIR, &notify_port, 0, &notify_token);
1728	if (status != NOTIFY_STATUS_OK) {
1729		my_log(LOG_ERR, "notify_register_mach_port() failed");
1730		return;
1731	}
1732
1733	status = notify_monitor_file(notify_token, "/private" _PATH_RESOLVER_DIR, 0);
1734	if (status != NOTIFY_STATUS_OK) {
1735		my_log(LOG_ERR, "notify_monitor_file() failed");
1736		(void)notify_cancel(notify_token);
1737		return;
1738	}
1739
1740	mp = _SC_CFMachPortCreateWithPort("IPMonitor/dns_configuration",
1741					  notify_port,
1742					  dns_configuration_changed,
1743					  NULL);
1744
1745	rls = CFMachPortCreateRunLoopSource(NULL, mp, -1);
1746	if (rls == NULL) {
1747		my_log(LOG_ERR, "SCDynamicStoreCreateRunLoopSource() failed");
1748		CFRelease(mp);
1749		(void)notify_cancel(notify_token);
1750		return;
1751	}
1752	CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
1753	CFRelease(rls);
1754
1755	CFRelease(mp);
1756	return;
1757}
1758#endif	// !TARGET_OS_IPHONE
1759
1760
1761__private_extern__
1762void
1763dns_configuration_init(CFBundleRef bundle)
1764{
1765	CFDictionaryRef	dict;
1766
1767	dict = CFBundleGetInfoDictionary(bundle);
1768	if (isA_CFDictionary(dict)) {
1769		S_mdns_timeout = CFDictionaryGetValue(dict, CFSTR("mdns_timeout"));
1770		S_mdns_timeout = isA_CFNumber(S_mdns_timeout);
1771
1772		S_pdns_timeout = CFDictionaryGetValue(dict, CFSTR("pdns_timeout"));
1773		S_pdns_timeout = isA_CFNumber(S_pdns_timeout);
1774	}
1775
1776	return;
1777}
1778
1779
1780#pragma mark -
1781#pragma mark Standalone test code
1782
1783
1784#ifdef	MAIN
1785
1786static void
1787split(const void * key, const void * value, void * context)
1788{
1789	CFArrayRef		components;
1790	CFStringRef		entity_id;
1791	CFStringRef		service_id;
1792	CFMutableDictionaryRef	state_dict;
1793
1794	components = CFStringCreateArrayBySeparatingStrings(NULL, (CFStringRef)key, CFSTR("/"));
1795	service_id = CFArrayGetValueAtIndex(components, 3);
1796	entity_id  = CFArrayGetValueAtIndex(components, 4);
1797	state_dict = (CFMutableDictionaryRef)CFDictionaryGetValue(context, service_id);
1798	if (state_dict != NULL) {
1799		state_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
1800	} else {
1801		state_dict = CFDictionaryCreateMutable(NULL,
1802						       0,
1803						       &kCFTypeDictionaryKeyCallBacks,
1804						       &kCFTypeDictionaryValueCallBacks);
1805	}
1806
1807	if (CFEqual(entity_id, kSCEntNetIPv4) ||
1808	    CFEqual(entity_id, kSCEntNetIPv6)) {
1809		CFStringRef		interface;
1810
1811		if (CFEqual(entity_id, kSCEntNetIPv4)) {
1812			CFMutableDictionaryRef	ipv4_dict;
1813
1814			ipv4_dict = CFDictionaryCreateMutable(NULL,
1815							      0,
1816							      &kCFTypeDictionaryKeyCallBacks,
1817							      &kCFTypeDictionaryValueCallBacks);
1818			CFDictionarySetValue(ipv4_dict, kIPv4DictService, (CFDictionaryRef)value);
1819			CFDictionarySetValue(state_dict, entity_id, ipv4_dict);
1820			CFRelease(ipv4_dict);
1821		} else {
1822			CFDictionarySetValue(state_dict, entity_id, (CFDictionaryRef)value);
1823		}
1824
1825		interface = CFDictionaryGetValue((CFDictionaryRef)value, kSCPropInterfaceName);
1826		if (interface != NULL) {
1827			CFDictionaryRef		dns;
1828			CFMutableDictionaryRef	new_dns;
1829
1830			dns = CFDictionaryGetValue(state_dict, kSCEntNetDNS);
1831			if (dns != NULL) {
1832				new_dns = CFDictionaryCreateMutableCopy(NULL, 0, dns);
1833			} else {
1834				new_dns = CFDictionaryCreateMutable(NULL,
1835								    0,
1836								    &kCFTypeDictionaryKeyCallBacks,
1837								    &kCFTypeDictionaryValueCallBacks);
1838			}
1839			CFDictionarySetValue(new_dns, kSCPropInterfaceName, interface);
1840			CFDictionarySetValue(state_dict, kSCEntNetDNS, new_dns);
1841			CFRelease(new_dns);
1842		}
1843	} else if (CFEqual(entity_id, kSCEntNetDNS)) {
1844		CFDictionaryRef	dns;
1845
1846		dns = CFDictionaryGetValue(state_dict, kSCEntNetDNS);
1847		if (dns != NULL) {
1848			CFStringRef	interface;
1849
1850			interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
1851			if (interface != NULL) {
1852				CFMutableDictionaryRef	new_dns;
1853
1854				new_dns = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)value);
1855				CFDictionarySetValue(new_dns, kSCPropInterfaceName, interface);
1856				CFDictionarySetValue(state_dict, kSCEntNetDNS, new_dns);
1857				CFRelease(new_dns);
1858			} else {
1859				CFDictionarySetValue(state_dict, kSCEntNetDNS, (CFDictionaryRef)value);
1860			}
1861		} else {
1862			CFDictionarySetValue(state_dict, kSCEntNetDNS, (CFDictionaryRef)value);
1863		}
1864	} else {
1865		CFDictionarySetValue(state_dict, entity_id, (CFDictionaryRef)value);
1866	}
1867
1868	CFDictionarySetValue((CFMutableDictionaryRef)context, service_id, state_dict);
1869	CFRelease(state_dict);
1870	CFRelease(components);
1871
1872	return;
1873}
1874
1875int
1876main(int argc, char **argv)
1877{
1878	CFDictionaryRef		entities;
1879	CFStringRef		key;
1880	CFArrayRef		multicast_resolvers;
1881	CFStringRef		pattern;
1882	CFMutableArrayRef	patterns;
1883	CFStringRef		primary		= NULL;
1884	CFDictionaryRef		primaryDNS	= NULL;
1885	CFArrayRef		private_resolvers;
1886	CFArrayRef		service_order	= NULL;
1887	CFMutableDictionaryRef	service_state_dict;
1888	CFDictionaryRef		setup_global_ipv4;
1889	CFDictionaryRef		state_global_ipv4;
1890	SCDynamicStoreRef	store;
1891
1892	_sc_log     = FALSE;
1893	_sc_verbose = (argc > 1) ? TRUE : FALSE;
1894
1895	store = SCDynamicStoreCreate(NULL, CFSTR("TEST"), NULL, NULL);
1896
1897	// get IPv4, IPv6, and DNS entities
1898	patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1899	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1900							      kSCDynamicStoreDomainState,
1901							      kSCCompAnyRegex,
1902							      kSCEntNetIPv4);
1903	CFArrayAppendValue(patterns, pattern);
1904	CFRelease(pattern);
1905	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1906							      kSCDynamicStoreDomainState,
1907							      kSCCompAnyRegex,
1908							      kSCEntNetIPv6);
1909	CFArrayAppendValue(patterns, pattern);
1910	CFRelease(pattern);
1911	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1912							      kSCDynamicStoreDomainState,
1913							      kSCCompAnyRegex,
1914							      kSCEntNetDNS);
1915	CFArrayAppendValue(patterns, pattern);
1916	CFRelease(pattern);
1917	entities = SCDynamicStoreCopyMultiple(store, NULL, patterns);
1918	CFRelease(patterns);
1919
1920	service_state_dict = CFDictionaryCreateMutable(NULL,
1921						       0,
1922						       &kCFTypeDictionaryKeyCallBacks,
1923						       &kCFTypeDictionaryValueCallBacks);
1924	CFDictionaryApplyFunction(entities, split, service_state_dict);
1925	CFRelease(entities);
1926
1927	// get primary service ID
1928	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
1929							 kSCDynamicStoreDomainState,
1930							 kSCEntNetIPv4);
1931	state_global_ipv4 = SCDynamicStoreCopyValue(store, key);
1932	CFRelease(key);
1933	if (state_global_ipv4 != NULL) {
1934		primary = CFDictionaryGetValue(state_global_ipv4, kSCDynamicStorePropNetPrimaryService);
1935		if (primary != NULL) {
1936			CFDictionaryRef	service_dict;
1937
1938			// get DNS configuration for primary service
1939			service_dict = CFDictionaryGetValue(service_state_dict, primary);
1940			if (service_dict != NULL) {
1941				primaryDNS = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
1942			}
1943		}
1944	}
1945
1946	// get serviceOrder
1947	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
1948							 kSCDynamicStoreDomainSetup,
1949							 kSCEntNetIPv4);
1950	setup_global_ipv4 = SCDynamicStoreCopyValue(store, key);
1951	CFRelease(key);
1952	if (setup_global_ipv4 != NULL) {
1953		service_order = CFDictionaryGetValue(setup_global_ipv4, kSCPropNetServiceOrder);
1954	}
1955
1956	// get multicast resolvers
1957	key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
1958				      kSCDynamicStoreDomainState,
1959				      kSCCompNetwork,
1960				      CFSTR(kDNSServiceCompMulticastDNS));
1961	multicast_resolvers = SCDynamicStoreCopyValue(store, key);
1962	CFRelease(key);
1963
1964	// get private resolvers
1965	key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
1966				      kSCDynamicStoreDomainState,
1967				      kSCCompNetwork,
1968				      CFSTR(kDNSServiceCompPrivateDNS));
1969	private_resolvers = SCDynamicStoreCopyValue(store, key);
1970	CFRelease(key);
1971
1972	// update DNS configuration
1973	dns_configuration_init(CFBundleGetMainBundle());
1974	(void)dns_configuration_set(primaryDNS,
1975				    service_state_dict,
1976				    service_order,
1977				    multicast_resolvers,
1978				    private_resolvers);
1979
1980	// cleanup
1981	if (setup_global_ipv4 != NULL)	CFRelease(setup_global_ipv4);
1982	if (state_global_ipv4 != NULL)	CFRelease(state_global_ipv4);
1983	if (multicast_resolvers != NULL) CFRelease(multicast_resolvers);
1984	if (private_resolvers != NULL)	CFRelease(private_resolvers);
1985	CFRelease(service_state_dict);
1986	CFRelease(store);
1987
1988	/* not reached */
1989	exit(0);
1990	return 0;
1991}
1992#endif
1993
1994