2 * Copyright (c) 2011-2014 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <CoreFoundation/CoreFoundation.h>
25 #include <SystemConfiguration/SystemConfiguration.h>
26 #include <SystemConfiguration/SCPrivate.h>
27 #include "SCNetworkReachabilityInternal.h"
31 #include <CommonCrypto/CommonDigest.h>
32 #include <dispatch/dispatch.h>
33 #include <dispatch/private.h>
35 #include <xpc/private.h>
36 #include <sys/rbtree.h>
45 * A boolean that enables additional logging.
47 static boolean_t S_debug
= FALSE
;
51 #pragma mark Support functions
55 log_xpc_object(const char *msg
, xpc_object_t obj
)
59 desc
= xpc_copy_description(obj
);
60 SCLog(S_debug
, LOG_INFO
, CFSTR("%s = %s"), msg
, desc
);
65 static __inline__
void
66 my_CFDictionaryApplyFunction(CFDictionaryRef theDict
,
67 CFDictionaryApplierFunction applier
,
70 CFAllocatorRef myAllocator
;
71 CFDictionaryRef myDict
;
73 myAllocator
= CFGetAllocator(theDict
);
74 myDict
= CFDictionaryCreateCopy(myAllocator
, theDict
);
75 CFDictionaryApplyFunction(myDict
, applier
, context
);
82 #pragma mark SCNetworkReachability target support
85 static CFMutableDictionaryRef reach_digest_map
;
88 static dispatch_queue_t
89 _server_concurrent_queue()
91 static dispatch_once_t once
;
92 static dispatch_queue_t q
;
94 dispatch_once(&once
, ^{
95 q
= dispatch_queue_create(REACH_SERVICE_NAME
".concurrent",
96 DISPATCH_QUEUE_CONCURRENT
);
103 static dispatch_queue_t
104 _server_digest_queue()
106 static dispatch_once_t once
;
107 static dispatch_queue_t q
;
109 dispatch_once(&once
, ^{
110 q
= dispatch_queue_create(REACH_SERVICE_NAME
".digest", NULL
);
117 static dispatch_group_t
118 _target_group(SCNetworkReachabilityRef target
)
120 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
122 return targetPrivate
->serverGroup
;
126 static dispatch_queue_t
127 _target_queue(SCNetworkReachabilityRef target
)
129 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
131 return targetPrivate
->serverQueue
;
139 * _target_reference_add
141 * Note: use dispatch_sync(_server_digest_queue(), ^{ ... });
144 _target_reference_add(SCNetworkReachabilityRef target
, CFDataRef digest
, xpc_connection_t connection
)
146 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
148 // take a reference to the target
151 // ensure that we have a dispatch group
152 if (targetPrivate
->serverGroup
== NULL
) {
153 targetPrivate
->serverGroup
= dispatch_group_create();
156 // ensure that we have a dispatch queue
157 if (targetPrivate
->serverQueue
== NULL
) {
160 snprintf(qname
, sizeof(qname
), "com.apple.SCNetworkReachability.%p.server", target
);
161 targetPrivate
->serverQueue
= dispatch_queue_create(qname
, NULL
);
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
);
174 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
175 SCLog(TRUE
, LOG_INFO
,
176 CFSTR("<%p> target %p: reference added (%@, %d)"),
180 targetPrivate
->serverReferences
);
189 * _target_reference_remove
191 * Note: use dispatch_sync(_server_digest_queue(), ^{ ... });
194 _target_reference_remove(SCNetworkReachabilityRef target
, xpc_connection_t connection
)
196 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
198 // drop the reference count
199 if (_SC_ATOMIC_DEC(&targetPrivate
->serverReferences
) == 0) {
201 * if that was the last reference, we no longer need to
202 * keep the digest-->target mapping
204 CFDictionaryRemoveValue(reach_digest_map
, targetPrivate
->serverDigest
);
205 CFRelease(targetPrivate
->serverDigest
);
206 targetPrivate
->serverDigest
= NULL
;
210 SCLog(TRUE
, LOG_INFO
,
211 CFSTR("<%p> target %p: reference removed (%d)"),
214 targetPrivate
->serverReferences
);
217 // release a reference to the target
227 #define MUTEX_LOCK(m) { \
228 int _lock_ = (pthread_mutex_lock(m) == 0); \
232 #define MUTEX_UNLOCK(m) { \
233 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
239 _target_reply_add_reachability(SCNetworkReachabilityRef target
,
242 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
244 MUTEX_LOCK(&targetPrivate
->lock
);
246 xpc_dictionary_set_uint64(reply
,
248 targetPrivate
->info
.cycle
);
249 xpc_dictionary_set_uint64(reply
,
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
;
268 addresses
= xpc_array_create(NULL
, 0);
270 n
= CFArrayGetCount(targetPrivate
->resolvedAddresses
);
271 for (i
= 0; i
< n
; i
++) {
272 if (targetPrivate
->type
== reachabilityTypeName
) {
275 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddresses
, i
);
276 xpc_array_set_data(addresses
,
278 CFDataGetBytePtr(address
),
279 CFDataGetLength(address
));
280 } else if (targetPrivate
->type
== reachabilityTypePTR
) {
282 char str
[MAXHOSTNAMELEN
];
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
);
290 xpc_dictionary_set_value(reply
,
291 REACH_STATUS_RESOLVED_ADDRESSES
,
293 xpc_release(addresses
);
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
);
304 MUTEX_UNLOCK(&targetPrivate
->lock
);
314 xpc_connection_t connection
;
316 } reach_watcher_key_t
;
319 unsigned int n_changes
;
320 } reach_watcher_val_t
;
324 _target_watcher_key_create(xpc_connection_t connection
,
328 reach_watcher_key_t watcher_key
;
330 watcher_key
.connection
= connection
;
331 watcher_key
.target_id
= target_id
;
333 key
= CFDataCreate(NULL
, (UInt8
*)&watcher_key
, sizeof(watcher_key
));
339 _target_watcher_add(SCNetworkReachabilityRef target
,
340 xpc_connection_t connection
,
343 __block Boolean ok
= TRUE
;
346 q
= _target_queue(target
);
349 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
351 if (targetPrivate
->serverWatchers
== NULL
) {
352 ok
= SCNetworkReachabilitySetDispatchQueue(target
, q
);
355 CFSTR("<%p> target %p: _watcher_add SCNetworkReachabilitySetDispatchQueue() failed: %s"),
358 SCErrorString(SCError()));
362 targetPrivate
->serverWatchers
= CFDictionaryCreateMutable(NULL
,
364 &kCFTypeDictionaryKeyCallBacks
,
365 &kCFTypeDictionaryValueCallBacks
);
368 xpc_retain(connection
);
370 key
= _target_watcher_key_create(connection
, target_id
);
371 if (CFDictionaryContainsKey(targetPrivate
->serverWatchers
, key
)) {
373 CFSTR("<%p> target %p: watcher not added, c=0x%0llx, \"serverWatchers\" key exists"),
379 static const reach_watcher_val_t watcher_val0
= { 0 };
381 val
= CFDataCreate(NULL
, (UInt8
*)&watcher_val0
, sizeof(watcher_val0
));
382 CFDictionaryAddValue(targetPrivate
->serverWatchers
, key
, val
);
386 SCLog(TRUE
, LOG_INFO
,
387 CFSTR("<%p> target %p: watcher added, c=0x%0llx, n=%ld"),
391 CFDictionaryGetCount(targetPrivate
->serverWatchers
));
402 _target_watcher_checkin(SCNetworkReachabilityRef target
,
403 xpc_connection_t connection
,
406 __block Boolean scheduled
= FALSE
;
408 dispatch_sync(_target_queue(target
), ^{
411 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
413 reach_watcher_val_t
*watcher_val
;
415 if (targetPrivate
->serverWatchers
== NULL
) {
420 key
= _target_watcher_key_create(connection
, target_id
);
421 val
= CFDictionaryGetValue(targetPrivate
->serverWatchers
, key
);
424 // if the target [for this client] was not scheduled
428 // indicate that the target was scheduled
432 * and note that the reachability flags for this target have
433 * been picked up by the client
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"),
444 (n
== 1) ? "" : "s");
453 _target_watcher_remove(SCNetworkReachabilityRef target
,
454 xpc_connection_t connection
,
457 __block Boolean ok
= TRUE
;
459 dispatch_sync(_target_queue(target
), ^{
462 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
464 if (targetPrivate
->serverWatchers
== NULL
) {
466 CFSTR("<%p> target %p: watcher not removed, c=0x%0llx, no \"serverWatchers\""),
474 key
= _target_watcher_key_create(connection
, target_id
);
475 if (!CFDictionaryContainsKey(targetPrivate
->serverWatchers
, key
)) {
477 CFSTR("<%p> target %p: watcher not removed, c=0x%0llx, no \"serverWatchers\" key"),
486 CFDictionaryRemoveValue(targetPrivate
->serverWatchers
, key
);
489 n
= CFDictionaryGetCount(targetPrivate
->serverWatchers
);
492 SCLog(TRUE
, LOG_INFO
,
493 CFSTR("<%p> target %p: watcher removed, c=0x%0llx, n=%ld"),
501 CFRelease(targetPrivate
->serverWatchers
);
502 targetPrivate
->serverWatchers
= NULL
;
504 ok
= SCNetworkReachabilitySetDispatchQueue(target
, NULL
);
507 CFSTR("<%p> target %p: _watcher_remove SCNetworkReachabilitySetDispatchQueue() failed: %s"),
510 SCErrorString(SCError()));
514 // no more watchers, flags are no longer valid
515 if (_SC_ATOMIC_CMPXCHG(&targetPrivate
->serverInfoValid
, TRUE
, FALSE
)) {
517 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags are no longer \"valid\""),
518 targetPrivate
->log_prefix
);
523 xpc_release(connection
);
531 #pragma mark Reachability [RBT] client support
536 xpc_connection_t connection
;
538 const char *proc_name
;
539 CFMutableDictionaryRef targets
; // target_id --> SCNetworkReachabilityRef
544 _rbt_compare_transaction_nodes(void *context
, const void *n1
, const void *n2
)
546 uint64_t a
= (uintptr_t)((reach_client_t
*)n1
)->connection
;
547 uint64_t b
= (uintptr_t)((reach_client_t
*)n2
)->connection
;
560 _rbt_compare_transaction_key(void *context
, const void *n1
, const void *key
)
562 uint64_t a
= (uintptr_t)((reach_client_t
*)n1
)->connection
;
563 uint64_t b
= *(uintptr_t *)key
;
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
),
585 static rb_tree_t rbt
;
587 dispatch_once(&once
, ^{
588 rb_tree_init(&rbt
, &ops
);
595 static dispatch_queue_t
596 _reach_clients_rbt_queue()
598 static dispatch_once_t once
;
599 static dispatch_queue_t q
;
601 dispatch_once(&once
, ^{
602 q
= dispatch_queue_create(REACH_SERVICE_NAME
".clients.rbt", NULL
);
609 static reach_client_t
*
610 _reach_client_create(xpc_connection_t connection
)
612 reach_client_t
*client
;
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
,
620 &kCFTypeDictionaryKeyCallBacks
,
621 &kCFTypeDictionaryValueCallBacks
);
628 _reach_client_release(reach_client_t
*client
)
630 if (client
->proc_name
!= NULL
) {
631 free((void *)client
->proc_name
);
633 CFRelease(client
->targets
);
640 _reach_client_remove_target(const void *key
, const void *value
, void *context
)
642 xpc_connection_t connection
= (xpc_connection_t
)context
;
643 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
644 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
646 // check if we have anyone watching this target
647 if (targetPrivate
->serverWatchers
!= NULL
) {
650 n
= CFDictionaryGetCount(targetPrivate
->serverWatchers
);
653 CFDictionaryRef serverWatchers
;
654 const void * watchers_q
[32];
655 const void ** watchers
= watchers_q
;
657 serverWatchers
= CFDictionaryCreateCopy(NULL
, targetPrivate
->serverWatchers
);
659 if (n
> sizeof(watchers_q
)/sizeof(watchers
[0])) {
660 watchers
= CFAllocatorAllocate(NULL
, n
* sizeof(CFDataRef
), 0);
662 CFDictionaryGetKeysAndValues(serverWatchers
, watchers
, NULL
);
664 for (i
= 0; i
< n
; i
++) {
666 reach_watcher_key_t
*watcher_key
;
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
);
679 if (watchers
!= watchers_q
) {
680 CFAllocatorDeallocate(NULL
, watchers
);
683 CFRelease(serverWatchers
);
687 // remove our reference to this target
688 dispatch_sync(_server_digest_queue(), ^{
689 _target_reference_remove(target
, connection
);
697 _reach_client_remove(xpc_connection_t connection
)
699 uint64_t connection_id
= (uintptr_t)connection
;
701 dispatch_sync(_reach_clients_rbt_queue(), ^{
702 reach_client_t
*client
;
703 rb_tree_t
*rbt
= _reach_clients_rbt();
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
,
712 rb_tree_remove_node(rbt
, client
);
713 _reach_client_release(client
);
716 CFSTR("<%p> _reach_client_remove: unexpected client"),
725 static __inline__ CFDataRef
726 _client_target_key_create(uint64_t target_id
)
728 CFDataRef target_key
;
730 target_key
= CFDataCreate(NULL
, (UInt8
*)&target_id
, sizeof(target_id
));
735 static SCNetworkReachabilityRef
736 _client_target_copy(reach_client_t
*client
, uint64_t target_id
)
738 SCNetworkReachabilityRef target
;
739 CFDataRef target_key
;
741 target_key
= _client_target_key_create(target_id
);
742 target
= CFDictionaryGetValue(client
->targets
, target_key
);
743 CFRelease(target_key
);
745 if (target
!= NULL
) {
754 _client_target_set(reach_client_t
*client
, uint64_t target_id
, SCNetworkReachabilityRef target
)
757 CFDataRef target_key
;
759 target_key
= _client_target_key_create(target_id
);
760 added
= !CFDictionaryContainsKey(client
->targets
, target_key
);
762 CFDictionarySetValue(client
->targets
, target_key
, target
);
764 CFRelease(target_key
);
771 _client_target_remove(reach_client_t
*client
, uint64_t target_id
)
773 CFDataRef target_key
;
775 target_key
= _client_target_key_create(target_id
);
776 CFDictionaryRemoveValue(client
->targets
, target_key
);
777 CFRelease(target_key
);
784 #pragma mark Reachability [XPC] server functions
787 static dispatch_queue_t
788 _reach_server_queue()
790 static dispatch_once_t once
;
791 static dispatch_queue_t q
;
793 dispatch_once(&once
, ^{
794 q
= dispatch_queue_create(REACH_SERVICE_NAME
, NULL
);
804 * Note: should be exec'd on the target queue
807 _reach_changed(SCNetworkReachabilityRef target
, SCNetworkReachabilityFlags flags
, void *info
)
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
;
818 SCLog(TRUE
, LOG_INFO
,
819 CFSTR("%sprocess reachability changed, flags = 0x%08x"),
820 targetPrivate
->log_prefix
,
824 if (targetPrivate
->serverWatchers
== NULL
) {
829 n
= CFDictionaryGetCount(targetPrivate
->serverWatchers
);
835 if (!isReachabilityTypeName(targetPrivate
->type
) || !targetPrivate
->dnsNoAddressesSinceLastTimeout
) {
837 * Because we are actively watching for additional changes
838 * we mark the flags as "valid"
840 if (_SC_ATOMIC_CMPXCHG(&targetPrivate
->serverInfoValid
, FALSE
, TRUE
)) {
842 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags are now \"valid\""),
843 targetPrivate
->log_prefix
);
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);
854 CFDictionaryGetKeysAndValues(targetPrivate
->serverWatchers
,
858 for (i
= 0; i
< n
; i
++) {
859 xpc_connection_t connection
;
863 reach_watcher_key_t
*watcher_key
;
864 reach_watcher_val_t
*watcher_val
;
866 val
= (CFDataRef
)watcher_vals
[i
];
867 /* ALIGN: CF aligns to >8 byte boundries */
868 watcher_val
= (reach_watcher_val_t
*)(void *)CFDataGetBytePtr(val
);
870 if (_SC_ATOMIC_INC(&watcher_val
->n_changes
) > 0) {
871 // if we've already sent a notification
875 key
= (CFDataRef
)watcher_keys
[i
];
876 /* ALIGN: CF aligns to >8 byte boundries */
877 watcher_key
= (reach_watcher_key_t
*)(void *)CFDataGetBytePtr(key
);
879 connection
= xpc_retain(watcher_key
->connection
);
880 target_id
= watcher_key
->target_id
;
881 dispatch_async(_reach_server_queue(), ^{
884 // create our [async] notification
885 reply
= xpc_dictionary_create(NULL
, NULL
, 0);
888 xpc_dictionary_set_int64(reply
,
890 MESSAGE_REACHABILITY_STATUS
);
893 xpc_dictionary_set_uint64(reply
,
894 REACH_CLIENT_TARGET_ID
,
897 log_xpc_object(" reply [async]", reply
);
898 xpc_connection_send_message(connection
, reply
);
901 xpc_release(connection
);
905 if (n
> sizeof(watcher_keys_q
)/sizeof(watcher_keys
[0])) {
906 CFAllocatorDeallocate(NULL
, watcher_keys
);
907 CFAllocatorDeallocate(NULL
, watcher_vals
);
915 sanitize_address(const struct sockaddr
*from
, struct sockaddr
*to
)
917 switch (from
->sa_family
) {
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
;
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
));
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
;
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
;
944 bcopy(from
, to
, from
->sa_len
);
953 target_add(reach_client_t
*client
, xpc_object_t request
)
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
;
963 const char *if_name
= NULL
;
964 bool onDemandBypass
= FALSE
;
965 bool resolverBypass
= FALSE
;
970 unsigned char bytes
[CC_SHA1_DIGEST_LENGTH
];
972 CFDataRef digest
= NULL
;
974 xpc_connection_t remote
;
976 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
979 __block Boolean ok
= TRUE
;
980 __block SCNetworkReachabilityRef target
= NULL
;
983 SCLog(TRUE
, LOG_INFO
,
984 CFSTR("<%p> create reachability target"),
986 // log_xpc_object(" create", request);
989 remote
= xpc_dictionary_get_remote_connection(request
);
990 reply
= xpc_dictionary_create_reply(request
);
993 CFSTR("<%p> target_add: xpc_dictionary_create_reply: failed"),
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
,
1006 // create a "digest" of the [new] target
1010 name
= xpc_dictionary_get_string(request
, REACH_TARGET_NAME
);
1012 CC_SHA1_Update(&ctx
, REACH_TARGET_NAME
, sizeof(REACH_TARGET_NAME
));
1013 CC_SHA1_Update(&ctx
, name
, (CC_LONG
)strlen(name
));
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
);
1023 xpc_dictionary_set_string(reply
,
1024 REACH_REQUEST_REPLY_DETAIL
,
1025 "local address: size error");
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
);
1037 xpc_dictionary_set_string(reply
,
1038 REACH_REQUEST_REPLY_DETAIL
,
1039 "remote address: size error");
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
);
1051 xpc_dictionary_set_string(reply
,
1052 REACH_REQUEST_REPLY_DETAIL
,
1053 "ptr address: size error");
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
));
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
));
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
));
1080 // Grab UID from XPC connection
1081 uid
= xpc_connection_get_euid(remote
);
1082 CC_SHA1_Update(&ctx
, &uid
, sizeof(uid
));
1085 CC_SHA1_Final(bytes
, &ctx
);
1086 digest
= CFDataCreate(NULL
, bytes
, sizeof(bytes
));
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.
1093 dispatch_sync(_server_digest_queue(), ^{
1094 target
= CFDictionaryGetValue(reach_digest_map
, digest
);
1095 if (target
!= NULL
) {
1099 CFMutableDictionaryRef options
;
1102 options
= CFDictionaryCreateMutable(NULL
,
1104 &kCFTypeDictionaryKeyCallBacks
,
1105 &kCFTypeDictionaryValueCallBacks
);
1107 str
= CFStringCreateWithCString(NULL
, name
, kCFStringEncodingUTF8
);
1108 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, str
);
1111 if (localAddress
!= NULL
) {
1112 data
= CFDataCreate(NULL
, (const UInt8
*)&localAddress0
, localAddress0
.ss_len
);
1113 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionLocalAddress
, data
);
1116 if (remoteAddress
!= NULL
) {
1117 data
= CFDataCreate(NULL
, (const UInt8
*)&remoteAddress0
, remoteAddress0
.ss_len
);
1118 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
, data
);
1121 if (ptrAddress
!= NULL
) {
1122 data
= CFDataCreate(NULL
, (const UInt8
*)&ptrAddress0
, ptrAddress0
.ss_len
);
1123 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionPTRAddress
, data
);
1126 if (onDemandBypass
) {
1127 CFDictionarySetValue(options
,
1128 kSCNetworkReachabilityOptionConnectionOnDemandBypass
,
1131 if (resolverBypass
) {
1132 CFDictionarySetValue(options
,
1133 kSCNetworkReachabilityOptionResolverBypass
,
1136 CFDictionarySetValue(options
,
1137 kSCNetworkReachabilityOptionServerBypass
,
1139 target
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
1141 if (target
== NULL
) {
1142 xpc_dictionary_set_string(reply
,
1143 REACH_REQUEST_REPLY_DETAIL
,
1144 "SCNetworkReachabilityCreateWithOptions failed");
1149 // Set the UID on the target
1150 ((SCNetworkReachabilityPrivateRef
)target
)->uid
= uid
;
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
;
1157 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1158 targetPrivate
->if_index
= (unsigned int)if_index
;
1159 strlcpy(targetPrivate
->if_name
, if_name
, sizeof(targetPrivate
->if_name
));
1163 ok
= SCNetworkReachabilitySetCallback(target
, _reach_changed
, NULL
);
1165 xpc_dictionary_set_string(reply
,
1166 REACH_REQUEST_REPLY_DETAIL
,
1167 "SCNetworkReachabilitySetCallback failed");
1174 // bump the number of references to this target
1175 _target_reference_add(target
, digest
, client
->connection
);
1183 * add an association for the client's target_id to the [shared]
1184 * SCNetworkReachability object.
1186 added
= _client_target_set(client
, target_id
, target
);
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
);
1194 status
= REACH_REQUEST_REPLY_OK
;
1198 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1199 // log_xpc_object(" reply", reply);
1200 xpc_connection_send_message(remote
, reply
);
1203 if (digest
!= NULL
) CFRelease(digest
);
1204 if (target
!= NULL
) CFRelease(target
);
1210 target_remove(reach_client_t
*client
, xpc_object_t request
)
1212 xpc_connection_t remote
;
1214 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1215 SCNetworkReachabilityRef target
= NULL
;
1219 SCLog(TRUE
, LOG_INFO
,
1220 CFSTR("<%p> remove reachability target"),
1221 client
->connection
);
1222 // log_xpc_object(" remove", request);
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
);
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
,
1242 target
= _client_target_copy(client
, target_id
);
1243 if (target
== NULL
) {
1244 xpc_dictionary_set_string(reply
,
1245 REACH_REQUEST_REPLY_DETAIL
,
1247 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1252 * remove the association from the client's target_id to the [shared]
1253 * SCNetworkReachability object.
1255 _client_target_remove(client
, target_id
);
1257 // drop the number of references to this target
1258 dispatch_sync(_server_digest_queue(), ^{
1259 _target_reference_remove(target
, client
->connection
);
1262 status
= REACH_REQUEST_REPLY_OK
;
1266 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1267 // log_xpc_object(" reply", reply);
1268 xpc_connection_send_message(remote
, reply
);
1271 if (target
!= NULL
) CFRelease(target
);
1277 target_schedule(reach_client_t
*client
, xpc_object_t request
)
1280 xpc_connection_t remote
;
1282 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1283 SCNetworkReachabilityRef target
= NULL
;
1287 SCLog(TRUE
, LOG_INFO
,
1288 CFSTR("<%p> schedule reachability target"),
1289 client
->connection
);
1290 // log_xpc_object(" schedule", request);
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
);
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
,
1310 target
= _client_target_copy(client
, target_id
);
1311 if (target
== NULL
) {
1312 xpc_dictionary_set_string(reply
,
1313 REACH_REQUEST_REPLY_DETAIL
,
1315 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1319 // enable monitoring
1320 ok
= _target_watcher_add(target
, client
->connection
, target_id
);
1322 xpc_dictionary_set_string(reply
,
1323 REACH_REQUEST_REPLY_DETAIL
,
1324 "could not add watcher");
1328 status
= REACH_REQUEST_REPLY_OK
;
1332 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1333 // log_xpc_object(" reply", reply);
1334 xpc_connection_send_message(remote
, reply
);
1337 if (target
!= NULL
) CFRelease(target
);
1343 target_status(reach_client_t
*client
, xpc_object_t request
)
1345 xpc_connection_t remote
;
1347 __block Boolean reply_now
= TRUE
;
1349 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1350 SCNetworkReachabilityRef target
= NULL
;
1354 SCLog(TRUE
, LOG_INFO
,
1355 CFSTR("<%p> get status of reachability target"),
1356 client
->connection
);
1357 // log_xpc_object(" status", request);
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
);
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
,
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)"),
1386 xpc_dictionary_set_string(reply
,
1387 REACH_REQUEST_REPLY_DETAIL
,
1389 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1394 * Check to see if the target [for this client] had been "scheduled".
1396 * If so, also mark that we've picked up the current reachability
1397 * flags and that any pending notifications have been processed.
1399 scheduled
= _target_watcher_checkin(target
, client
->connection
, target_id
);
1402 * return current reachability information to the caller
1404 dispatch_sync(_target_queue(target
), ^{
1405 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
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.
1413 _target_reply_add_reachability(target
, reply
);
1414 // log_xpc_object(" reply [scheduled]", reply);
1417 * ... and if it's not a "name" query then we can mark the
1420 if (!isReachabilityTypeName(targetPrivate
->type
)) {
1421 if (_SC_ATOMIC_CMPXCHG(&targetPrivate
->serverInfoValid
, FALSE
, TRUE
)) {
1423 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags are now \"valid\"."),
1424 targetPrivate
->log_prefix
);
1432 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1433 SCLog(TRUE
, LOG_INFO
,
1434 CFSTR("<%p> reply [scheduled%s], %@"),
1436 targetPrivate
->serverInfoValid
? "/valid" : "",
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.
1446 if (targetPrivate
->scheduled
&& targetPrivate
->serverInfoValid
) {
1448 * The server target has been "scheduled" and we
1449 * have flags that are "current".
1451 _target_reply_add_reachability(target
, reply
);
1452 // log_xpc_object(" reply [scheduled/valid]", reply);
1457 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1458 SCLog(TRUE
, LOG_INFO
,
1459 CFSTR("<%p> reply [scheduled/valid], %@"),
1465 dispatch_group_t group
;
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
1477 group
= _target_group(target
);
1478 if (_SC_ATOMIC_INC(&targetPrivate
->serverSyncQueryActive
) == 0) {
1480 dispatch_group_async(group
, _server_concurrent_queue(), ^{
1481 SCNetworkReachabilityFlags flags
;
1485 // query for the flags
1486 ok
= SCNetworkReachabilityGetFlags(target
, &flags
);
1487 flags
= targetPrivate
->info
.flags
; // get the "raw" flags
1489 SCLog(TRUE
, LOG_ERR
,
1490 CFSTR("SCNetworkReachabilityGetFlags() [sync query] failed"
1494 SCErrorString(SCError()));
1498 * if we have current flags, if the target has since been
1499 * scheduled, and this is not a "name" query, then mark as
1503 targetPrivate
->scheduled
&&
1504 !isReachabilityTypeName(targetPrivate
->type
)) {
1505 if (_SC_ATOMIC_CMPXCHG(&targetPrivate
->serverInfoValid
, FALSE
, TRUE
)) {
1507 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags are now \"valid\"!"),
1508 targetPrivate
->log_prefix
);
1513 // sync query complete
1514 n
= _SC_ATOMIC_ZERO(&targetPrivate
->serverSyncQueryActive
);
1516 SCLog(TRUE
, LOG_INFO
,
1517 CFSTR("%sSCNetworkReachabilityGetFlags() [sync query] complete, n = %d"),
1518 targetPrivate
->log_prefix
,
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);
1536 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1537 SCLog(TRUE
, LOG_INFO
,
1538 CFSTR("<%p> reply [delayed], %@"),
1544 xpc_connection_send_message(remote
, reply
);
1553 status
= REACH_REQUEST_REPLY_OK
;
1558 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1560 if (status
!= REACH_REQUEST_REPLY_OK
) {
1561 // log_xpc_object(" reply [!]", reply);
1564 SCLog(TRUE
, LOG_INFO
,
1565 CFSTR("<%p> reply [!]"),
1566 client
->connection
);
1570 xpc_connection_send_message(remote
, reply
);
1572 } else if (S_debug
) {
1575 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1576 SCLog(TRUE
, LOG_INFO
,
1577 CFSTR("<%p> no reply [yet], %@"),
1583 if (target
!= NULL
) CFRelease(target
);
1589 target_unschedule(reach_client_t
*client
, xpc_object_t request
)
1591 xpc_connection_t remote
;
1593 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1594 SCNetworkReachabilityRef target
= NULL
;
1598 SCLog(TRUE
, LOG_INFO
,
1599 CFSTR("<%p> unschedule reachability target"),
1600 client
->connection
);
1601 // log_xpc_object(" unschedule", request);
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
);
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
,
1621 target
= _client_target_copy(client
, target_id
);
1622 if (target
== NULL
) {
1623 xpc_dictionary_set_string(reply
,
1624 REACH_REQUEST_REPLY_DETAIL
,
1626 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1630 // disable monitoring
1631 _target_watcher_remove(target
, client
->connection
, target_id
);
1633 status
= REACH_REQUEST_REPLY_OK
;
1637 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1638 // log_xpc_object(" reply", reply);
1639 xpc_connection_send_message(remote
, reply
);
1642 if (target
!= NULL
) CFRelease(target
);
1647 #define SNAPSHOT_PATH_STATE _PATH_VARTMP "configd-reachability"
1651 _snapshot_digest_watcher(const void *key
, const void *value
, void *context
)
1653 __block reach_client_t
*client
= NULL
;
1654 FILE *f
= (FILE *)context
;
1655 static reach_client_t no_client
= {
1659 reach_watcher_key_t
*watcher_key
;
1660 reach_watcher_val_t
*watcher_val
;
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
);
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();
1670 client
= rb_tree_find_node(rbt
, &connection_id
);
1671 if (client
== NULL
) {
1672 client
= &no_client
;
1677 CFSTR(" connection = %p, target(c) = 0x%0llx, command = %s, pid = %d, changes = %u\n"),
1678 watcher_key
->connection
,
1679 watcher_key
->target_id
,
1682 watcher_val
->n_changes
);
1689 _snapshot_digest(const void *key
, const void *value
, void *context
)
1691 FILE *f
= (FILE *)context
;
1692 CFStringRef digest
= (CFStringRef
)key
;
1694 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
1695 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1697 q
= _target_queue(target
);
1699 CFIndex nWatchers
= 0;
1701 if (targetPrivate
->serverWatchers
!= NULL
) {
1702 nWatchers
= CFDictionaryGetCount(targetPrivate
->serverWatchers
);
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",
1710 targetPrivate
->serverSyncQueryActive
,
1711 targetPrivate
->serverReferences
);
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
;
1728 timersub(&targetPrivate
->dnsQueryEnd
,
1729 &targetPrivate
->dnsQueryStart
,
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);
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);
1743 SCPrint(TRUE
, f
, CFSTR(", uid %d"),
1744 targetPrivate
->uid
);
1745 SCPrint(TRUE
, f
, CFSTR("\n"));
1747 if (nWatchers
> 0) {
1748 CFDictionaryApplyFunction(targetPrivate
->serverWatchers
,
1749 _snapshot_digest_watcher
,
1759 _snapshot_target(const void *key
, const void *value
, void *context
)
1761 FILE *f
= (FILE *)context
;
1762 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
1764 CFDataRef target_key
= (CFDataRef
)key
;
1766 /* ALIGN: CF aligns > 8 byte boundries */
1767 target_id
= *(uint64_t *)(void *)CFDataGetBytePtr(target_key
);
1770 CFSTR(" target(c) = 0x%0llx, target(s) = %@\n"),
1779 _snapshot(reach_client_t
*client
, xpc_object_t request
)
1785 xpc_connection_t remote
;
1789 SCLog(TRUE
, LOG_INFO
,
1790 CFSTR("<%p> snapshot"),
1791 client
->connection
);
1792 // log_xpc_object(" create", request);
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
);
1804 euid
= xpc_connection_get_euid(remote
);
1806 xpc_dictionary_set_string(reply
,
1807 REACH_REQUEST_REPLY_DETAIL
,
1808 "Permission denied.");
1812 // Save a snapshot of the SCNetworkReachability server "state"
1814 (void) unlink(SNAPSHOT_PATH_STATE
);
1815 fd
= open(SNAPSHOT_PATH_STATE
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_EXCL
, 0644);
1817 xpc_dictionary_set_string(reply
,
1818 REACH_REQUEST_REPLY_DETAIL
,
1822 f
= fdopen(fd
, "w");
1824 xpc_dictionary_set_string(reply
,
1825 REACH_REQUEST_REPLY_DETAIL
,
1830 // provide connection/client info
1832 dispatch_sync(_reach_clients_rbt_queue(), ^{
1833 rb_tree_t
*rbt
= _reach_clients_rbt();
1835 SCPrint(TRUE
, f
, CFSTR("Clients :\n"));
1837 if (rb_tree_count(rbt
) > 0) {
1838 reach_client_t
*client
;
1840 RB_TREE_FOREACH(client
, rbt
) {
1842 CFSTR("\n connection = %p, client = %p, command = %s, pid = %d\n"),
1845 client
->proc_name
!= NULL
? client
->proc_name
: "?",
1847 my_CFDictionaryApplyFunction(client
->targets
,
1852 SCPrint(TRUE
, f
, CFSTR(" None.\n"));
1855 SCPrint(TRUE
, f
, CFSTR("\n"));
1858 // provide "digest" info
1860 SCPrint(TRUE
, f
, CFSTR("Digests :\n"));
1861 dispatch_sync(_server_digest_queue(), ^{
1862 if (reach_digest_map
!= NULL
) {
1863 CFDictionaryApplyFunction(reach_digest_map
,
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
);
1886 static __inline__
void
1887 _extract_client_info(reach_client_t
*client
, xpc_object_t request
)
1889 // if available/needed, save the process name
1890 if (client
->proc_name
== NULL
) {
1891 const char *proc_name
;
1893 proc_name
= xpc_dictionary_get_string(request
, REACH_CLIENT_PROC_NAME
);
1894 if (proc_name
!= NULL
) {
1895 client
->proc_name
= strdup(proc_name
);
1904 process_request(reach_client_t
*client
, xpc_object_t request
)
1908 op
= xpc_dictionary_get_int64(request
, REACH_REQUEST
);
1910 case REACH_REQUEST_CREATE
:
1911 _extract_client_info(client
, request
);
1912 target_add(client
, request
);
1914 case REACH_REQUEST_REMOVE
:
1915 target_remove(client
, request
);
1917 case REACH_REQUEST_STATUS
:
1918 target_status(client
, request
);
1920 case REACH_REQUEST_SCHEDULE
:
1921 target_schedule(client
, request
);
1923 case REACH_REQUEST_UNSCHEDULE
:
1924 target_unschedule(client
, request
);
1926 case REACH_REQUEST_SNAPSHOT
:
1927 _extract_client_info(client
, request
);
1928 _snapshot(client
, request
);
1931 SCLog(TRUE
, LOG_ERR
,
1932 CFSTR("<%p> unknown request : %lld"),
1943 process_new_connection(xpc_connection_t connection
)
1945 reach_client_t
*client
;
1948 SCLog(TRUE
, LOG_INFO
, CFSTR("<%p> new reach client, pid=%d"),
1950 xpc_connection_get_pid(connection
));
1953 client
= _reach_client_create(connection
);
1954 assert(client
!= NULL
);
1956 dispatch_sync(_reach_clients_rbt_queue(), ^{
1957 rb_tree_t
*rbt
= _reach_clients_rbt();
1959 rb_tree_insert_node(rbt
, client
);
1962 xpc_connection_set_target_queue(connection
, _reach_server_queue());
1964 xpc_connection_set_event_handler(connection
, ^(xpc_object_t xobj
) {
1967 type
= xpc_get_type(xobj
);
1968 if (type
== XPC_TYPE_DICTIONARY
) {
1969 __block reach_client_t
*client
= NULL
;
1971 dispatch_sync(_reach_clients_rbt_queue(), ^{
1972 uint64_t connection_id
= (uintptr_t)connection
;
1973 rb_tree_t
*rbt
= _reach_clients_rbt();
1975 client
= rb_tree_find_node(rbt
, &connection_id
);
1978 if (client
!= NULL
) {
1979 // process the request
1980 process_request(client
, xobj
);
1984 SCLog(TRUE
, LOG_ERR
,
1985 CFSTR("<%p:%d> unexpected SCNetworkReachability request"),
1987 xpc_connection_get_pid(connection
));
1989 desc
= xpc_copy_description(xobj
);
1990 SCLog(TRUE
, LOG_ERR
,
1991 CFSTR(" request = %s"),
1995 xpc_connection_cancel(connection
);
1998 } else if (type
== XPC_TYPE_ERROR
) {
2001 desc
= xpc_dictionary_get_string(xobj
, XPC_ERROR_KEY_DESCRIPTION
);
2002 if (xobj
== XPC_ERROR_CONNECTION_INVALID
) {
2004 SCLog(TRUE
, LOG_INFO
,
2005 CFSTR("<%p:%d> %s"),
2007 xpc_connection_get_pid(connection
),
2011 _reach_client_remove(connection
);
2013 } else if (xobj
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
2014 SCLog(TRUE
, LOG_ERR
,
2015 CFSTR("<%p:%d> %s"),
2017 xpc_connection_get_pid(connection
),
2021 SCLog(TRUE
, LOG_ERR
,
2022 CFSTR("<%p:%d> Connection error: %p : %s"),
2024 xpc_connection_get_pid(connection
),
2030 SCLog(TRUE
, LOG_ERR
,
2031 CFSTR("<%p:%d> unknown event type : %p"),
2033 xpc_connection_get_pid(connection
),
2038 xpc_connection_resume(connection
);
2045 #pragma mark Reachability server "main"
2050 load_SCNetworkReachability(CFBundleRef bundle
, Boolean bundleVerbose
)
2052 xpc_connection_t connection
;
2055 S_debug
= bundleVerbose
;
2058 * create a dictionary mapping SCNetworkReachability [CFData] digests
2059 * to SCNetworkReachability objects.
2061 reach_digest_map
= CFDictionaryCreateMutable(NULL
,
2063 &kCFTypeDictionaryKeyCallBacks
,
2064 &kCFTypeDictionaryValueCallBacks
);
2066 // create XPC listener
2067 name
= getenv("REACH_SERVER");
2069 name
= REACH_SERVICE_NAME
;
2071 connection
= xpc_connection_create_mach_service(name
,
2072 _reach_server_queue(),
2073 XPC_CONNECTION_MACH_SERVICE_LISTENER
);
2075 xpc_connection_set_event_handler(connection
, ^(xpc_object_t event
) {
2078 type
= xpc_get_type(event
);
2079 if (type
== XPC_TYPE_CONNECTION
) {
2080 process_new_connection(event
);
2082 } else if (type
== XPC_TYPE_ERROR
) {
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
);
2092 SCLog(TRUE
, LOG_ERR
,
2093 CFSTR("reach server: Connection error: %p : %s"),
2099 SCLog(TRUE
, LOG_ERR
,
2100 CFSTR("reach server: unknown event type : %p"),
2104 xpc_connection_resume(connection
);
2112 main(int argc
, char **argv
)
2115 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
2118 load_SCNetworkReachability(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);