1/*
2 * Copyright (c) 2003-2014 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 * April 12, 2011		Allan Nathanson <ajn@apple.com>
28 * - add SCNetworkReachability "server"
29 *
30 * March 31, 2004		Allan Nathanson <ajn@apple.com>
31 * - use [SC] DNS configuration information
32 *
33 * January 19, 2003		Allan Nathanson <ajn@apple.com>
34 * - add advanced reachability APIs
35 */
36
37#include <Availability.h>
38#include <TargetConditionals.h>
39#include <sys/cdefs.h>
40#include <dispatch/dispatch.h>
41#include <dispatch/private.h>
42#include <CoreFoundation/CoreFoundation.h>
43#include <CoreFoundation/CFRuntime.h>
44#include <SystemConfiguration/SystemConfiguration.h>
45#include <SystemConfiguration/SCValidation.h>
46#include <SystemConfiguration/SCPrivate.h>
47#include <SystemConfiguration/VPNAppLayerPrivate.h>
48#include <pthread.h>
49#include <libkern/OSAtomic.h>
50
51#if	!TARGET_OS_IPHONE
52#include <IOKit/pwr_mgt/IOPMLibPrivate.h>
53#endif	// !TARGET_OS_IPHONE
54
55#include <notify.h>
56#include <dnsinfo.h>
57#include <netinet/in.h>
58#include <arpa/inet.h>
59#include <netdb.h>
60#include <resolv.h>
61#include <unistd.h>
62#include <sys/ioctl.h>
63#include <sys/socket.h>
64#include <net/if.h>
65#include <net/if_dl.h>
66#include <net/if_types.h>
67#define	KERNEL_PRIVATE
68#include <net/route.h>
69#undef	KERNEL_PRIVATE
70
71#ifndef s6_addr16
72#define s6_addr16 __u6_addr.__u6_addr16
73#endif
74
75#include "SCNetworkConnectionInternal.h"
76#include "SCNetworkReachabilityInternal.h"
77
78#include <ppp/ppp_msg.h>
79#include <ppp/PPPControllerPriv.h>
80
81#include <network_information.h>
82
83
84
85
86
87
88
89#define	DEBUG_REACHABILITY_TYPE_NAME			"create w/name"
90#define	DEBUG_REACHABILITY_TYPE_NAME_CLONE		"      > clone"
91#define	DEBUG_REACHABILITY_TYPE_NAME_OPTIONS		"    + options"
92
93#define	DEBUG_REACHABILITY_TYPE_ADDRESS			"create w/address"
94#define	DEBUG_REACHABILITY_TYPE_ADDRESS_CLONE		"         > clone"
95#define	DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS		"       + options"
96
97#define	DEBUG_REACHABILITY_TYPE_ADDRESSPAIR		"create w/address pair"
98#define	DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_CLONE	"              > clone"
99#define	DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS	"            + options"
100
101#define	DEBUG_REACHABILITY_TYPE_PTR			"create w/ptr"
102#define	DEBUG_REACHABILITY_TYPE_PTR_CLONE		"     > clone"
103#define	DEBUG_REACHABILITY_TYPE_PTR_OPTIONS		"   + options"
104
105#define	DNS_FLAGS_FORMAT	"[%s%s%s%s%s]"
106#define	DNS_FLAGS_VALUES(t)	t->dnsHaveV4      ? "4" : "",	\
107				t->dnsHaveV6      ? "6" : "",	\
108				t->dnsHavePTR     ? "P" : "",	\
109				t->dnsHaveTimeout ? "T" : "",	\
110				t->dnsHaveError   ? "E" : ""
111
112
113static pthread_mutexattr_t	lock_attr;
114
115#define MUTEX_INIT(m) {							\
116	int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0);		\
117	assert(_lock_);							\
118}
119
120#define	MUTEX_LOCK(m) {							\
121	int _lock_ = (pthread_mutex_lock(m) == 0);			\
122	assert(_lock_);							\
123}
124
125#define	MUTEX_UNLOCK(m) {						\
126	int _unlock_ = (pthread_mutex_unlock(m) == 0);			\
127	assert(_unlock_);						\
128}
129
130#define	MUTEX_ASSERT_HELD(m) {						\
131	int _locked_ = (pthread_mutex_lock(m) == EDEADLK);		\
132	assert(_locked_);						\
133}
134
135
136#define SCNETWORKREACHABILITY_TRIGGER_KEY	CFSTR("com.apple.SCNetworkReachability:FORCE-CHANGE")
137
138
139#define	N_QUICK	64
140
141
142static CFStringRef	__SCNetworkReachabilityCopyDescription	(CFTypeRef cf);
143static void		__SCNetworkReachabilityDeallocate	(CFTypeRef cf);
144static void		reachPerform				(void *info);
145static Boolean		reachUpdate				(SCNetworkReachabilityRef target);
146
147
148static void
149__SCNetworkReachabilityHandleChanges		(SCDynamicStoreRef		store,
150						 CFArrayRef			changedKeys,
151						 void				*info);
152
153static Boolean
154__SCNetworkReachabilityScheduleWithRunLoop	(SCNetworkReachabilityRef	target,
155						 CFRunLoopRef			runLoop,
156						 CFStringRef			runLoopMode,
157						 dispatch_queue_t		queue,
158						 Boolean			onDemand);
159
160static Boolean
161__SCNetworkReachabilityUnscheduleFromRunLoop	(SCNetworkReachabilityRef	target,
162						 CFRunLoopRef			runLoop,
163						 CFStringRef			runLoopMode,
164						 Boolean			onDemand);
165
166
167static CFTypeID __kSCNetworkReachabilityTypeID	= _kCFRuntimeNotATypeID;
168
169
170static const CFRuntimeClass __SCNetworkReachabilityClass = {
171	0,					// version
172	"SCNetworkReachability",		// className
173	NULL,					// init
174	NULL,					// copy
175	__SCNetworkReachabilityDeallocate,	// dealloc
176	NULL,					// equal
177	NULL,					// hash
178	NULL,					// copyFormattingDesc
179	__SCNetworkReachabilityCopyDescription	// copyDebugDesc
180};
181
182
183static pthread_once_t		initialized	= PTHREAD_ONCE_INIT;
184static const ReachabilityInfo	NOT_REACHABLE	= { 0, 0,		0, { 0 }, FALSE };
185static const ReachabilityInfo	NOT_REPORTED	= { 0, 0xFFFFFFFF,	0, { 0 }, FALSE };
186static int			rtm_seq		= 0;
187
188
189static const struct timeval	TIME_ZERO	= { 0, 0 };
190
191
192static int			dnsCount	= 0;
193static int			dnsGeneration	= 0;
194static DNSServiceRef		dnsMain		= NULL;
195static CFMutableSetRef		dnsUpdated	= NULL;
196
197static Boolean			D_serverBypass	= FALSE;
198
199
200
201#if	!TARGET_OS_IPHONE
202/*
203 * Power capabilities (sleep/wake)
204 */
205#define POWER_CAPABILITIES_NETWORK	( kIOPMCapabilityCPU		\
206					| kIOPMCapabilityNetwork	\
207					| kIOPMCapabilityVideo)
208static IOPMSystemPowerStateCapabilities	power_capabilities	= POWER_CAPABILITIES_NETWORK;
209#endif	// !TARGET_OS_IPHONE
210
211
212/*
213 * host "something has changed" notifications
214 */
215
216// Note: protected by _hn_target_queue()
217static SCDynamicStoreRef	hn_store	= NULL;
218static CFMutableSetRef		hn_targets	= NULL;
219
220
221static dispatch_queue_t
222_hn_changes_queue()
223{
224	static dispatch_once_t	once;
225	static dispatch_queue_t	q = NULL;
226
227	dispatch_once(&once, ^{
228		q = dispatch_queue_create("SCNetworkReachability.handleChanges", NULL);
229	});
230
231	return q;
232}
233
234
235static dispatch_queue_t
236_hn_target_queue()
237{
238	static dispatch_once_t	once;
239	static dispatch_queue_t	q;
240
241	dispatch_once(&once, ^{
242		q = dispatch_queue_create("SCNetworkReachability.targetManagement", NULL);
243	});
244
245	return q;
246}
247
248
249/*
250 * DNS configuration
251 */
252
253typedef struct {
254	dns_config_t	*config;
255	int		refs;
256} dns_configuration_t;
257
258
259// Note: protected by "dns_lock"
260static pthread_mutex_t		dns_lock		= PTHREAD_MUTEX_INITIALIZER;
261static dns_configuration_t	*dns_configuration	= NULL;
262static int			dns_token;
263static Boolean			dns_token_valid		= FALSE;
264
265
266
267
268typedef enum {
269	dns_query_async,
270	dns_query_mdns,
271	dns_query_mdns_timeout,
272} query_type;
273
274
275static void
276__mark_operation_start(struct timeval	*queryStart,
277		       struct timeval	*queryEnd)
278{
279	(void) gettimeofday(queryStart, NULL);
280	*queryEnd = TIME_ZERO;
281
282	return;
283}
284
285
286static void
287__mark_operation_end(SCNetworkReachabilityRef	target,
288		     Boolean			found,
289		     query_type			query_type,
290		     struct timeval		*queryStart,
291		     struct timeval		*queryEnd)
292{
293	struct timeval			queryElapsed;
294	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
295
296	(void) gettimeofday(queryEnd, NULL);
297
298	if (!_sc_debug &&
299	    (query_type != dns_query_mdns_timeout)) {
300		return;
301	}
302
303	if (!timerisset(queryStart)) {
304		return;
305	}
306
307	timersub(queryEnd, queryStart, &queryElapsed);
308	switch (query_type) {
309
310		#define	QUERY_TIME__FMT	"%ld.%6.6d"
311		#define	QUERY_TIME__DIV	1
312
313		case dns_query_async :
314			SCLog(TRUE, LOG_INFO,
315			      CFSTR("%sasync DNS complete%s (query time = " QUERY_TIME__FMT ")"),
316			      targetPrivate->log_prefix,
317			      found ? "" : ", host not found",
318			      queryElapsed.tv_sec,
319			      queryElapsed.tv_usec / QUERY_TIME__DIV);
320			break;
321		case dns_query_mdns :
322			SCLog(TRUE, LOG_INFO,
323			      CFSTR("%s[m]DNS query complete%s (query time = " QUERY_TIME__FMT "), " DNS_FLAGS_FORMAT),
324			      targetPrivate->log_prefix,
325			      found ? "" : ", host not found",
326			      queryElapsed.tv_sec,
327			      queryElapsed.tv_usec / QUERY_TIME__DIV,
328			      DNS_FLAGS_VALUES(targetPrivate));
329			break;
330		case dns_query_mdns_timeout :
331			SCLog(TRUE, LOG_ERR,
332			      CFSTR("%s[m]DNS query timeout (query time = " QUERY_TIME__FMT "), " DNS_FLAGS_FORMAT),
333			      targetPrivate->log_prefix,
334			      queryElapsed.tv_sec,
335			      queryElapsed.tv_usec / QUERY_TIME__DIV,
336			      DNS_FLAGS_VALUES(targetPrivate));
337			break;
338	}
339
340	return;
341}
342
343
344static __inline__ Boolean
345__reach_changed(ReachabilityInfo *r1, ReachabilityInfo *r2)
346{
347	if (r1->flags != r2->flags) {
348		// if the reachability flags changed
349		return TRUE;
350	}
351
352	if (r1->if_index != r2->if_index) {
353		// if the target interface changed
354		return TRUE;
355	}
356
357	if ((r1->sleeping != r2->sleeping) && !r2->sleeping) {
358		// if our sleep/wake status changed and if we
359		// are no longer sleeping
360		return TRUE;
361	}
362
363	return FALSE;
364}
365
366
367static __inline__ void
368_reach_set(ReachabilityInfo		*dst,
369	   const ReachabilityInfo	*src,
370	   uint64_t			cycle,
371	   unsigned int			requested_if_index,
372	   const char			*requested_if_name)
373{
374	memcpy(dst, src, sizeof(ReachabilityInfo));
375	dst->cycle = cycle;
376
377	if (!(dst->flags & kSCNetworkReachabilityFlagsReachable) ||
378		(dst->flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
379		// if not reachable or connection required, return the
380		// requested if_index and if_name.
381		dst->if_index = requested_if_index;
382		if (requested_if_name != NULL) {
383			strlcpy(dst->if_name, requested_if_name, sizeof(dst->if_name));
384		} else {
385			dst->if_name[0] = '\0';
386		}
387	}
388
389	return;
390}
391
392
393#pragma mark -
394#pragma mark SCDynamicStore info
395
396
397typedef struct {
398	SCDynamicStoreRef	store;
399	CFStringRef		entity;
400	CFDictionaryRef		dict;
401	CFIndex			n;
402	const void **		keys;
403	const void *		keys_q[N_QUICK];
404	const void **		values;
405	const void *		values_q[N_QUICK];
406} ReachabilityStoreInfo, *ReachabilityStoreInfoRef;
407
408
409static ReachabilityStoreInfo	S_storeInfo		= { 0 };
410static Boolean			S_storeInfoActive	= FALSE;
411
412
413static dispatch_queue_t
414_storeInfo_queue()
415{
416	static dispatch_once_t	once;
417	static dispatch_queue_t	q;
418
419	dispatch_once(&once, ^{
420		q = dispatch_queue_create("SCNetworkReachability.storeInfo", NULL);
421	});
422
423	return q;
424}
425
426
427static void
428ReachabilityStoreInfo_copy(ReachabilityStoreInfoRef	src,
429			   ReachabilityStoreInfoRef	dst)
430{
431	if (src->dict != NULL) {
432		dst->store = src->store;
433		CFRetain(dst->store);
434
435		dst->dict = src->dict;
436		CFRetain(dst->dict);
437
438		dst->n = src->n;
439		if (dst->n > 0) {
440			if (dst->n <= (CFIndex)(sizeof(dst->keys_q) / sizeof(CFTypeRef))) {
441				dst->keys   = dst->keys_q;
442				dst->values = dst->values_q;
443			} else {
444				dst->keys   = CFAllocatorAllocate(NULL, dst->n * sizeof(CFTypeRef), 0);
445				dst->values = CFAllocatorAllocate(NULL, dst->n * sizeof(CFTypeRef), 0);
446			}
447			memcpy(dst->keys,   src->keys,   dst->n * sizeof(CFTypeRef));
448			memcpy(dst->values, src->values, dst->n * sizeof(CFTypeRef));
449		}
450	}
451
452	return;
453}
454
455
456static void
457ReachabilityStoreInfo_enable(Boolean enable)
458{
459	dispatch_sync(_storeInfo_queue(), ^{
460		S_storeInfoActive = enable;
461	});
462
463	return;
464}
465
466
467static void
468ReachabilityStoreInfo_free(ReachabilityStoreInfoRef store_info)
469{
470	if ((store_info->n > 0) && (store_info->keys != store_info->keys_q)) {
471		CFAllocatorDeallocate(NULL, store_info->keys);
472		store_info->keys = NULL;
473
474		CFAllocatorDeallocate(NULL, store_info->values);
475		store_info->values = NULL;
476	}
477	store_info->n = 0;
478
479	if (store_info->dict != NULL) {
480		CFRelease(store_info->dict);
481		store_info->dict = NULL;
482	}
483
484	if (store_info->store != NULL) {
485		CFRelease(store_info->store);
486		store_info->store = NULL;
487	}
488
489	return;
490}
491
492
493static void
494ReachabilityStoreInfo_init(ReachabilityStoreInfoRef store_info)
495{
496	dispatch_sync(_storeInfo_queue(), ^{
497		bzero(store_info, sizeof(ReachabilityStoreInfo));
498
499		if (S_storeInfoActive && (S_storeInfo.dict != NULL)) {
500			ReachabilityStoreInfo_copy(&S_storeInfo, store_info);
501		}
502	});
503
504	return;
505}
506
507
508static void
509ReachabilityStoreInfo_save(ReachabilityStoreInfoRef store_info)
510{
511	dispatch_sync(_storeInfo_queue(), ^{
512		if ((store_info == NULL) ||
513		    !_SC_CFEqual(store_info->dict, S_storeInfo.dict)) {
514			// free any old info
515			ReachabilityStoreInfo_free(&S_storeInfo);
516
517			// save new info
518			if (S_storeInfoActive &&
519			    (store_info != NULL) &&
520			    (store_info->dict != NULL)) {
521				ReachabilityStoreInfo_copy(store_info, &S_storeInfo);
522			}
523		}
524	});
525
526	return;
527}
528
529
530static void
531ReachabilityStoreInfo_keys(CFMutableArrayRef *fill_keys, CFMutableArrayRef *fill_patterns)
532{
533	CFStringRef		key;
534	CFMutableArrayRef	keys;
535	CFStringRef		pattern;
536	CFMutableArrayRef	patterns;
537
538	keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
539	patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
540
541	// get info for IPv4 services
542	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
543							 kSCDynamicStoreDomainState,
544							 kSCEntNetIPv4);
545	CFArrayAppendValue(keys, key);
546	CFRelease(key);
547	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
548							      kSCDynamicStoreDomainSetup,
549							      kSCCompAnyRegex,
550							      kSCEntNetIPv4);
551	CFArrayAppendValue(patterns, pattern);
552	CFRelease(pattern);
553	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
554							      kSCDynamicStoreDomainState,
555							      kSCCompAnyRegex,
556							      kSCEntNetIPv4);
557	CFArrayAppendValue(patterns, pattern);
558	CFRelease(pattern);
559
560	// get info for IPv6 services
561	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
562							 kSCDynamicStoreDomainState,
563							 kSCEntNetIPv6);
564	CFArrayAppendValue(keys, key);
565	CFRelease(key);
566	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
567							      kSCDynamicStoreDomainSetup,
568							      kSCCompAnyRegex,
569							      kSCEntNetIPv6);
570	CFArrayAppendValue(patterns, pattern);
571	CFRelease(pattern);
572	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
573							      kSCDynamicStoreDomainState,
574							      kSCCompAnyRegex,
575							      kSCEntNetIPv6);
576	CFArrayAppendValue(patterns, pattern);
577	CFRelease(pattern);
578
579	// get info for PPP services
580	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
581							      kSCDynamicStoreDomainSetup,
582							      kSCCompAnyRegex,
583							      kSCEntNetPPP);
584	CFArrayAppendValue(patterns, pattern);
585	CFRelease(pattern);
586	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
587							      kSCDynamicStoreDomainState,
588							      kSCCompAnyRegex,
589							      kSCEntNetPPP);
590	CFArrayAppendValue(patterns, pattern);
591	CFRelease(pattern);
592
593	// get info for VPN services
594	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
595							      kSCDynamicStoreDomainSetup,
596							      kSCCompAnyRegex,
597							      kSCEntNetVPN);
598	CFArrayAppendValue(patterns, pattern);
599	CFRelease(pattern);
600	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
601							      kSCDynamicStoreDomainState,
602							      kSCCompAnyRegex,
603							      kSCEntNetVPN);
604	CFArrayAppendValue(patterns, pattern);
605	CFRelease(pattern);
606
607	// get info for IPSec services
608//	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
609//							      kSCDynamicStoreDomainSetup,
610//							      kSCCompAnyRegex,
611//							      kSCEntNetIPSec);
612//	CFArrayAppendValue(patterns, pattern);
613//	CFRelease(pattern);
614	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
615							      kSCDynamicStoreDomainState,
616							      kSCCompAnyRegex,
617							      kSCEntNetIPSec);
618	CFArrayAppendValue(patterns, pattern);
619	CFRelease(pattern);
620
621	// get info to identify "available" services
622	pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
623							      kSCDynamicStoreDomainSetup,
624							      kSCCompAnyRegex,
625							      kSCEntNetInterface);
626	CFArrayAppendValue(patterns, pattern);
627	CFRelease(pattern);
628
629
630	*fill_keys     = keys;
631	*fill_patterns = patterns;
632	return;
633}
634
635
636static Boolean
637ReachabilityStoreInfo_fill(ReachabilityStoreInfoRef store_info)
638{
639	CFMutableArrayRef	keys;
640	CFMutableArrayRef	patterns;
641
642	// get the SCDynamicStore info
643	ReachabilityStoreInfo_keys(&keys, &patterns);
644	store_info->dict = SCDynamicStoreCopyMultiple(store_info->store, keys, patterns);
645	CFRelease(keys);
646	CFRelease(patterns);
647	if (store_info->dict == NULL) {
648		return FALSE;
649	}
650
651	// and extract the keys/values for post-processing
652	store_info->n = CFDictionaryGetCount(store_info->dict);
653	if (store_info->n > 0) {
654		if (store_info->n <= (CFIndex)(sizeof(store_info->keys_q) / sizeof(CFTypeRef))) {
655			store_info->keys   = store_info->keys_q;
656			store_info->values = store_info->values_q;
657		} else {
658			store_info->keys   = CFAllocatorAllocate(NULL, store_info->n * sizeof(CFTypeRef), 0);
659			store_info->values = CFAllocatorAllocate(NULL, store_info->n * sizeof(CFTypeRef), 0);
660		}
661		CFDictionaryGetKeysAndValues(store_info->dict,
662					     store_info->keys,
663					     store_info->values);
664	}
665
666	return TRUE;
667}
668
669
670static Boolean
671ReachabilityStoreInfo_update(ReachabilityStoreInfoRef	store_info,
672			     SCDynamicStoreRef		*storeP,
673			     sa_family_t		sa_family)
674{
675	__block Boolean		ok	= TRUE;
676
677	switch (sa_family) {
678		case AF_UNSPEC :
679			store_info->entity = NULL;
680			break;
681		case AF_INET :
682			store_info->entity = kSCEntNetIPv4;
683			break;
684		case AF_INET6 :
685			store_info->entity = kSCEntNetIPv6;
686			break;
687		default :
688			return FALSE;
689	}
690
691	if (store_info->dict != NULL) {
692		// if info already available
693		return TRUE;
694	}
695
696	dispatch_sync(_storeInfo_queue(), ^{
697		if (S_storeInfoActive && (S_storeInfo.dict != NULL)) {
698			// free any info
699			ReachabilityStoreInfo_free(store_info);
700
701			// copy the shared/available info
702			ReachabilityStoreInfo_copy(&S_storeInfo, store_info);
703		}
704
705		if (store_info->store == NULL) {
706			store_info->store = (storeP != NULL) ? *storeP : NULL;
707			if (store_info->store != NULL) {
708				// keep a reference to the passed in SCDynamicStore
709				CFRetain(store_info->store);
710			} else {
711				store_info->store = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkReachability"), NULL, NULL);
712				if (store_info->store == NULL) {
713					SCLog(TRUE, LOG_ERR, CFSTR("ReachabilityStoreInfo_update SCDynamicStoreCreate() failed"));
714					return;
715				}
716
717				if (storeP != NULL) {
718					// and pass back a reference
719					*storeP = store_info->store;
720					CFRetain(*storeP);
721				}
722			}
723		}
724
725		if (sa_family == AF_UNSPEC) {
726			// if the address family was not specified than
727			// all we wanted, for now, was to establish the
728			// SCDynamicStore session
729			return;
730		}
731
732		if (store_info->dict != NULL) {
733			// or we have picked up the shared info
734			return;
735		}
736
737		ok = ReachabilityStoreInfo_fill(store_info);
738		if (!ok) {
739			return;
740		}
741
742		if (!_SC_CFEqual(store_info->dict, S_storeInfo.dict)) {
743			// free any old info
744			ReachabilityStoreInfo_free(&S_storeInfo);
745
746			// save new info
747			if (S_storeInfoActive &&
748			    (store_info->dict != NULL)) {
749				ReachabilityStoreInfo_copy(store_info, &S_storeInfo);
750			}
751		}
752	});
753
754	return ok;
755}
756
757
758#pragma mark -
759#pragma mark Reachability engine
760
761
762#define ROUNDUP(a, size) \
763	(((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
764
765#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
766	((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
767						 sizeof(uint32_t)) :\
768						 sizeof(uint32_t)))
769
770static void
771get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
772{
773	int             i;
774
775	for (i = 0; i < RTAX_MAX; i++) {
776		if (addrs & (1 << i)) {
777			rti_info[i] = sa;
778			NEXT_SA(sa);
779		} else
780			rti_info[i] = NULL;
781	}
782}
783
784
785#define BUFLEN (sizeof(struct rt_msghdr) + 512)	/* 8 * sizeof(struct sockaddr_in6) = 192 */
786
787
788typedef struct {
789	union {
790		char			bytes[BUFLEN];
791		struct rt_msghdr	rtm;
792	} buf;
793	int			error;
794	struct sockaddr         *rti_info[RTAX_MAX];
795	struct rt_msghdr	*rtm;
796	struct sockaddr_dl	*sdl;
797} route_info, *route_info_p;
798
799
800/*
801 * route_get()
802 *	returns	zero if route exists and data returned, EHOSTUNREACH
803 *	if no route, or errno for any other error.
804 */
805static int
806route_get(const struct sockaddr	*address,
807	  unsigned int		if_index,
808	  route_info		*info)
809{
810	int			n;
811	int			opt;
812	pid_t			pid		= getpid();
813	int			rsock;
814	struct sockaddr         *sa;
815	int32_t			seq		= OSAtomicIncrement32Barrier(&rtm_seq);
816#ifndef	RTM_GET_SILENT
817#warning Note: Using RTM_GET (and not RTM_GET_SILENT)
818	static pthread_mutex_t	lock		= PTHREAD_MUTEX_INITIALIZER;
819	int			sosize		= 48 * 1024;
820#endif
821
822	bzero(info, sizeof(*info));
823
824	info->rtm = &info->buf.rtm;
825	info->rtm->rtm_msglen  = sizeof(struct rt_msghdr);
826	info->rtm->rtm_version = RTM_VERSION;
827#ifdef	RTM_GET_SILENT
828	info->rtm->rtm_type    = RTM_GET_SILENT;
829#else
830	info->rtm->rtm_type    = RTM_GET;
831#endif
832	info->rtm->rtm_flags   = RTF_STATIC|RTF_UP|RTF_HOST|RTF_GATEWAY;
833	info->rtm->rtm_addrs   = RTA_DST|RTA_IFP; /* Both destination and device */
834	info->rtm->rtm_pid     = pid;
835	info->rtm->rtm_seq     = seq;
836
837	if (if_index != 0) {
838		info->rtm->rtm_flags |= RTF_IFSCOPE;
839		info->rtm->rtm_index = if_index;
840	}
841
842	switch (address->sa_family) {
843		case AF_INET6: {
844			struct sockaddr_in6	*sin6;
845
846			/* ALIGN: caller ensures that the address is aligned */
847			sin6 = (struct sockaddr_in6 *)(void *)address;
848			if ((IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
849			     IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) &&
850			    (sin6->sin6_scope_id != 0)) {
851				sin6->sin6_addr.s6_addr16[1] = htons(sin6->sin6_scope_id);
852				sin6->sin6_scope_id = 0;
853			}
854			break;
855		}
856	}
857
858	sa  = (struct sockaddr *) (info->rtm + 1);
859	bcopy(address, sa, address->sa_len);
860	n = ROUNDUP(sa->sa_len, sizeof(uint32_t));
861	info->rtm->rtm_msglen += n;
862
863	info->sdl = (struct sockaddr_dl *) ((void *)sa + n);
864	info->sdl->sdl_family = AF_LINK;
865	info->sdl->sdl_len = sizeof (struct sockaddr_dl);
866	n = ROUNDUP(info->sdl->sdl_len, sizeof(uint32_t));
867	info->rtm->rtm_msglen += n;
868
869#ifndef	RTM_GET_SILENT
870	pthread_mutex_lock(&lock);
871#endif
872	rsock = socket(PF_ROUTE, SOCK_RAW, PF_ROUTE);
873	if (rsock == -1) {
874		int	error	= errno;
875
876#ifndef	RTM_GET_SILENT
877		pthread_mutex_unlock(&lock);
878#endif
879		SCLog(TRUE, LOG_ERR, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error));
880		return error;
881	}
882	opt = 1;
883	if (ioctl(rsock, FIONBIO, &opt) < 0) {
884		int	error	= errno;
885
886		(void)close(rsock);
887#ifndef	RTM_GET_SILENT
888		pthread_mutex_unlock(&lock);
889#endif
890		SCLog(TRUE, LOG_ERR, CFSTR("ioctl(FIONBIO) failed: %s"), strerror(error));
891		return error;
892	}
893
894#ifndef	RTM_GET_SILENT
895	if (setsockopt(rsock, SOL_SOCKET, SO_RCVBUF, &sosize, sizeof(sosize)) == -1) {
896		int	error	= errno;
897
898		(void)close(rsock);
899		pthread_mutex_unlock(&lock);
900		SCLog(TRUE, LOG_ERR, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error));
901		return error;
902	}
903#endif
904
905	if (write(rsock, &info->buf, info->rtm->rtm_msglen) == -1) {
906		int	error	= errno;
907
908		(void)close(rsock);
909#ifndef	RTM_GET_SILENT
910		pthread_mutex_unlock(&lock);
911#endif
912		if (error != ESRCH) {
913			SCLog(TRUE, LOG_ERR, CFSTR("write() failed: %s"), strerror(error));
914			return error;
915		}
916		return EHOSTUNREACH;
917	}
918
919	/*
920	 * Type, seq, pid identify our response.
921	 * Routing sockets are broadcasters on input.
922	 */
923	while (TRUE) {
924		ssize_t		n;
925
926		n = read(rsock, &info->buf, sizeof(info->buf));
927		if (n == -1) {
928			int	error	= errno;
929
930			if (error == EINTR) {
931				continue;
932			}
933			(void)close(rsock);
934#ifndef	RTM_GET_SILENT
935			pthread_mutex_unlock(&lock);
936#endif
937			SCLog(TRUE, LOG_ERR,
938			      CFSTR("SCNetworkReachability: routing socket"
939				    " read() failed: %s"), strerror(error));
940			return error;
941		}
942		if ((info->rtm->rtm_type == RTM_GET) 	&&
943		    (info->rtm->rtm_seq == seq) 	&&
944		    (info->rtm->rtm_pid == pid)) {
945		    break;
946		}
947	}
948
949	(void)close(rsock);
950#ifndef	RTM_GET_SILENT
951	pthread_mutex_unlock(&lock);
952#endif
953
954	get_rtaddrs(info->rtm->rtm_addrs, sa, info->rti_info);
955
956//#define LOG_RTADDRS
957#ifdef	LOG_RTADDRS
958	{
959		int	i;
960
961		SCLog(_sc_debug, LOG_DEBUG, CFSTR("rtm_flags = 0x%8.8x"), info->rtm->rtm_flags);
962
963		if ((info->rti_info[RTAX_NETMASK] != NULL) && (info->rti_info[RTAX_DST] != NULL)) {
964			info->rti_info[RTAX_NETMASK]->sa_family = info->rti_info[RTAX_DST]->sa_family;
965		}
966
967		for (i = 0; i < RTAX_MAX; i++) {
968			if (info->rti_info[i] != NULL) {
969				char	addr[128];
970
971				_SC_sockaddr_to_string(info->rti_info[i], addr, sizeof(addr));
972				SCLog(_sc_debug, LOG_DEBUG, CFSTR("%d: %s"), i, addr);
973			}
974		}
975	}
976#endif	// LOG_RTADDRS
977
978	if ((info->rti_info[RTAX_IFP] == NULL) ||
979	    (info->rti_info[RTAX_IFP]->sa_family != AF_LINK)) {
980		/* no interface info */
981		SCLog(TRUE, LOG_DEBUG, CFSTR("route_get() no interface info"));
982		return EINVAL;
983	}
984
985	/* ALIGN: accessors are retrieving byte values, cast ok. */
986	info->sdl = (struct sockaddr_dl *)(void *) info->rti_info[RTAX_IFP];
987	if ((info->sdl->sdl_nlen == 0) || (info->sdl->sdl_nlen > IFNAMSIZ)) {
988		/* no interface name */
989		return EHOSTUNREACH;
990	}
991
992	return 0;
993}
994
995
996static void
997log_address(const char			*str,
998	    const struct sockaddr	*sa,
999	    unsigned int		if_index,
1000	    const char			*log_prefix)
1001{
1002	char	addr[128];
1003	char	if_name[IFNAMSIZ + 1];
1004
1005	_SC_sockaddr_to_string(sa, addr, sizeof(addr));
1006
1007	if ((if_index != 0) &&
1008	    (if_indextoname(if_index, &if_name[1]) != NULL)) {
1009		if_name[0] = '%';
1010	} else {
1011		if_name[0] = '\0';
1012	}
1013
1014	SCLog(TRUE, LOG_INFO, CFSTR("%s%s(%s%s)"),
1015	      log_prefix,
1016	      str,
1017	      addr,
1018	      if_name);
1019
1020	return;
1021}
1022
1023
1024static int
1025checkAddress_route(const struct sockaddr	*address,
1026		   unsigned int			if_index,
1027		   char				*if_name,
1028		   struct ifreq			*ifr,
1029		   ReachabilityInfo		*reach_info,
1030		   route_info			*info,
1031		   int				*sc_status,
1032		   const char			*log_prefix)
1033{
1034	int			isock		= -1;
1035	int			ret		= 0;
1036	char			*statusMessage	= NULL;
1037	struct sockaddr_in	v4mapped;
1038
1039	switch (address->sa_family) {
1040		case AF_INET :
1041		case AF_INET6 :
1042			if (_sc_debug) {
1043				log_address("checkAddress", address, if_index, log_prefix);
1044			}
1045			break;
1046		default :
1047			/*
1048			 * if no code for this address family (yet)
1049			 */
1050			SCLog(TRUE, LOG_INFO,
1051			      CFSTR("checkAddress(): unexpected address family %d"),
1052			      address->sa_family);
1053			*sc_status = kSCStatusInvalidArgument;
1054			ret = EPERM;
1055			goto done;
1056	}
1057
1058	if (address->sa_family == AF_INET6) {
1059		/* ALIGN: sin6_addr accessed aligned, cast ok. */
1060		struct sockaddr_in6	*sin6	= (struct sockaddr_in6 *)(void *)address;
1061
1062		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1063			bzero(&v4mapped, sizeof(v4mapped));
1064			v4mapped.sin_len         = sizeof(v4mapped);
1065			v4mapped.sin_family      = AF_INET;
1066			v4mapped.sin_port        = sin6->sin6_port;
1067			v4mapped.sin_addr.s_addr = sin6->sin6_addr.__u6_addr.__u6_addr32[3];
1068			address = (struct sockaddr *)&v4mapped;
1069		}
1070	}
1071
1072	ret = route_get(address, if_index, info);
1073	switch (ret) {
1074		case 0 :
1075			break;
1076		case EHOSTUNREACH :
1077			// if no route
1078			goto done;
1079		default :
1080			// if error
1081			*sc_status = ret;
1082			goto done;
1083	}
1084
1085	/* get the interface flags */
1086
1087	isock = socket(AF_INET, SOCK_DGRAM, 0);
1088	if (isock == -1) {
1089		ret = errno;
1090		SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
1091		goto done;
1092	}
1093
1094	bzero(ifr, sizeof(*ifr));
1095	bcopy(info->sdl->sdl_data, ifr->ifr_name, info->sdl->sdl_nlen);
1096
1097	if (ioctl(isock, SIOCGIFFLAGS, (char *)ifr) == -1) {
1098		ret = errno;
1099		SCLog(TRUE, LOG_ERR, CFSTR("ioctl(SIOCGIFFLAGS) failed: %s"), strerror(errno));
1100		goto done;
1101	}
1102
1103	if (!(ifr->ifr_flags & IFF_UP)) {
1104		ret = EHOSTUNREACH;
1105		goto done;
1106	}
1107
1108	statusMessage = "isReachable";
1109	reach_info->flags |= kSCNetworkReachabilityFlagsReachable;
1110
1111	if (info->rtm->rtm_flags & RTF_LOCAL) {
1112		statusMessage = "isReachable (is a local address)";
1113		reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1114	} else if (ifr->ifr_flags & IFF_LOOPBACK) {
1115		statusMessage = "isReachable (is loopback network)";
1116		reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1117	} else if ((info->rti_info[RTAX_IFA] != NULL) &&
1118		   (info->rti_info[RTAX_IFA]->sa_family != AF_LINK)) {
1119		void	*addr1	= (void *)address;
1120		void	*addr2	= (void *)info->rti_info[RTAX_IFA];
1121		size_t	len	= address->sa_len;
1122
1123		if ((address->sa_family != info->rti_info[RTAX_IFA]->sa_family) &&
1124		    (address->sa_len    != info->rti_info[RTAX_IFA]->sa_len)) {
1125			SCLog(TRUE, LOG_NOTICE,
1126			      CFSTR("address family/length mismatch: %d/%d != %d/%d"),
1127			      address->sa_family,
1128			      address->sa_len,
1129			      info->rti_info[RTAX_IFA]->sa_family,
1130			      info->rti_info[RTAX_IFA]->sa_len);
1131			goto done;
1132		}
1133
1134		switch (address->sa_family) {
1135			case AF_INET :
1136				/* ALIGN: cast ok, because only bcmp is used. */
1137				addr1 = &((struct sockaddr_in *)(void *)address)->sin_addr;
1138				addr2 = &((struct sockaddr_in *)(void *)info->rti_info[RTAX_IFA])->sin_addr;
1139				len = sizeof(struct in_addr);
1140
1141				/*
1142				 * check if 0.0.0.0
1143				 */
1144				/* ALIGN: sin_addr should be aligned, cast ok. */
1145				if (((struct sockaddr_in *)(void *)address)->sin_addr.s_addr == 0) {
1146					statusMessage = "isReachable (this host)";
1147					reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1148				}
1149				break;
1150			case AF_INET6 :
1151				/* ALIGN: cast ok, because only bcmp is used. */
1152				addr1 = &((struct sockaddr_in6 *)(void *)address)->sin6_addr;
1153				addr2 = &((struct sockaddr_in6 *)(void *)info->rti_info[RTAX_IFA])->sin6_addr;
1154				len = sizeof(struct in6_addr);
1155				break;
1156			default :
1157				break;
1158		}
1159
1160		if (bcmp(addr1, addr2, len) == 0) {
1161			statusMessage = "isReachable (is interface address)";
1162			reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1163		}
1164	}
1165
1166	if (!(info->rtm->rtm_flags & RTF_GATEWAY) &&
1167	    (info->rti_info[RTAX_GATEWAY] != NULL) &&
1168	    (info->rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) &&
1169	    !(ifr->ifr_flags & IFF_POINTOPOINT)) {
1170		reach_info->flags |= kSCNetworkReachabilityFlagsIsDirect;
1171	}
1172
1173	bzero(if_name, IFNAMSIZ);
1174	bcopy(info->sdl->sdl_data,
1175	      if_name,
1176	      (info->sdl->sdl_nlen <= IFNAMSIZ) ? info->sdl->sdl_nlen : IFNAMSIZ);
1177
1178	strlcpy(reach_info->if_name, if_name, sizeof(reach_info->if_name));
1179	reach_info->if_index = info->sdl->sdl_index;
1180
1181	if (_sc_debug) {
1182		SCLog(TRUE, LOG_INFO, CFSTR("%s  status    = %s"), log_prefix, statusMessage);
1183		SCLog(TRUE, LOG_INFO, CFSTR("%s  device    = %s (%hu)"), log_prefix, if_name, info->sdl->sdl_index);
1184		SCLog(TRUE, LOG_INFO, CFSTR("%s  sdl_type  = 0x%x"), log_prefix, info->sdl->sdl_type);
1185		SCLog(TRUE, LOG_INFO, CFSTR("%s  ifr_flags = 0x%04hx"), log_prefix, ifr->ifr_flags);
1186		SCLog(TRUE, LOG_INFO, CFSTR("%s  rtm_flags = 0x%08x"), log_prefix, info->rtm->rtm_flags);
1187	}
1188
1189    done :
1190	if (isock != -1) (void)close(isock);
1191	return ret;
1192}
1193
1194
1195static Boolean
1196checkAddress(ReachabilityStoreInfoRef	store_info,
1197	     const struct sockaddr	*address,
1198	     unsigned int		if_index,
1199	     ReachabilityInfo		*reach_info,
1200	     const char			*log_prefix)
1201{
1202	route_info		info;
1203	struct ifreq		ifr;
1204	char			if_name[IFNAMSIZ];
1205	nwi_ifstate_t		ifstate;
1206	nwi_state_t		nwi_state;
1207	int			ret;
1208	int			sc_status	= kSCStatusReachabilityUnknown;
1209
1210	_reach_set(reach_info, &NOT_REACHABLE, reach_info->cycle, if_index, NULL);
1211
1212	nwi_state = nwi_state_copy();
1213
1214	if (address != NULL) {
1215		ret = checkAddress_route(address,
1216					if_index,
1217					if_name,
1218					&ifr,
1219					reach_info,
1220					&info,
1221					&sc_status,
1222					log_prefix);
1223	} else {
1224		/* special case: check only for available paths off the system */
1225		ret = EHOSTUNREACH;
1226	}
1227
1228	if (ret == 0) {
1229		const struct sockaddr	*vpn_server_address;
1230
1231		sc_status = kSCStatusOK;
1232
1233		ifstate = nwi_state_get_ifstate(nwi_state, if_name);
1234		if (ifstate == NULL) {
1235			goto done;
1236		}
1237
1238		reach_info->flags |= nwi_ifstate_get_reachability_flags(ifstate);
1239
1240
1241		vpn_server_address = nwi_ifstate_get_vpn_server(ifstate);
1242		if (vpn_server_address != NULL) {
1243			char		dst_if_name[IFNAMSIZ];
1244			route_info	dst_info;
1245
1246			ret = route_get(vpn_server_address, 0, &dst_info);
1247			if (ret != 0) {
1248				goto done;
1249			}
1250
1251			bzero(&dst_if_name, sizeof(dst_if_name));
1252			bcopy(dst_info.sdl->sdl_data,
1253			      dst_if_name,
1254			      (dst_info.sdl->sdl_nlen <= IFNAMSIZ) ? dst_info.sdl->sdl_nlen : IFNAMSIZ);
1255			if (bcmp(if_name, dst_if_name, sizeof(if_name)) != 0) {
1256				nwi_ifstate_t ifstate;
1257
1258				ifstate = nwi_state_get_ifstate(nwi_state, dst_if_name);
1259				if (ifstate != NULL) {
1260					reach_info->flags |= nwi_ifstate_get_reachability_flags(ifstate);
1261				}
1262			}
1263		}
1264	} else if (ret == EHOSTUNREACH) {
1265		if (if_index == 0) {
1266			int	af;
1267
1268			// if not "scoped" request
1269			af = (address != NULL) ? address->sa_family : AF_UNSPEC;
1270			reach_info->flags |= nwi_state_get_reachability_flags(nwi_state, af);
1271			sc_status = kSCStatusOK;
1272		} else {
1273			// if "scoped" request
1274			sc_status = kSCStatusNoKey;
1275		}
1276	}
1277
1278    done:
1279
1280	if (reach_info->flags == 0) {
1281		SCLog(_sc_debug, LOG_INFO, CFSTR("%s  cannot be reached"), log_prefix);
1282	}
1283
1284	if (nwi_state != NULL) {
1285		nwi_state_release(nwi_state);
1286	}
1287
1288	if ((sc_status != kSCStatusOK) && (sc_status != kSCStatusNoKey)) {
1289		_SCErrorSet(sc_status);
1290		return FALSE;
1291	}
1292
1293	return TRUE;
1294}
1295
1296
1297#pragma mark -
1298#pragma mark SCNetworkReachability APIs
1299
1300
1301static __inline__ CFTypeRef
1302isA_SCNetworkReachability(CFTypeRef obj)
1303{
1304	return (isA_CFType(obj, SCNetworkReachabilityGetTypeID()));
1305}
1306
1307
1308static Boolean
1309addr_to_PTR_name(const struct sockaddr *sa, char *name, size_t name_len)
1310{
1311	int	n;
1312
1313	switch (sa->sa_family) {
1314		case AF_INET : {
1315			union {
1316				in_addr_t	s_addr;
1317				unsigned char	b[4];
1318			} rev;
1319			/* ALIGN: assuming sa is aligned, then cast ok. */
1320			struct sockaddr_in	*sin	= (struct sockaddr_in *)(void *)sa;
1321
1322			/*
1323			 * build "PTR" query name
1324			 *   NNN.NNN.NNN.NNN.in-addr.arpa.
1325			 */
1326			rev.s_addr = sin->sin_addr.s_addr;
1327			n = snprintf(name, name_len, "%u.%u.%u.%u.in-addr.arpa.",
1328				     rev.b[3],
1329				     rev.b[2],
1330				     rev.b[1],
1331				     rev.b[0]);
1332			if ((n == -1) || (n >= name_len)) {
1333				return FALSE;
1334			}
1335
1336			break;
1337		}
1338
1339		case AF_INET6 : {
1340			int			i;
1341			int			s	= 0;
1342			/* ALIGN: assume sa is aligned, cast ok. */
1343			struct sockaddr_in6	*sin6	= (struct sockaddr_in6 *)(void *)sa;
1344			size_t			x	= name_len;
1345
1346			/*
1347			 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
1348			 *   N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.ip6.arpa.
1349			 */
1350			for (i = sizeof(sin6->sin6_addr) - 1; i >= 0; i--) {
1351				n = snprintf(&name[s], x, "%x.%x.",
1352					     ( sin6->sin6_addr.s6_addr[i]       & 0xf),
1353					     ((sin6->sin6_addr.s6_addr[i] >> 4) & 0xf));
1354				if ((n == -1) || (n >= x)) {
1355					return FALSE;
1356				}
1357
1358				s += n;
1359				x -= n;
1360			}
1361
1362			n = snprintf(&name[s], x, "ip6.arpa.");
1363			if ((n == -1) || (n >= x)) {
1364				return FALSE;
1365			}
1366
1367			break;
1368		}
1369
1370		default :
1371			return FALSE;
1372	}
1373
1374	return TRUE;
1375}
1376
1377
1378CFStringRef
1379_SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target)
1380{
1381	CFAllocatorRef			allocator	= CFGetAllocator(target);
1382	CFMutableStringRef		str;
1383	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
1384
1385	str = CFStringCreateMutable(allocator, 0);
1386	switch (targetPrivate->type) {
1387		case reachabilityTypeAddress :
1388		case reachabilityTypeAddressPair : {
1389			char		buf[64];
1390
1391			if (targetPrivate->localAddress != NULL) {
1392				_SC_sockaddr_to_string(targetPrivate->localAddress, buf, sizeof(buf));
1393				CFStringAppendFormat(str, NULL, CFSTR("local address = %s"),
1394						     buf);
1395			}
1396
1397			if (targetPrivate->remoteAddress != NULL) {
1398				_SC_sockaddr_to_string(targetPrivate->remoteAddress, buf, sizeof(buf));
1399				CFStringAppendFormat(str, NULL, CFSTR("%s%saddress = %s"),
1400						     targetPrivate->localAddress ? ", " : "",
1401						     (targetPrivate->type == reachabilityTypeAddressPair) ? "remote " : "",
1402						     buf);
1403			}
1404			break;
1405		}
1406		case reachabilityTypeName : {
1407			CFStringAppendFormat(str, NULL, CFSTR("name = %s"), targetPrivate->name);
1408			break;
1409		}
1410		case reachabilityTypePTR : {
1411			CFStringAppendFormat(str, NULL, CFSTR("ptr = %s"), targetPrivate->name);
1412			break;
1413		}
1414	}
1415
1416	return str;
1417}
1418
1419
1420CFStringRef
1421_SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target)
1422{
1423	CFAllocatorRef			allocator	= CFGetAllocator(target);
1424	CFStringRef			str;
1425	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
1426
1427	str = CFStringCreateWithFormat(allocator,
1428				       NULL,
1429				       CFSTR("flags = 0x%08x, if_index = %u%s"),
1430				       targetPrivate->info.flags,
1431				       targetPrivate->info.if_index,
1432				       targetPrivate->info.sleeping ? ", z" : "");
1433	return str;
1434}
1435
1436
1437static CFStringRef
1438__SCNetworkReachabilityCopyDescription(CFTypeRef cf)
1439{
1440	CFAllocatorRef			allocator	= CFGetAllocator(cf);
1441	CFMutableStringRef		result;
1442	CFStringRef			str;
1443	SCNetworkReachabilityRef	target		= (SCNetworkReachabilityRef)cf;
1444	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
1445
1446	result = CFStringCreateMutable(allocator, 0);
1447	CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkReachability %p [%p]> {"), cf, allocator);
1448
1449	// add target description
1450	str = _SCNetworkReachabilityCopyTargetDescription(target);
1451	CFStringAppend(result, str);
1452	CFRelease(str);
1453
1454	// add additional "name" info
1455	if (isReachabilityTypeName(targetPrivate->type)) {
1456		if (targetPrivate->dnsActive) {
1457			CFStringAppendFormat(result, NULL, CFSTR(" (DNS query active)"));
1458		} else if (targetPrivate->serverActive &&
1459			   (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending)) {
1460			CFStringAppendFormat(result, NULL, CFSTR(" (server query active)"));
1461		} else if ((targetPrivate->resolvedAddresses != NULL) || (targetPrivate->resolvedError != NETDB_SUCCESS)) {
1462			if (targetPrivate->resolvedAddresses != NULL) {
1463				if (isA_CFArray(targetPrivate->resolvedAddresses)) {
1464					CFIndex	i;
1465					CFIndex	n	= CFArrayGetCount(targetPrivate->resolvedAddresses);
1466
1467					CFStringAppendFormat(result, NULL, CFSTR(" ("));
1468					for (i = 0; i < n; i++) {
1469						CFDataRef	address;
1470
1471						CFStringAppendFormat(result, NULL, CFSTR("%s"),
1472								     i > 0 ? ", " : "");
1473
1474						address	= CFArrayGetValueAtIndex(targetPrivate->resolvedAddresses, i);
1475						if (isA_CFData(address)) {
1476							char		buf[64];
1477							struct sockaddr	*sa;
1478
1479							sa      = (struct sockaddr *)CFDataGetBytePtr(address);
1480							_SC_sockaddr_to_string(sa, buf, sizeof(buf));
1481							CFStringAppendFormat(result, NULL, CFSTR("%s"), buf);
1482						} else {
1483							CFStringAppendFormat(result, NULL, CFSTR("%@"), address);
1484						}
1485					}
1486					CFStringAppendFormat(result, NULL, CFSTR(")"));
1487				} else if (CFEqual(targetPrivate->resolvedAddresses, kCFNull)) {
1488					CFStringAppendFormat(result, NULL, CFSTR(" (%s)"),
1489							     gai_strerror(targetPrivate->resolvedError));
1490				} else {
1491					CFStringAppendFormat(result, NULL, CFSTR(" (no addresses)"));
1492				}
1493			} else {
1494				CFStringAppendFormat(result, NULL, CFSTR(" (%s)"),
1495						     gai_strerror(targetPrivate->resolvedError));
1496			}
1497		}
1498		if (targetPrivate->dnsFlags != 0) {
1499			CFStringAppendFormat(result, NULL, CFSTR(", " DNS_FLAGS_FORMAT),
1500					     DNS_FLAGS_VALUES(targetPrivate));
1501		}
1502	}
1503
1504	if (targetPrivate->onDemandBypass) {
1505		CFStringAppendFormat(result, NULL, CFSTR(", !ondemand"));
1506	}
1507
1508
1509	if (targetPrivate->resolverBypass) {
1510		CFStringAppendFormat(result, NULL, CFSTR(", !resolve"));
1511	}
1512
1513
1514	// add flags
1515	if (targetPrivate->scheduled) {
1516		str = _SCNetworkReachabilityCopyTargetFlags(target);
1517		CFStringAppendFormat(result, NULL, CFSTR(", %@"), str);
1518		CFRelease(str);
1519	}
1520
1521	CFStringAppendFormat(result, NULL, CFSTR("}"));
1522
1523	return result;
1524}
1525
1526
1527static void
1528__SCNetworkReachabilityDeallocate(CFTypeRef cf)
1529{
1530	SCNetworkReachabilityRef	target		= (SCNetworkReachabilityRef)cf;
1531	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
1532
1533	SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%srelease"),
1534	      targetPrivate->log_prefix);
1535
1536	/* disconnect from the reachability server */
1537
1538	if (targetPrivate->serverActive) {
1539		__SCNetworkReachabilityServer_targetRemove(target);
1540	}
1541
1542	/* release resources */
1543
1544	pthread_mutex_destroy(&targetPrivate->lock);
1545
1546	if (targetPrivate->name != NULL)
1547		CFAllocatorDeallocate(NULL, (void *)targetPrivate->name);
1548
1549	if (targetPrivate->resolvedAddresses != NULL)
1550		CFRelease(targetPrivate->resolvedAddresses);
1551
1552	if (targetPrivate->localAddress != NULL) {
1553		if (targetPrivate->localAddress == targetPrivate->remoteAddress) {
1554			targetPrivate->remoteAddress = NULL;
1555		}
1556		CFAllocatorDeallocate(NULL, (void *)targetPrivate->localAddress);
1557	}
1558
1559	if (targetPrivate->remoteAddress != NULL)
1560		CFAllocatorDeallocate(NULL, (void *)targetPrivate->remoteAddress);
1561
1562	if (targetPrivate->rlsContext.release != NULL) {
1563		(*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
1564	}
1565
1566	if (targetPrivate->onDemandName != NULL) {
1567		CFRelease(targetPrivate->onDemandName);
1568	}
1569
1570	if (targetPrivate->onDemandRemoteAddress != NULL) {
1571		CFRelease(targetPrivate->onDemandRemoteAddress);
1572	}
1573
1574	if (targetPrivate->onDemandServer != NULL) {
1575		CFRelease(targetPrivate->onDemandServer);
1576	}
1577
1578	if (targetPrivate->onDemandServiceID != NULL) {
1579		CFRelease(targetPrivate->onDemandServiceID);
1580	}
1581
1582	if (targetPrivate->serverDigest != NULL) {
1583		CFRelease(targetPrivate->serverDigest);
1584	}
1585
1586	if (targetPrivate->serverGroup != NULL) {
1587		dispatch_release(targetPrivate->serverGroup);
1588	}
1589
1590	if (targetPrivate->serverQueue != NULL) {
1591		dispatch_release(targetPrivate->serverQueue);
1592	}
1593
1594	if (targetPrivate->serverWatchers != NULL) {
1595		CFRelease(targetPrivate->serverWatchers);
1596	}
1597
1598	if (targetPrivate->nePolicyResult) {
1599		free(targetPrivate->nePolicyResult);
1600	}
1601
1602	return;
1603}
1604
1605
1606static void
1607__SCNetworkReachabilityInitialize(void)
1608{
1609	__kSCNetworkReachabilityTypeID = _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass);
1610
1611	// provide a way to enable SCNetworkReachability logging without
1612	// having to set _sc_debug=1.
1613	if ((getenv("REACH_LOGGING") != NULL) ||
1614	    (CFPreferencesGetAppBooleanValue(CFSTR("com.apple.SCNetworkReachability.debug"),
1615					     kCFPreferencesCurrentApplication,
1616					     NULL))) {
1617		_sc_debug = TRUE;
1618	}
1619
1620	// set per-process "bypass" of the SCNetworkReachability server
1621	if (getenv("REACH_SERVER_BYPASS") != NULL) {
1622		D_serverBypass = TRUE;
1623	}
1624
1625
1626	pthread_mutexattr_init(&lock_attr);
1627	pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_ERRORCHECK);
1628
1629	return;
1630}
1631
1632
1633__private_extern__
1634dispatch_queue_t
1635__SCNetworkReachability_concurrent_queue()
1636{
1637	static dispatch_once_t	once;
1638	static dispatch_queue_t	q;
1639
1640	dispatch_once(&once, ^{
1641		q = dispatch_queue_create("SCNetworkReachability.concurrent",
1642					  DISPATCH_QUEUE_CONCURRENT);
1643	});
1644
1645	return q;
1646}
1647
1648
1649/*
1650 * __SCNetworkReachabilityUpdateConcurrent
1651 *
1652 * Calls reachUpdate()
1653 * - caller must be holding a reference to the target
1654 * - caller must *not* be holding the target lock
1655 * - caller must be running on the __SCNetworkReachability_concurrent_queue()
1656 */
1657__private_extern__
1658void
1659__SCNetworkReachabilityUpdateConcurrent(SCNetworkReachabilityRef target)
1660{
1661	Boolean				changed;
1662	unsigned int			n;
1663	dispatch_queue_t		queue;
1664	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
1665
1666	changed = reachUpdate((void *)target);
1667	if (!changed) {
1668		return;
1669	}
1670
1671	n = _SC_ATOMIC_INC(&targetPrivate->pending);
1672	if (n > 0) {
1673		// if we already have a notification pending
1674		return;
1675	}
1676
1677	MUTEX_LOCK(&targetPrivate->lock);
1678
1679	queue = targetPrivate->dispatchQueue;
1680	if (queue != NULL) {
1681		dispatch_group_t	group;
1682
1683		dispatch_retain(queue);
1684
1685		group = targetPrivate->dispatchGroup;
1686		dispatch_group_enter(group);
1687
1688		MUTEX_UNLOCK(&targetPrivate->lock);
1689
1690		dispatch_sync(queue, ^{
1691			reachPerform((void *)target);
1692			dispatch_group_leave(group);
1693		});
1694
1695		dispatch_release(queue);
1696	} else {
1697		if (targetPrivate->rls != NULL) {
1698			CFRunLoopSourceSignal(targetPrivate->rls);
1699			_SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
1700		}
1701
1702		MUTEX_UNLOCK(&targetPrivate->lock);
1703	}
1704
1705	return;
1706}
1707
1708
1709/*
1710 * __SCNetworkReachabilityUpdate
1711 *
1712 * Calls reachUpdate() [indirectly]
1713 * - caller can be holding the target lock
1714 * - caller can be running on any dispatch queue
1715 */
1716__private_extern__
1717void
1718__SCNetworkReachabilityUpdate(SCNetworkReachabilityRef target)
1719{
1720	CFRetain(target);
1721	dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
1722		__SCNetworkReachabilityUpdateConcurrent(target);
1723		CFRelease(target);
1724	});
1725
1726	return;
1727}
1728
1729
1730static SCNetworkReachabilityPrivateRef
1731__SCNetworkReachabilityCreatePrivate(CFAllocatorRef	allocator)
1732{
1733	SCNetworkReachabilityPrivateRef		targetPrivate;
1734	uint32_t				size;
1735
1736	/* initialize runtime */
1737	pthread_once(&initialized, __SCNetworkReachabilityInitialize);
1738
1739	/* allocate target */
1740	size          = sizeof(SCNetworkReachabilityPrivate) - sizeof(CFRuntimeBase);
1741	targetPrivate = (SCNetworkReachabilityPrivateRef)_CFRuntimeCreateInstance(allocator,
1742										  __kSCNetworkReachabilityTypeID,
1743										  size,
1744										  NULL);
1745	if (targetPrivate == NULL) {
1746		return NULL;
1747	}
1748
1749	bzero((void *)targetPrivate + sizeof(CFRuntimeBase), size);
1750
1751	MUTEX_INIT(&targetPrivate->lock);
1752
1753	targetPrivate->cycle				= 1;
1754	targetPrivate->last_notify			= NOT_REPORTED;
1755	targetPrivate->serverBypass			= D_serverBypass;
1756
1757
1758
1759	targetPrivate->log_prefix[0] = '\0';
1760	if (_sc_log > 0) {
1761		snprintf(targetPrivate->log_prefix,
1762			 sizeof(targetPrivate->log_prefix),
1763			 "[%p] ",
1764			 targetPrivate);
1765	}
1766
1767	return targetPrivate;
1768}
1769
1770
1771
1772
1773static const struct sockaddr *
1774is_valid_address(const struct sockaddr *address)
1775{
1776	const struct sockaddr	*valid	= NULL;
1777	static Boolean	warned	= FALSE;
1778
1779	if ((address != NULL) &&
1780	    (address->sa_len <= sizeof(struct sockaddr_storage))) {
1781		switch (address->sa_family) {
1782			case AF_INET :
1783				if (address->sa_len >= sizeof(struct sockaddr_in)) {
1784					valid = address;
1785				} else {
1786					if (!warned) {
1787						SCLog(TRUE, LOG_ERR,
1788						      CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu"),
1789						      address->sa_len,
1790						      sizeof(struct sockaddr_in));
1791						warned = TRUE;
1792					}
1793				}
1794				break;
1795			case AF_INET6 :
1796				if (address->sa_len >= sizeof(struct sockaddr_in6)) {
1797					valid = address;
1798				} else if (!warned) {
1799					SCLog(TRUE, LOG_ERR,
1800					      CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu"),
1801					      address->sa_len,
1802					      sizeof(struct sockaddr_in6));
1803					warned = TRUE;
1804				}
1805				break;
1806			default :
1807				if (!warned) {
1808					SCLog(TRUE, LOG_ERR,
1809					      CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"),
1810					      address->sa_family);
1811					warned = TRUE;
1812				}
1813		}
1814	}
1815
1816	return valid;
1817}
1818
1819
1820
1821
1822
1823
1824SCNetworkReachabilityRef
1825SCNetworkReachabilityCreateWithAddress(CFAllocatorRef		allocator,
1826				       const struct sockaddr	*address)
1827{
1828	SCNetworkReachabilityPrivateRef	targetPrivate;
1829
1830	address = is_valid_address(address);
1831	if (address == NULL) {
1832		_SCErrorSet(kSCStatusInvalidArgument);
1833		return NULL;
1834	}
1835
1836	targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
1837	if (targetPrivate == NULL) {
1838		return NULL;
1839	}
1840
1841	targetPrivate->type = reachabilityTypeAddress;
1842	targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, address->sa_len, 0);
1843	bcopy(address, targetPrivate->remoteAddress, address->sa_len);
1844
1845
1846
1847	SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
1848	      targetPrivate->log_prefix,
1849	      DEBUG_REACHABILITY_TYPE_ADDRESS,
1850	      targetPrivate);
1851
1852	return (SCNetworkReachabilityRef)targetPrivate;
1853}
1854
1855
1856static Boolean
1857is_same_address(const struct sockaddr *a, const struct sockaddr *b)
1858{
1859	const void	*a_addr;
1860	const void	*b_addr;
1861	size_t		len;
1862
1863	if ((a == NULL) ||
1864	    (b == NULL) ||
1865	    (a->sa_family != b->sa_family) ||
1866	    (a->sa_len    != b->sa_len   )) {
1867		return FALSE;
1868	}
1869
1870	switch (a->sa_family) {
1871		case AF_INET : {
1872			struct sockaddr_in	*a_sin	= (struct sockaddr_in *)(void *)a;
1873			struct sockaddr_in	*b_sin	= (struct sockaddr_in *)(void *)b;
1874
1875			/* ALIGN: assuming a (and b) are aligned, then cast ok. */
1876			a_addr = &a_sin->sin_addr;
1877			b_addr = &b_sin->sin_addr;
1878			len = sizeof(struct in_addr);
1879			break;
1880		}
1881
1882		case AF_INET6 : {
1883			struct sockaddr_in6	*a_sin6	= (struct sockaddr_in6 *)(void *)a;
1884			struct sockaddr_in6	*b_sin6	= (struct sockaddr_in6 *)(void *)b;
1885
1886			if (a_sin6->sin6_scope_id != b_sin6->sin6_scope_id) {
1887				return FALSE;
1888			}
1889
1890			a_addr = &a_sin6->sin6_addr;
1891			b_addr = &b_sin6->sin6_addr;
1892			len = sizeof(struct in6_addr);
1893			break;
1894		}
1895
1896		default :
1897			a_addr = a;
1898			b_addr = b;
1899			len = a->sa_len;
1900			break;
1901	}
1902
1903	return (bcmp(a_addr, b_addr, len) == 0);
1904}
1905
1906
1907SCNetworkReachabilityRef
1908SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef		allocator,
1909					   const struct sockaddr	*localAddress,
1910					   const struct sockaddr	*remoteAddress)
1911{
1912	SCNetworkReachabilityPrivateRef	targetPrivate;
1913
1914	if ((localAddress == NULL) && (remoteAddress == NULL)) {
1915		_SCErrorSet(kSCStatusInvalidArgument);
1916		return NULL;
1917	}
1918
1919	if (localAddress != NULL) {
1920		localAddress = is_valid_address(localAddress);
1921		if (localAddress == NULL) {
1922			_SCErrorSet(kSCStatusInvalidArgument);
1923			return NULL;
1924		}
1925	}
1926
1927	if (remoteAddress != NULL) {
1928		remoteAddress = is_valid_address(remoteAddress);
1929		if (remoteAddress == NULL) {
1930			_SCErrorSet(kSCStatusInvalidArgument);
1931			return NULL;
1932		}
1933	}
1934
1935	targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
1936	if (targetPrivate == NULL) {
1937		return NULL;
1938	}
1939
1940	targetPrivate->type = reachabilityTypeAddressPair;
1941
1942	if (localAddress != NULL) {
1943		targetPrivate->localAddress = CFAllocatorAllocate(NULL, localAddress->sa_len, 0);
1944		bcopy(localAddress, targetPrivate->localAddress, localAddress->sa_len);
1945	}
1946
1947	if (remoteAddress != NULL) {
1948		if (is_same_address(localAddress, remoteAddress)) {
1949			targetPrivate->remoteAddress = targetPrivate->localAddress;
1950		} else {
1951			targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, remoteAddress->sa_len, 0);
1952			bcopy(remoteAddress, targetPrivate->remoteAddress, remoteAddress->sa_len);
1953		}
1954	}
1955
1956
1957
1958	SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
1959	      targetPrivate->log_prefix,
1960	      DEBUG_REACHABILITY_TYPE_ADDRESSPAIR,
1961	      targetPrivate);
1962
1963	return (SCNetworkReachabilityRef)targetPrivate;
1964}
1965
1966
1967SCNetworkReachabilityRef
1968SCNetworkReachabilityCreateWithName(CFAllocatorRef	allocator,
1969				    const char		*nodename)
1970{
1971	union {
1972		struct sockaddr		sa;
1973		struct sockaddr_in	sin;
1974		struct sockaddr_in6	sin6;
1975	} addr;
1976	size_t				nodenameLen;
1977	SCNetworkReachabilityPrivateRef	targetPrivate;
1978
1979	if (nodename == NULL) {
1980		_SCErrorSet(kSCStatusInvalidArgument);
1981		return NULL;
1982	}
1983
1984	nodenameLen = strlen(nodename);
1985	if (nodenameLen == 0) {
1986		_SCErrorSet(kSCStatusInvalidArgument);
1987		return NULL;
1988	}
1989
1990	if (nodename[nodenameLen - 1] == '.') {
1991		int		dots;
1992		size_t		i;
1993
1994		// trim trailing "."s
1995		do {
1996			--nodenameLen;
1997		} while ((nodenameLen > 0) && (nodename[nodenameLen - 1] == '.'));
1998
1999		if (nodenameLen == 0) {
2000			// if only trailing "."s
2001			_SCErrorSet(kSCStatusInvalidArgument);
2002			return NULL;
2003		}
2004
2005		// count the remaining "."s
2006		dots = 0;
2007		for (i = 0; i < nodenameLen; i++) {
2008			if (nodename[i] == '.') dots++;
2009		}
2010
2011		if (dots == 0) {
2012			// if only a single-label, add back the FQDN "."
2013			nodenameLen++;
2014		}
2015	}
2016
2017	if (_SC_string_to_sockaddr(nodename, AF_UNSPEC, (void *)&addr, sizeof(addr)) != NULL) {
2018		/* if this "nodename" is really an IP[v6] address in disguise */
2019		return SCNetworkReachabilityCreateWithAddress(allocator, &addr.sa);
2020	}
2021
2022	targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
2023	if (targetPrivate == NULL) {
2024		return NULL;
2025	}
2026
2027	targetPrivate->type = reachabilityTypeName;
2028
2029	targetPrivate->name = CFAllocatorAllocate(NULL, nodenameLen + 1, 0);
2030	strlcpy((char *)targetPrivate->name, nodename, nodenameLen + 1);
2031
2032	targetPrivate->needResolve = TRUE;
2033	targetPrivate->info.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
2034	targetPrivate->serverInfo.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
2035
2036	{
2037		/* make sure AppLayerVPN only is in client mode */
2038		CFDictionaryRef	appLayerVPNProperties;
2039
2040		appLayerVPNProperties = VPNAppLayerCopyCurrentAppProperties();
2041		if (appLayerVPNProperties != NULL) {
2042			targetPrivate->serverBypassForVPN = TRUE;
2043			targetPrivate->serverBypass = YES;
2044			CFRelease(appLayerVPNProperties);
2045		}
2046	}
2047
2048	SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
2049	      targetPrivate->log_prefix,
2050	      DEBUG_REACHABILITY_TYPE_NAME,
2051	      targetPrivate);
2052
2053	return (SCNetworkReachabilityRef)targetPrivate;
2054}
2055
2056
2057static SCNetworkReachabilityRef
2058__SCNetworkReachabilityCreateWithPtr(CFAllocatorRef		allocator,
2059				     const char			*ptrName,
2060				     const struct sockaddr	*ptrAddress)
2061{
2062	SCNetworkReachabilityRef	target;
2063	SCNetworkReachabilityPrivateRef	targetPrivate;
2064
2065	target = SCNetworkReachabilityCreateWithName(NULL, ptrName);
2066	if (target == NULL) {
2067		return NULL;
2068	}
2069
2070	targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2071
2072	// change type
2073	targetPrivate->type = reachabilityTypePTR;
2074
2075	// and keep the address
2076	targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, ptrAddress->sa_len, 0);
2077	bcopy(ptrAddress, targetPrivate->remoteAddress, ptrAddress->sa_len);
2078
2079	return target;
2080}
2081
2082
2083
2084
2085SCNetworkReachabilityRef
2086SCNetworkReachabilityCreateWithOptions(CFAllocatorRef	allocator,
2087				       CFDictionaryRef	options)
2088{
2089	const struct sockaddr		*addr_l		= NULL;
2090	const struct sockaddr		*addr_p		= NULL;
2091	const struct sockaddr		*addr_r		= NULL;
2092	CFDataRef			data;
2093	CFStringRef			interface	= NULL;
2094	CFStringRef			nodename;
2095	CFBooleanRef			onDemandBypass;
2096	CFBooleanRef			resolverBypass;
2097	CFBooleanRef			serverBypass;
2098	SCNetworkReachabilityRef	target;
2099	SCNetworkReachabilityPrivateRef	targetPrivate;
2100
2101	if (!isA_CFDictionary(options)) {
2102		_SCErrorSet(kSCStatusInvalidArgument);
2103		return NULL;
2104	}
2105
2106	nodename = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionNodeName);
2107	if ((nodename != NULL) &&
2108	    (!isA_CFString(nodename) || (CFStringGetLength(nodename) == 0))) {
2109		_SCErrorSet(kSCStatusInvalidArgument);
2110		return NULL;
2111	}
2112	data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionLocalAddress);
2113	if (data != NULL) {
2114		if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
2115			_SCErrorSet(kSCStatusInvalidArgument);
2116			return NULL;
2117		}
2118		addr_l = (const struct sockaddr *)CFDataGetBytePtr(data);
2119	}
2120	data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionPTRAddress);
2121	if (data != NULL) {
2122		if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
2123			_SCErrorSet(kSCStatusInvalidArgument);
2124			return NULL;
2125		}
2126		addr_p = (const struct sockaddr *)CFDataGetBytePtr(data);
2127	}
2128	data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionRemoteAddress);
2129	if (data != NULL) {
2130		if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
2131			_SCErrorSet(kSCStatusInvalidArgument);
2132			return NULL;
2133		}
2134		addr_r = (const struct sockaddr *)CFDataGetBytePtr(data);
2135	}
2136	interface = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionInterface);
2137	if ((interface != NULL) &&
2138	    (!isA_CFString(interface) || (CFStringGetLength(interface) == 0))) {
2139		_SCErrorSet(kSCStatusInvalidArgument);
2140		return NULL;
2141	}
2142	onDemandBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandBypass);
2143	if ((onDemandBypass != NULL) && !isA_CFBoolean(onDemandBypass)) {
2144		_SCErrorSet(kSCStatusInvalidArgument);
2145		return NULL;
2146	}
2147	resolverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionResolverBypass);
2148	if ((resolverBypass != NULL) && !isA_CFBoolean(resolverBypass)) {
2149		_SCErrorSet(kSCStatusInvalidArgument);
2150		return NULL;
2151	}
2152
2153
2154	serverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionServerBypass);
2155	if ((serverBypass != NULL) && !isA_CFBoolean(serverBypass)) {
2156		_SCErrorSet(kSCStatusInvalidArgument);
2157		return NULL;
2158	}
2159
2160
2161	if (nodename != NULL) {
2162		const char	*name;
2163
2164		if ((addr_l != NULL) || (addr_r != NULL) || (addr_p != NULL)) {
2165			// can't have both a nodename and an address
2166			_SCErrorSet(kSCStatusInvalidArgument);
2167			return NULL;
2168		}
2169
2170		name = _SC_cfstring_to_cstring(nodename, NULL, 0, kCFStringEncodingUTF8);
2171		target = SCNetworkReachabilityCreateWithName(allocator, name);
2172		CFAllocatorDeallocate(NULL, (void *)name);
2173	} else if (addr_p != NULL) {
2174		char	name[MAXHOSTNAMELEN];
2175
2176		if ((addr_l != NULL) ||					// can't have PTR and target address
2177		    (addr_r != NULL) ||					// can't have PTR and target address
2178		    !addr_to_PTR_name(addr_p, name, sizeof(name))) {	// can't convert PTR
2179			_SCErrorSet(kSCStatusInvalidArgument);
2180			return NULL;
2181		}
2182
2183		target = __SCNetworkReachabilityCreateWithPtr(NULL, name, addr_p);
2184	} else {
2185		if ((addr_l != NULL) && (addr_r != NULL)) {
2186			target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, addr_r);
2187		} else if (addr_r != NULL) {
2188			target = SCNetworkReachabilityCreateWithAddress(NULL, addr_r);
2189		} else if (addr_l != NULL) {
2190			target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, NULL);
2191		} else {
2192			_SCErrorSet(kSCStatusInvalidArgument);
2193			return NULL;
2194		}
2195	}
2196	if (target == NULL) {
2197		return NULL;
2198	}
2199
2200	targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2201
2202	if (interface != NULL) {
2203		if ((_SC_cfstring_to_cstring(interface,
2204					     targetPrivate->if_name,
2205					     sizeof(targetPrivate->if_name),
2206					     kCFStringEncodingASCII) == NULL) ||
2207		    ((targetPrivate->if_index = if_nametoindex(targetPrivate->if_name)) == 0)) {
2208			CFRelease(targetPrivate);
2209			_SCErrorSet(kSCStatusInvalidArgument);
2210			return NULL;
2211		}
2212	}
2213
2214
2215	if (onDemandBypass != NULL) {
2216		targetPrivate->onDemandBypass = CFBooleanGetValue(onDemandBypass);
2217	}
2218
2219	if (resolverBypass != NULL) {
2220		targetPrivate->resolverBypass = CFBooleanGetValue(resolverBypass);
2221	}
2222
2223	/* if by name, make sure client-only VPN types stay in client mode */
2224	if (serverBypass != NULL && targetPrivate->serverBypassForVPN == FALSE) {
2225		targetPrivate->serverBypass = CFBooleanGetValue(serverBypass);
2226	}
2227
2228
2229	if (_sc_debug && (_sc_log > 0)) {
2230		const char	*opt	= "???";
2231
2232		switch (targetPrivate->type) {
2233			case reachabilityTypeAddress :
2234				opt = DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS;
2235				break;
2236			case reachabilityTypeAddressPair :
2237				opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS;
2238				break;
2239			case reachabilityTypeName :
2240				opt = DEBUG_REACHABILITY_TYPE_NAME_OPTIONS;
2241				break;
2242			case reachabilityTypePTR :
2243				opt = DEBUG_REACHABILITY_TYPE_PTR_OPTIONS;
2244				break;
2245		}
2246
2247		SCLog(TRUE, LOG_INFO, CFSTR("%s%s %@"),
2248		      targetPrivate->log_prefix,
2249		      opt,
2250		      targetPrivate);
2251	}
2252
2253	return (SCNetworkReachabilityRef)targetPrivate;
2254}
2255
2256
2257static SCNetworkReachabilityRef
2258__SCNetworkReachabilityCreateCopy(SCNetworkReachabilityRef target)
2259{
2260	SCNetworkReachabilityRef	clone		= NULL;
2261	SCNetworkReachabilityPrivateRef	clonePrivate;
2262	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
2263
2264	switch (targetPrivate->type) {
2265		case reachabilityTypeAddress :
2266			clone = SCNetworkReachabilityCreateWithAddress(NULL,
2267								       targetPrivate->remoteAddress);
2268			break;
2269		case reachabilityTypeAddressPair :
2270			clone = SCNetworkReachabilityCreateWithAddressPair(NULL,
2271									   targetPrivate->localAddress,
2272									   targetPrivate->remoteAddress);
2273			break;
2274		case reachabilityTypeName :
2275			clone = SCNetworkReachabilityCreateWithName(NULL,
2276								    targetPrivate->name);
2277			break;
2278		case reachabilityTypePTR :
2279			clone = __SCNetworkReachabilityCreateWithPtr(NULL,
2280								     targetPrivate->name,
2281								     targetPrivate->remoteAddress);
2282			break;
2283	}
2284	if (clone == NULL) {
2285		return NULL;
2286	}
2287
2288	clonePrivate = (SCNetworkReachabilityPrivateRef)clone;
2289
2290	clonePrivate->quiet = TRUE;
2291
2292	clonePrivate->if_index = targetPrivate->if_index;
2293	bcopy(targetPrivate->if_name, clonePrivate->if_name, sizeof(clonePrivate->if_name));
2294
2295	clonePrivate->onDemandBypass = targetPrivate->onDemandBypass;
2296
2297
2298	clonePrivate->serverBypass = targetPrivate->serverBypass;
2299
2300	clonePrivate->resolverBypass = targetPrivate->resolverBypass;
2301
2302
2303	if (_sc_debug && (_sc_log > 0)) {
2304		const char	*opt	= "???";
2305
2306		switch (clonePrivate->type) {
2307			case reachabilityTypeAddress :
2308				opt = DEBUG_REACHABILITY_TYPE_ADDRESS_CLONE;
2309				break;
2310			case reachabilityTypeAddressPair :
2311				opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_CLONE;
2312				break;
2313			case reachabilityTypeName :
2314				opt = DEBUG_REACHABILITY_TYPE_NAME_CLONE;
2315				break;
2316			case reachabilityTypePTR :
2317				opt = DEBUG_REACHABILITY_TYPE_PTR_CLONE;
2318				break;
2319		}
2320
2321		SCLog(TRUE, LOG_INFO, CFSTR("%s%s %p %@"),
2322		      clonePrivate->log_prefix,
2323		      opt,
2324		      targetPrivate,
2325		      clone);
2326	}
2327
2328	return clone;
2329}
2330
2331
2332CFTypeID
2333SCNetworkReachabilityGetTypeID(void)
2334{
2335	pthread_once(&initialized, __SCNetworkReachabilityInitialize);	/* initialize runtime */
2336	return __kSCNetworkReachabilityTypeID;
2337}
2338
2339
2340CFArrayRef	/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
2341SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef	target,
2342					 int				*error_num)
2343{
2344	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
2345
2346	if (!isA_SCNetworkReachability(target)) {
2347		_SCErrorSet(kSCStatusInvalidArgument);
2348	       return NULL;
2349	}
2350
2351	if (!isReachabilityTypeName(targetPrivate->type)) {
2352		_SCErrorSet(kSCStatusInvalidArgument);
2353	       return NULL;
2354	}
2355
2356	if (error_num) {
2357		*error_num = targetPrivate->resolvedError;
2358	}
2359
2360	if (targetPrivate->resolvedAddresses != NULL) {
2361		if (isA_CFArray(targetPrivate->resolvedAddresses)) {
2362			return CFRetain(targetPrivate->resolvedAddresses);
2363		} else {
2364			/* if status is known but no resolved addresses to return */
2365			_SCErrorSet(kSCStatusOK);
2366			return NULL;
2367		}
2368	}
2369
2370	_SCErrorSet(kSCStatusReachabilityUnknown);
2371	return NULL;
2372}
2373
2374
2375static void
2376__SCNetworkReachabilitySetResolvedError(SCNetworkReachabilityRef	target,
2377					int32_t				status)
2378{
2379	SCNetworkReachabilityPrivateRef		targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
2380
2381	MUTEX_ASSERT_HELD(&targetPrivate->lock);
2382
2383	__mark_operation_end(target,
2384			     FALSE,				// if successful query
2385			     dns_query_async,			// async
2386			     &targetPrivate->dnsQueryStart,	// start time
2387			     &targetPrivate->dnsQueryEnd);	// end time
2388
2389	if (targetPrivate->resolvedAddresses != NULL) {
2390		CFRelease(targetPrivate->resolvedAddresses);
2391		targetPrivate->resolvedAddresses = NULL;
2392	}
2393
2394	SCLog(_sc_debug, LOG_INFO, CFSTR("%scould not be resolved: %s"),
2395	      targetPrivate->log_prefix,
2396	      gai_strerror(status));
2397
2398	/* save the error associated with the attempt to resolve the name */
2399	targetPrivate->resolvedAddresses = CFRetain(kCFNull);
2400	targetPrivate->resolvedError     = status;
2401	targetPrivate->needResolve       = FALSE;
2402
2403	return;
2404}
2405
2406
2407/*
2408 * rankReachability()
2409 *   Not reachable       == 0
2410 *   Connection Required == 1
2411 *   Reachable           == 2
2412 */
2413static int
2414rankReachability(SCNetworkReachabilityFlags flags)
2415{
2416	int	rank = 0;
2417
2418	if (flags & kSCNetworkReachabilityFlagsReachable)		rank = 2;
2419	if (flags & kSCNetworkReachabilityFlagsConnectionRequired)	rank = 1;
2420	return rank;
2421}
2422
2423
2424#pragma mark -
2425#pragma mark DNS name resolution
2426
2427
2428static void
2429update_resolver_reachability(ReachabilityStoreInfoRef	store_info,
2430			     dns_resolver_t		*resolver,
2431			     SCNetworkReachabilityFlags	*flags,
2432			     Boolean			*haveDNS,
2433			     uint32_t			*resolver_if_index,
2434			     const char			*log_prefix)
2435{
2436	if (resolver_if_index) *resolver_if_index = 0;
2437
2438	if (resolver->n_nameserver > 0) {
2439		*flags   = (SCNetworkReachabilityFlags)resolver->reach_flags;
2440		if (resolver_if_index != NULL) {
2441			*resolver_if_index = resolver->if_index;
2442		}
2443		*haveDNS = TRUE;
2444	} else {
2445		*flags   = kSCNetworkReachabilityFlagsReachable;
2446		*haveDNS = FALSE;
2447	}
2448
2449	return;
2450}
2451
2452
2453static Boolean
2454check_matching_resolvers(ReachabilityStoreInfoRef	store_info,
2455			 dns_config_t			*dns_config,
2456			 const char			*fqdn,
2457			 unsigned int			if_index,
2458			 SCNetworkReachabilityFlags	*flags,
2459			 Boolean			*haveDNS,
2460			 uint32_t			*resolver_if_index,
2461			 int				*dns_config_index,
2462			 const char			*log_prefix)
2463{
2464	int		i;
2465	Boolean		matched		= FALSE;
2466	const char	*name		= fqdn;
2467	int32_t		n_resolvers;
2468	dns_resolver_t	**resolvers;
2469
2470	if (if_index == 0) {
2471		n_resolvers = dns_config->n_resolver;
2472		resolvers   = dns_config->resolver;
2473	} else {
2474		n_resolvers = dns_config->n_scoped_resolver;
2475		resolvers   = dns_config->scoped_resolver;
2476	}
2477
2478	/* In case we couldn't find a match, setting an index of -1
2479	   and resolver_if_index 0 */
2480	if (dns_config_index != NULL) *dns_config_index = -1;
2481	if (resolver_if_index != NULL) *resolver_if_index = 0;
2482
2483	while (!matched && (name != NULL)) {
2484		size_t	len;
2485
2486		/*
2487		 * check if the provided name (or sub-component)
2488		 * matches one of our resolver configurations.
2489		 */
2490		len = strlen(name);
2491		for (i = 0; i < n_resolvers; i++) {
2492			char		*domain;
2493			dns_resolver_t	*resolver;
2494
2495			resolver = resolvers[i];
2496			if ((if_index != 0) && (if_index != resolver->if_index)) {
2497				continue;
2498			}
2499
2500			domain   = resolver->domain;
2501			if (domain != NULL && (len == strlen(domain))) {
2502				if (strcasecmp(name, domain) == 0) {
2503					/*
2504					 * if name matches domain
2505					 */
2506					matched = TRUE;
2507					update_resolver_reachability(store_info,
2508								     resolver,
2509								     flags,
2510								     haveDNS,
2511								     resolver_if_index,
2512								     log_prefix);
2513					if (dns_config_index != NULL) *dns_config_index = i;
2514					break;
2515				}
2516			}
2517		}
2518
2519		if (!matched) {
2520			/*
2521			 * we have not found a matching resolver, try
2522			 * a less qualified domain
2523			 */
2524			name = strchr(name, '.');
2525			if ((name != NULL) && (*name != '\0')) {
2526				name++;
2527			} else {
2528				name = NULL;
2529			}
2530		}
2531	}
2532
2533	return matched;
2534}
2535
2536
2537static dns_resolver_t *
2538get_default_resolver(dns_config_t *dns_config, unsigned int if_index)
2539{
2540	int		i;
2541	int32_t		n_resolvers;
2542	dns_resolver_t	*resolver	= NULL;
2543	dns_resolver_t	**resolvers;
2544
2545	if (if_index == 0) {
2546		n_resolvers = dns_config->n_resolver;
2547		resolvers   = dns_config->resolver;
2548	} else {
2549		n_resolvers = dns_config->n_scoped_resolver;
2550		resolvers   = dns_config->scoped_resolver;
2551	}
2552
2553	for (i = 0; i < n_resolvers; i++) {
2554		if ((if_index != 0) && (if_index != resolvers[i]->if_index)) {
2555			continue;
2556		}
2557
2558		if (((if_index == 0) && (i == 0)) ||
2559		    ((if_index != 0) && (resolver == NULL))) {
2560			// if this is the first (aka default) resolver
2561			resolver = resolvers[i];
2562		} else if ((resolvers[i]->domain == NULL) &&
2563			   (resolvers[i]->search_order < resolver->search_order)) {
2564			// if this is a default resolver with a lower search order
2565			resolver = resolvers[i];
2566		}
2567	}
2568
2569	return resolver;
2570}
2571
2572
2573static dns_configuration_t *
2574dns_configuration_retain()
2575{
2576	dns_configuration_t	*config;
2577
2578	pthread_mutex_lock(&dns_lock);
2579
2580	if (dns_configuration != NULL) {
2581		Boolean		refresh	= TRUE;
2582
2583		if (dns_token_valid) {
2584			int		check	= 0;
2585			uint32_t	status;
2586
2587			/*
2588			 * check if the global [DNS] configuration snapshot needs
2589			 * to be updated
2590			 */
2591			status = notify_check(dns_token, &check);
2592			if (status != NOTIFY_STATUS_OK) {
2593				SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%u"), status);
2594			} else if (check == 0) {
2595				// if the snapshot does not need to be refreshed
2596				refresh = FALSE;
2597			}
2598		}
2599
2600		if (refresh) {
2601			if (dns_configuration->refs == 0) {
2602				dns_configuration_free(dns_configuration->config);
2603				CFAllocatorDeallocate(NULL, dns_configuration);
2604			}
2605			dns_configuration = NULL;
2606		}
2607	}
2608
2609	if (dns_configuration == NULL) {
2610		dns_config_t	*new_config;
2611
2612		new_config = dns_configuration_copy();
2613		if (new_config != NULL) {
2614			dns_configuration = CFAllocatorAllocate(NULL, sizeof(dns_configuration_t), 0);
2615			dns_configuration->config = new_config;
2616			dns_configuration->refs   = 0;
2617		}
2618	}
2619
2620	if (dns_configuration != NULL) {
2621		dns_configuration->refs++;
2622	}
2623
2624	config = dns_configuration;
2625	pthread_mutex_unlock(&dns_lock);
2626	return config;
2627}
2628
2629
2630static void
2631dns_configuration_release(dns_configuration_t *config)
2632{
2633	pthread_mutex_lock(&dns_lock);
2634
2635	config->refs--;
2636	if (config->refs == 0) {
2637		if (!dns_token_valid && (config == dns_configuration)) {
2638			dns_configuration = NULL;
2639		}
2640
2641		if (config != dns_configuration) {
2642			dns_configuration_free(config->config);
2643			CFAllocatorDeallocate(NULL, config);
2644		}
2645	}
2646
2647	pthread_mutex_unlock(&dns_lock);
2648	return;
2649}
2650
2651
2652static Boolean
2653dns_configuration_watch()
2654{
2655	int		dns_check	= 0;
2656	const char	*dns_key;
2657	Boolean		ok		= FALSE;
2658	uint32_t	status;
2659
2660	pthread_mutex_lock(&dns_lock);
2661
2662	dns_key = dns_configuration_notify_key();
2663	if (dns_key == NULL) {
2664		SCLog(TRUE, LOG_INFO, CFSTR("dns_configuration_notify_key() failed"));
2665		goto done;
2666	}
2667
2668	status = notify_register_check(dns_key, &dns_token);
2669	if (status == NOTIFY_STATUS_OK) {
2670		dns_token_valid = TRUE;
2671	} else {
2672		SCLog(TRUE, LOG_INFO, CFSTR("notify_register_check() failed, status=%u"), status);
2673		goto done;
2674	}
2675
2676	status = notify_check(dns_token, &dns_check);
2677	if (status != NOTIFY_STATUS_OK) {
2678		SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%u"), status);
2679		(void)notify_cancel(dns_token);
2680		dns_token_valid = FALSE;
2681		goto done;
2682	}
2683
2684	ok = TRUE;
2685
2686    done :
2687
2688	pthread_mutex_unlock(&dns_lock);
2689	return ok;
2690}
2691
2692
2693static void
2694dns_configuration_unwatch()
2695{
2696	pthread_mutex_lock(&dns_lock);
2697
2698	(void)notify_cancel(dns_token);
2699	dns_token_valid = FALSE;
2700
2701	if ((dns_configuration != NULL) && (dns_configuration->refs == 0)) {
2702		dns_configuration_free(dns_configuration->config);
2703		CFAllocatorDeallocate(NULL, dns_configuration);
2704		dns_configuration = NULL;
2705	}
2706
2707	pthread_mutex_unlock(&dns_lock);
2708	return;
2709}
2710
2711
2712static void
2713_SC_R_updateResolverReachability(ReachabilityStoreInfoRef	store_info,
2714				 SCNetworkReachabilityFlags	*flags,
2715				 Boolean			*haveDNS,
2716				 const char			*nodename,
2717				 unsigned int			if_index,
2718				 uint32_t			*resolver_if_index,
2719				 int				*dns_config_index,
2720				 const char			*log_prefix
2721				 )
2722{
2723	dns_resolver_t		*default_resolver;
2724	dns_configuration_t	*dns;
2725	Boolean			found			= FALSE;
2726	char			*fqdn			= (char *)nodename;
2727	int			i;
2728	Boolean			isFQDN			= FALSE;
2729	size_t			len;
2730	const int		ndots			= 1;
2731	Boolean			useDefault		= FALSE;
2732
2733	if (resolver_if_index) *resolver_if_index = 0;
2734	if (dns_config_index) *dns_config_index = -1;
2735
2736	/*
2737	 * We first assume that all of the configured DNS servers
2738	 * are available.  Since we don't know which name server will
2739	 * be consulted to resolve the specified nodename we need to
2740	 * check the availability of ALL name servers.  We can only
2741	 * proceed if we know that our query can be answered.
2742	 */
2743
2744	*flags   = kSCNetworkReachabilityFlagsReachable;
2745	*haveDNS = FALSE;
2746
2747	len = (nodename != NULL) ? strlen(nodename) : 0;
2748	if (len == 0) {
2749		// if no nodename, return not reachable
2750		*flags = 0;
2751		return;
2752	}
2753
2754	dns = dns_configuration_retain();
2755	if (dns == NULL) {
2756		// if error
2757		SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no configuration"), log_prefix);
2758		goto done;
2759	}
2760
2761	default_resolver = get_default_resolver(dns->config, if_index);
2762	if (default_resolver == NULL) {
2763		// if no resolver configuration
2764		SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no resolvers"), log_prefix);
2765		goto done;
2766	}
2767
2768	if (fqdn[len - 1] == '.') {
2769		isFQDN = TRUE;
2770
2771		// trim trailing '.''s
2772		while ((len > 0) && (fqdn[len-1] == '.')) {
2773			if (fqdn == nodename) {
2774				fqdn = strdup(nodename);
2775				assert(fqdn != nodename);
2776			}
2777			fqdn[--len] = '\0';
2778		}
2779	}
2780
2781	/*
2782	 * check if the provided name matches a supplemental domain
2783	 */
2784	found = check_matching_resolvers(store_info, dns->config, fqdn, if_index,
2785					 flags, haveDNS, resolver_if_index,
2786					 dns_config_index, log_prefix);
2787
2788	if (!found && !isFQDN) {
2789		/*
2790		 * if we did not match a supplemental domain name and if the
2791		 * provided name has enough "."s then the first query will be
2792		 * directed to the default resolver.
2793		 */
2794		char	*cp;
2795		int	dots;
2796
2797		dots = 0;
2798		for (cp = fqdn; *cp != '\0'; cp++) {
2799			if (*cp == '.') dots++;
2800		}
2801
2802		/* Per KB: HT4845 */
2803		if (dots >= ndots) {
2804			useDefault = TRUE;
2805		}
2806	}
2807
2808	if (!found && !isFQDN && !useDefault && (dns->config->n_resolver > 1)) {
2809		/*
2810		 * FQDN not specified, try matching w/search domains
2811		 */
2812		if (default_resolver->n_search > 0) {
2813			for (i = 0; !found && (i < default_resolver->n_search); i++) {
2814				int	ret;
2815				char	*search_fqdn	= NULL;
2816
2817				ret = asprintf(&search_fqdn, "%s.%s", fqdn, default_resolver->search[i]);
2818				if (ret == -1) {
2819					continue;
2820				}
2821
2822				// try the provided name with the search domain appended
2823				found = check_matching_resolvers(store_info,
2824								 dns->config,
2825								 search_fqdn,
2826								 if_index,
2827								 flags,
2828								 haveDNS,
2829								 resolver_if_index,
2830								 dns_config_index,
2831								 log_prefix);
2832				free(search_fqdn);
2833			}
2834		} else if (default_resolver->domain != NULL) {
2835			char	*dp;
2836			int	domain_parts	= 0;
2837
2838			// count domain parts
2839			for (dp = default_resolver->domain; *dp != '\0'; dp++) {
2840				if (*dp == '.') {
2841					domain_parts++;
2842				}
2843			}
2844
2845			// remove trailing dots
2846			for (dp--; (dp >= default_resolver->domain) && (*dp == '.'); dp--) {
2847				*dp = '\0';
2848				domain_parts--;
2849			}
2850
2851			if (dp >= default_resolver->domain) {
2852				// dots are separators, bump # of components
2853				domain_parts++;
2854			}
2855
2856			dp = default_resolver->domain;
2857			for (i = LOCALDOMAINPARTS; !found && (i <= (domain_parts - ndots)); i++) {
2858				int	ret;
2859				char	*search_fqdn	= NULL;
2860
2861				ret = asprintf(&search_fqdn, "%s.%s", fqdn, dp);
2862				if (ret == -1) {
2863					continue;
2864				}
2865
2866				// try the provided name with the [default] domain appended
2867				found = check_matching_resolvers(store_info,
2868								 dns->config,
2869								 search_fqdn,
2870								 if_index,
2871								 flags,
2872								 haveDNS,
2873								 resolver_if_index,
2874								 dns_config_index,
2875								 log_prefix);
2876				free(search_fqdn);
2877
2878				// move to the next component of the [default] domain
2879				dp = strchr(dp, '.') + 1;
2880			}
2881		}
2882	}
2883
2884	if (!found) {
2885		// update the reachability of the default resolver
2886		update_resolver_reachability(store_info,
2887					     default_resolver,
2888					     flags,
2889					     haveDNS,
2890					     resolver_if_index,
2891					     log_prefix);
2892		if (dns_config_index != NULL) *dns_config_index = 0;
2893	}
2894
2895    done :
2896
2897	if (fqdn != nodename)	free(fqdn);
2898
2899	if (dns != NULL) {
2900		dns_configuration_release(dns);
2901	}
2902
2903	return;
2904}
2905
2906
2907Boolean
2908__SC_checkResolverReachabilityInternal(SCDynamicStoreRef		*storeP,
2909				       SCNetworkReachabilityFlags	*flags,
2910				       Boolean				*haveDNS,
2911				       const char			*nodename,
2912				       uint32_t				*resolver_if_index,
2913				       int				*dns_config_index)
2914{
2915	Boolean			ok;
2916	ReachabilityStoreInfo	store_info;
2917
2918	ReachabilityStoreInfo_init(&store_info);
2919	ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC);
2920	if (!ok) {
2921		goto done;
2922	}
2923
2924	_SC_R_updateResolverReachability(&store_info,
2925					 flags,
2926					 haveDNS,
2927					 nodename,
2928					 0,
2929					 resolver_if_index,
2930					 dns_config_index,
2931					 "");
2932
2933    done :
2934
2935	ReachabilityStoreInfo_free(&store_info);
2936	return ok;
2937}
2938
2939
2940/*
2941 * _SC_checkResolverReachabilityByAddress()
2942 *
2943 * Given an IP address, determine whether a reverse DNS query can be issued
2944 * using the current network configuration.
2945 */
2946Boolean
2947_SC_checkResolverReachabilityByAddress(SCDynamicStoreRef		*storeP,
2948				       SCNetworkReachabilityFlags	*flags,
2949				       Boolean				*haveDNS,
2950				       struct sockaddr			*sa)
2951{
2952	Boolean			ok;
2953	char			ptr_name[128];
2954	ReachabilityStoreInfo	store_info;
2955
2956	ReachabilityStoreInfo_init(&store_info);
2957	ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC);
2958	if (!ok) {
2959		goto done;
2960	}
2961
2962	/*
2963	 * Ideally, we would have an API that given a local IP
2964	 * address would return the DNS server(s) that would field
2965	 * a given PTR query.  Fortunately, we do have an SPI which
2966	 * which will provide this information given a "name" so we
2967	 * take the address, convert it into the inverse query name,
2968	 * and find out which servers should be consulted.
2969	 */
2970	ok = addr_to_PTR_name(sa, ptr_name, sizeof(ptr_name));
2971	if (!ok) {
2972		goto done;
2973	}
2974
2975	_SC_R_updateResolverReachability(&store_info, flags, haveDNS, ptr_name, 0, NULL, NULL, "");
2976
2977    done :
2978
2979	ReachabilityStoreInfo_free(&store_info);
2980	return ok;
2981}
2982
2983
2984#pragma mark -
2985#pragma mark DNSServiceGetAddrInfo support
2986
2987
2988/*
2989 * DNS query handling
2990 *
2991 * Notes :
2992 *
2993 * 1. We have a "contract" with discoveryd that for EVERY network
2994 *    or DNS configuration change that should warrant our [re-]starting
2995 *    a query, discoveryd will acknowledge the latest DNS configuration.
2996 *
2997 * 2. IPMonitor also posts a notification AFTER every network or DNS
2998 *    configuration change.
2999 *
3000 * 3. We use IPMonitor's "trailing edge" as a signal to restart any
3001 *    by-name queries.
3002 */
3003
3004
3005// Note: protected by _hn_target_queue()
3006static int			dns_refresh_token;
3007static Boolean			dns_refresh_token_valid	= FALSE;
3008
3009
3010/*
3011 * dns_refresh_handler
3012 *
3013 * Called to notify/update all SCNetworkReachability by-name targets of
3014 * a network/DNS change.  The change should [re-]start a DNS query to
3015 * resolve the name.
3016 * - should be exec'd on the _hn_target_queue()
3017 */
3018static void
3019dns_refresh_handler()
3020{
3021	CFArrayRef			changes;
3022	CFStringRef			key;
3023	__block SCDynamicStoreRef	store	= NULL;
3024
3025	dispatch_sync(_hn_target_queue(), ^{
3026		if (dns_refresh_token_valid && (hn_store != NULL)) {
3027			store = CFRetain(hn_store);
3028		}
3029	});
3030
3031	if (store == NULL) {
3032		return;
3033	}
3034
3035	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3036							 kSCDynamicStoreDomainState,
3037							 kSCEntNetDNS);
3038	changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
3039	__SCNetworkReachabilityHandleChanges(store, changes, NULL);
3040	CFRelease(changes);
3041	CFRelease(key);
3042
3043	CFRelease(store);
3044	return;
3045}
3046
3047
3048/*
3049 * dns_refresh_enable
3050 *
3051 * Called to monitor for network/DNS changes that should restart a DNS query.
3052 * - caller must be running on the _hn_target_queue()
3053 */
3054static Boolean
3055dns_refresh_enable(dispatch_queue_t q)
3056{
3057	uint32_t	status;
3058
3059	status = notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE,
3060					  &dns_refresh_token,
3061					  q,
3062					  ^(int token){
3063						  dns_refresh_handler();
3064					  });
3065	if (status != NOTIFY_STATUS_OK) {
3066		SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed, status=%u"), status);
3067		return FALSE;
3068	}
3069
3070	dns_refresh_token_valid = TRUE;
3071
3072	return TRUE;
3073}
3074
3075
3076/*
3077 * dns_refresh_disable
3078 *
3079 * Called to stop monitoring for network/DNS changes
3080 * - caller must be running on the _hn_target_queue()
3081 */
3082static void
3083dns_refresh_disable()
3084{
3085	(void)notify_cancel(dns_refresh_token);
3086	dns_refresh_token_valid = FALSE;
3087	return;
3088}
3089
3090
3091#pragma mark -
3092#pragma mark [m]DNS Queries
3093
3094
3095static void
3096dequeueDNSQuery(SCNetworkReachabilityRef target);
3097
3098
3099static dispatch_queue_t
3100_dns_queue()
3101{
3102	static dispatch_once_t	once;
3103	static dispatch_queue_t	q;
3104
3105	dispatch_once(&once, ^{
3106		q = dispatch_queue_create("SCNetworkReachability.DNSService", NULL);
3107	});
3108
3109	return q;
3110}
3111
3112
3113/*
3114 * _dns_complete
3115 */
3116static __inline__ Boolean
3117_dns_complete(SCNetworkReachabilityRef target)
3118{
3119	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
3120
3121	if ((targetPrivate->dnsHaveV4 && targetPrivate->dnsHaveV6) ||
3122	    targetPrivate->dnsHavePTR ||
3123	    targetPrivate->dnsHaveError ||
3124	    targetPrivate->dnsHaveTimeout) {
3125		return TRUE;
3126	}
3127
3128	return FALSE;
3129}
3130
3131
3132/*
3133 * _dns_notify
3134 *
3135 * Called to push out a target's DNS changes
3136 * - caller must be running on the _dns_queue()
3137 */
3138static void
3139_dns_notify(const void *value, void *context)
3140{
3141	SCNetworkReachabilityRef	target		= (SCNetworkReachabilityRef)value;
3142	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
3143
3144	MUTEX_LOCK(&targetPrivate->lock);
3145
3146	if (_dns_complete(target)) {
3147		__mark_operation_end(target,
3148				     (targetPrivate->resolvedError == NETDB_SUCCESS),	// if successful query
3149				     dns_query_mdns,					// [m]DNS query
3150				     &targetPrivate->dnsQueryStart,			// start time
3151				     &targetPrivate->dnsQueryEnd);			// end time
3152
3153		// update target info
3154		if (targetPrivate->resolvedAddresses != NULL) {
3155			CFRelease(targetPrivate->resolvedAddresses);
3156		}
3157		targetPrivate->resolvedAddresses = targetPrivate->dnsAddresses;
3158		targetPrivate->dnsAddresses      = NULL;
3159
3160		targetPrivate->resolvedError     = targetPrivate->dnsError;
3161		targetPrivate->dnsError          = NETDB_SUCCESS;
3162
3163		dequeueDNSQuery(target);
3164
3165		targetPrivate->needResolve = FALSE;
3166
3167		if (targetPrivate->scheduled) {
3168			__SCNetworkReachabilityUpdate(target);
3169		}
3170	}
3171
3172	MUTEX_UNLOCK(&targetPrivate->lock);
3173	return;
3174}
3175
3176
3177typedef enum {
3178	MARK_NONE,
3179	MARK_ERROR,
3180	MARK_TIMEOUT,
3181	MARK_HAVE_V4,
3182	MARK_HAVE_V6,
3183	MARK_HAVE_PTR,
3184} _dns_mark_t;
3185
3186
3187/*
3188 * _dns_mark
3189 */
3190static __inline__ void
3191_dns_mark(SCNetworkReachabilityRef target, _dns_mark_t mark)
3192{
3193	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
3194
3195	switch (mark) {
3196		case MARK_NONE :
3197			break;
3198		case MARK_ERROR :
3199			targetPrivate->dnsHaveError = TRUE;
3200			break;
3201		case MARK_TIMEOUT :
3202			targetPrivate->dnsHaveTimeout = TRUE;
3203			break;
3204		case MARK_HAVE_V4 :
3205			targetPrivate->dnsHaveV4 = TRUE;
3206			break;
3207		case MARK_HAVE_V6 :
3208			targetPrivate->dnsHaveV6 = TRUE;
3209			break;
3210		case MARK_HAVE_PTR :
3211			targetPrivate->dnsHavePTR = TRUE;
3212			break;
3213	}
3214
3215	return;
3216}
3217
3218
3219/*
3220 * _dns_callback
3221 *
3222 * Called to process [m]DNS query updates
3223 * - caller must be running on the _dns_queue()
3224 */
3225static void
3226_dns_callback(DNSServiceRef		sdRef,
3227	      DNSServiceFlags		flags,
3228	      DNSServiceErrorType	errorCode,
3229	      _dns_mark_t		dnsMark,
3230	      CFTypeRef			dnsAddress,	// CFData(struct sockaddr) or CFString(ptr_name)
3231	      void			*context)
3232{
3233	int				failures	= 0;
3234	Boolean				restart		= FALSE;
3235	SCNetworkReachabilityRef	target		= (SCNetworkReachabilityRef)context;
3236	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
3237
3238	MUTEX_LOCK(&targetPrivate->lock);
3239
3240	if (sdRef != targetPrivate->dnsTarget) {
3241		// if this DNSServiceRef is no longer associated with the target
3242		MUTEX_UNLOCK(&targetPrivate->lock);
3243		return;
3244	}
3245
3246	switch (errorCode) {
3247		case kDNSServiceErr_NoError :
3248			if (dnsAddress != NULL) {
3249				CFMutableArrayRef	addresses;
3250				CFIndex			i;
3251
3252				_dns_mark(target, dnsMark);
3253
3254				if (targetPrivate->dnsAddresses != NULL) {
3255					if (isA_CFArray(targetPrivate->dnsAddresses)) {
3256						addresses = CFArrayCreateMutableCopy(NULL, 0, targetPrivate->dnsAddresses);
3257					} else {
3258						addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3259					}
3260
3261					CFRelease(targetPrivate->dnsAddresses);
3262					targetPrivate->dnsAddresses = NULL;
3263				} else {
3264					addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3265				}
3266
3267				i = CFArrayGetFirstIndexOfValue(addresses,
3268								CFRangeMake(0, CFArrayGetCount(addresses)),
3269								dnsAddress);
3270				if (flags & kDNSServiceFlagsAdd) {
3271					// add address
3272					if (i == kCFNotFound) {
3273						CFArrayAppendValue(addresses, dnsAddress);
3274					}
3275#ifdef	HANDLE_RMV_REQUESTS
3276				} else {
3277					// remove address
3278					if (i != kCFNotFound) {
3279						CFArrayRemoveValueAtIndex(addresses, i);
3280					}
3281#endif	// HANDLE_RMV_REQUESTS
3282				}
3283
3284				if (CFArrayGetCount(addresses) > 0) {
3285					targetPrivate->dnsAddresses = addresses;
3286					targetPrivate->dnsError     = NETDB_SUCCESS;
3287				} else {
3288					// if host not found
3289					targetPrivate->dnsAddresses = CFRetain(kCFNull);
3290					targetPrivate->dnsError     = EAI_NONAME;
3291					CFRelease(addresses);
3292				}
3293
3294			}
3295			break;
3296		case kDNSServiceErr_BadParam :
3297			_dns_mark(target, MARK_ERROR);
3298
3299			if (targetPrivate->dnsAddresses != NULL) {
3300				CFRelease(targetPrivate->dnsAddresses);
3301			}
3302			targetPrivate->dnsAddresses = CFRetain(kCFNull);
3303			targetPrivate->dnsError     = EAI_NONAME;
3304			break;
3305		case kDNSServiceErr_NoSuchRecord :
3306			_dns_mark(target, dnsMark);
3307
3308			if (targetPrivate->dnsAddresses == NULL) {
3309				targetPrivate->dnsAddresses = CFRetain(kCFNull);
3310				targetPrivate->dnsError     = EAI_NONAME;
3311			}
3312			break;
3313		case kDNSServiceErr_Timeout :
3314			_dns_mark(target, MARK_TIMEOUT);
3315
3316			if (targetPrivate->dnsAddresses == NULL) {
3317				targetPrivate->dnsAddresses = CFRetain(kCFNull);
3318				targetPrivate->dnsError     = EAI_NONAME;
3319			}
3320			break;
3321		default :
3322			SCLog(TRUE, LOG_ERR,
3323			      CFSTR("%sSCNetworkReachability _dns_callback w/error=%d (n=%d)"),
3324			      targetPrivate->log_prefix,
3325			      errorCode,
3326			      targetPrivate->dnsFailures + 1);
3327			// fall through
3328		case kDNSServiceErr_ServiceNotRunning :
3329			_dns_mark(target, MARK_ERROR);
3330
3331			// bump per-target failure count
3332			failures = ++targetPrivate->dnsFailures;
3333
3334			// Check to see if we've seen too many failures for this target
3335			if (failures > 2) {
3336				// if so, there's little point in retrying over
3337				// and over again so let's just return an error
3338				// and move on.
3339				if (targetPrivate->dnsAddresses != NULL) {
3340					CFRelease(targetPrivate->dnsAddresses);
3341				}
3342				targetPrivate->dnsAddresses = CFRetain(kCFNull);
3343				targetPrivate->dnsError     = EAI_NONAME;
3344			} else if (targetPrivate->dnsGeneration == dnsGeneration) {
3345				// if not, then "discoveryd" crashed or some
3346				// other/unexpected error occurred.  In this
3347				// case, we'll try again with a clean slate and
3348				// restart all requests.
3349				if (dnsMain != NULL) {
3350					DNSServiceRefDeallocate(dnsMain);
3351					dnsMain = NULL;
3352					dnsCount = 0;
3353					dnsGeneration++;
3354					restart = TRUE;
3355				}
3356			}
3357			break;
3358	}
3359
3360	// update DNS failure count (and [re-]set to zero if we're OK)
3361	targetPrivate->dnsFailures = failures;
3362
3363	MUTEX_UNLOCK(&targetPrivate->lock);
3364
3365	if (restart) {
3366		SCLog(TRUE, LOG_DEBUG,
3367		      CFSTR("%sreconnecting SCNetworkReachability w/\"discoveryd\" (%d)"),
3368		      targetPrivate->log_prefix,
3369		      dnsGeneration);
3370
3371		dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC),
3372			       _hn_changes_queue(),
3373			       ^{
3374			dns_refresh_handler();
3375		});
3376
3377		// and flush the dnsUpdated queue as any DNS results we may have
3378		// accumulated are no longer valid.
3379		if (dnsUpdated != NULL) {
3380			CFRelease(dnsUpdated);
3381			dnsUpdated = NULL;
3382		}
3383		return;
3384	}
3385
3386	if (targetPrivate->dnsHaveTimeout) {
3387	    targetPrivate->dnsNoAddressesSinceLastTimeout = TRUE;
3388	} else if (targetPrivate->dnsNoAddressesSinceLastTimeout &&
3389	           isA_CFArray(targetPrivate->dnsAddresses) &&
3390	           CFArrayGetCount(targetPrivate->dnsAddresses) > 0)
3391	{
3392	    targetPrivate->dnsNoAddressesSinceLastTimeout = FALSE;
3393	}
3394
3395	// the "more coming" flag applies to DNSService callouts for any/all
3396	// hosts that are being watched so we need to keep track of the targets
3397	// we have updated.  When we [finally] have the last callout then we
3398	// push our notifications for all of the updated targets.
3399
3400	if (dnsUpdated == NULL) {
3401		dnsUpdated = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
3402	}
3403	CFSetAddValue(dnsUpdated, target);
3404
3405	if (!(flags & kDNSServiceFlagsMoreComing)) {
3406		CFSetApplyFunction(dnsUpdated, _dns_notify, NULL);
3407		CFRelease(dnsUpdated);
3408		dnsUpdated = NULL;
3409	}
3410
3411	return;
3412}
3413
3414
3415/*
3416 * _dns_getaddrinfo_callback
3417 *
3418 * Called to process [m]DNS query updates
3419 * - caller must be running on the _dns_queue()
3420 */
3421static void
3422_dns_getaddrinfo_callback(DNSServiceRef			sdRef,
3423			  DNSServiceFlags		flags,
3424			  uint32_t			interfaceIndex,
3425			  DNSServiceErrorType		errorCode,
3426			  const char			*hostname,
3427			  const struct sockaddr		*address,
3428			  uint32_t			ttl,
3429			  void				*context)
3430{
3431	CFDataRef	dnsAddress	= NULL;
3432	_dns_mark_t	dnsMark		= MARK_NONE;
3433
3434	if (address != NULL) {
3435		switch (errorCode) {
3436			case kDNSServiceErr_NoError :
3437				dnsAddress = CFDataCreate(NULL, (void *)address, address->sa_len);
3438				// ... and fall through
3439			case kDNSServiceErr_NoSuchRecord :
3440				switch (address->sa_family) {
3441					case AF_INET :
3442						dnsMark = MARK_HAVE_V4;
3443						break;
3444					case AF_INET6 :
3445						dnsMark = MARK_HAVE_V6;
3446						break;
3447				}
3448				break;
3449			default :
3450				break;
3451		}
3452	}
3453
3454	_dns_callback(sdRef, flags, errorCode, dnsMark, dnsAddress, context);
3455
3456	if (dnsAddress != NULL) {
3457		CFRelease(dnsAddress);
3458	}
3459
3460	return;
3461}
3462
3463
3464static CFStringRef
3465_dns_copy_domain_name(const uint8_t *rdata, uint16_t rdlen)
3466{
3467	CFMutableStringRef	domain;
3468	const uint8_t		*label;
3469	uint8_t			label_len;
3470
3471	domain = CFStringCreateMutable(NULL, 0);
3472
3473	label = rdata;
3474	label_len = *(label++);
3475	while (label_len != 0) {
3476		while (label_len-- > 0) {
3477			uint8_t		byte	= *label++;
3478
3479			if ((byte == '.') || (byte == '\\')) {
3480				// if escape needed
3481				CFStringAppendFormat(domain, NULL, CFSTR("\\%c"), byte);
3482			} else if (byte <= ' ') {
3483				CFStringAppendFormat(domain, NULL, CFSTR("\\%c%c%c"),
3484						     '0' + (byte / 100),
3485						     '0' + ((byte / 10) % 10),
3486						     '0' + (byte % 10));
3487			} else {
3488				CFStringAppendFormat(domain, NULL, CFSTR("%c"), byte);
3489			}
3490		}
3491
3492		label_len = *(label++);
3493		if (label_len != 0) {
3494			CFStringAppendFormat(domain, NULL, CFSTR("."));
3495		}
3496	}
3497
3498	return domain;
3499}
3500
3501
3502static void
3503_dns_queryrecord_callback(DNSServiceRef			sdRef,
3504			  DNSServiceFlags		flags,
3505			  uint32_t			interfaceIndex,
3506			  DNSServiceErrorType		errorCode,
3507			  const char			*fullname,
3508			  uint16_t			rrtype,
3509			  uint16_t			rrclass,
3510			  uint16_t			rdlen,
3511			  const void			*rdata,
3512			  uint32_t			ttl,
3513			  void				*context)
3514{
3515	_dns_mark_t			dnsMark		= MARK_NONE;
3516	CFStringRef			dnsPTRName	= NULL;
3517	SCNetworkReachabilityRef	target		= (SCNetworkReachabilityRef)context;
3518	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
3519
3520	// for now, we only support using DNSServiceQueryRecord for PTR queries
3521	assert(targetPrivate->type == reachabilityTypePTR);
3522
3523	if (rdata != NULL) {
3524		switch (errorCode) {
3525			case kDNSServiceErr_NoError :
3526				if (rrtype == kDNSServiceType_PTR) {
3527					dnsPTRName = _dns_copy_domain_name(rdata, rdlen);
3528				}
3529				// ... and fall through
3530			case kDNSServiceErr_NoSuchRecord :
3531				dnsMark = MARK_HAVE_PTR;
3532				break;
3533			default :
3534				break;
3535		}
3536	}
3537
3538	_dns_callback(sdRef, flags, errorCode, dnsMark, dnsPTRName, context);
3539
3540	if (dnsPTRName != NULL) {
3541		CFRelease(dnsPTRName);
3542	}
3543
3544	return;
3545}
3546
3547
3548static Boolean
3549enqueueDNSQuery(SCNetworkReachabilityRef target)
3550{
3551	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
3552
3553	MUTEX_ASSERT_HELD(&targetPrivate->lock);
3554
3555	// clear DNS flags, mark the query active
3556	targetPrivate->dnsFlags = 0;
3557	targetPrivate->dnsActive = TRUE;
3558
3559	// track the DNS resolution time
3560	__mark_operation_start(&targetPrivate->dnsQueryStart, &targetPrivate->dnsQueryEnd);
3561
3562	CFRetain(target);
3563	dispatch_async(_dns_queue(), ^{
3564		DNSServiceErrorType	err;
3565		const char		*fcn	= "???";
3566		DNSServiceRef		sdRef	= NULL;
3567
3568		if (targetPrivate->dnsTarget != NULL) {
3569			// if already running
3570			CFRelease(target);
3571			return;
3572		}
3573
3574		// if needed, start interacting with "discoveryd"
3575		if (dnsMain == NULL) {
3576			err = DNSServiceCreateConnection(&dnsMain);
3577			if (err != kDNSServiceErr_NoError) {
3578				SCLog(TRUE, LOG_ERR,
3579				      CFSTR("%sDNSServiceCreateConnection(&dnsMain) failed, error = %d"),
3580				      targetPrivate->log_prefix,
3581				      err);
3582				goto done;
3583			}
3584
3585			err = DNSServiceSetDispatchQueue(dnsMain, _dns_queue());
3586			if (err != kDNSServiceErr_NoError) {
3587				SCLog(TRUE, LOG_ERR,
3588				      CFSTR("%sDNSServiceSetDispatchQueue() failed, error = %d"),
3589				      targetPrivate->log_prefix,
3590				      err);
3591				DNSServiceRefDeallocate(dnsMain);
3592				dnsMain = NULL;
3593				dnsGeneration++;
3594				goto done;
3595			}
3596		}
3597
3598		// start a query for this target
3599		sdRef = dnsMain;
3600
3601		switch (targetPrivate->type) {
3602			case reachabilityTypeName :
3603				fcn = "DNSServiceGetAddrInfo";
3604				err = DNSServiceGetAddrInfo(&sdRef,				// sdRef
3605							    kDNSServiceFlagsReturnIntermediates // flags
3606							    | kDNSServiceFlagsShareConnection
3607							    | kDNSServiceFlagsSuppressUnusable
3608							    | kDNSServiceFlagsTimeout,
3609							    targetPrivate->if_index,		// interfaceIndex
3610							    0,					// protocol
3611							    targetPrivate->name,		// hostname
3612							    _dns_getaddrinfo_callback,		// callback
3613							    (void *)target);			// context
3614				break;
3615			case reachabilityTypePTR :
3616				fcn = "DNSServiceQueryRecord";
3617				err = DNSServiceQueryRecord(&sdRef,				// sdRef
3618							    kDNSServiceFlagsReturnIntermediates	// flags
3619							    | kDNSServiceFlagsShareConnection
3620							    | kDNSServiceFlagsSuppressUnusable
3621							    | kDNSServiceFlagsTimeout,
3622							    targetPrivate->if_index,		// interfaceIndex
3623							    targetPrivate->name,		// fullname
3624							    kDNSServiceType_PTR,		// rrtype
3625							    kDNSServiceClass_IN,		// rrclass
3626							    _dns_queryrecord_callback,		// callback
3627							    (void *)target);			// context
3628				break;
3629			default :
3630				err = kDNSServiceErr_Unknown;
3631				break;
3632		}
3633
3634		switch (err) {
3635			case kDNSServiceErr_NoError :
3636				dnsCount++;
3637				break;
3638
3639			default :
3640				SCLog(TRUE, LOG_ERR,
3641				      CFSTR("%s%s() failed, error = %d (%d)"),
3642				      targetPrivate->log_prefix,
3643				      fcn,
3644				      err,
3645				      dnsCount);
3646				// fall through
3647
3648			case kDNSServiceErr_BadParam :
3649				if (dnsCount == 0) {
3650					// if this was the first request
3651					DNSServiceRefDeallocate(dnsMain);
3652					dnsMain = NULL;
3653					dnsGeneration++;
3654				}
3655				sdRef = NULL;
3656				break;
3657		}
3658
3659	    done :
3660
3661		MUTEX_LOCK(&targetPrivate->lock);
3662
3663		if (err == kDNSServiceErr_NoError) {
3664			targetPrivate->dnsGeneration = dnsGeneration;
3665			targetPrivate->dnsTarget = sdRef;
3666		} else {
3667			targetPrivate->dnsActive = FALSE;
3668
3669			// queue up the returned error
3670			dispatch_async(_dns_queue(), ^{
3671				_dns_callback(NULL,			// sdRef
3672					      0,			// flags
3673					      err,			// errorCode
3674					      MARK_ERROR,		// dnsMark
3675					      NULL,			// dnsAddress
3676					      (void *)target);		// context
3677				CFRelease(target);
3678			});
3679		}
3680
3681		MUTEX_UNLOCK(&targetPrivate->lock);
3682
3683		return;
3684	});
3685
3686	return TRUE;
3687}
3688
3689
3690static void
3691dequeueDNSQuery(SCNetworkReachabilityRef target)
3692{
3693	DNSServiceRef			sdRef;
3694	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
3695
3696	MUTEX_ASSERT_HELD(&targetPrivate->lock);
3697
3698	// terminate the [target] [m]DNS query
3699	sdRef = targetPrivate->dnsTarget;
3700	targetPrivate->dnsTarget = NULL;
3701
3702	// mark the query NOT active
3703	targetPrivate->dnsActive = FALSE;
3704
3705	// don't do anything if the sdRef is not valid
3706	if (sdRef != NULL) {
3707		int			generation;
3708
3709		generation = targetPrivate->dnsGeneration;
3710		dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3LL * NSEC_PER_SEC),
3711			       _dns_queue(),
3712			       ^{
3713			if (generation == dnsGeneration) {
3714				// if we're pointing to the same DNSService
3715				// generation as the main/active session
3716				// deallocate per-target query
3717				DNSServiceRefDeallocate(sdRef);
3718				dnsCount--;
3719				if (dnsCount == 0) {
3720					// if no more queries active
3721					DNSServiceRefDeallocate(dnsMain);
3722					dnsMain = NULL;
3723					dnsGeneration++;
3724				}
3725			}
3726
3727			CFRelease(target);
3728		});
3729	}
3730
3731	if (targetPrivate->dnsAddresses != NULL) {
3732		CFRelease(targetPrivate->dnsAddresses);
3733		targetPrivate->dnsAddresses = NULL;
3734	}
3735	targetPrivate->dnsError = NETDB_SUCCESS;
3736
3737	return;
3738}
3739
3740
3741#pragma mark -
3742#pragma mark Synchronous DNS query support
3743
3744
3745#define SYNC_DNS_QUERY_TIMEOUT_NSEC	35 * NSEC_PER_SEC	// 35s
3746
3747
3748static void
3749sync_DNS_query_callback(SCNetworkReachabilityRef	clone,
3750			SCNetworkReachabilityFlags	cloneFlags,
3751			void				*info)
3752{
3753	dispatch_semaphore_t		s	= (dispatch_semaphore_t)info;
3754
3755	dispatch_semaphore_signal(s);
3756	return;
3757}
3758
3759
3760static void
3761sync_DNS_query(SCNetworkReachabilityRef target)
3762{
3763	SCNetworkReachabilityRef	clone;
3764	SCNetworkReachabilityPrivateRef	clonePrivate;
3765	SCNetworkReachabilityContext	context	= { 0, NULL, NULL, NULL, NULL };
3766	dispatch_queue_t		q;
3767	long				ret;
3768	dispatch_semaphore_t		s;
3769	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
3770
3771	clone = __SCNetworkReachabilityCreateCopy(target);
3772	if (clone == NULL) {
3773		return;
3774	}
3775	clonePrivate = (SCNetworkReachabilityPrivateRef)clone;
3776
3777	q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3778	s = dispatch_semaphore_create(0);
3779
3780	// start async query
3781	context.info = (void *)s;
3782	SCNetworkReachabilitySetCallback(clone, sync_DNS_query_callback, &context);
3783	SCNetworkReachabilitySetDispatchQueue(clone, q);
3784
3785	// wait for reply (or timeout)
3786	ret = dispatch_semaphore_wait(s, dispatch_time(DISPATCH_TIME_NOW,
3787						       SYNC_DNS_QUERY_TIMEOUT_NSEC));
3788	if (ret != 0) {
3789		dispatch_sync(_dns_queue(), ^{
3790			// mark as both a timeout *and* an error
3791			_dns_mark(clone, MARK_TIMEOUT);
3792			_dns_mark(clone, MARK_ERROR);
3793
3794			__mark_operation_end(clone,
3795					     FALSE,				// if successful query
3796					     dns_query_mdns_timeout,		// [m]DNS query
3797					     &clonePrivate->dnsQueryStart,	// start time
3798					     &clonePrivate->dnsQueryEnd);	// end time
3799
3800			MUTEX_LOCK(&clonePrivate->lock);
3801
3802			// update target info with what's available
3803			if (clonePrivate->resolvedAddresses != NULL) {
3804				CFRelease(clonePrivate->resolvedAddresses);
3805				clonePrivate->resolvedAddresses = NULL;
3806			}
3807			if ((clonePrivate->dnsAddresses != NULL) &&
3808			    isA_CFArray(clonePrivate->dnsAddresses) &&
3809			    (CFArrayGetCount(clonePrivate->dnsAddresses) > 0)) {
3810				clonePrivate->resolvedAddresses = CFArrayCreateMutableCopy(NULL,
3811											   0,
3812											   clonePrivate->dnsAddresses);
3813			}
3814			if (clonePrivate->resolvedAddresses != NULL) {
3815				// if timeout w/partial results
3816				clonePrivate->resolvedError     = NETDB_SUCCESS;
3817			} else {
3818				// if timeout w/no results
3819				clonePrivate->resolvedAddresses = CFRetain(kCFNull);
3820				clonePrivate->resolvedError     = EAI_NONAME;
3821			}
3822
3823			MUTEX_UNLOCK(&clonePrivate->lock);
3824		});
3825	}
3826
3827	// cancel request
3828	SCNetworkReachabilitySetDispatchQueue(clone, NULL);
3829	SCNetworkReachabilitySetCallback(clone, NULL, NULL);
3830
3831	// transfer reply
3832	if (clonePrivate->resolvedAddresses  != NULL) CFRetain(clonePrivate->resolvedAddresses);
3833	if (targetPrivate->resolvedAddresses != NULL) CFRelease(targetPrivate->resolvedAddresses);
3834	targetPrivate->resolvedAddresses = clonePrivate->resolvedAddresses;
3835	targetPrivate->resolvedError     = clonePrivate->resolvedError;
3836	targetPrivate->resolverFlags     = clonePrivate->resolverFlags;
3837	targetPrivate->cycle             = clonePrivate->cycle;
3838	targetPrivate->dnsFlags          = clonePrivate->dnsFlags;
3839	memcpy(&targetPrivate->info, &clonePrivate->info, sizeof(ReachabilityInfo));
3840	memcpy(&targetPrivate->last_notify, &clonePrivate->last_notify, sizeof(ReachabilityInfo));
3841
3842	CFRelease(clone);
3843	dispatch_release(s);
3844
3845	return;
3846}
3847
3848
3849#pragma mark -
3850#pragma mark Network Information support
3851
3852
3853// Note: protected by _hn_target_queue()
3854static int			network_changed_token;
3855static Boolean			network_changed_token_valid	= FALSE;
3856
3857
3858/*
3859 * nwi_refresh_handler
3860 *
3861 * Called to notify/update network changed events
3862 * - should be exec'd on the _hn_changes_queue()
3863 */
3864static void
3865nwi_refresh_handler()
3866{
3867	CFArrayRef			changes;
3868	CFStringRef			key;
3869	__block SCDynamicStoreRef	store	= NULL;
3870
3871	dispatch_sync(_hn_target_queue(), ^{
3872		if (network_changed_token_valid && (hn_store != NULL)) {
3873			store = CFRetain(hn_store);
3874		}
3875	});
3876
3877	if (store == NULL) {
3878		return;
3879	}
3880
3881	// Fake a network change.
3882	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3883							 kSCDynamicStoreDomainState,
3884							 kSCEntNetIPv4);
3885	changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
3886	__SCNetworkReachabilityHandleChanges(store, changes, NULL);
3887	CFRelease(changes);
3888	CFRelease(key);
3889
3890	CFRelease(store);
3891	return;
3892}
3893
3894
3895/*
3896 * nwi_refresh_enable
3897 *
3898 * Called to monitor for network changes.
3899 * - caller must be running on the _hn_target_queue()
3900 * - passed in queue should be _hn_changes_queue()
3901 */
3902static Boolean
3903nwi_refresh_enable(dispatch_queue_t q)
3904{
3905	uint32_t	status;
3906
3907	status = notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE_NWI,	// trailing nwi_state_get_notify_key()
3908					  &network_changed_token,
3909					  q,
3910					  ^(int token){
3911						  nwi_refresh_handler();
3912					  });
3913	if (status != NOTIFY_STATUS_OK) {
3914		SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed for network changes, status=%u"), status);
3915		return FALSE;
3916	}
3917
3918	network_changed_token_valid = TRUE;
3919
3920	return TRUE;
3921}
3922
3923
3924/*
3925 * nwi_refresh_disable
3926 *
3927 * Called to stop monitoring for network changes
3928 * - caller must be running on the _hn_target_queue()
3929 */
3930static void
3931nwi_refresh_disable()
3932{
3933	if (network_changed_token_valid) {
3934		(void)notify_cancel(network_changed_token);
3935		network_changed_token_valid = FALSE;
3936	}
3937
3938	return;
3939}
3940
3941
3942#pragma mark -
3943#pragma mark Sleep/wake support
3944
3945
3946#if	!TARGET_OS_IPHONE
3947
3948// Note: protected by _hn_target_queue()
3949static IOPMConnection		power_changed_connection	= NULL;
3950static const CFStringRef	power_changed_key		= CFSTR("*** EARLY WAKE ***");
3951
3952
3953/*
3954 * power_refresh_handler
3955 *
3956 * Called to notify/update power capability changed events
3957 * - should be exec'd on the _hn_changes_queue()
3958 */
3959static void
3960power_refresh_handler(void				*param,
3961		      IOPMConnection			connection,
3962		      IOPMConnectionMessageToken	token,
3963		      IOPMSystemPowerStateCapabilities	capabilities)
3964{
3965	Boolean				change;
3966	IOReturn			ret;
3967	__block SCDynamicStoreRef	store	= NULL;
3968
3969	dispatch_sync(_hn_target_queue(), ^{
3970		if ((power_changed_connection != NULL) && (hn_store != NULL)) {
3971			store = CFRetain(hn_store);
3972		}
3973	});
3974
3975	if (store == NULL) {
3976		return;
3977	}
3978
3979	// check for [relevant] changes
3980	change = ((power_capabilities ^ capabilities) & POWER_CAPABILITIES_NETWORK) != 0;
3981
3982	// update capabilities
3983	power_capabilities = capabilities;
3984
3985	if (change) {
3986		CFArrayRef	changes;
3987
3988		// fake a network change.
3989		changes = CFArrayCreate(NULL, (const void **)&power_changed_key, 1, &kCFTypeArrayCallBacks);
3990		__SCNetworkReachabilityHandleChanges(store, changes, NULL);
3991		CFRelease(changes);
3992	}
3993
3994	ret = IOPMConnectionAcknowledgeEvent(connection, token);
3995	if (ret != kIOReturnSuccess) {
3996		SCLog(TRUE, LOG_ERR, CFSTR("IOPMConnectionAcknowledgeEvent failed, 0x%08x"), ret);
3997	}
3998
3999	CFRelease(store);
4000	return;
4001}
4002
4003
4004/*
4005 * power_refresh_enable
4006 *
4007 * Called to monitor power changes.
4008 * - caller must be running on the _hn_target_queue()
4009 * - passed in queue should be _hn_changes_queue()
4010 */
4011static Boolean
4012power_refresh_enable(dispatch_queue_t q)
4013{
4014	IOPMConnection	connection	= NULL;
4015	IOReturn	ret;
4016
4017	ret = IOPMConnectionCreate(CFSTR("com.apple.SCNetworkReachability"),
4018				   kIOPMEarlyWakeNotification | kIOPMSleepWakeInterest,
4019				   &connection);
4020	if (ret != kIOReturnSuccess) {
4021		SCLog(TRUE, LOG_ERR, CFSTR("IOPMConnectionCreate failed, 0x%08x"), ret);
4022		goto failed;
4023	}
4024
4025	ret = IOPMConnectionSetNotification(connection, NULL, power_refresh_handler);
4026	if (ret != kIOReturnSuccess) {
4027		SCLog(TRUE, LOG_ERR, CFSTR("IOPMConnectionSetNotification failed, 0x%08x"), ret);
4028		goto failed;
4029	}
4030
4031	power_changed_connection = connection;
4032	IOPMConnectionSetDispatchQueue(connection, q);
4033	power_capabilities = IOPMConnectionGetSystemCapabilities();
4034
4035	return TRUE;
4036
4037    failed:
4038
4039	if (connection != NULL) {
4040		IOPMConnectionRelease(connection);
4041	}
4042
4043	return FALSE;
4044}
4045
4046
4047static void
4048power_refresh_disable()
4049{
4050	if (power_changed_connection != NULL) {
4051		IOPMConnectionSetDispatchQueue(power_changed_connection, NULL);
4052		IOPMConnectionRelease(power_changed_connection);
4053		power_changed_connection = NULL;
4054	}
4055
4056	return;
4057}
4058
4059#endif	// !TARGET_OS_IPHONE
4060
4061
4062
4063
4064
4065
4066#pragma mark -
4067#pragma mark OnDemand
4068
4069
4070SCNetworkServiceRef
4071SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef	target,
4072					 CFDictionaryRef		*userOptions)
4073{
4074	SCNetworkServiceRef		service		= NULL;
4075	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
4076
4077	if (!isA_SCNetworkReachability(target)) {
4078		_SCErrorSet(kSCStatusInvalidArgument);
4079		return NULL;
4080	}
4081
4082	if (targetPrivate->onDemandServiceID != NULL) {
4083		service = _SCNetworkServiceCopyActive(NULL, targetPrivate->onDemandServiceID);
4084	}
4085
4086	if (userOptions != NULL) {
4087		if (targetPrivate->onDemandName != NULL) {
4088			*userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
4089			CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, targetPrivate->onDemandName);
4090		} else {
4091			*userOptions = NULL;
4092		}
4093	}
4094
4095	return service;
4096}
4097
4098
4099
4100
4101static void
4102__SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef	onDemandServer,
4103					     SCNetworkReachabilityFlags	onDemandFlags,
4104					     void			*info)
4105{
4106	SCNetworkReachabilityRef	target		= (SCNetworkReachabilityRef)info;
4107	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
4108
4109	MUTEX_LOCK(&targetPrivate->lock);
4110
4111	if (!targetPrivate->scheduled) {
4112		// if not currently scheduled
4113		MUTEX_UNLOCK(&targetPrivate->lock);
4114		return;
4115	}
4116
4117	SCLog(_sc_debug, LOG_INFO, CFSTR("%sOnDemand \"server\" status changed (now 0x%08x)"),
4118	      targetPrivate->log_prefix,
4119	      onDemandFlags);
4120
4121	if (targetPrivate->type == reachabilityTypeName) {
4122		// make sure that we resolve the name again
4123		targetPrivate->needResolve = TRUE;
4124	}
4125
4126	__SCNetworkReachabilityUpdate(target);
4127
4128	MUTEX_UNLOCK(&targetPrivate->lock);
4129
4130	return;
4131}
4132
4133
4134static Boolean
4135__SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef	store_info,
4136				     SCNetworkReachabilityRef	target,
4137				     Boolean			onDemandRetry,
4138				     SCNetworkReachabilityFlags	*flags)
4139{
4140	SCNetworkConnectionRef		connection		= NULL;
4141	SCNetworkConnectionType		connectionType		= kSCNetworkConnectionTypeUnknown;
4142	Boolean				isAppLayerVPN		= FALSE;
4143	Boolean				isOnDemandService	= FALSE;
4144	Boolean				ok			= FALSE;
4145	CFStringRef			onDemandRemoteAddress	= NULL;
4146	CFStringRef			onDemandServiceID	= NULL;
4147	SCNetworkConnectionStatus	onDemandStatus		= kSCNetworkConnectionInvalid;
4148	CFMutableDictionaryRef		selectOptions		= NULL;
4149	Boolean				success			= FALSE;
4150	SCNetworkReachabilityPrivateRef	targetPrivate		= (SCNetworkReachabilityPrivateRef)target;
4151
4152	MUTEX_ASSERT_HELD(&targetPrivate->lock);
4153
4154	if (targetPrivate->onDemandName == NULL) {
4155		targetPrivate->onDemandName = CFStringCreateWithCString(NULL, targetPrivate->name, kCFStringEncodingUTF8);
4156	}
4157
4158	/*
4159	 * check if an OnDemand VPN configuration matches the name.
4160	 */
4161
4162	connection = SCNetworkConnectionCreate(kCFAllocatorDefault, NULL, NULL);
4163	if (connection == NULL) {
4164		goto done;
4165	}
4166
4167	/* set select options */
4168	selectOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
4169	if (selectOptions == NULL) {
4170		goto done;
4171	}
4172
4173	CFDictionaryAddValue(selectOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, targetPrivate->onDemandName);
4174	CFDictionaryAddValue(selectOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry, onDemandRetry ? kCFBooleanTrue : kCFBooleanFalse);
4175	CFDictionaryAddValue(selectOptions, kSCNetworkConnectionSelectionOptionNoUserPrefs, kCFBooleanTrue);
4176
4177	/* select service. May be On Demand or App Layer VPN */
4178	if (!SCNetworkConnectionSelectServiceWithOptions(connection, selectOptions)) {
4179		goto done;
4180	}
4181
4182	/* get reachability flags (of VPN server) */
4183	(void) SCNetworkConnectionGetReachabilityInfo(connection, flags, NULL);
4184
4185	connectionType = SCNetworkConnectionGetType(connection);
4186	if (connectionType == kSCNetworkConnectionTypeAppLayerVPN) {
4187		isAppLayerVPN = TRUE;
4188	}
4189
4190	/* get on-demand info */
4191	onDemandServiceID = SCNetworkConnectionCopyServiceID(connection);
4192	if (SCNetworkConnectionCopyOnDemandInfo(connection, &onDemandRemoteAddress, &onDemandStatus)) {
4193		if (onDemandRemoteAddress != NULL) {
4194			isOnDemandService = TRUE;
4195			ok = TRUE;
4196		}
4197	}
4198
4199	/* handle non-OnDemand App Layer VPN */
4200	if (isAppLayerVPN && !isOnDemandService) {
4201		SCLog(_sc_debug, LOG_INFO, CFSTR("%s  status  * = 0x%08x (App Layer VPN)"),
4202		      targetPrivate->log_prefix,
4203		      *flags);
4204		if (*flags & kSCNetworkReachabilityFlagsReachable) {
4205			// if VPN "server" is reachable
4206
4207			if (!(*flags & kSCNetworkReachabilityFlagsTransientConnection)) {
4208				// start w/clean flags if not already layered on a transient network
4209				*flags = kSCNetworkReachabilityFlagsReachable;
4210			}
4211
4212			*flags |= kSCNetworkReachabilityFlagsTransientConnection;
4213			if (onDemandStatus != kSCNetworkConnectionConnected) {
4214				*flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4215			}
4216
4217			SCLog(_sc_debug, LOG_INFO, CFSTR("%s  status    = isReachable%s"),
4218			      (onDemandStatus != kSCNetworkConnectionConnected)
4219					? " (after App Layer connect)" : "",
4220			      targetPrivate->log_prefix);
4221		}
4222
4223		success = TRUE;
4224		goto done;
4225	}
4226
4227	if (!_SC_CFEqual(targetPrivate->onDemandRemoteAddress, onDemandRemoteAddress) ||
4228	    !_SC_CFEqual(targetPrivate->onDemandServiceID, onDemandServiceID)) {
4229		if (targetPrivate->onDemandRemoteAddress != NULL) {
4230			CFRelease(targetPrivate->onDemandRemoteAddress);
4231			targetPrivate->onDemandRemoteAddress = NULL;
4232		}
4233
4234		if (targetPrivate->onDemandServer != NULL) {
4235			SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer, NULL, NULL);
4236			if (targetPrivate->dispatchQueue != NULL) {
4237				// unschedule
4238				__SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE);
4239			} else if (targetPrivate->rls != NULL) {
4240				CFIndex	i;
4241				CFIndex	n;
4242
4243				// unschedule
4244				n = CFArrayGetCount(targetPrivate->rlList);
4245				for (i = 0; i < n; i += 3) {
4246					CFRunLoopRef	rl	= (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
4247					CFStringRef	rlMode	= (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
4248
4249					__SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, rl, rlMode, TRUE);
4250				}
4251			}
4252
4253			CFRelease(targetPrivate->onDemandServer);
4254			targetPrivate->onDemandServer = NULL;
4255		}
4256
4257		if (targetPrivate->onDemandServiceID != NULL) {
4258			CFRelease(targetPrivate->onDemandServiceID);
4259			targetPrivate->onDemandServiceID = NULL;
4260		}
4261	}
4262
4263	if (ok) {
4264		if (onDemandStatus != kSCNetworkConnectionConnected) {
4265			/*
4266			 * if we have a VPN configuration matching the name *and* we need to
4267			 * bring the VPN up.  Combine our flags with those of the VPN server.
4268			 */
4269			if (targetPrivate->onDemandServer == NULL) {
4270				SCNetworkReachabilityPrivateRef	demandPrivate;
4271				CFMutableDictionaryRef		options;
4272
4273				options = CFDictionaryCreateMutable(NULL,
4274								    0,
4275								    &kCFTypeDictionaryKeyCallBacks,
4276								    &kCFTypeDictionaryValueCallBacks);
4277				CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, onDemandRemoteAddress);
4278				CFDictionarySetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandBypass, kCFBooleanTrue);
4279				if (targetPrivate->serverBypass) {
4280					CFDictionarySetValue(options, kSCNetworkReachabilityOptionServerBypass, kCFBooleanTrue);
4281				}
4282				targetPrivate->onDemandServer = SCNetworkReachabilityCreateWithOptions(NULL, options);
4283				CFRelease(options);
4284
4285				// indent OnDemand target
4286				demandPrivate = (SCNetworkReachabilityPrivateRef)targetPrivate->onDemandServer;
4287				strlcat(demandPrivate->log_prefix, ".... ", sizeof(demandPrivate->log_prefix));
4288
4289				if (targetPrivate->scheduled) {
4290					SCNetworkReachabilityContext	context	= { 0, NULL, CFRetain, CFRelease, CFCopyDescription };
4291
4292					context.info = (void *)target;
4293					SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer,
4294									 __SCNetworkReachabilityOnDemandCheckCallback,
4295									 &context);
4296
4297					// schedule server reachability to match that of the target
4298					if (targetPrivate->dispatchQueue != NULL) {
4299						__SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, NULL, NULL, targetPrivate->dispatchQueue, TRUE);
4300					} else {
4301						CFIndex	i;
4302						CFIndex	n;
4303
4304						n = CFArrayGetCount(targetPrivate->rlList);
4305						for (i = 0; i < n; i += 3) {
4306							CFRunLoopRef	rl	= (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
4307							CFStringRef	rlMode	= (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
4308
4309							__SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, rl, rlMode, NULL, TRUE);
4310						}
4311					}
4312				}
4313			}
4314
4315			SCLog(_sc_debug, LOG_INFO, CFSTR("%s  status  * = 0x%08x"),
4316			      targetPrivate->log_prefix,
4317			      *flags);
4318
4319
4320			if ((*flags & kSCNetworkReachabilityFlagsReachable) && !(*flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
4321				// if VPN "server" is [still] reachable
4322
4323				if (!(*flags & kSCNetworkReachabilityFlagsTransientConnection)) {
4324					// start w/clean flags if not already layered on a transient network
4325					*flags = kSCNetworkReachabilityFlagsReachable;
4326				}
4327
4328				*flags |= kSCNetworkReachabilityFlagsTransientConnection;
4329				*flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4330				*flags |= kSCNetworkReachabilityFlagsConnectionOnDemand;
4331
4332				// set 'InterventionRequired' if the OnDemand connection is paused
4333				if (SCNetworkConnectionIsOnDemandSuspended(connection)) {
4334					*flags |= kSCNetworkReachabilityFlagsInterventionRequired;
4335				}
4336
4337				if (_sc_debug) {
4338					SCLog(TRUE, LOG_INFO, CFSTR("%s  service * = %@"),
4339					      targetPrivate->log_prefix,
4340					      onDemandServiceID);
4341					SCLog(TRUE, LOG_INFO, CFSTR("%s  status    = isReachable (after OnDemand connect)"),
4342					      targetPrivate->log_prefix);
4343				}
4344
4345				success = TRUE;
4346			}
4347		}
4348
4349		if (onDemandRemoteAddress != NULL) {
4350			if (targetPrivate->onDemandRemoteAddress == NULL) {
4351				targetPrivate->onDemandRemoteAddress = CFRetain(onDemandRemoteAddress);
4352			}
4353		}
4354
4355		if (onDemandServiceID != NULL) {
4356			if (targetPrivate->onDemandServiceID == NULL) {
4357				targetPrivate->onDemandServiceID = CFRetain(onDemandServiceID);
4358			}
4359		}
4360	}
4361
4362    done:
4363
4364	if (onDemandServiceID != NULL) {
4365		CFRelease(onDemandServiceID);
4366	}
4367	if (onDemandRemoteAddress != NULL) {
4368		CFRelease(onDemandRemoteAddress);
4369	}
4370	if (connection != NULL) {
4371		CFRelease(connection);
4372	}
4373	if (selectOptions != NULL) {
4374		CFRelease(selectOptions);
4375	}
4376	return success;
4377}
4378
4379
4380/*
4381 * OnDemand configuration handling
4382 *
4383 * Notes :
4384 *
4385 * 1. We have a "contract" with discoveryd that for EVERY network
4386 *    or DNS configuration change that should warrant our [re-]starting
4387 *    a query, discoveryd will acknowledge the latest DNS configuration.
4388 *
4389 * 2. IPMonitor also posts a notification AFTER every network or DNS
4390 *    configuration change.
4391 *
4392 * 3. We use IPMonitor's "trailing edge" as a signal to restart any
4393 *    by-name queries.
4394 */
4395
4396
4397// Note: protected by _hn_target_queue()
4398static int			onDemand_refresh_token;
4399static Boolean			onDemand_refresh_token_valid	= FALSE;
4400
4401
4402/*
4403 * onDemand_refresh_handler
4404 *
4405 * Called to notify/update all SCNetworkReachability targets of
4406 * OnDemand changes.
4407 * - should be exec'd on the _hn_changes_queue()
4408 */
4409static void
4410onDemand_refresh_handler()
4411{
4412	CFArrayRef			changes;
4413	CFStringRef			key;
4414	__block SCDynamicStoreRef	store	= NULL;
4415
4416	dispatch_sync(_hn_target_queue(), ^{
4417		if (onDemand_refresh_token_valid && (hn_store != NULL)) {
4418			store = CFRetain(hn_store);
4419		}
4420	});
4421
4422	if (store == NULL) {
4423		return;
4424	}
4425
4426	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
4427							 kSCDynamicStoreDomainState,
4428							 kSCEntNetOnDemand);
4429	changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
4430	__SCNetworkReachabilityHandleChanges(store, changes, NULL);
4431	CFRelease(changes);
4432	CFRelease(key);
4433
4434	CFRelease(store);
4435	return;
4436}
4437
4438
4439/*
4440 * onDemand_refresh_enable
4441 *
4442 * Called to monitor for OnDemand changes.
4443 * - caller must be running on the _hn_target_queue()
4444 */
4445static Boolean
4446onDemand_refresh_enable(dispatch_queue_t q)
4447{
4448	uint32_t	status;
4449
4450	status = notify_register_dispatch(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY,
4451					  &onDemand_refresh_token,
4452					  q,
4453					  ^(int token){
4454						  onDemand_refresh_handler();
4455					  });
4456	if (status != NOTIFY_STATUS_OK) {
4457		SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed, status=%u"), status);
4458		return FALSE;
4459	}
4460
4461	onDemand_refresh_token_valid = TRUE;
4462
4463	return TRUE;
4464}
4465
4466
4467/*
4468 * onDemand_refresh_disable
4469 *
4470 * Called to stop monitoring for OnDemand changes
4471 * - caller must be running on the _hn_target_queue()
4472 */
4473static void
4474onDemand_refresh_disable()
4475{
4476	(void)notify_cancel(onDemand_refresh_token);
4477	onDemand_refresh_token_valid = FALSE;
4478	return;
4479}
4480
4481
4482
4483
4484#pragma mark -
4485#pragma mark Reachability Flags
4486
4487
4488static Boolean
4489__SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef	store_info,
4490				SCNetworkReachabilityRef	target,
4491				ReachabilityInfo		*reach_info,
4492				Boolean				async)
4493{
4494	CFMutableArrayRef		addresses	= NULL;
4495	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
4496	ReachabilityInfo		my_info		= NOT_REACHABLE;
4497	Boolean				ok		= TRUE;
4498
4499	MUTEX_ASSERT_HELD(&targetPrivate->lock);
4500
4501	_reach_set(reach_info, &NOT_REACHABLE, reach_info->cycle, targetPrivate->if_index, targetPrivate->if_name);
4502
4503	if (!isA_SCNetworkReachability(target)) {
4504		_SCErrorSet(kSCStatusInvalidArgument);
4505		return FALSE;
4506	}
4507
4508#if	TARGET_OS_IPHONE
4509	if (isReachabilityTypeName(targetPrivate->type) &&
4510	    !async &&
4511	    pthread_is_threaded_np() &&
4512	    pthread_main_np()) {
4513		SCLog(TRUE, LOG_WARNING, CFSTR("Warning: sync SCNetworkReachability (by-name) query on main thread"));
4514	}
4515#endif	// TARGET_OS_IPHONE
4516
4517	if (!targetPrivate->serverBypass) {
4518		if (!targetPrivate->serverActive) {
4519
4520			ok = __SCNetworkReachabilityServer_targetAdd(target);
4521			if (!ok) {
4522				targetPrivate->serverBypass = TRUE;
4523			}
4524		}
4525
4526		if (targetPrivate->serverActive) {
4527			ok = __SCNetworkReachabilityServer_targetStatus(target);
4528			if (!ok) {
4529				SCLog(TRUE, LOG_DEBUG,
4530				      CFSTR("__SCNetworkReachabilityGetFlags _targetStatus() failed"));
4531				_SCErrorSet(kSCStatusFailed);
4532				goto done;
4533			}
4534
4535			targetPrivate->cycle = targetPrivate->serverInfo.cycle;
4536			_reach_set(&my_info,
4537				   &targetPrivate->serverInfo,
4538				   targetPrivate->serverInfo.cycle,
4539				   targetPrivate->if_index,
4540				   targetPrivate->if_name);
4541			goto done;
4542		}
4543	}
4544
4545
4546	switch (targetPrivate->type) {
4547		case reachabilityTypeAddress :
4548		case reachabilityTypeAddressPair : {
4549			/*
4550			 * Check "local" address
4551			 */
4552			if (targetPrivate->localAddress != NULL) {
4553				/*
4554				 * Check "local" address
4555				 */
4556				ok = checkAddress(store_info,
4557						  targetPrivate->localAddress,
4558						  targetPrivate->if_index,
4559						  &my_info,
4560						  targetPrivate->log_prefix);
4561				if (!ok) {
4562					goto done2;	/* not today */
4563				}
4564
4565				if (!(my_info.flags & kSCNetworkReachabilityFlagsIsLocalAddress)) {
4566					goto done2;	/* not reachable, non-"local" address */
4567				}
4568			}
4569
4570			/*
4571			 * Check "remote" address
4572			 */
4573			if ((targetPrivate->remoteAddress != NULL) &&
4574			    (targetPrivate->localAddress != targetPrivate->remoteAddress)) {
4575				/*
4576				 * in cases where we have different "local" and "remote" addresses
4577				 * we need to re-initialize the to-be-returned flags.
4578				 */
4579				my_info = NOT_REACHABLE;
4580
4581				/*
4582				 * Check "remote" address
4583				 */
4584				ok = checkAddress(store_info,
4585						  targetPrivate->remoteAddress,
4586						  targetPrivate->if_index,
4587						  &my_info,
4588						  targetPrivate->log_prefix);
4589				if (!ok) {
4590					goto done2;	/* not today */
4591				}
4592			}
4593
4594			break;
4595
4596		}
4597
4598		case reachabilityTypeName :
4599		case reachabilityTypePTR  : {
4600			int				error;
4601			int				ns_dns_config	= -1;
4602			SCNetworkReachabilityFlags	ns_flags	= 0;
4603			uint32_t			ns_if_index	= 0;
4604
4605			addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
4606			if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
4607				/* if resolved or an error had been detected */
4608				if (!async) {
4609					/* if not an async request */
4610					goto checkResolvedAddresses;
4611				} else if (targetPrivate->dnsActive) {
4612					/* if [m]DNS query active */
4613					goto checkResolvedAddresses;
4614				} else if (!targetPrivate->needResolve) {
4615					/*
4616					 * if this is an async request (i.e. someone is watching the reachability
4617					 * of this target), if no query active, and if no query is needed
4618					 */
4619					goto checkResolvedAddresses;
4620				}
4621			}
4622
4623			if (!targetPrivate->onDemandBypass) {
4624				Boolean				onDemand;
4625				SCNetworkReachabilityFlags	onDemandFlags	= 0;
4626
4627				/*
4628				 * before we attempt our initial DNS query, check if there is
4629				 * an OnDemand configuration that we should be using.
4630				 */
4631				onDemand = __SCNetworkReachabilityOnDemandCheck(store_info, target, FALSE, &onDemandFlags);
4632				if (onDemand) {
4633					/* if OnDemand connection is needed */
4634					my_info.flags = onDemandFlags;
4635					goto done;
4636				}
4637			}
4638
4639			targetPrivate->dnsBlocked = FALSE;
4640
4641			/* update the reachability of the DNS servers */
4642			_SC_R_updateResolverReachability(store_info,
4643							 &ns_flags,
4644							 &targetPrivate->haveDNS,
4645							 targetPrivate->name,
4646							 targetPrivate->if_index,
4647							 &ns_if_index,
4648							 &ns_dns_config,
4649							 targetPrivate->log_prefix);
4650
4651
4652			// save resolver reachability flags
4653			targetPrivate->resolverFlags = ns_flags;
4654
4655			if (rankReachability(ns_flags) < 2) {
4656				/*
4657				 * if DNS servers are not (or are no longer) reachable, set
4658				 * flags based on the availability of configured (but not
4659				 * active) services.
4660				 */
4661
4662				SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"),
4663				      targetPrivate->log_prefix);
4664
4665				if (!targetPrivate->dnsBlocked) {
4666					ok = checkAddress(store_info,
4667							  NULL,
4668							  targetPrivate->if_index,
4669							  &my_info,
4670							  targetPrivate->log_prefix);
4671					if (!ok) {
4672						SCLog(_sc_debug, LOG_INFO, CFSTR("%sNo available networks"),
4673						      targetPrivate->log_prefix);
4674						goto done2;
4675					}
4676				} else {
4677					// if not checking "available" networks
4678					my_info.flags = ns_flags;
4679					my_info.if_index = ns_if_index;
4680				}
4681
4682				if (async && targetPrivate->scheduled) {
4683					/*
4684					 * return "host not found", set flags appropriately,
4685					 * and schedule notification.
4686					 */
4687					__SCNetworkReachabilitySetResolvedError(target, EAI_NONAME);
4688					my_info.flags |= (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending);
4689
4690					SCLog(_sc_debug, LOG_INFO, CFSTR("%sno DNS servers are reachable"),
4691					      targetPrivate->log_prefix);
4692					__SCNetworkReachabilityUpdate(target);
4693				}
4694
4695				break;
4696			}
4697
4698			if (targetPrivate->resolverBypass) {
4699				if (targetPrivate->haveDNS) {
4700					/*
4701					 * if we are not resolving the name, and if we have
4702					 * one or more DNS resolvers, then return flags that
4703					 * reflect the reachability of the resolvers (and
4704					 * not the actual name).
4705					 */
4706					my_info.flags = ns_flags;
4707					my_info.if_index = ns_if_index;
4708				}
4709				break;
4710			}
4711
4712			if (async) {
4713				/* for async requests we return the last known status */
4714				my_info = targetPrivate->info;
4715
4716				if (targetPrivate->dnsActive) {
4717					/* if [m]DNS query active */
4718					if (_sc_debug && !targetPrivate->quiet) {
4719						SCLog(TRUE, LOG_INFO,
4720						      CFSTR("%swaiting for DNS reply"),
4721						      targetPrivate->log_prefix);
4722					}
4723					if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
4724						/* updated reachability based on the previous reply */
4725						goto checkResolvedAddresses;
4726					}
4727					break;
4728				}
4729
4730				SCLog(_sc_debug, LOG_INFO,
4731				      CFSTR("%sstart DNS query for name = %s"),
4732				      targetPrivate->log_prefix,
4733				      targetPrivate->name);
4734
4735				/*
4736				 * initiate an DNS query w/DNSServiceGetAddrInfo
4737				 */
4738				enqueueDNSQuery(target);
4739				break;
4740			}
4741
4742			SCLog(_sc_debug, LOG_INFO,
4743			      CFSTR("%scheckName(%s)"),
4744			      targetPrivate->log_prefix,
4745			      targetPrivate->name);
4746
4747			/*
4748			 * OK, all of the DNS name servers are available.  Let's
4749			 * resolve the nodename into an address.
4750			 */
4751			sync_DNS_query(target);
4752
4753			if (!(targetPrivate->dnsHaveTimeout && targetPrivate->dnsHaveError)) {
4754				// if target reach info is valid
4755				memcpy(reach_info, &targetPrivate->info, sizeof(ReachabilityInfo));
4756				goto done2;
4757			}
4758
4759			if (addresses != NULL)	CFRelease(addresses);
4760			addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
4761
4762		    checkResolvedAddresses :
4763
4764			/*
4765			 * We first assume that the requested host is NOT available.
4766			 * Then, check each address for accessibility and return the
4767			 * best status available.
4768			 */
4769			my_info = NOT_REACHABLE;
4770
4771			if ((targetPrivate->type == reachabilityTypeName) && isA_CFArray(addresses)) {
4772				CFIndex		i;
4773				CFIndex		n	= CFArrayGetCount(addresses);
4774				struct sockaddr	*sa;
4775
4776				for (i = 0; i < n; i++) {
4777					ReachabilityInfo	ns_info	= NOT_REACHABLE;
4778
4779					sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
4780
4781					ok = checkAddress(store_info,
4782							  sa,
4783							  targetPrivate->if_index,
4784							  &ns_info,
4785							  targetPrivate->log_prefix);
4786					if (!ok) {
4787						goto done2;	/* not today */
4788					}
4789
4790					if (rankReachability(ns_info.flags) > rankReachability(my_info.flags)) {
4791						/* return the best case result */
4792						my_info = ns_info;
4793						if (rankReachability(my_info.flags) == 2) {
4794							/* can't get any better than REACHABLE */
4795							break;
4796						}
4797					}
4798				}
4799
4800				if (_sc_debug) {
4801					for (i++; i < n; i++) {
4802						sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
4803						log_address("skipAddress",
4804							    sa,
4805							    targetPrivate->if_index,
4806							    targetPrivate->log_prefix);
4807					}
4808				}
4809			} else if ((targetPrivate->type == reachabilityTypePTR) && isA_CFArray(addresses)) {
4810				CFIndex		i;
4811				CFIndex		n	= CFArrayGetCount(addresses);
4812
4813				my_info = NOT_REACHABLE;
4814
4815				for (i = 0; i < n; i++) {
4816					if (i == 0) {
4817						my_info.flags = kSCNetworkReachabilityFlagsReachable;
4818					}
4819
4820					if (_sc_debug) {
4821						CFStringRef	ptrName;
4822
4823						ptrName = CFArrayGetValueAtIndex(addresses, i);
4824						SCLog(TRUE, LOG_INFO, CFSTR("%sPTR name(%@)"),
4825						      targetPrivate->log_prefix,
4826						      ptrName);
4827					}
4828				}
4829			} else {
4830				if ((error == EAI_NONAME)
4831#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
4832				     || (error == EAI_NODATA)
4833#endif
4834				     ) {
4835					/*
4836					 * the target host name could not be resolved
4837					 */
4838					if (!targetPrivate->onDemandBypass) {
4839						Boolean				onDemand;
4840						SCNetworkReachabilityFlags	onDemandFlags	= 0;
4841
4842						/*
4843						 * our initial DNS query failed, check again to see if there
4844						 * there is an OnDemand configuration that we should be using.
4845						 */
4846						onDemand = __SCNetworkReachabilityOnDemandCheck(store_info, target, TRUE, &onDemandFlags);
4847						if (onDemand) {
4848							/* if OnDemand connection is needed */
4849							my_info.flags = onDemandFlags;
4850							goto done;
4851						}
4852					}
4853
4854
4855					if (!targetPrivate->haveDNS) {
4856						/*
4857						 * No DNS servers are defined. Set flags based on
4858						 * the availability of configured (but not active)
4859						 * services.
4860						 */
4861						ok = checkAddress(store_info,
4862								  NULL,
4863								  targetPrivate->if_index,
4864								  &my_info,
4865								  targetPrivate->log_prefix);
4866						if (!ok) {
4867							goto done2;	/* not today */
4868						}
4869
4870						if ((my_info.flags & kSCNetworkReachabilityFlagsReachable) &&
4871							(my_info.flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
4872							/*
4873							 * Since we might pick up a set of DNS servers when this connection
4874							 * is established, don't reply with a "HOST NOT FOUND" error just yet.
4875							 */
4876							break;
4877						}
4878
4879						/* Host not found, not reachable! */
4880						my_info = NOT_REACHABLE;
4881					}
4882				}
4883			}
4884
4885			break;
4886		}
4887	}
4888
4889    done:
4890
4891
4892	_reach_set(reach_info, &my_info, targetPrivate->cycle, targetPrivate->if_index, targetPrivate->if_name);
4893
4894    done2 :
4895
4896	if (addresses != NULL)	CFRelease(addresses);
4897	return ok;
4898}
4899
4900int
4901SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target)
4902{
4903	int				if_index	= -1;
4904	Boolean				ok		= TRUE;
4905	ReachabilityStoreInfo		store_info;
4906	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
4907
4908	if (!isA_SCNetworkReachability(target)) {
4909		_SCErrorSet(kSCStatusInvalidArgument);
4910		return if_index;
4911	}
4912
4913	ReachabilityStoreInfo_init(&store_info);
4914
4915	MUTEX_LOCK(&targetPrivate->lock);
4916
4917	if (targetPrivate->scheduled) {
4918		// if being watched, return the last known (and what should be current) status
4919		goto done;
4920	}
4921
4922
4923	ok = __SCNetworkReachabilityGetFlags(&store_info, target, &targetPrivate->info, FALSE);
4924
4925    done :
4926
4927	/* Only return the if_index if the connection is reachable not for reachable connection
4928	 * required etc ... */
4929	if (ok && rankReachability(targetPrivate->info.flags) == 2) {
4930		if_index = targetPrivate->info.if_index;
4931	}
4932
4933	MUTEX_UNLOCK(&targetPrivate->lock);
4934	ReachabilityStoreInfo_free(&store_info);
4935	return if_index;
4936}
4937
4938
4939Boolean
4940SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef		target,
4941			      SCNetworkReachabilityFlags	*flags)
4942{
4943	Boolean				ok		= TRUE;
4944	ReachabilityStoreInfo		store_info;
4945	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
4946
4947	if (!isA_SCNetworkReachability(target)) {
4948		_SCErrorSet(kSCStatusInvalidArgument);
4949		return FALSE;
4950	}
4951
4952	ReachabilityStoreInfo_init(&store_info);
4953
4954	MUTEX_LOCK(&targetPrivate->lock);
4955
4956	if (targetPrivate->scheduled) {
4957		// if being watched, return the last known (and what should be current) status
4958		*flags = targetPrivate->info.flags & kSCNetworkReachabilityFlagsMask;
4959
4960		if (isReachabilityTypeName(targetPrivate->type) && targetPrivate->dnsNoAddressesSinceLastTimeout) {
4961			targetPrivate->needResolve = TRUE;
4962			ReachabilityInfo tmp_reach_info	= NOT_REACHABLE;
4963			__SCNetworkReachabilityGetFlags(&store_info, target, &tmp_reach_info, TRUE);
4964		}
4965
4966		goto done;
4967	}
4968
4969
4970	ok = __SCNetworkReachabilityGetFlags(&store_info, target, &targetPrivate->info, FALSE);
4971	if (_sc_debug) {
4972		SCLog(TRUE, LOG_INFO, CFSTR("%s  flags     = 0x%08x"), targetPrivate->log_prefix, targetPrivate->info.flags);
4973	}
4974
4975	*flags = targetPrivate->info.flags & kSCNetworkReachabilityFlagsMask;
4976
4977    done :
4978
4979	MUTEX_UNLOCK(&targetPrivate->lock);
4980	ReachabilityStoreInfo_free(&store_info);
4981	return ok;
4982}
4983
4984
4985#pragma mark -
4986#pragma mark Notifications
4987
4988
4989/*
4990 * __SCNetworkReachabilityHandleChanges
4991 *
4992 * Called to process network configuration changes and determine
4993 * if a reachability notification is warranted.
4994 * - should be exec'd on the _hn_changes_queue()
4995 */
4996static void
4997__SCNetworkReachabilityHandleChanges(SCDynamicStoreRef	store,
4998				     CFArrayRef		changedKeys,
4999				     void		*info)
5000{
5001	Boolean			dnsConfigChanged	= FALSE;
5002	CFIndex			i;
5003	Boolean			forcedChange		= FALSE;
5004	CFStringRef		key;
5005	Boolean			match;
5006	CFIndex			nChanges;
5007	CFIndex			nGlobals		= 0;
5008	CFIndex			nTargets;
5009	Boolean			neChanged		= FALSE;
5010	Boolean			networkConfigChanged	= FALSE;
5011	struct timeval		now;
5012	Boolean			onDemandConfigChanged	= FALSE;
5013#if	!TARGET_OS_IPHONE
5014	Boolean			powerStatusChanged	= FALSE;
5015#endif	// !TARGET_OS_IPHONE
5016	ReachabilityStoreInfo	store_info;
5017	const void *		targets_q[N_QUICK];
5018	const void **		targets			= targets_q;
5019	__block CFSetRef	watchers		= NULL;
5020
5021	nChanges = CFArrayGetCount(changedKeys);
5022	if (nChanges == 0) {
5023		/* if no changes */
5024		return;
5025	}
5026
5027
5028	dispatch_sync(_hn_target_queue(), ^{
5029		/* grab the currently watched targets */
5030		if (hn_targets != NULL) {
5031			watchers = CFSetCreateCopy(NULL, hn_targets);
5032		}
5033	});
5034
5035	nTargets = (watchers != NULL) ? CFSetGetCount(watchers) : 0;
5036	if (nTargets == 0) {
5037		/* if no addresses being monitored */
5038		goto done;
5039	}
5040
5041	/* grab the current time */
5042	(void)gettimeofday(&now, NULL);
5043
5044#if	!TARGET_OS_IPHONE
5045	match = CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), power_changed_key);
5046	if (match) {
5047		/* handle "early" wake notification */
5048		nGlobals++;
5049		powerStatusChanged = TRUE;
5050	}
5051#endif	// !TARGET_OS_IPHONE
5052
5053	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
5054							 kSCDynamicStoreDomainState,
5055							 kSCEntNetDNS);
5056	match = CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key);
5057	CFRelease(key);
5058	if (match) {
5059		nGlobals++;
5060		dnsConfigChanged = TRUE;	/* the DNS server(s) have changed */
5061	}
5062
5063	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
5064							 kSCDynamicStoreDomainState,
5065							 kSCEntNetOnDemand);
5066	match = CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key);
5067	CFRelease(key);
5068	if (match) {
5069		nGlobals++;
5070		onDemandConfigChanged = TRUE;	/* the OnDemand configuration has changed */
5071
5072		// force OnDemand configuration refresh (if SC notification arrives before BSD notify)
5073		__SCNetworkConnectionForceOnDemandConfigurationRefresh();
5074	}
5075
5076
5077
5078	match = CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), SCNETWORKREACHABILITY_TRIGGER_KEY);
5079	if (match) {
5080		nGlobals++;
5081		forcedChange = TRUE;		/* an SCDynamicStore driven "network" change */
5082	}
5083
5084	if (nChanges > nGlobals) {
5085		networkConfigChanged = TRUE;
5086	}
5087
5088	if (_sc_debug) {
5089		unsigned int		changes			= 0;
5090		static const char	*change_strings[]	= {
5091			// with no "power" status change
5092			"",						// 00000
5093			"network ",					// 00001
5094			"DNS ",						// 00010
5095			"network and DNS ",				// 00011
5096			"OnDemand ",					// 00100
5097			"network and OnDemand ",			// 00101
5098			"DNS and OnDemand ",				// 00110
5099			"network, DNS, and OnDemand ",			// 00111
5100			"NE ",						// 01000
5101			"network and NE ",				// 01001
5102			"DNS and NE ",					// 01010
5103			"network, DNS, and NE ",			// 01011
5104			"OnDemand and NE ",				// 01100
5105			"network, OnDemand, and NE ",			// 01101
5106			"DNS, OnDemand, and NE ",			// 01110
5107			"network, DNS, OnDemand, and NE ",		// 01111
5108#if	!TARGET_OS_IPHONE
5109			// with "power" status change
5110			"power",					// 10000
5111			"network and power ",				// 10001
5112			"DNS and power ",				// 10010
5113			"network, DNS, and power ",			// 10011
5114			"OnDemand and power ",				// 10100
5115			"network, OnDemand, and power ",		// 10101
5116			"DNS, OnDemand, and power ",			// 10110
5117			"network, DNS, OnDemand, and power ",		// 10111
5118			"NE and power ",				// 11000
5119			"network, NE, and power ",			// 11001
5120			"DNS, NE, and power ",				// 11010
5121			"network, DNS, NE, and power ",			// 11011
5122			"OnDemand, NE, and power ",			// 11100
5123			"network, OnDemand, NE, and power ",		// 11101
5124			"DNS, OnDemand, NE, and power ",		// 11110
5125			"network, DNS, OnDemand, NE, and power ",	// 11111
5126#endif	// !TARGET_OS_IPHONE
5127		};
5128
5129#if	!TARGET_OS_IPHONE
5130		#define	PWR	16
5131		if (powerStatusChanged) {
5132			changes |= PWR;
5133		}
5134#endif	// !TARGET_OS_IPHONE
5135
5136		#define	NE	8
5137		if (neChanged) {
5138			changes |= NE;
5139		}
5140
5141		#define	VOD	4
5142		if (onDemandConfigChanged) {
5143			changes |= VOD;
5144		}
5145
5146		#define	DNS	2
5147		if (dnsConfigChanged) {
5148			changes |= DNS;
5149		}
5150
5151		#define	NET	1
5152		if (networkConfigChanged) {
5153			changes |= NET;
5154		}
5155
5156		SCLog(TRUE, LOG_INFO,
5157		      CFSTR("process %s%s%sconfiguration change"),
5158		      forcedChange ? "[forced] " : "",
5159		      change_strings[changes]);
5160	}
5161
5162	ReachabilityStoreInfo_init(&store_info);
5163
5164	if (nTargets > (CFIndex)(sizeof(targets_q) / sizeof(CFTypeRef)))
5165		targets = CFAllocatorAllocate(NULL, nTargets * sizeof(CFTypeRef), 0);
5166	CFSetGetValues(watchers, targets);
5167	for (i = 0; i < nTargets; i++) {
5168		Boolean				dnsNeedsUpdate	= FALSE;
5169		SCNetworkReachabilityRef	target		= targets[i];
5170		SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
5171
5172		MUTEX_LOCK(&targetPrivate->lock);
5173
5174
5175		if (dnsConfigChanged) {
5176			targetPrivate->last_dns = now;
5177		}
5178
5179		if (networkConfigChanged) {
5180			targetPrivate->last_network = now;
5181		}
5182
5183#if	!TARGET_OS_IPHONE
5184		if (powerStatusChanged) {
5185			targetPrivate->last_power = now;
5186		}
5187#endif	// !TARGET_OS_IPHONE
5188
5189		if (isReachabilityTypeName(targetPrivate->type)) {
5190			Boolean		dnsChanged	= (dnsConfigChanged	 |
5191							   dnsNeedsUpdate	 |
5192							   onDemandConfigChanged |
5193							   neChanged);
5194
5195			if (!dnsChanged) {
5196				/*
5197				 * if the DNS configuration didn't change we still need to
5198				 * check that the DNS servers are accessible.
5199				 */
5200				Boolean				ns_blocked	= FALSE;
5201				int				ns_dns_config	= -1;
5202				SCNetworkReachabilityFlags	ns_flags	= 0;
5203				uint32_t			ns_if_index	= 0;
5204				Boolean				ok;
5205
5206				/* update the reachability of the DNS servers */
5207				ok = ReachabilityStoreInfo_update(&store_info, &store, AF_UNSPEC);
5208				if (ok) {
5209					_SC_R_updateResolverReachability(&store_info,
5210									 &ns_flags,
5211									 &targetPrivate->haveDNS,
5212									 targetPrivate->name,
5213									 targetPrivate->if_index,
5214									 &ns_if_index,
5215									 &ns_dns_config,
5216									 targetPrivate->log_prefix);
5217				} else {
5218					ns_flags = kSCNetworkReachabilityFlagsReachable;
5219					dnsChanged = TRUE;
5220				}
5221
5222
5223				if (rankReachability(ns_flags) < 2) {
5224					/*
5225					 * if DNS servers are not (or are no longer) reachable, set
5226					 * flags based on the availability of configured (but not
5227					 * active) services.
5228					 */
5229					SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"),
5230					      targetPrivate->log_prefix);
5231					dnsChanged = TRUE;
5232				}
5233
5234				if ((targetPrivate->dnsBlocked != ns_blocked) ||
5235				    (targetPrivate->resolverFlags != ns_flags)) {
5236					// if the DNS blocked or resolver reachability changed
5237					targetPrivate->dnsBlocked = ns_blocked;
5238					targetPrivate->resolverFlags = ns_flags;
5239					dnsChanged = TRUE;
5240				}
5241			}
5242
5243			if (dnsChanged) {
5244				if (targetPrivate->dnsActive) {
5245					// if we have an outstanding [m]DNS query
5246					SCLog(_sc_debug, LOG_INFO,
5247					      CFSTR("%scancel [m]DNS query for name = %s"),
5248					      targetPrivate->log_prefix,
5249					      targetPrivate->name);
5250					dequeueDNSQuery(target);
5251				}
5252
5253				/* schedule request to resolve the name again */
5254				targetPrivate->needResolve = TRUE;
5255			}
5256		}
5257
5258		if (forcedChange) {
5259			targetPrivate->cycle++;
5260		}
5261
5262		if (targetPrivate->scheduled) {
5263			__SCNetworkReachabilityUpdate(target);
5264		}
5265
5266		MUTEX_UNLOCK(&targetPrivate->lock);
5267	}
5268	if (targets != targets_q)	CFAllocatorDeallocate(NULL, targets);
5269
5270	ReachabilityStoreInfo_free(&store_info);
5271
5272    done :
5273
5274	if (watchers != NULL) CFRelease(watchers);
5275	return;
5276}
5277
5278
5279/*
5280 * __SCNetworkReachabilityHandleStoreChanges
5281 *
5282 * Called to process SCDynamicStore network configuration changes.
5283 * - should be exec'd on the _hn_changes_queue()
5284 */
5285static void
5286__SCNetworkReachabilityHandleStoreChanges(SCDynamicStoreRef	store,
5287					  CFArrayRef		changedKeys,
5288					  void			*info)
5289{
5290	nwi_state_t	nwi_state;
5291
5292	if ((CFArrayGetCount(changedKeys) == 1) &&
5293	    CFArrayContainsValue(changedKeys, CFRangeMake(0, 1), SCNETWORKREACHABILITY_TRIGGER_KEY)) {
5294		goto update;
5295	}
5296
5297	/* "something" [else] changed, start fresh */
5298	ReachabilityStoreInfo_save(NULL);
5299
5300	nwi_state = nwi_state_copy();
5301	if (nwi_state != NULL) {
5302		// if we have some networking
5303		nwi_state_release(nwi_state);
5304		return;
5305	}
5306
5307	// if no networking, use the [SC] changes to add/update
5308	// the kSCNetworkReachabilityFlagsConnectionRequired flag
5309
5310    update :
5311
5312	__SCNetworkReachabilityHandleChanges(store, changedKeys, info);
5313	return;
5314}
5315
5316
5317#if	!TARGET_OS_IPHONE
5318
5319static Boolean
5320darkWakeNotify(SCNetworkReachabilityRef target)
5321{
5322	return FALSE;
5323}
5324
5325
5326static Boolean
5327systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities)
5328{
5329	if ((power_capabilities & POWER_CAPABILITIES_NETWORK) != POWER_CAPABILITIES_NETWORK) {
5330		// if we're not fully awake (from a networking point of view).
5331		return FALSE;
5332	}
5333
5334	return TRUE;
5335}
5336
5337#endif	// !TARGET_OS_IPHONE
5338
5339
5340static void
5341reachPerform(void *info)
5342{
5343	void				*context_info;
5344	void				(*context_release)(const void *);
5345	unsigned int			n;
5346	ReachabilityInfo		reach_info;
5347	SCNetworkReachabilityCallBack	rlsFunction;
5348	SCNetworkReachabilityRef	target		= (SCNetworkReachabilityRef)info;
5349	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
5350
5351	n = _SC_ATOMIC_ZERO(&targetPrivate->pending);
5352	if (_sc_debug && (n > 1)) {
5353		SCLog(TRUE, LOG_DEBUG,
5354		      CFSTR("%sdelivering SCNetworkReachability notifications (%u)"),
5355		      targetPrivate->log_prefix,
5356		      n);
5357	}
5358
5359	MUTEX_LOCK(&targetPrivate->lock);
5360
5361	if (!targetPrivate->scheduled) {
5362		// if no longer scheduled
5363		SCLog(_sc_debug, LOG_DEBUG,
5364		      CFSTR("%sskipping SCNetworkReachability callback, no longer scheduled"),
5365		      targetPrivate->log_prefix);
5366		MUTEX_UNLOCK(&targetPrivate->lock);
5367		return;
5368	}
5369
5370	// capture current state
5371	memcpy(&reach_info, &targetPrivate->info, sizeof(ReachabilityInfo));
5372
5373	// callout
5374	rlsFunction = targetPrivate->rlsFunction;
5375	if (targetPrivate->rlsContext.retain != NULL) {
5376		context_info	= (void *)(*targetPrivate->rlsContext.retain)(targetPrivate->rlsContext.info);
5377		context_release	= targetPrivate->rlsContext.release;
5378	} else {
5379		context_info	= targetPrivate->rlsContext.info;
5380		context_release	= NULL;
5381	}
5382
5383	// update last notification info
5384	_reach_set(&targetPrivate->last_notify, &reach_info, targetPrivate->cycle, targetPrivate->if_index, targetPrivate->if_name);
5385	(void)gettimeofday(&targetPrivate->last_push, NULL);
5386
5387	MUTEX_UNLOCK(&targetPrivate->lock);
5388
5389	if (rlsFunction != NULL) {
5390		(*rlsFunction)(target,
5391			       reach_info.flags & kSCNetworkReachabilityFlagsMask,
5392			       context_info);
5393	}
5394
5395	if (context_release != NULL) {
5396		(*context_release)(context_info);
5397	}
5398
5399	return;
5400}
5401
5402
5403/*
5404 * reachUpdate
5405 *
5406 * - caller must *not* be holding the target lock
5407 * - caller must be running on the __SCNetworkReachability_concurrent_queue()
5408 */
5409static Boolean
5410reachUpdate(SCNetworkReachabilityRef target)
5411{
5412	uint64_t			cycle;
5413	Boolean				defer		= FALSE;
5414	Boolean				forced;
5415	Boolean				ok;
5416	ReachabilityInfo		reach_info	= NOT_REACHABLE;
5417	ReachabilityStoreInfo		store_info;
5418	Boolean				target_debug;
5419	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
5420
5421	target_debug = (_sc_debug && !targetPrivate->quiet);
5422
5423	if (target_debug) {
5424		SCLog(TRUE, LOG_INFO, CFSTR("%schecking target reachability"),
5425		      targetPrivate->log_prefix);
5426	}
5427
5428
5429	MUTEX_LOCK(&targetPrivate->lock);
5430
5431	if (!targetPrivate->scheduled) {
5432		// if not currently scheduled
5433		MUTEX_UNLOCK(&targetPrivate->lock);
5434		return FALSE;
5435	}
5436
5437	/* update reachability, notify if status changed */
5438	ReachabilityStoreInfo_init(&store_info);
5439	ok = __SCNetworkReachabilityGetFlags(&store_info, target, &reach_info, TRUE);
5440	ReachabilityStoreInfo_free(&store_info);
5441	if (!ok) {
5442		/* if reachability status not available */
5443		if (target_debug) {
5444			SCLog(TRUE, LOG_INFO, CFSTR("%sflags not available"),
5445			      targetPrivate->log_prefix);
5446		}
5447		reach_info = NOT_REACHABLE;
5448	}
5449
5450#if	!TARGET_OS_IPHONE
5451	/*
5452	 * We want to defer the notification if this is a maintenance wake *and*
5453	 * the reachability flags that we would be reporting to the application
5454	 * are better than those that we last reported.
5455	 */
5456	if (!systemIsAwake(power_capabilities)) {
5457		/* if this is a maintenace wake */
5458		reach_info.sleeping = TRUE;
5459
5460		if (rankReachability(reach_info.flags) >= rankReachability(targetPrivate->info.flags)) {
5461			/*
5462			 * don't report the change if the new reachability flags are
5463			 * the same or "better"
5464			 */
5465			defer = !darkWakeNotify(target);
5466		} else if (!__reach_changed(&targetPrivate->last_notify, &reach_info)) {
5467			/*
5468			 * if we have already posted this change
5469			 */
5470			defer = !darkWakeNotify(target);
5471		}
5472	}
5473#endif	// !TARGET_OS_IPHONE
5474
5475	cycle = targetPrivate->cycle;
5476	forced = ((cycle != 0) && (targetPrivate->info.cycle != cycle));
5477
5478	/*
5479	 *	  (A)	  (B)	  (C)
5480	 *	 reach		resolve
5481	 *	changed	forced	pending
5482	 *	=======	=======	=======
5483	 *	   N	   N	   N	No change
5484	 *	   N	   N	   Y	No change
5485	 *	   N	   Y	   N	Change		(forced && !resolve pending)
5486	 *	   N	   Y	   Y	No change	(suppress forced w/resolve pending)
5487	 *	   Y	  N/A	  N/A	Change
5488	 *	   Y	  N/A	  N/A	Change
5489	 *	   Y	  N/A	  N/A	Change
5490	 *	   Y	  N/A	  N/A	Change
5491	 *
5492	 *	Change    == A || (B && !C)
5493	 *	No Change == !(A || (B && !C))
5494	 *	No Change == !A && !(B && !C)
5495	 *	No Change == !A && (!B || C)
5496	 *	No Change == (!B || C) && !A
5497	 */
5498	if ((!forced || (reach_info.flags == kSCNetworkReachabilityFlagsFirstResolvePending))
5499	    && !__reach_changed(&targetPrivate->info, &reach_info)) {
5500		if (target_debug) {
5501			if (targetPrivate->info.sleeping == reach_info.sleeping) {
5502				SCLog(TRUE, LOG_INFO,
5503				      CFSTR("%sflags/interface match (now 0x%08x/%u%s)%s%s"),
5504				      targetPrivate->log_prefix,
5505				      reach_info.flags,
5506				      reach_info.if_index,
5507				      reach_info.sleeping ? ", z" : "",
5508				      defer ? ", deferred" : "",
5509				      forced ? ", forced" : "");
5510			} else {
5511				SCLog(TRUE, LOG_INFO,
5512				      CFSTR("%sflags/interface equiv (was 0x%08x/%u%s, now 0x%08x/%u%s)%s%s"),
5513				      targetPrivate->log_prefix,
5514				      targetPrivate->info.flags,
5515				      targetPrivate->info.if_index,
5516				      targetPrivate->info.sleeping ? ", z" : "",
5517				      reach_info.flags,
5518				      reach_info.if_index,
5519				      reach_info.sleeping ? ", z" : "",
5520				      defer ? ", deferred" : "",
5521				      forced ? ", forced" : "");
5522			}
5523		}
5524		MUTEX_UNLOCK(&targetPrivate->lock);
5525		return FALSE;
5526	}
5527
5528	if (target_debug) {
5529		SCLog(TRUE, LOG_INFO,
5530		      CFSTR("%sflags/interface have changed (was 0x%08x/%u%s, now 0x%08x/%u%s)%s%s"),
5531		      targetPrivate->log_prefix,
5532		      targetPrivate->info.flags,
5533		      targetPrivate->info.if_index,
5534		      targetPrivate->info.sleeping ? ", z" : "",
5535		      reach_info.flags,
5536		      reach_info.if_index,
5537		      reach_info.sleeping ? ", z" : "",
5538		      defer ? ", deferred" : "",
5539		      forced ? ", forced" : "");
5540	}
5541
5542	/* update flags / interface */
5543	_reach_set(&targetPrivate->info, &reach_info, cycle, targetPrivate->if_index, targetPrivate->if_name);
5544
5545	/* as needed, defer the notification */
5546	if (defer) {
5547		MUTEX_UNLOCK(&targetPrivate->lock);
5548		return FALSE;
5549	}
5550
5551	MUTEX_UNLOCK(&targetPrivate->lock);
5552
5553	return TRUE;
5554}
5555
5556
5557Boolean
5558SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef	target,
5559				 SCNetworkReachabilityCallBack	callout,
5560				 SCNetworkReachabilityContext	*context)
5561{
5562	SCNetworkReachabilityPrivateRef	targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5563
5564	MUTEX_LOCK(&targetPrivate->lock);
5565
5566	if (targetPrivate->rlsContext.release != NULL) {
5567		/* let go of the current context */
5568		(*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
5569	}
5570
5571	targetPrivate->rlsFunction   			= callout;
5572	targetPrivate->rlsContext.info			= NULL;
5573	targetPrivate->rlsContext.retain		= NULL;
5574	targetPrivate->rlsContext.release		= NULL;
5575	targetPrivate->rlsContext.copyDescription	= NULL;
5576	if (context) {
5577		bcopy(context, &targetPrivate->rlsContext, sizeof(SCNetworkReachabilityContext));
5578		if (context->retain != NULL) {
5579			targetPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
5580		}
5581	}
5582
5583	MUTEX_UNLOCK(&targetPrivate->lock);
5584
5585	return TRUE;
5586}
5587
5588
5589static CFStringRef
5590reachRLSCopyDescription(const void *info)
5591{
5592	SCNetworkReachabilityRef		target	= (SCNetworkReachabilityRef)info;
5593
5594	return CFStringCreateWithFormat(NULL,
5595					NULL,
5596					CFSTR("<SCNetworkReachability RLS> {target = %p}"),
5597					target);
5598}
5599
5600
5601static Boolean
5602__SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef	target,
5603					   CFRunLoopRef			runLoop,
5604					   CFStringRef			runLoopMode,
5605					   dispatch_queue_t		queue,
5606					   Boolean			onDemand)
5607{
5608	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
5609	Boolean				init		= FALSE;
5610	__block Boolean			ok		= FALSE;
5611
5612	MUTEX_LOCK(&targetPrivate->lock);
5613
5614	if ((targetPrivate->dispatchQueue != NULL) ||		// if we are already scheduled with a dispatch queue
5615	    ((queue != NULL) && targetPrivate->scheduled)) {	// if we are already scheduled on a CFRunLoop
5616		_SCErrorSet(kSCStatusInvalidArgument);
5617		goto done;
5618	}
5619
5620	if (!targetPrivate->serverBypass) {
5621		if (!targetPrivate->serverActive) {
5622
5623			ok = __SCNetworkReachabilityServer_targetAdd(target);
5624			if (!ok) {
5625				targetPrivate->serverBypass = TRUE;
5626			}
5627		}
5628
5629		if (targetPrivate->serverActive) {
5630			if (targetPrivate->scheduled) {
5631				// if already scheduled
5632				goto watch;
5633			}
5634
5635			ok = __SCNetworkReachabilityServer_targetSchedule(target);
5636			if (!ok) {
5637				SCLog(TRUE, LOG_DEBUG,
5638				      CFSTR("__SCNetworkReachabilityScheduleWithRunLoop _targetMonitor() failed"));
5639				_SCErrorSet(kSCStatusFailed);
5640				goto done;
5641			}
5642
5643			goto watch;
5644		}
5645	}
5646
5647	/* schedule the SCNetworkReachability did-something-change handler */
5648
5649	dispatch_sync(_hn_target_queue(), ^{
5650		ok = FALSE;
5651
5652		if (!onDemand && (hn_store == NULL)) {
5653			CFMutableArrayRef	keys;
5654			CFMutableArrayRef	patterns;
5655			Boolean			watch_dns_configuration		= FALSE;
5656			Boolean			watch_dns_changes		= FALSE;
5657			Boolean			watch_nwi			= FALSE;
5658			Boolean			watch_onDemand_networking	= FALSE;
5659#if	!TARGET_OS_IPHONE
5660			Boolean			watch_power			= FALSE;
5661#endif	// !TARGET_OS_IPHONE
5662
5663			hn_store = SCDynamicStoreCreate(NULL,
5664							CFSTR("SCNetworkReachability"),
5665							__SCNetworkReachabilityHandleStoreChanges,
5666							NULL);
5667			if (hn_store == NULL) {
5668				SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreCreate() failed"));
5669				return;
5670			}
5671
5672			ReachabilityStoreInfo_keys(&keys, &patterns);
5673			CFArrayAppendValue(keys, SCNETWORKREACHABILITY_TRIGGER_KEY);	// force posting reach change
5674			ok = SCDynamicStoreSetNotificationKeys(hn_store, keys, patterns);
5675			CFRelease(keys);
5676			CFRelease(patterns);
5677			if (!ok) {
5678				SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreSetNotificationKeys() failed"));
5679				CFRelease(hn_store);
5680				hn_store = NULL;
5681				return;
5682			}
5683
5684			ok = SCDynamicStoreSetDispatchQueue(hn_store, _hn_changes_queue());
5685			if (!ok) {
5686				SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
5687				CFRelease(hn_store);
5688				hn_store = NULL;
5689				return;
5690			}
5691
5692			// watch for network information changes
5693			watch_nwi = nwi_refresh_enable(_hn_changes_queue());
5694			if (!watch_nwi) {
5695				goto fail;
5696			}
5697
5698			// watch for DNS configuration (resolver reachability) changes
5699			watch_dns_configuration = dns_configuration_watch();
5700			if (!watch_dns_configuration) {
5701				goto fail;
5702			}
5703
5704			// watch for changes affecting DNS queries
5705			watch_dns_changes = dns_refresh_enable(_hn_changes_queue());
5706			if (!watch_dns_changes) {
5707				goto fail;
5708			}
5709
5710#if	!TARGET_OS_IPHONE
5711			// watch for power capabilities (sleep/wake) changes
5712			watch_power = power_refresh_enable(_hn_changes_queue());
5713			if (!watch_power) {
5714				goto fail;
5715			}
5716#endif	// !TARGET_OS_IPHONE
5717
5718			// watch for OnDemand network changes
5719			watch_onDemand_networking = onDemand_refresh_enable(_hn_changes_queue());
5720			if (!watch_onDemand_networking) {
5721				goto fail;
5722			}
5723
5724
5725
5726			hn_targets = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
5727			ReachabilityStoreInfo_enable(TRUE);
5728
5729			goto scheduled;
5730
5731		    fail :
5732
5733			ok = FALSE;
5734
5735
5736
5737			if (watch_onDemand_networking) {
5738				onDemand_refresh_disable();
5739			}
5740
5741#if	!TARGET_OS_IPHONE
5742			if (watch_power) {
5743				power_refresh_disable();
5744			}
5745#endif	// !TARGET_OS_IPHONE
5746
5747			if (watch_dns_changes) {
5748				dns_refresh_disable();
5749			}
5750
5751			if (watch_dns_configuration) {
5752				dns_configuration_unwatch();
5753			}
5754
5755			if (watch_nwi) {
5756				nwi_refresh_disable();
5757			}
5758
5759			SCDynamicStoreSetDispatchQueue(hn_store, NULL);
5760			CFRelease(hn_store);
5761			hn_store = NULL;
5762
5763			_SCErrorSet(kSCStatusFailed);
5764
5765			return;
5766		}
5767
5768	    scheduled :
5769
5770		CFSetAddValue(hn_targets, target);
5771
5772		ok = TRUE;
5773	});
5774
5775	if (!ok) {
5776		goto done;
5777	}
5778
5779    watch :
5780
5781	if (!targetPrivate->scheduled) {
5782		CFRunLoopSourceContext	context = { 0				// version
5783						  , (void *)target		// info
5784						  , CFRetain			// retain
5785						  , CFRelease			// release
5786						  , reachRLSCopyDescription	// copyDescription
5787						  , CFEqual			// equal
5788						  , CFHash			// hash
5789						  , NULL			// schedule
5790						  , NULL			// cancel
5791						  , reachPerform		// perform
5792						  };
5793
5794		if (runLoop != NULL) {
5795			targetPrivate->rls    = CFRunLoopSourceCreate(NULL, 0, &context);
5796			targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5797		}
5798
5799		if (isReachabilityTypeName(targetPrivate->type)) {
5800			/*
5801			 * we're now scheduled so let's ensure that we
5802			 * are starting with a clean slate before we
5803			 * resolve the name
5804			 */
5805			if (targetPrivate->resolvedAddresses != NULL) {
5806				CFRelease(targetPrivate->resolvedAddresses);
5807				targetPrivate->resolvedAddresses = NULL;
5808			}
5809			targetPrivate->resolvedError = NETDB_SUCCESS;
5810			targetPrivate->needResolve = TRUE;
5811			_reach_set(&targetPrivate->info,
5812				   &NOT_REACHABLE,
5813				   targetPrivate->info.cycle,
5814				   targetPrivate->if_index,
5815				   targetPrivate->if_name);
5816			targetPrivate->info.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
5817			_reach_set(&targetPrivate->serverInfo,
5818				   &NOT_REACHABLE,
5819				   targetPrivate->serverInfo.cycle,
5820				   targetPrivate->if_index,
5821				   targetPrivate->if_name);
5822			targetPrivate->serverInfo.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
5823		}
5824
5825
5826		targetPrivate->scheduled = TRUE;
5827
5828		init = TRUE;
5829	}
5830
5831	if (queue != NULL) {
5832		// retain dispatch queue
5833		dispatch_retain(queue);
5834		targetPrivate->dispatchQueue = queue;
5835
5836		//
5837		// We've taken a reference to the client's dispatch_queue and we
5838		// want to hold on to that reference until we've processed any/all
5839		// notifications.  To facilitate this we create a group, dispatch
5840		// any notification blocks to via that group, and when the caller
5841		// has told us to stop the notifications (unschedule) we wait for
5842		// the group to empty and use the group's finalizer to release
5843		// our reference to the client's queue.
5844		//
5845
5846		// make sure that we have group to track any async requests
5847		targetPrivate->dispatchGroup = dispatch_group_create();
5848
5849		// retain the target ... and release it when the group is released
5850		CFRetain(target);
5851		dispatch_set_context(targetPrivate->dispatchGroup, (void *)target);
5852		dispatch_set_finalizer_f(targetPrivate->dispatchGroup, (dispatch_function_t)CFRelease);
5853	} else {
5854		if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
5855			/*
5856			 * if we do not already have host notifications scheduled with
5857			 * this runLoop / runLoopMode
5858			 */
5859			CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode);
5860		}
5861
5862		_SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
5863	}
5864
5865	if (init) {
5866		ReachabilityInfo	reach_info	= NOT_REACHABLE;
5867		ReachabilityStoreInfo	store_info;
5868
5869		/*
5870		 * if we have yet to schedule SC notifications for this address
5871		 * - initialize current reachability status
5872		 */
5873		ReachabilityStoreInfo_init(&store_info);
5874		if (__SCNetworkReachabilityGetFlags(&store_info, target, &reach_info, TRUE)) {
5875			/*
5876			 * if reachability status available
5877			 * - set flags
5878			 * - schedule notification to report status via callback
5879			 */
5880			reach_info.flags |= (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending);
5881			_reach_set(&targetPrivate->info,
5882				   &reach_info,
5883				   targetPrivate->cycle,
5884				   targetPrivate->if_index,
5885				   targetPrivate->if_name);
5886			__SCNetworkReachabilityUpdate(target);
5887		} else {
5888			/* if reachability status not available, async lookup started */
5889			_reach_set(&targetPrivate->info,
5890				   &NOT_REACHABLE,
5891				   targetPrivate->cycle,
5892				   targetPrivate->if_index,
5893				   targetPrivate->if_name);
5894			_reach_set(&targetPrivate->serverInfo,
5895				   &NOT_REACHABLE,
5896				   targetPrivate->cycle,
5897				   targetPrivate->if_index,
5898				   targetPrivate->if_name);
5899		}
5900		ReachabilityStoreInfo_free(&store_info);
5901	}
5902
5903	if (targetPrivate->onDemandServer != NULL) {
5904		__SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, runLoop, runLoopMode, queue, TRUE);
5905	}
5906
5907	SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%sscheduled"),
5908	      targetPrivate->log_prefix);
5909
5910	ok = TRUE;
5911
5912    done :
5913
5914	MUTEX_UNLOCK(&targetPrivate->lock);
5915	return ok;
5916}
5917
5918
5919static Boolean
5920__SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef	target,
5921					     CFRunLoopRef		runLoop,
5922					     CFStringRef		runLoopMode,
5923					     Boolean			onDemand)
5924{
5925	dispatch_group_t		drainGroup	= NULL;
5926	dispatch_queue_t		drainQueue	= NULL;
5927	CFIndex				n		= 0;
5928	Boolean				ok		= FALSE;
5929	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
5930
5931	// hold a reference while we unschedule
5932	CFRetain(target);
5933
5934	MUTEX_LOCK(&targetPrivate->lock);
5935
5936	if (((runLoop == NULL) && (targetPrivate->dispatchQueue == NULL)) ||	// if we should be scheduled on a dispatch queue (but are not)
5937	    ((runLoop != NULL) && (targetPrivate->dispatchQueue != NULL))) {	// if we should be scheduled on a CFRunLoop (but are not)
5938		_SCErrorSet(kSCStatusInvalidArgument);
5939		goto done;
5940	}
5941
5942	if (!targetPrivate->scheduled) {
5943		// if not currently scheduled
5944		_SCErrorSet(kSCStatusInvalidArgument);
5945		goto done;
5946	}
5947
5948	// unschedule the target specific sources
5949	if (targetPrivate->dispatchQueue != NULL) {
5950		if (targetPrivate->onDemandServer != NULL) {
5951			SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer, NULL, NULL);
5952			__SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE);
5953		}
5954
5955		// save dispatchQueue, release reference when we've queue'd blocks complete, allow re-scheduling
5956		drainGroup = targetPrivate->dispatchGroup;
5957		targetPrivate->dispatchGroup = NULL;
5958		drainQueue = targetPrivate->dispatchQueue;
5959		targetPrivate->dispatchQueue = NULL;
5960	} else {
5961		if (!_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
5962			// if not currently scheduled
5963			_SCErrorSet(kSCStatusInvalidArgument);
5964			goto done;
5965		}
5966
5967		if (targetPrivate->onDemandServer != NULL) {
5968			__SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, runLoop, runLoopMode, TRUE);
5969		}
5970
5971		n = CFArrayGetCount(targetPrivate->rlList);
5972		if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
5973			// if target is no longer scheduled for this runLoop / runLoopMode
5974			CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode);
5975
5976			if (n == 0) {
5977				// if *all* notifications have been unscheduled
5978				if (targetPrivate->onDemandServer != NULL) {
5979					SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer, NULL, NULL);
5980				}
5981				CFRelease(targetPrivate->rlList);
5982				targetPrivate->rlList = NULL;
5983				CFRunLoopSourceInvalidate(targetPrivate->rls);
5984				CFRelease(targetPrivate->rls);
5985				targetPrivate->rls = NULL;
5986			}
5987		}
5988	}
5989
5990	if (n == 0) {
5991		//
5992		// Cancel our request for server monitoring
5993		//
5994		if (targetPrivate->serverActive) {
5995			ok = __SCNetworkReachabilityServer_targetUnschedule(target);
5996			if (!ok) {
5997				SCLog(TRUE, LOG_DEBUG,
5998				      CFSTR("__SCNetworkReachabilityUnscheduleFromRunLoop _targetMonitor() failed"));
5999				_SCErrorSet(kSCStatusFailed);
6000			}
6001		}
6002
6003		// if *all* notifications have been unscheduled
6004		targetPrivate->scheduled = FALSE;
6005	}
6006
6007	if (targetPrivate->serverActive) {
6008		goto unwatch;
6009	}
6010
6011	if (n == 0) {
6012		if (targetPrivate->dnsActive) {
6013			// if we have an active [m]DNS query
6014			dequeueDNSQuery(target);
6015		}
6016
6017		dispatch_sync(_hn_target_queue(), ^{
6018			CFSetRemoveValue(hn_targets, target);
6019
6020			if (onDemand) {
6021				return;
6022			}
6023
6024			if (CFSetGetCount(hn_targets) > 0) {
6025				return;
6026			}
6027
6028			// if we are no longer monitoring any targets
6029			SCDynamicStoreSetDispatchQueue(hn_store, NULL);
6030			CFRelease(hn_store);
6031			hn_store = NULL;
6032			CFRelease(hn_targets);
6033			hn_targets = NULL;
6034
6035			ReachabilityStoreInfo_enable(FALSE);
6036			ReachabilityStoreInfo_save(NULL);
6037
6038			/*
6039			 * until we start monitoring again, ensure that
6040			 * any resources associated with watching network
6041			 * and configuration changes have been released.
6042			 */
6043
6044
6045			// OnDemand configuration
6046			onDemand_refresh_disable();
6047
6048#if	!TARGET_OS_IPHONE
6049			// sleep/wake & power capabilities
6050			power_refresh_disable();
6051#endif	// !TARGET_OS_IPHONE
6052
6053			// outstanding DNS queries
6054			dns_refresh_disable();
6055
6056			// DNS configuration
6057			dns_configuration_unwatch();
6058
6059			// nwi
6060			nwi_refresh_disable();
6061		});
6062	}
6063
6064    unwatch :
6065
6066	SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%sunscheduled"),
6067	      targetPrivate->log_prefix);
6068
6069	ok = TRUE;
6070
6071    done :
6072
6073	MUTEX_UNLOCK(&targetPrivate->lock);
6074
6075	if (drainGroup != NULL) {
6076		dispatch_group_notify(drainGroup, __SCNetworkReachability_concurrent_queue(), ^{
6077			// release group/queue references
6078			dispatch_release(drainQueue);
6079			dispatch_release(drainGroup);	// releases our target reference
6080		});
6081	}
6082
6083	// release our reference
6084	CFRelease(target);
6085
6086	return ok;
6087}
6088
6089Boolean
6090SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef	target,
6091					 CFRunLoopRef			runLoop,
6092					 CFStringRef			runLoopMode)
6093{
6094	if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) {
6095		_SCErrorSet(kSCStatusInvalidArgument);
6096		return FALSE;
6097	}
6098
6099	return __SCNetworkReachabilityScheduleWithRunLoop(target, runLoop, runLoopMode, NULL, FALSE);
6100}
6101
6102Boolean
6103SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef	target,
6104					   CFRunLoopRef			runLoop,
6105					   CFStringRef			runLoopMode)
6106{
6107	if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) {
6108		_SCErrorSet(kSCStatusInvalidArgument);
6109		return FALSE;
6110	}
6111
6112	return __SCNetworkReachabilityUnscheduleFromRunLoop(target, runLoop, runLoopMode, FALSE);
6113}
6114
6115Boolean
6116SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef	target,
6117				      dispatch_queue_t		queue)
6118{
6119	Boolean	ok	= FALSE;
6120
6121	if (!isA_SCNetworkReachability(target)) {
6122		_SCErrorSet(kSCStatusInvalidArgument);
6123		return FALSE;
6124	}
6125
6126	if (queue != NULL) {
6127		ok = __SCNetworkReachabilityScheduleWithRunLoop(target, NULL, NULL, queue, FALSE);
6128	} else {
6129		ok = __SCNetworkReachabilityUnscheduleFromRunLoop(target, NULL, NULL, FALSE);
6130	}
6131
6132	return ok;
6133}
6134