1/*
2 * Copyright (c) 2011-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#include <CoreFoundation/CoreFoundation.h>
25#include <SystemConfiguration/SystemConfiguration.h>
26#include <SystemConfiguration/SCPrivate.h>
27#include "SCNetworkReachabilityInternal.h"
28
29#include <fcntl.h>
30#include <paths.h>
31#include <CommonCrypto/CommonDigest.h>
32#include <dispatch/dispatch.h>
33#include <dispatch/private.h>
34#include <xpc/xpc.h>
35#include <xpc/private.h>
36#include <sys/rbtree.h>
37
38
39#pragma mark -
40#pragma mark Globals
41
42
43/*
44 * S_debug
45 *   A boolean that enables additional logging.
46 */
47static boolean_t	S_debug		= FALSE;
48
49
50#pragma mark -
51#pragma mark Support functions
52
53
54static void
55log_xpc_object(const char *msg, xpc_object_t obj)
56{
57	char		*desc;
58
59	desc = xpc_copy_description(obj);
60	SCLog(S_debug, LOG_INFO, CFSTR("%s = %s"), msg, desc);
61	free(desc);
62}
63
64
65static __inline__ void
66my_CFDictionaryApplyFunction(CFDictionaryRef			theDict,
67			     CFDictionaryApplierFunction	applier,
68			     void				*context)
69{
70	CFAllocatorRef	myAllocator;
71	CFDictionaryRef	myDict;
72
73	myAllocator = CFGetAllocator(theDict);
74	myDict      = CFDictionaryCreateCopy(myAllocator, theDict);
75	CFDictionaryApplyFunction(myDict, applier, context);
76	CFRelease(myDict);
77	return;
78}
79
80
81#pragma mark -
82#pragma mark SCNetworkReachability target support
83
84
85static CFMutableDictionaryRef	reach_digest_map;
86
87
88static dispatch_queue_t
89_server_concurrent_queue()
90{
91	static dispatch_once_t	once;
92	static dispatch_queue_t	q;
93
94	dispatch_once(&once, ^{
95		q = dispatch_queue_create(REACH_SERVICE_NAME ".concurrent",
96					  DISPATCH_QUEUE_CONCURRENT);
97	});
98
99	return q;
100}
101
102
103static dispatch_queue_t
104_server_digest_queue()
105{
106	static dispatch_once_t	once;
107	static dispatch_queue_t	q;
108
109	dispatch_once(&once, ^{
110		q = dispatch_queue_create(REACH_SERVICE_NAME ".digest", NULL);
111	});
112
113	return q;
114}
115
116
117static dispatch_group_t
118_target_group(SCNetworkReachabilityRef target)
119{
120	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
121
122	return targetPrivate->serverGroup;
123}
124
125
126static dispatch_queue_t
127_target_queue(SCNetworkReachabilityRef target)
128{
129	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
130
131	return targetPrivate->serverQueue;
132}
133
134
135#pragma mark -
136
137
138/*
139 * _target_reference_add
140 *
141 * Note: use dispatch_sync(_server_digest_queue(), ^{ ... });
142 */
143static void
144_target_reference_add(SCNetworkReachabilityRef target, CFDataRef digest, xpc_connection_t connection)
145{
146	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
147
148	// take a reference to the target
149	CFRetain(target);
150
151	// ensure that we have a dispatch group
152	if (targetPrivate->serverGroup == NULL) {
153		targetPrivate->serverGroup = dispatch_group_create();
154	}
155
156	// ensure that we have a dispatch queue
157	if (targetPrivate->serverQueue == NULL) {
158		char	qname[256];
159
160		snprintf(qname, sizeof(qname), "com.apple.SCNetworkReachability.%p.server", target);
161		targetPrivate->serverQueue = dispatch_queue_create(qname, NULL);
162	}
163
164	// bump the reference count
165	if (_SC_ATOMIC_INC(&targetPrivate->serverReferences) == 0) {
166		// and maintain a digest-->target mapping
167		targetPrivate->serverDigest = CFRetain(digest);
168		CFDictionarySetValue(reach_digest_map, digest, target);
169	}
170
171	if (S_debug) {
172		CFStringRef	str;
173
174		str = _SCNetworkReachabilityCopyTargetDescription(target);
175		SCLog(TRUE, LOG_INFO,
176		      CFSTR("<%p>   target %p: reference added (%@, %d)"),
177		      connection,
178		      target,
179		      str,
180		      targetPrivate->serverReferences);
181		CFRelease(str);
182	}
183
184	return;
185}
186
187
188/*
189 * _target_reference_remove
190 *
191 * Note: use dispatch_sync(_server_digest_queue(), ^{ ... });
192 */
193static void
194_target_reference_remove(SCNetworkReachabilityRef target, xpc_connection_t connection)
195{
196	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
197
198	// drop the reference count
199	if (_SC_ATOMIC_DEC(&targetPrivate->serverReferences) == 0) {
200		/*
201		 * if that was the last reference, we no longer need to
202		 * keep the digest-->target mapping
203		 */
204		CFDictionaryRemoveValue(reach_digest_map, targetPrivate->serverDigest);
205		CFRelease(targetPrivate->serverDigest);
206		targetPrivate->serverDigest = NULL;
207	}
208
209	if (S_debug) {
210		SCLog(TRUE, LOG_INFO,
211		      CFSTR("<%p>   target %p: reference removed (%d)"),
212		      connection,
213		      target,
214		      targetPrivate->serverReferences);
215	}
216
217	// release a reference to the target
218	CFRelease(target);
219
220	return;
221}
222
223
224#pragma mark -
225
226
227#define	MUTEX_LOCK(m) {							\
228	int _lock_ = (pthread_mutex_lock(m) == 0);			\
229	assert(_lock_);							\
230}
231
232#define	MUTEX_UNLOCK(m) {						\
233	int _unlock_ = (pthread_mutex_unlock(m) == 0);			\
234	assert(_unlock_);						\
235}
236
237
238static void
239_target_reply_add_reachability(SCNetworkReachabilityRef target,
240			       xpc_object_t		reply)
241{
242	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
243
244	MUTEX_LOCK(&targetPrivate->lock);
245
246	xpc_dictionary_set_uint64(reply,
247				  REACH_STATUS_CYCLE,
248				  targetPrivate->info.cycle);
249	xpc_dictionary_set_uint64(reply,
250				  REACH_STATUS_FLAGS,
251				  targetPrivate->info.flags);
252	xpc_dictionary_set_uint64(reply,
253				  REACH_STATUS_IF_INDEX,
254				  targetPrivate->info.if_index);
255	xpc_dictionary_set_data  (reply,
256				  REACH_STATUS_IF_NAME,
257				  targetPrivate->info.if_name,
258				  sizeof(targetPrivate->info.if_name));
259	xpc_dictionary_set_bool	 (reply,
260				  REACH_STATUS_SLEEPING,
261				  targetPrivate->info.sleeping);
262	if (isReachabilityTypeName(targetPrivate->type)) {
263		if (isA_CFArray(targetPrivate->resolvedAddresses)) {
264			xpc_object_t	addresses;
265			CFIndex		i;
266			CFIndex		n;
267
268			addresses = xpc_array_create(NULL, 0);
269
270			n = CFArrayGetCount(targetPrivate->resolvedAddresses);
271			for (i = 0; i < n; i++) {
272				if (targetPrivate->type == reachabilityTypeName) {
273					CFDataRef	address;
274
275					address = CFArrayGetValueAtIndex(targetPrivate->resolvedAddresses, i);
276					xpc_array_set_data(addresses,
277							   XPC_ARRAY_APPEND,
278							   CFDataGetBytePtr(address),
279							   CFDataGetLength(address));
280				} else if (targetPrivate->type == reachabilityTypePTR) {
281					CFStringRef	name;
282					char		str[MAXHOSTNAMELEN];
283
284					name = CFArrayGetValueAtIndex(targetPrivate->resolvedAddresses, i);
285					_SC_cfstring_to_cstring(name, str, sizeof(str), kCFStringEncodingUTF8);
286					xpc_array_set_string(addresses, XPC_ARRAY_APPEND, str);
287				}
288			}
289
290			xpc_dictionary_set_value(reply,
291						 REACH_STATUS_RESOLVED_ADDRESSES,
292						 addresses);
293			xpc_release(addresses);
294		}
295		xpc_dictionary_set_int64(reply,
296					 REACH_STATUS_RESOLVED_ERROR,
297					 targetPrivate->resolvedError);
298		xpc_dictionary_set_uint64(reply,
299					  REACH_STATUS_DNS_FLAGS,
300					  targetPrivate->dnsFlags);
301
302	}
303
304	MUTEX_UNLOCK(&targetPrivate->lock);
305
306	return;
307}
308
309
310#pragma mark -
311
312
313typedef struct {
314	xpc_connection_t	connection;
315	uint64_t		target_id;
316} reach_watcher_key_t;
317
318typedef struct {
319	unsigned int		n_changes;
320} reach_watcher_val_t;
321
322
323static CFDataRef
324_target_watcher_key_create(xpc_connection_t	connection,
325			   uint64_t		target_id)
326{
327	CFDataRef		key;
328	reach_watcher_key_t	watcher_key;
329
330	watcher_key.connection = connection;
331	watcher_key.target_id  = target_id;
332
333	key = CFDataCreate(NULL, (UInt8 *)&watcher_key, sizeof(watcher_key));
334	return key;
335}
336
337
338static Boolean
339_target_watcher_add(SCNetworkReachabilityRef	target,
340		    xpc_connection_t		connection,
341		    uint64_t			target_id)
342{
343	__block Boolean		ok	= TRUE;
344	dispatch_queue_t	q;
345
346	q = _target_queue(target);
347	dispatch_sync(q, ^{
348		CFDataRef			key;
349		SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
350
351		if (targetPrivate->serverWatchers == NULL) {
352			ok = SCNetworkReachabilitySetDispatchQueue(target, q);
353			if (!ok) {
354				SCLog(TRUE, LOG_ERR,
355				      CFSTR("<%p> target %p: _watcher_add SCNetworkReachabilitySetDispatchQueue() failed: %s"),
356				      connection,
357				      target,
358				      SCErrorString(SCError()));
359				return;
360			}
361
362			targetPrivate->serverWatchers = CFDictionaryCreateMutable(NULL,
363										  0,
364										  &kCFTypeDictionaryKeyCallBacks,
365										  &kCFTypeDictionaryValueCallBacks);
366		}
367
368		xpc_retain(connection);
369
370		key = _target_watcher_key_create(connection, target_id);
371		if (CFDictionaryContainsKey(targetPrivate->serverWatchers, key)) {
372			SCLog(TRUE, LOG_ERR,
373			      CFSTR("<%p>   target %p: watcher not added, c=0x%0llx, \"serverWatchers\" key exists"),
374			      connection,
375			      target,
376			      target_id);
377		} else {
378			CFDataRef				val;
379			static const reach_watcher_val_t	watcher_val0	= { 0 };
380
381			val = CFDataCreate(NULL, (UInt8 *)&watcher_val0, sizeof(watcher_val0));
382			CFDictionaryAddValue(targetPrivate->serverWatchers, key, val);
383			CFRelease(val);
384
385			if (S_debug) {
386				SCLog(TRUE, LOG_INFO,
387				      CFSTR("<%p>   target %p: watcher added, c=0x%0llx, n=%ld"),
388				      connection,
389				      target,
390				      target_id,
391				      CFDictionaryGetCount(targetPrivate->serverWatchers));
392			}
393		}
394		CFRelease(key);
395	});
396
397	return ok;
398}
399
400
401static Boolean
402_target_watcher_checkin(SCNetworkReachabilityRef	target,
403			xpc_connection_t		connection,
404			uint64_t			target_id)
405{
406	__block Boolean		scheduled	= FALSE;
407
408	dispatch_sync(_target_queue(target), ^{
409		CFDataRef			key;
410		unsigned int			n;
411		SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
412		CFDataRef			val;
413		reach_watcher_val_t		*watcher_val;
414
415		if (targetPrivate->serverWatchers == NULL) {
416			// if no watchers
417			return;
418		}
419
420		key = _target_watcher_key_create(connection, target_id);
421		val = CFDictionaryGetValue(targetPrivate->serverWatchers, key);
422		CFRelease(key);
423		if (val == NULL) {
424			// if the target [for this client] was not scheduled
425			return;
426		}
427
428		// indicate that the target was scheduled
429		scheduled = TRUE;
430
431		/*
432		 * and note that the reachability flags for this target have
433		 * been picked up by the client
434		 */
435		/* ALIGN: CF aligns to at least >8 byte boundries */
436		watcher_val = (reach_watcher_val_t *)(void *)CFDataGetBytePtr(val);
437		n = _SC_ATOMIC_ZERO(&watcher_val->n_changes);
438		if (S_debug && (n > 0)) {
439			SCLog(TRUE, LOG_INFO,
440			      CFSTR("<%p> target %p: SCNetworkReachabilityGetFlags() after %d notification%s"),
441			      connection,
442			      target,
443			      n,
444			      (n == 1) ? "" : "s");
445		}
446	});
447
448	return scheduled;
449}
450
451
452static Boolean
453_target_watcher_remove(SCNetworkReachabilityRef	target,
454		       xpc_connection_t		connection,
455		       uint64_t			target_id)
456{
457	__block Boolean		ok	= TRUE;
458
459	dispatch_sync(_target_queue(target), ^{
460		CFDataRef			key;
461		CFIndex				n;
462		SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
463
464		if (targetPrivate->serverWatchers == NULL) {
465			SCLog(TRUE, LOG_ERR,
466			      CFSTR("<%p>   target %p: watcher not removed, c=0x%0llx, no \"serverWatchers\""),
467			      connection,
468			      target,
469			      target_id);
470			ok = FALSE;
471			return;
472		}
473
474		key = _target_watcher_key_create(connection, target_id);
475		if (!CFDictionaryContainsKey(targetPrivate->serverWatchers, key)) {
476			SCLog(TRUE, LOG_ERR,
477			      CFSTR("<%p>   target %p: watcher not removed, c=0x%0llx, no \"serverWatchers\" key"),
478			      connection,
479			      target,
480			      target_id);
481			CFRelease(key);
482			ok = FALSE;
483			return;
484		}
485
486		CFDictionaryRemoveValue(targetPrivate->serverWatchers, key);
487		CFRelease(key);
488
489		n = CFDictionaryGetCount(targetPrivate->serverWatchers);
490
491		if (S_debug) {
492			SCLog(TRUE, LOG_INFO,
493			      CFSTR("<%p>   target %p: watcher removed, c=0x%0llx, n=%ld"),
494			      connection,
495			      target,		// server
496			      target_id,	// client
497			      n);
498		}
499
500		if (n == 0) {
501			CFRelease(targetPrivate->serverWatchers);
502			targetPrivate->serverWatchers = NULL;
503
504			ok = SCNetworkReachabilitySetDispatchQueue(target, NULL);
505			if (!ok) {
506				SCLog(TRUE, LOG_ERR,
507				      CFSTR("<%p> target %p: _watcher_remove SCNetworkReachabilitySetDispatchQueue() failed: %s"),
508				      connection,
509				      target,
510				      SCErrorString(SCError()));
511				return;
512			}
513
514			// no more watchers, flags are no longer valid
515			if (_SC_ATOMIC_CMPXCHG(&targetPrivate->serverInfoValid, TRUE, FALSE)) {
516				if (S_debug) {
517					SCLog(TRUE, LOG_INFO, CFSTR("%s  flags are no longer \"valid\""),
518					      targetPrivate->log_prefix);
519				}
520			}
521		}
522
523		xpc_release(connection);
524	});
525
526	return ok;
527}
528
529
530#pragma mark -
531#pragma mark Reachability [RBT] client support
532
533
534typedef struct {
535	rb_node_t		rbn;
536	xpc_connection_t	connection;
537	pid_t			pid;
538	const char		*proc_name;
539	CFMutableDictionaryRef	targets;	// target_id --> SCNetworkReachabilityRef
540} reach_client_t;
541
542
543static int
544_rbt_compare_transaction_nodes(void *context, const void *n1, const void *n2)
545{
546	uint64_t	a = (uintptr_t)((reach_client_t *)n1)->connection;
547	uint64_t	b = (uintptr_t)((reach_client_t *)n2)->connection;
548
549	if (a == b) {
550		return 0;
551	} else if (a < b) {
552		return -1;
553	} else {
554		return 1;
555	}
556}
557
558
559static int
560_rbt_compare_transaction_key(void *context, const void *n1, const void *key)
561{
562	uint64_t	a = (uintptr_t)((reach_client_t *)n1)->connection;
563	uint64_t	b = *(uintptr_t *)key;
564
565	if (a == b) {
566		return 0;
567	} else if (a < b) {
568		return -1;
569	} else {
570		return 1;
571	}
572}
573
574
575static rb_tree_t *
576_reach_clients_rbt()
577{
578	static dispatch_once_t		once;
579	static const rb_tree_ops_t	ops = {
580		.rbto_compare_nodes	= _rbt_compare_transaction_nodes,
581		.rbto_compare_key	= _rbt_compare_transaction_key,
582		.rbto_node_offset	= offsetof(reach_client_t, rbn),
583		.rbto_context		= NULL
584	};
585	static rb_tree_t		rbt;
586
587	dispatch_once(&once, ^{
588		rb_tree_init(&rbt, &ops);
589	});
590
591	return &rbt;
592}
593
594
595static dispatch_queue_t
596_reach_clients_rbt_queue()
597{
598	static dispatch_once_t	once;
599	static dispatch_queue_t	q;
600
601	dispatch_once(&once, ^{
602		q = dispatch_queue_create(REACH_SERVICE_NAME ".clients.rbt", NULL);
603	});
604
605	return q;
606}
607
608
609static reach_client_t *
610_reach_client_create(xpc_connection_t connection)
611{
612	reach_client_t	*client;
613
614	client = calloc(1, sizeof(*client));
615	client->connection = connection;
616	client->pid = xpc_connection_get_pid(connection);
617	client->proc_name = NULL;
618	client->targets = CFDictionaryCreateMutable(NULL,
619						    0,
620						    &kCFTypeDictionaryKeyCallBacks,
621						    &kCFTypeDictionaryValueCallBacks);
622
623	return client;
624}
625
626
627static void
628_reach_client_release(reach_client_t *client)
629{
630	if (client->proc_name != NULL) {
631		free((void *)client->proc_name);
632	}
633	CFRelease(client->targets);
634	free(client);
635	return;
636}
637
638
639static void
640_reach_client_remove_target(const void *key, const void *value, void *context)
641{
642	xpc_connection_t		connection	= (xpc_connection_t)context;
643	SCNetworkReachabilityRef	target		= (SCNetworkReachabilityRef)value;
644	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
645
646	// check if we have anyone watching this target
647	if (targetPrivate->serverWatchers != NULL) {
648		CFIndex		n;
649
650		n = CFDictionaryGetCount(targetPrivate->serverWatchers);
651		if (n > 0) {
652			CFIndex		i;
653			CFDictionaryRef	serverWatchers;
654			const void *	watchers_q[32];
655			const void **	watchers	= watchers_q;
656
657			serverWatchers = CFDictionaryCreateCopy(NULL, targetPrivate->serverWatchers);
658
659			if (n > sizeof(watchers_q)/sizeof(watchers[0])) {
660				watchers = CFAllocatorAllocate(NULL, n * sizeof(CFDataRef), 0);
661			}
662			CFDictionaryGetKeysAndValues(serverWatchers, watchers, NULL);
663
664			for (i = 0; i < n; i++) {
665				CFDataRef		key;
666				reach_watcher_key_t	*watcher_key;
667
668				key = (CFDataRef)watchers[i];
669				/* ALIGN: CF aligns to >8 byte boundries */
670				watcher_key = (reach_watcher_key_t *)(void *)CFDataGetBytePtr(key);
671				if (watcher_key->connection == connection) {
672					// remove watcher references for THIS connection
673					_target_watcher_remove(target,
674							       watcher_key->connection,
675							       watcher_key->target_id);
676				}
677			}
678
679			if (watchers != watchers_q) {
680				CFAllocatorDeallocate(NULL, watchers);
681			}
682
683			CFRelease(serverWatchers);
684		}
685	}
686
687	// remove our reference to this target
688	dispatch_sync(_server_digest_queue(), ^{
689		_target_reference_remove(target, connection);
690	});
691
692	return;
693}
694
695
696static void
697_reach_client_remove(xpc_connection_t connection)
698{
699	uint64_t	connection_id	= (uintptr_t)connection;
700
701	dispatch_sync(_reach_clients_rbt_queue(), ^{
702		reach_client_t	*client;
703		rb_tree_t	*rbt	= _reach_clients_rbt();
704
705		client = rb_tree_find_node(rbt, &connection_id);
706		if (client != NULL) {
707			// remove any remaining target references (for this client)
708			my_CFDictionaryApplyFunction(client->targets,
709						     _reach_client_remove_target,
710						     (void *)connection);
711
712			rb_tree_remove_node(rbt, client);
713			_reach_client_release(client);
714		} else {
715			SCLog(TRUE, LOG_ERR,
716			      CFSTR("<%p> _reach_client_remove: unexpected client"),
717			      connection);
718		}
719	});
720
721	return;
722}
723
724
725static __inline__ CFDataRef
726_client_target_key_create(uint64_t target_id)
727{
728	CFDataRef	target_key;
729
730	target_key = CFDataCreate(NULL, (UInt8 *)&target_id, sizeof(target_id));
731	return target_key;
732}
733
734
735static SCNetworkReachabilityRef
736_client_target_copy(reach_client_t *client, uint64_t target_id)
737{
738	SCNetworkReachabilityRef	target;
739	CFDataRef			target_key;
740
741	target_key = _client_target_key_create(target_id);
742	target = CFDictionaryGetValue(client->targets, target_key);
743	CFRelease(target_key);
744
745	if (target != NULL) {
746		CFRetain(target);
747	}
748
749	return target;
750}
751
752
753static Boolean
754_client_target_set(reach_client_t *client, uint64_t target_id, SCNetworkReachabilityRef target)
755{
756	Boolean		added;
757	CFDataRef	target_key;
758
759	target_key = _client_target_key_create(target_id);
760	added = !CFDictionaryContainsKey(client->targets, target_key);
761	if (added) {
762		CFDictionarySetValue(client->targets, target_key, target);
763	}
764	CFRelease(target_key);
765
766	return added;
767}
768
769
770static void
771_client_target_remove(reach_client_t *client, uint64_t target_id)
772{
773	CFDataRef	target_key;
774
775	target_key = _client_target_key_create(target_id);
776	CFDictionaryRemoveValue(client->targets, target_key);
777	CFRelease(target_key);
778
779	return;
780}
781
782
783#pragma mark -
784#pragma mark Reachability [XPC] server functions
785
786
787static dispatch_queue_t
788_reach_server_queue()
789{
790	static dispatch_once_t	once;
791	static dispatch_queue_t	q;
792
793	dispatch_once(&once, ^{
794		q = dispatch_queue_create(REACH_SERVICE_NAME, NULL);
795	});
796
797	return q;
798}
799
800
801/*
802 * _reach_changed
803 *
804 * Note: should be exec'd on the target queue
805 */
806static void
807_reach_changed(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
808{
809	CFIndex				i;
810	CFIndex				n;
811	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
812	const void *			watcher_keys_q[32];
813	const void **			watcher_keys	= watcher_keys_q;
814	const void *			watcher_vals_q[32];
815	const void **			watcher_vals	= watcher_vals_q;
816
817	if (S_debug) {
818		SCLog(TRUE, LOG_INFO,
819		      CFSTR("%sprocess reachability changed, flags = 0x%08x"),
820		      targetPrivate->log_prefix,
821		      flags);
822	}
823
824	if (targetPrivate->serverWatchers == NULL) {
825		// if no watchers
826		return;
827	}
828
829	n = CFDictionaryGetCount(targetPrivate->serverWatchers);
830	if (n == 0) {
831		// if no watchers
832		return;
833	}
834
835	if (!isReachabilityTypeName(targetPrivate->type) || !targetPrivate->dnsNoAddressesSinceLastTimeout) {
836		/*
837		 * Because we are actively watching for additional changes
838		 * we mark the flags as "valid"
839		 */
840		if (_SC_ATOMIC_CMPXCHG(&targetPrivate->serverInfoValid, FALSE, TRUE)) {
841			if (S_debug) {
842				SCLog(TRUE, LOG_INFO, CFSTR("%s  flags are now \"valid\""),
843				      targetPrivate->log_prefix);
844			}
845		}
846	}
847
848	// notify all of the watchers
849	if (n > sizeof(watcher_keys_q)/sizeof(watcher_keys[0])) {
850		watcher_keys = CFAllocatorAllocate(NULL, n * sizeof(CFDataRef), 0);
851		watcher_vals = CFAllocatorAllocate(NULL, n * sizeof(CFDataRef), 0);
852	}
853
854	CFDictionaryGetKeysAndValues(targetPrivate->serverWatchers,
855				     watcher_keys,
856				     watcher_vals);
857
858	for (i = 0; i < n; i++) {
859		xpc_connection_t	connection;
860		CFDataRef		key;
861		uint64_t		target_id;
862		CFDataRef		val;
863		reach_watcher_key_t	*watcher_key;
864		reach_watcher_val_t	*watcher_val;
865
866		val = (CFDataRef)watcher_vals[i];
867		/* ALIGN: CF aligns to >8 byte boundries */
868		watcher_val = (reach_watcher_val_t *)(void *)CFDataGetBytePtr(val);
869
870		if (_SC_ATOMIC_INC(&watcher_val->n_changes) > 0) {
871			// if we've already sent a notification
872			continue;
873		}
874
875		key = (CFDataRef)watcher_keys[i];
876		/* ALIGN: CF aligns to >8 byte boundries */
877		watcher_key = (reach_watcher_key_t *)(void *)CFDataGetBytePtr(key);
878
879		connection = xpc_retain(watcher_key->connection);
880		target_id  = watcher_key->target_id;
881		dispatch_async(_reach_server_queue(), ^{
882			xpc_object_t	reply;
883
884			// create our [async] notification
885			reply = xpc_dictionary_create(NULL, NULL, 0);
886
887			// set notification
888			xpc_dictionary_set_int64(reply,
889						 MESSAGE_NOTIFY,
890						 MESSAGE_REACHABILITY_STATUS);
891
892			// set target ID
893			xpc_dictionary_set_uint64(reply,
894						  REACH_CLIENT_TARGET_ID,
895						  target_id);
896
897			log_xpc_object("  reply [async]", reply);
898			xpc_connection_send_message(connection, reply);
899
900			xpc_release(reply);
901			xpc_release(connection);
902		});
903	}
904
905	if (n > sizeof(watcher_keys_q)/sizeof(watcher_keys[0])) {
906		CFAllocatorDeallocate(NULL, watcher_keys);
907		CFAllocatorDeallocate(NULL, watcher_vals);
908	}
909
910	return;
911}
912
913
914static void
915sanitize_address(const struct sockaddr *from, struct sockaddr *to)
916{
917	switch (from->sa_family) {
918		case AF_INET : {
919			/* ALIGN: cast okay, alignment not assumed. */
920			struct sockaddr_in *from4	= (struct sockaddr_in *)(void *)from;
921			struct sockaddr_in *to4		= (struct sockaddr_in *)(void *)to;
922
923			bzero(to4, sizeof(*to4));
924			to4->sin_len = sizeof(*to4);
925			to4->sin_family = AF_INET;
926			bcopy(&from4->sin_addr, &to4->sin_addr, sizeof(to4->sin_addr));
927			break;
928		}
929
930		case AF_INET6 : {
931			/* ALIGN: cast okay, alignment not assumed. */
932			struct sockaddr_in6 *from6	= (struct sockaddr_in6 *)(void *)from;
933			struct sockaddr_in6 *to6	= (struct sockaddr_in6 *)(void *)to;
934
935			bzero(to6, sizeof(*to6));
936			to6->sin6_len = sizeof(*to6);
937			to6->sin6_family = AF_INET6;
938			bcopy(&from6->sin6_addr, &to6->sin6_addr, sizeof(to6->sin6_addr));
939			to6->sin6_scope_id = from6->sin6_scope_id;
940			break;
941		}
942
943		default:
944			bcopy(from, to, from->sa_len);
945			break;
946	}
947
948	return;
949}
950
951
952static void
953target_add(reach_client_t *client, xpc_object_t request)
954{
955	const char				*name;
956	const struct sockaddr			*localAddress;
957	struct sockaddr_storage			localAddress0;
958	const struct sockaddr			*remoteAddress;
959	struct sockaddr_storage			remoteAddress0;
960	const struct sockaddr			*ptrAddress;
961	struct sockaddr_storage			ptrAddress0;
962	int64_t					if_index;
963	const char				*if_name	= NULL;
964	bool					onDemandBypass	= FALSE;
965	bool					resolverBypass	= FALSE;
966	uint64_t				target_id;
967	uid_t					uid		= 0;
968
969
970	unsigned char				bytes[CC_SHA1_DIGEST_LENGTH];
971	CC_SHA1_CTX				ctx;
972	CFDataRef				digest		= NULL;
973	size_t					len;
974	xpc_connection_t			remote;
975	xpc_object_t				reply;
976	uint64_t				status		= REACH_REQUEST_REPLY_FAILED;
977
978	Boolean					added;
979	__block Boolean				ok		= TRUE;
980	__block SCNetworkReachabilityRef	target		= NULL;
981
982	if (S_debug) {
983		SCLog(TRUE, LOG_INFO,
984		      CFSTR("<%p> create reachability target"),
985		      client->connection);
986//		log_xpc_object("  create", request);
987	}
988
989	remote = xpc_dictionary_get_remote_connection(request);
990	reply = xpc_dictionary_create_reply(request);
991	if (reply == NULL) {
992		SCLog(TRUE, LOG_ERR,
993		      CFSTR("<%p> target_add: xpc_dictionary_create_reply: failed"),
994		      client->connection);
995		return;
996	}
997
998	target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
999	if (target_id == 0) {
1000		xpc_dictionary_set_string(reply,
1001					  REACH_REQUEST_REPLY_DETAIL,
1002					  "no target ID");
1003		goto done;
1004	}
1005
1006	// create a "digest" of the [new] target
1007
1008	CC_SHA1_Init(&ctx);
1009
1010	name = xpc_dictionary_get_string(request, REACH_TARGET_NAME);
1011	if (name != NULL) {
1012		CC_SHA1_Update(&ctx, REACH_TARGET_NAME, sizeof(REACH_TARGET_NAME));
1013		CC_SHA1_Update(&ctx, name, (CC_LONG)strlen(name));
1014	}
1015
1016	localAddress = xpc_dictionary_get_data(request, REACH_TARGET_LOCAL_ADDR, &len);
1017	if (localAddress != NULL) {
1018		if ((len == localAddress->sa_len) && (len <= sizeof(struct sockaddr_storage))) {
1019			sanitize_address(localAddress, (struct sockaddr *)&localAddress0);
1020			CC_SHA1_Update(&ctx, REACH_TARGET_LOCAL_ADDR, sizeof(REACH_TARGET_LOCAL_ADDR));
1021			CC_SHA1_Update(&ctx, &localAddress0, (CC_LONG)len);
1022		} else {
1023			xpc_dictionary_set_string(reply,
1024						  REACH_REQUEST_REPLY_DETAIL,
1025						  "local address: size error");
1026			goto done;
1027		}
1028	}
1029
1030	remoteAddress = xpc_dictionary_get_data(request, REACH_TARGET_REMOTE_ADDR, &len);
1031	if (remoteAddress != NULL) {
1032		if ((len == remoteAddress->sa_len) && (len <= sizeof(struct sockaddr_storage))) {
1033			sanitize_address(remoteAddress, (struct sockaddr *)&remoteAddress0);
1034			CC_SHA1_Update(&ctx, REACH_TARGET_REMOTE_ADDR, sizeof(REACH_TARGET_REMOTE_ADDR));
1035			CC_SHA1_Update(&ctx, &remoteAddress0, (CC_LONG)len);
1036		} else {
1037			xpc_dictionary_set_string(reply,
1038						  REACH_REQUEST_REPLY_DETAIL,
1039						  "remote address: size error");
1040			goto done;
1041		}
1042	}
1043
1044	ptrAddress = xpc_dictionary_get_data(request, REACH_TARGET_PTR_ADDR, &len);
1045	if (ptrAddress != NULL) {
1046		if ((len == ptrAddress->sa_len) && (len <= sizeof(struct sockaddr_storage))) {
1047			sanitize_address(ptrAddress, (struct sockaddr *)&ptrAddress0);
1048			CC_SHA1_Update(&ctx, REACH_TARGET_PTR_ADDR, sizeof(REACH_TARGET_PTR_ADDR));
1049			CC_SHA1_Update(&ctx, &ptrAddress0, (CC_LONG)len);
1050		} else {
1051			xpc_dictionary_set_string(reply,
1052						  REACH_REQUEST_REPLY_DETAIL,
1053						  "ptr address: size error");
1054			goto done;
1055		}
1056	}
1057
1058	if_index = xpc_dictionary_get_int64(request, REACH_TARGET_IF_INDEX);
1059	if (if_index != 0) {
1060		if_name = xpc_dictionary_get_string(request, REACH_TARGET_IF_NAME);
1061		if (if_name != NULL) {
1062			CC_SHA1_Update(&ctx, REACH_TARGET_IF_NAME, sizeof(REACH_TARGET_IF_NAME));
1063			CC_SHA1_Update(&ctx, if_name, (CC_LONG)strlen(if_name));
1064		}
1065	}
1066
1067
1068	onDemandBypass = xpc_dictionary_get_bool(request, REACH_TARGET_ONDEMAND_BYPASS);
1069	if (onDemandBypass) {
1070		CC_SHA1_Update(&ctx, REACH_TARGET_ONDEMAND_BYPASS, sizeof(REACH_TARGET_ONDEMAND_BYPASS));
1071		CC_SHA1_Update(&ctx, &onDemandBypass, sizeof(onDemandBypass));
1072	}
1073
1074	resolverBypass = xpc_dictionary_get_bool(request, REACH_TARGET_RESOLVER_BYPASS);
1075	if (resolverBypass) {
1076		CC_SHA1_Update(&ctx, REACH_TARGET_RESOLVER_BYPASS, sizeof(REACH_TARGET_RESOLVER_BYPASS));
1077		CC_SHA1_Update(&ctx, &resolverBypass, sizeof(resolverBypass));
1078	}
1079
1080	// Grab UID from XPC connection
1081	uid = xpc_connection_get_euid(remote);
1082	CC_SHA1_Update(&ctx, &uid, sizeof(uid));
1083
1084
1085	CC_SHA1_Final(bytes, &ctx);
1086	digest = CFDataCreate(NULL, bytes, sizeof(bytes));
1087
1088	/*
1089	 * Check to see if we already have a SCNetworkReachability object
1090	 * for this digest. If so, we'll share the existing target. If not,
1091	 * create a new [shared] target.
1092	 */
1093	dispatch_sync(_server_digest_queue(), ^{
1094		target = CFDictionaryGetValue(reach_digest_map, digest);
1095		if (target != NULL) {
1096			CFRetain(target);
1097		} else {
1098			CFDataRef			data;
1099			CFMutableDictionaryRef		options;
1100			CFStringRef			str;
1101
1102			options = CFDictionaryCreateMutable(NULL,
1103							    0,
1104							    &kCFTypeDictionaryKeyCallBacks,
1105							    &kCFTypeDictionaryValueCallBacks);
1106			if (name != NULL) {
1107				str = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
1108				CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, str);
1109				CFRelease(str);
1110			}
1111			if (localAddress != NULL) {
1112				data = CFDataCreate(NULL, (const UInt8 *)&localAddress0, localAddress0.ss_len);
1113				CFDictionarySetValue(options, kSCNetworkReachabilityOptionLocalAddress, data);
1114				CFRelease(data);
1115			}
1116			if (remoteAddress != NULL) {
1117				data = CFDataCreate(NULL, (const UInt8 *)&remoteAddress0, remoteAddress0.ss_len);
1118				CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data);
1119				CFRelease(data);
1120			}
1121			if (ptrAddress != NULL) {
1122				data = CFDataCreate(NULL, (const UInt8 *)&ptrAddress0, ptrAddress0.ss_len);
1123				CFDictionarySetValue(options, kSCNetworkReachabilityOptionPTRAddress, data);
1124				CFRelease(data);
1125			}
1126			if (onDemandBypass) {
1127				CFDictionarySetValue(options,
1128						     kSCNetworkReachabilityOptionConnectionOnDemandBypass,
1129						     kCFBooleanTrue);
1130			}
1131			if (resolverBypass) {
1132				CFDictionarySetValue(options,
1133						     kSCNetworkReachabilityOptionResolverBypass,
1134						     kCFBooleanTrue);
1135			}
1136			CFDictionarySetValue(options,
1137					     kSCNetworkReachabilityOptionServerBypass,
1138					     kCFBooleanTrue);
1139			target = SCNetworkReachabilityCreateWithOptions(NULL, options);
1140			CFRelease(options);
1141			if (target == NULL) {
1142				xpc_dictionary_set_string(reply,
1143							  REACH_REQUEST_REPLY_DETAIL,
1144							  "SCNetworkReachabilityCreateWithOptions failed");
1145				ok = FALSE;
1146				return;
1147			}
1148
1149			// Set the UID on the target
1150			((SCNetworkReachabilityPrivateRef)target)->uid = uid;
1151
1152			// because the interface name may not (no longer) be valid we set
1153			// this after we've created the SCNetworkReachability object
1154			if ((if_index != 0) && (if_name != NULL)) {
1155				SCNetworkReachabilityPrivateRef	targetPrivate;
1156
1157				targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1158				targetPrivate->if_index = (unsigned int)if_index;
1159				strlcpy(targetPrivate->if_name, if_name, sizeof(targetPrivate->if_name));
1160			}
1161
1162
1163			ok = SCNetworkReachabilitySetCallback(target, _reach_changed, NULL);
1164			if (!ok) {
1165				xpc_dictionary_set_string(reply,
1166							  REACH_REQUEST_REPLY_DETAIL,
1167							  "SCNetworkReachabilitySetCallback failed");
1168				CFRelease(target);
1169				target = NULL;
1170				return;
1171			}
1172		}
1173
1174		// bump the number of references to this target
1175		_target_reference_add(target, digest, client->connection);
1176	});
1177
1178	if (!ok) {
1179		goto done;
1180	}
1181
1182	/*
1183	 * add an association for the client's target_id to the [shared]
1184	 * SCNetworkReachability object.
1185	 */
1186	added = _client_target_set(client, target_id, target);
1187	if (!added) {
1188		// if we already had a reference to the target (e.g. reconnect)
1189		dispatch_sync(_server_digest_queue(), ^{
1190			_target_reference_remove(target, client->connection);
1191		});
1192	}
1193
1194	status = REACH_REQUEST_REPLY_OK;
1195
1196    done :
1197
1198	xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1199//	log_xpc_object("  reply", reply);
1200	xpc_connection_send_message(remote, reply);
1201	xpc_release(reply);
1202
1203	if (digest != NULL) CFRelease(digest);
1204	if (target != NULL) CFRelease(target);
1205	return;
1206}
1207
1208
1209static void
1210target_remove(reach_client_t *client, xpc_object_t request)
1211{
1212	xpc_connection_t		remote;
1213	xpc_object_t			reply;
1214	uint64_t			status		= REACH_REQUEST_REPLY_FAILED;
1215	SCNetworkReachabilityRef	target		= NULL;
1216	uint64_t			target_id;
1217
1218	if (S_debug) {
1219		SCLog(TRUE, LOG_INFO,
1220		      CFSTR("<%p> remove reachability target"),
1221		      client->connection);
1222//		log_xpc_object("  remove", request);
1223	}
1224
1225	remote = xpc_dictionary_get_remote_connection(request);
1226	reply = xpc_dictionary_create_reply(request);
1227	if (reply == NULL) {
1228		SCLog(TRUE, LOG_ERR,
1229		      CFSTR("<%p> target_remove: xpc_dictionary_create_reply: failed"),
1230		      client->connection);
1231		return;
1232	}
1233
1234	target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
1235	if (target_id == 0) {
1236		xpc_dictionary_set_string(reply,
1237					  REACH_REQUEST_REPLY_DETAIL,
1238					  "no target ID");
1239		goto done;
1240	}
1241
1242	target = _client_target_copy(client, target_id);
1243	if (target == NULL) {
1244		xpc_dictionary_set_string(reply,
1245					  REACH_REQUEST_REPLY_DETAIL,
1246					  "no target");
1247		status = REACH_REQUEST_REPLY_UNKNOWN;
1248		goto done;
1249	}
1250
1251	/*
1252	 * remove the association from the client's target_id to the [shared]
1253	 * SCNetworkReachability object.
1254	 */
1255	_client_target_remove(client, target_id);
1256
1257	// drop the number of references to this target
1258	dispatch_sync(_server_digest_queue(), ^{
1259		_target_reference_remove(target, client->connection);
1260	});
1261
1262	status = REACH_REQUEST_REPLY_OK;
1263
1264    done :
1265
1266	xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1267//	log_xpc_object("  reply", reply);
1268	xpc_connection_send_message(remote, reply);
1269	xpc_release(reply);
1270
1271	if (target != NULL) CFRelease(target);
1272	return;
1273}
1274
1275
1276static void
1277target_schedule(reach_client_t *client, xpc_object_t request)
1278{
1279	Boolean				ok;
1280	xpc_connection_t		remote;
1281	xpc_object_t			reply;
1282	uint64_t			status		= REACH_REQUEST_REPLY_FAILED;
1283	SCNetworkReachabilityRef	target		= NULL;
1284	uint64_t			target_id;
1285
1286	if (S_debug) {
1287		SCLog(TRUE, LOG_INFO,
1288		      CFSTR("<%p> schedule reachability target"),
1289		      client->connection);
1290//		log_xpc_object("  schedule", request);
1291	}
1292
1293	remote = xpc_dictionary_get_remote_connection(request);
1294	reply = xpc_dictionary_create_reply(request);
1295	if (reply == NULL) {
1296		SCLog(TRUE, LOG_ERR,
1297		      CFSTR("<%p> target_schedule: xpc_dictionary_create_reply: failed"),
1298		      client->connection);
1299		return;
1300	}
1301
1302	target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
1303	if (target_id == 0) {
1304		xpc_dictionary_set_string(reply,
1305					  REACH_REQUEST_REPLY_DETAIL,
1306					  "no target ID");
1307		goto done;
1308	}
1309
1310	target = _client_target_copy(client, target_id);
1311	if (target == NULL) {
1312		xpc_dictionary_set_string(reply,
1313					  REACH_REQUEST_REPLY_DETAIL,
1314					  "no target");
1315		status = REACH_REQUEST_REPLY_UNKNOWN;
1316		goto done;
1317	}
1318
1319	// enable monitoring
1320	ok = _target_watcher_add(target, client->connection, target_id);
1321	if (!ok) {
1322		xpc_dictionary_set_string(reply,
1323					  REACH_REQUEST_REPLY_DETAIL,
1324					  "could not add watcher");
1325		goto done;
1326	}
1327
1328	status = REACH_REQUEST_REPLY_OK;
1329
1330    done :
1331
1332	xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1333//	log_xpc_object("  reply", reply);
1334	xpc_connection_send_message(remote, reply);
1335	xpc_release(reply);
1336
1337	if (target != NULL) CFRelease(target);
1338	return;
1339}
1340
1341
1342static void
1343target_status(reach_client_t *client, xpc_object_t request)
1344{
1345	xpc_connection_t		remote;
1346	xpc_object_t			reply;
1347	__block Boolean			reply_now	= TRUE;
1348	Boolean				scheduled;
1349	uint64_t			status		= REACH_REQUEST_REPLY_FAILED;
1350	SCNetworkReachabilityRef	target		= NULL;
1351	uint64_t			target_id;
1352
1353	if(S_debug) {
1354		SCLog(TRUE, LOG_INFO,
1355		      CFSTR("<%p> get status of reachability target"),
1356		      client->connection);
1357//		log_xpc_object("  status", request);
1358	}
1359
1360	remote = xpc_dictionary_get_remote_connection(request);
1361	reply = xpc_dictionary_create_reply(request);
1362	if (reply == NULL) {
1363		SCLog(TRUE, LOG_ERR,
1364		      CFSTR("<%p> target_status: xpc_dictionary_create_reply: failed"),
1365		      client->connection);
1366		return;
1367	}
1368
1369	target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
1370	if (target_id == 0) {
1371		SCLog(TRUE, LOG_ERR,
1372		      CFSTR("<%p>   target_status: no target"),
1373		      client->connection);
1374		xpc_dictionary_set_string(reply,
1375					  REACH_REQUEST_REPLY_DETAIL,
1376					  "no target ID");
1377		goto done;
1378	}
1379
1380	target = _client_target_copy(client, target_id);
1381	if (target == NULL) {
1382		SCLog(TRUE, LOG_ERR,
1383		      CFSTR("<%p>   target_status: no target (0x%0llx)"),
1384		      client->connection,
1385		      target_id);
1386		xpc_dictionary_set_string(reply,
1387					  REACH_REQUEST_REPLY_DETAIL,
1388					  "no target");
1389		status = REACH_REQUEST_REPLY_UNKNOWN;
1390		goto done;
1391	}
1392
1393	/*
1394	 * Check to see if the target [for this client] had been "scheduled".
1395	 *
1396	 * If so, also mark that we've picked up the current reachability
1397	 * flags and that any pending notifications have been processed.
1398	 */
1399	scheduled = _target_watcher_checkin(target, client->connection, target_id);
1400
1401	/*
1402	 * return current reachability information to the caller
1403	 */
1404	dispatch_sync(_target_queue(target), ^{
1405		SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
1406
1407		if (scheduled) {
1408			/*
1409			 * The client "scheduled" this target.  As such, we
1410			 * know that this is an async query and that we only
1411			 * need to return the "last known" flags.
1412			 */
1413			_target_reply_add_reachability(target, reply);
1414//			log_xpc_object("  reply [scheduled]", reply);
1415
1416			/*
1417			 * ... and if it's not a "name" query then we can mark the
1418			 * flags as valid.
1419			 */
1420			if (!isReachabilityTypeName(targetPrivate->type)) {
1421				if (_SC_ATOMIC_CMPXCHG(&targetPrivate->serverInfoValid, FALSE, TRUE)) {
1422					if (S_debug) {
1423						SCLog(TRUE, LOG_INFO, CFSTR("%s  flags are now \"valid\"."),
1424						      targetPrivate->log_prefix);
1425					}
1426				}
1427			}
1428
1429			if (S_debug) {
1430				CFStringRef	str;
1431
1432				str = _SCNetworkReachabilityCopyTargetFlags(target);
1433				SCLog(TRUE, LOG_INFO,
1434				      CFSTR("<%p>   reply [scheduled%s], %@"),
1435				      client->connection,
1436				      targetPrivate->serverInfoValid ? "/valid" : "",
1437				      str);
1438				CFRelease(str);
1439			}
1440		} else {
1441			/*
1442			 * The client has NOT "scheduled" this target.  As
1443			 * such, we know that this is a sync query and that
1444			 * we must return "current" flags.
1445			 */
1446			if (targetPrivate->scheduled && targetPrivate->serverInfoValid) {
1447				/*
1448				 * The server target has been "scheduled" and we
1449				 * have flags that are "current".
1450				 */
1451				_target_reply_add_reachability(target, reply);
1452//				log_xpc_object("  reply [scheduled/valid]", reply);
1453
1454				if (S_debug) {
1455					CFStringRef	str;
1456
1457					str = _SCNetworkReachabilityCopyTargetFlags(target);
1458					SCLog(TRUE, LOG_INFO,
1459					      CFSTR("<%p>   reply [scheduled/valid], %@"),
1460					      client->connection,
1461					      str);
1462					CFRelease(str);
1463				}
1464			} else {
1465				dispatch_group_t	group;
1466
1467				/*
1468				 * The server target has NOT been "scheduled" or
1469				 * we do not have "current" flags.  This means that
1470				 * we must query for the current information and
1471				 * return the flags to the client when they are
1472				 * available.
1473				 */
1474
1475				reply_now = FALSE;
1476
1477				group = _target_group(target);
1478				if (_SC_ATOMIC_INC(&targetPrivate->serverSyncQueryActive) == 0) {
1479					CFRetain(target);
1480					dispatch_group_async(group, _server_concurrent_queue(), ^{
1481						SCNetworkReachabilityFlags	flags;
1482						unsigned int			n;
1483						Boolean				ok;
1484
1485						// query for the flags
1486						ok = SCNetworkReachabilityGetFlags(target, &flags);
1487						flags = targetPrivate->info.flags;	// get the "raw" flags
1488						if (!ok) {
1489							SCLog(TRUE, LOG_ERR,
1490							      CFSTR("SCNetworkReachabilityGetFlags() [sync query] failed"
1491								    "\n  target = %@"
1492								    "\n  status = %s"),
1493							      target,
1494							      SCErrorString(SCError()));
1495						}
1496
1497						/*
1498						 * if we have current flags, if the target has since been
1499						 * scheduled, and this is not a "name" query, then mark as
1500						 * valid.
1501						 */
1502						if (ok &&
1503						    targetPrivate->scheduled &&
1504						    !isReachabilityTypeName(targetPrivate->type)) {
1505							if (_SC_ATOMIC_CMPXCHG(&targetPrivate->serverInfoValid, FALSE, TRUE)) {
1506								if (S_debug) {
1507									SCLog(TRUE, LOG_INFO, CFSTR("%s  flags are now \"valid\"!"),
1508									      targetPrivate->log_prefix);
1509								}
1510							}
1511						}
1512
1513						// sync query complete
1514						n = _SC_ATOMIC_ZERO(&targetPrivate->serverSyncQueryActive);
1515						if (S_debug) {
1516							SCLog(TRUE, LOG_INFO,
1517							      CFSTR("%sSCNetworkReachabilityGetFlags() [sync query] complete, n = %d"),
1518							      targetPrivate->log_prefix,
1519							      n);
1520						}
1521
1522						CFRelease(target);
1523					});
1524				}
1525
1526				CFRetain(target);
1527				dispatch_group_notify(group, _target_queue(target), ^{
1528					// flags are now available
1529					_target_reply_add_reachability(target, reply);
1530					xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, REACH_REQUEST_REPLY_OK);
1531//					log_xpc_object("  reply [delayed]", reply);
1532
1533					if (S_debug) {
1534						CFStringRef	str;
1535
1536						str = _SCNetworkReachabilityCopyTargetFlags(target);
1537						SCLog(TRUE, LOG_INFO,
1538						      CFSTR("<%p>   reply [delayed], %@"),
1539						      client->connection,
1540						      str);
1541						CFRelease(str);
1542					}
1543
1544					xpc_connection_send_message(remote, reply);
1545					xpc_release(reply);
1546
1547					CFRelease(target);
1548				});
1549			}
1550		}
1551	});
1552
1553	status = REACH_REQUEST_REPLY_OK;
1554
1555    done :
1556
1557	if (reply_now) {
1558		xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1559
1560		if (status != REACH_REQUEST_REPLY_OK) {
1561//			log_xpc_object("  reply [!]", reply);
1562
1563			if (S_debug) {
1564				SCLog(TRUE, LOG_INFO,
1565				      CFSTR("<%p>   reply [!]"),
1566				      client->connection);
1567			}
1568		}
1569
1570		xpc_connection_send_message(remote, reply);
1571		xpc_release(reply);
1572	} else if (S_debug) {
1573		CFStringRef	str;
1574
1575		str = _SCNetworkReachabilityCopyTargetFlags(target);
1576		SCLog(TRUE, LOG_INFO,
1577		      CFSTR("<%p>   no reply [yet], %@"),
1578		      client->connection,
1579		      str);
1580		CFRelease(str);
1581	}
1582
1583	if (target != NULL) CFRelease(target);
1584	return;
1585}
1586
1587
1588static void
1589target_unschedule(reach_client_t *client, xpc_object_t request)
1590{
1591	xpc_connection_t		remote;
1592	xpc_object_t			reply;
1593	uint64_t			status		= REACH_REQUEST_REPLY_FAILED;
1594	SCNetworkReachabilityRef	target		= NULL;
1595	uint64_t			target_id;
1596
1597	if (S_debug) {
1598		SCLog(TRUE, LOG_INFO,
1599		      CFSTR("<%p> unschedule reachability target"),
1600		      client->connection);
1601//		log_xpc_object("  unschedule", request);
1602	}
1603
1604	remote = xpc_dictionary_get_remote_connection(request);
1605	reply = xpc_dictionary_create_reply(request);
1606	if (reply == NULL) {
1607		SCLog(TRUE, LOG_ERR,
1608		      CFSTR("<%p> target_unschedule: xpc_dictionary_create_reply: failed"),
1609		      client->connection);
1610		return;
1611	}
1612
1613	target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
1614	if (target_id == 0) {
1615		xpc_dictionary_set_string(reply,
1616					  REACH_REQUEST_REPLY_DETAIL,
1617					  "no target ID");
1618		goto done;
1619	}
1620
1621	target = _client_target_copy(client, target_id);
1622	if (target == NULL) {
1623		xpc_dictionary_set_string(reply,
1624					  REACH_REQUEST_REPLY_DETAIL,
1625					  "no target");
1626		status = REACH_REQUEST_REPLY_UNKNOWN;
1627		goto done;
1628	}
1629
1630	// disable monitoring
1631	_target_watcher_remove(target, client->connection, target_id);
1632
1633	status = REACH_REQUEST_REPLY_OK;
1634
1635    done :
1636
1637	xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1638//	log_xpc_object("  reply", reply);
1639	xpc_connection_send_message(remote, reply);
1640	xpc_release(reply);
1641
1642	if (target != NULL) CFRelease(target);
1643	return;
1644}
1645
1646
1647#define	SNAPSHOT_PATH_STATE	_PATH_VARTMP "configd-reachability"
1648
1649
1650static void
1651_snapshot_digest_watcher(const void *key, const void *value, void *context)
1652{
1653	__block reach_client_t	*client		= NULL;
1654	FILE			*f		= (FILE *)context;
1655	static reach_client_t	no_client	= {
1656		.pid = 0,
1657		.proc_name = "?",
1658	};
1659	reach_watcher_key_t	*watcher_key;
1660	reach_watcher_val_t	*watcher_val;
1661
1662	/* ALIGN: CF aligns to >8 byte boundries */
1663	watcher_key = (reach_watcher_key_t *)(void *)CFDataGetBytePtr(key);
1664	watcher_val = (reach_watcher_val_t *)(void *)CFDataGetBytePtr(value);
1665
1666	dispatch_sync(_reach_clients_rbt_queue(), ^{
1667		uint64_t	connection_id	= (uintptr_t)watcher_key->connection;
1668		rb_tree_t	*rbt		= _reach_clients_rbt();
1669
1670		client = rb_tree_find_node(rbt, &connection_id);
1671		if (client == NULL) {
1672			client = &no_client;
1673		}
1674	});
1675
1676	SCPrint(TRUE, f,
1677		CFSTR("      connection = %p, target(c) = 0x%0llx, command = %s, pid = %d, changes = %u\n"),
1678		watcher_key->connection,
1679		watcher_key->target_id,
1680		client->proc_name,
1681		client->pid,
1682		watcher_val->n_changes);
1683
1684	return;
1685}
1686
1687
1688static void
1689_snapshot_digest(const void *key, const void *value, void *context)
1690{
1691	FILE				*f		= (FILE *)context;
1692	CFStringRef			digest		= (CFStringRef)key;
1693	dispatch_queue_t		q;
1694	SCNetworkReachabilityRef	target		= (SCNetworkReachabilityRef)value;
1695	SCNetworkReachabilityPrivateRef	targetPrivate	= (SCNetworkReachabilityPrivateRef)target;
1696
1697	q = _target_queue(target);
1698	dispatch_sync(q, ^{
1699		CFIndex		nWatchers	= 0;
1700
1701		if (targetPrivate->serverWatchers != NULL) {
1702			nWatchers = CFDictionaryGetCount(targetPrivate->serverWatchers);
1703		}
1704
1705		SCPrint(TRUE, f, CFSTR("\n  digest : %@\n"), digest);
1706		SCPrint(TRUE, f, CFSTR("    %@\n"), target);
1707		SCPrint(TRUE, f, CFSTR("    valid = %s, async watchers = %ld, sync queries = %u, refs = %u\n"),
1708			targetPrivate->serverInfoValid ? "Y" : "N",
1709			nWatchers,
1710			targetPrivate->serverSyncQueryActive,
1711			targetPrivate->serverReferences);
1712
1713		SCPrint(TRUE, f, CFSTR("    network %ld.%3.3d"),
1714			targetPrivate->last_network.tv_sec,
1715			targetPrivate->last_network.tv_usec / 1000);
1716#if	!TARGET_OS_IPHONE
1717		SCPrint(TRUE, f, CFSTR(", power %ld.%3.3d"),
1718			targetPrivate->last_power.tv_sec,
1719			targetPrivate->last_power.tv_usec / 1000);
1720#endif	// !TARGET_OS_IPHONE
1721		if (isReachabilityTypeName(targetPrivate->type)) {
1722			SCPrint(TRUE, f, CFSTR(", DNS %ld.%3.3d"),
1723				targetPrivate->last_dns.tv_sec,
1724				targetPrivate->last_dns.tv_usec / 1000);
1725			if (timerisset(&targetPrivate->dnsQueryEnd)) {
1726				struct timeval	dnsQueryElapsed;
1727
1728				timersub(&targetPrivate->dnsQueryEnd,
1729					 &targetPrivate->dnsQueryStart,
1730					 &dnsQueryElapsed);
1731				SCPrint(TRUE, f, CFSTR(" (query %ld.%3.3d / reply %ld.%3.3d)"),
1732					targetPrivate->dnsQueryStart.tv_sec,
1733					targetPrivate->dnsQueryStart.tv_usec / 1000,
1734					dnsQueryElapsed.tv_sec,
1735					dnsQueryElapsed.tv_usec / 1000);
1736			}
1737		}
1738		if (timerisset(&targetPrivate->last_push)) {
1739			SCPrint(TRUE, f, CFSTR(", last notify %ld.%3.3d"),
1740				targetPrivate->last_push.tv_sec,
1741				targetPrivate->last_push.tv_usec / 1000);
1742		}
1743		SCPrint(TRUE, f, CFSTR(", uid %d"),
1744			targetPrivate->uid);
1745		SCPrint(TRUE, f, CFSTR("\n"));
1746
1747		if (nWatchers > 0) {
1748			CFDictionaryApplyFunction(targetPrivate->serverWatchers,
1749						  _snapshot_digest_watcher,
1750						  f);
1751		}
1752	});
1753
1754	return;
1755}
1756
1757
1758static void
1759_snapshot_target(const void *key, const void *value, void *context)
1760{
1761	FILE				*f		= (FILE *)context;
1762	SCNetworkReachabilityRef	target		= (SCNetworkReachabilityRef)value;
1763	uint64_t			target_id;
1764	CFDataRef			target_key	= (CFDataRef)key;
1765
1766	/* ALIGN: CF aligns > 8 byte boundries */
1767	target_id = *(uint64_t *)(void *)CFDataGetBytePtr(target_key);
1768
1769	SCPrint(TRUE, f,
1770		CFSTR("    target(c) = 0x%0llx, target(s) = %@\n"),
1771		target_id,
1772		target);
1773
1774	return;
1775}
1776
1777
1778static void
1779_snapshot(reach_client_t *client, xpc_object_t request)
1780{
1781	uid_t			euid;
1782	FILE			*f;
1783	int			fd;
1784	Boolean			ok	= FALSE;
1785	xpc_connection_t	remote;
1786	xpc_object_t		reply;
1787
1788	if (S_debug) {
1789		SCLog(TRUE, LOG_INFO,
1790		      CFSTR("<%p> snapshot"),
1791		      client->connection);
1792//		log_xpc_object("  create", request);
1793	}
1794
1795	remote = xpc_dictionary_get_remote_connection(request);
1796	reply = xpc_dictionary_create_reply(request);
1797	if (reply == NULL) {
1798		SCLog(TRUE, LOG_ERR,
1799		      CFSTR("<%p> _snapshot: xpc_dictionary_create_reply: failed"),
1800		      client->connection);
1801		return;
1802	}
1803
1804	euid = xpc_connection_get_euid(remote);
1805	if (euid != 0) {
1806		xpc_dictionary_set_string(reply,
1807					  REACH_REQUEST_REPLY_DETAIL,
1808					  "Permission denied.");
1809		goto done;
1810	}
1811
1812	// Save a snapshot of the SCNetworkReachability server "state"
1813
1814	(void) unlink(SNAPSHOT_PATH_STATE);
1815	fd = open(SNAPSHOT_PATH_STATE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
1816	if (fd == -1) {
1817		xpc_dictionary_set_string(reply,
1818					  REACH_REQUEST_REPLY_DETAIL,
1819					  "open: failed");
1820		goto done;
1821	}
1822	f = fdopen(fd, "w");
1823	if (f == NULL) {
1824		xpc_dictionary_set_string(reply,
1825					  REACH_REQUEST_REPLY_DETAIL,
1826					  "fdopen: failed");
1827		goto done;
1828	}
1829
1830	// provide connection/client info
1831
1832	dispatch_sync(_reach_clients_rbt_queue(), ^{
1833		rb_tree_t	*rbt	= _reach_clients_rbt();
1834
1835		SCPrint(TRUE, f, CFSTR("Clients :\n"));
1836
1837		if (rb_tree_count(rbt) > 0) {
1838			reach_client_t	*client;
1839
1840			RB_TREE_FOREACH(client, rbt) {
1841				SCPrint(TRUE, f,
1842					CFSTR("\n  connection = %p, client = %p, command = %s, pid = %d\n"),
1843					client->connection,
1844					client,
1845					client->proc_name != NULL ? client->proc_name : "?",
1846					client->pid);
1847				my_CFDictionaryApplyFunction(client->targets,
1848							     _snapshot_target,
1849							     f);
1850			}
1851		} else {
1852			SCPrint(TRUE, f, CFSTR("  None.\n"));
1853		}
1854
1855		SCPrint(TRUE, f, CFSTR("\n"));
1856	});
1857
1858	// provide "digest" info
1859
1860	SCPrint(TRUE, f, CFSTR("Digests :\n"));
1861	dispatch_sync(_server_digest_queue(), ^{
1862		if (reach_digest_map != NULL) {
1863			CFDictionaryApplyFunction(reach_digest_map,
1864						  _snapshot_digest,
1865						  f);
1866		}
1867	});
1868
1869	(void) fclose(f);
1870
1871	ok = TRUE;
1872
1873    done :
1874
1875	xpc_dictionary_set_int64(reply,
1876				 REACH_REQUEST_REPLY,
1877				 ok ? REACH_REQUEST_REPLY_OK : REACH_REQUEST_REPLY_FAILED);
1878//	log_xpc_object("  reply", reply);
1879	xpc_connection_send_message(remote, reply);
1880	xpc_release(reply);
1881
1882	return;
1883}
1884
1885
1886static __inline__ void
1887_extract_client_info(reach_client_t *client, xpc_object_t request)
1888{
1889	// if available/needed, save the process name
1890	if (client->proc_name == NULL) {
1891		const char	*proc_name;
1892
1893		proc_name = xpc_dictionary_get_string(request, REACH_CLIENT_PROC_NAME);
1894		if (proc_name != NULL) {
1895			client->proc_name = strdup(proc_name);
1896		}
1897	}
1898
1899	return;
1900}
1901
1902
1903static void
1904process_request(reach_client_t *client, xpc_object_t request)
1905{
1906	int64_t		op;
1907
1908	op = xpc_dictionary_get_int64(request, REACH_REQUEST);
1909	switch (op) {
1910		case REACH_REQUEST_CREATE :
1911			_extract_client_info(client, request);
1912			target_add(client, request);
1913			break;
1914		case REACH_REQUEST_REMOVE :
1915			target_remove(client, request);
1916			break;
1917		case REACH_REQUEST_STATUS :
1918			target_status(client, request);
1919			break;
1920		case REACH_REQUEST_SCHEDULE :
1921			target_schedule(client, request);
1922			break;
1923		case REACH_REQUEST_UNSCHEDULE :
1924			target_unschedule(client, request);
1925			break;
1926		case REACH_REQUEST_SNAPSHOT :
1927			_extract_client_info(client, request);
1928			_snapshot(client, request);
1929			break;
1930		default :
1931			SCLog(TRUE, LOG_ERR,
1932			      CFSTR("<%p> unknown request : %lld"),
1933			      client->connection,
1934			      op);
1935			break;
1936	}
1937
1938	return;
1939}
1940
1941
1942static void
1943process_new_connection(xpc_connection_t connection)
1944{
1945	reach_client_t	*client;
1946
1947	if (S_debug) {
1948		SCLog(TRUE, LOG_INFO, CFSTR("<%p> new reach client, pid=%d"),
1949		      connection,
1950		      xpc_connection_get_pid(connection));
1951	}
1952
1953	client = _reach_client_create(connection);
1954	assert(client != NULL);
1955
1956	dispatch_sync(_reach_clients_rbt_queue(), ^{
1957		rb_tree_t	*rbt	= _reach_clients_rbt();
1958
1959		rb_tree_insert_node(rbt, client);
1960	});
1961
1962	xpc_connection_set_target_queue(connection, _reach_server_queue());
1963
1964	xpc_connection_set_event_handler(connection, ^(xpc_object_t xobj) {
1965		xpc_type_t	type;
1966
1967		type = xpc_get_type(xobj);
1968		if (type == XPC_TYPE_DICTIONARY) {
1969			__block reach_client_t	*client	= NULL;
1970
1971			dispatch_sync(_reach_clients_rbt_queue(), ^{
1972				uint64_t	connection_id	= (uintptr_t)connection;
1973				rb_tree_t	*rbt		= _reach_clients_rbt();
1974
1975				client = rb_tree_find_node(rbt, &connection_id);
1976			});
1977
1978			if (client != NULL) {
1979				// process the request
1980				process_request(client, xobj);
1981			} else {
1982				char		*desc;
1983
1984				SCLog(TRUE, LOG_ERR,
1985				      CFSTR("<%p:%d> unexpected SCNetworkReachability request"),
1986				      connection,
1987				      xpc_connection_get_pid(connection));
1988
1989				desc = xpc_copy_description(xobj);
1990				SCLog(TRUE, LOG_ERR,
1991				      CFSTR("  request = %s"),
1992				      desc);
1993				free(desc);
1994
1995				xpc_connection_cancel(connection);
1996			}
1997
1998		} else if (type == XPC_TYPE_ERROR) {
1999			const char	*desc;
2000
2001			desc = xpc_dictionary_get_string(xobj, XPC_ERROR_KEY_DESCRIPTION);
2002			if (xobj == XPC_ERROR_CONNECTION_INVALID) {
2003				if (S_debug) {
2004					SCLog(TRUE, LOG_INFO,
2005					      CFSTR("<%p:%d> %s"),
2006					      connection,
2007					      xpc_connection_get_pid(connection),
2008					      desc);
2009				}
2010
2011				_reach_client_remove(connection);
2012
2013			} else if (xobj == XPC_ERROR_CONNECTION_INTERRUPTED) {
2014				SCLog(TRUE, LOG_ERR,
2015				      CFSTR("<%p:%d> %s"),
2016				      connection,
2017				      xpc_connection_get_pid(connection),
2018				      desc);
2019
2020			} else {
2021				SCLog(TRUE, LOG_ERR,
2022				      CFSTR("<%p:%d> Connection error: %p : %s"),
2023				      connection,
2024				      xpc_connection_get_pid(connection),
2025				      xobj,
2026				      desc);
2027			}
2028
2029		}  else {
2030			SCLog(TRUE, LOG_ERR,
2031			      CFSTR("<%p:%d> unknown event type : %p"),
2032			      connection,
2033			      xpc_connection_get_pid(connection),
2034			      type);
2035		}
2036	});
2037
2038	xpc_connection_resume(connection);
2039
2040	return;
2041}
2042
2043
2044#pragma mark -
2045#pragma mark Reachability server "main"
2046
2047
2048__private_extern__
2049void
2050load_SCNetworkReachability(CFBundleRef bundle, Boolean bundleVerbose)
2051{
2052	xpc_connection_t	connection;
2053	const char		*name;
2054
2055	S_debug = bundleVerbose;
2056
2057	/*
2058	 * create a dictionary mapping SCNetworkReachability [CFData] digests
2059	 * to SCNetworkReachability objects.
2060	 */
2061	reach_digest_map = CFDictionaryCreateMutable(NULL,
2062						     0,
2063						     &kCFTypeDictionaryKeyCallBacks,
2064						     &kCFTypeDictionaryValueCallBacks);
2065
2066	// create XPC listener
2067	name = getenv("REACH_SERVER");
2068	if (name == NULL) {
2069		name = REACH_SERVICE_NAME;
2070	}
2071	connection = xpc_connection_create_mach_service(name,
2072							_reach_server_queue(),
2073							XPC_CONNECTION_MACH_SERVICE_LISTENER);
2074
2075	xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
2076		xpc_type_t	type;
2077
2078		type = xpc_get_type(event);
2079		if (type == XPC_TYPE_CONNECTION) {
2080			process_new_connection(event);
2081
2082		} else if (type == XPC_TYPE_ERROR) {
2083			const char	*desc;
2084
2085			desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
2086			if (event == XPC_ERROR_CONNECTION_INVALID) {
2087				SCLog(TRUE, LOG_ERR, CFSTR("reach server: %s"), desc);
2088				xpc_release(connection);
2089			} else if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
2090				SCLog(TRUE, LOG_ERR, CFSTR("reach server: %s"), desc);
2091			} else {
2092				SCLog(TRUE, LOG_ERR,
2093				      CFSTR("reach server: Connection error: %p : %s"),
2094				      event,
2095				      desc);
2096			}
2097
2098		} else {
2099			SCLog(TRUE, LOG_ERR,
2100			      CFSTR("reach server: unknown event type : %p"),
2101			      type);
2102		}
2103	});
2104	xpc_connection_resume(connection);
2105
2106	return;
2107}
2108
2109#ifdef  MAIN
2110
2111int
2112main(int argc, char **argv)
2113{
2114//	_sc_log     = FALSE;
2115	_sc_verbose = (argc > 1) ? TRUE : FALSE;
2116	_sc_debug   = TRUE;
2117
2118	load_SCNetworkReachability(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
2119	CFRunLoopRun();
2120	/* not reached */
2121	exit(0);
2122	return 0;
2123}
2124
2125#endif  /* MAIN */
2126