2 * Copyright (c) 2011-2013 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"
29 #ifdef HAVE_REACHABILITY_SERVER
33 #include <CommonCrypto/CommonDigest.h>
34 #include <dispatch/dispatch.h>
35 #include <dispatch/private.h>
37 #include <xpc/private.h>
38 #include <sys/rbtree.h>
47 * A boolean that enables additional logging.
49 static boolean_t S_debug
= FALSE
;
53 #pragma mark Support functions
57 log_xpc_object(const char *msg
, xpc_object_t obj
)
61 desc
= xpc_copy_description(obj
);
62 SCLog(S_debug
, LOG_INFO
, CFSTR("%s = %s"), msg
, desc
);
67 static __inline__
void
68 my_CFDictionaryApplyFunction(CFDictionaryRef theDict
,
69 CFDictionaryApplierFunction applier
,
72 CFAllocatorRef myAllocator
;
73 CFDictionaryRef myDict
;
75 myAllocator
= CFGetAllocator(theDict
);
76 myDict
= CFDictionaryCreateCopy(myAllocator
, theDict
);
77 CFDictionaryApplyFunction(myDict
, applier
, context
);
84 #pragma mark SCNetworkReachability target support
87 static CFMutableDictionaryRef reach_digest_map
;
90 static dispatch_queue_t
91 _server_concurrent_queue()
93 static dispatch_once_t once
;
94 static dispatch_queue_t q
;
96 dispatch_once(&once
, ^{
97 q
= dispatch_queue_create(REACH_SERVICE_NAME
".concurrent",
98 DISPATCH_QUEUE_CONCURRENT
);
99 dispatch_queue_set_width(q
, 32);
106 static dispatch_queue_t
107 _server_digest_queue()
109 static dispatch_once_t once
;
110 static dispatch_queue_t q
;
112 dispatch_once(&once
, ^{
113 q
= dispatch_queue_create(REACH_SERVICE_NAME
".digest", NULL
);
120 static dispatch_group_t
121 _target_group(SCNetworkReachabilityRef target
)
123 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
125 return targetPrivate
->serverGroup
;
129 static dispatch_queue_t
130 _target_queue(SCNetworkReachabilityRef target
)
132 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
134 return targetPrivate
->serverQueue
;
142 * _target_reference_add
144 * Note: use dispatch_sync(_server_digest_queue(), ^{ ... });
147 _target_reference_add(SCNetworkReachabilityRef target
, CFDataRef digest
, xpc_connection_t connection
)
149 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
151 // take a reference to the target
154 // ensure that we have a dispatch group
155 if (targetPrivate
->serverGroup
== NULL
) {
156 targetPrivate
->serverGroup
= dispatch_group_create();
159 // ensure that we have a dispatch queue
160 if (targetPrivate
->serverQueue
== NULL
) {
163 snprintf(qname
, sizeof(qname
), "com.apple.SCNetworkReachability.%p.server", target
);
164 targetPrivate
->serverQueue
= dispatch_queue_create(qname
, NULL
);
167 // bump the reference count
168 if (_SC_ATOMIC_INC(&targetPrivate
->serverReferences
) == 0) {
169 // and maintain a digest-->target mapping
170 targetPrivate
->serverDigest
= CFRetain(digest
);
171 CFDictionarySetValue(reach_digest_map
, digest
, target
);
177 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
178 SCLog(TRUE
, LOG_INFO
,
179 CFSTR("<%p> target %p: reference added (%@, %d)"),
183 targetPrivate
->serverReferences
);
192 * _target_reference_remove
194 * Note: use dispatch_sync(_server_digest_queue(), ^{ ... });
197 _target_reference_remove(SCNetworkReachabilityRef target
, xpc_connection_t connection
)
199 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
201 // drop the reference count
202 if (_SC_ATOMIC_DEC(&targetPrivate
->serverReferences
) == 0) {
204 * if that was the last reference, we no longer need to
205 * keep the digest-->target mapping
207 CFDictionaryRemoveValue(reach_digest_map
, targetPrivate
->serverDigest
);
208 CFRelease(targetPrivate
->serverDigest
);
209 targetPrivate
->serverDigest
= NULL
;
213 SCLog(TRUE
, LOG_INFO
,
214 CFSTR("<%p> target %p: reference removed (%d)"),
217 targetPrivate
->serverReferences
);
220 // release a reference to the target
230 #define MUTEX_LOCK(m) { \
231 int _lock_ = (pthread_mutex_lock(m) == 0); \
235 #define MUTEX_UNLOCK(m) { \
236 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
242 _target_reply_add_reachability(SCNetworkReachabilityRef target
,
245 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
247 MUTEX_LOCK(&targetPrivate
->lock
);
249 xpc_dictionary_set_uint64(reply
,
251 targetPrivate
->info
.cycle
);
252 xpc_dictionary_set_uint64(reply
,
254 targetPrivate
->info
.flags
);
255 xpc_dictionary_set_uint64(reply
,
256 REACH_STATUS_IF_INDEX
,
257 targetPrivate
->info
.if_index
);
258 xpc_dictionary_set_data (reply
,
259 REACH_STATUS_IF_NAME
,
260 targetPrivate
->info
.if_name
,
261 sizeof(targetPrivate
->info
.if_name
));
262 xpc_dictionary_set_bool (reply
,
263 REACH_STATUS_SLEEPING
,
264 targetPrivate
->info
.sleeping
);
265 if (targetPrivate
->type
== reachabilityTypeName
) {
266 if (isA_CFArray(targetPrivate
->resolvedAddresses
)) {
267 xpc_object_t addresses
;
271 addresses
= xpc_array_create(NULL
, 0);
273 n
= CFArrayGetCount(targetPrivate
->resolvedAddresses
);
274 for (i
= 0; i
< n
; i
++) {
277 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddresses
, i
);
278 xpc_array_set_data(addresses
,
280 CFDataGetBytePtr(address
),
281 CFDataGetLength(address
));
284 xpc_dictionary_set_value(reply
,
285 REACH_STATUS_RESOLVED_ADDRESSES
,
287 xpc_release(addresses
);
289 xpc_dictionary_set_int64(reply
,
290 REACH_STATUS_RESOLVED_ERROR
,
291 targetPrivate
->resolvedError
);
294 MUTEX_UNLOCK(&targetPrivate
->lock
);
304 xpc_connection_t connection
;
306 } reach_watcher_key_t
;
309 unsigned int n_changes
;
310 } reach_watcher_val_t
;
314 _target_watcher_key_create(xpc_connection_t connection
,
318 reach_watcher_key_t watcher_key
;
320 watcher_key
.connection
= connection
;
321 watcher_key
.target_id
= target_id
;
323 key
= CFDataCreate(NULL
, (UInt8
*)&watcher_key
, sizeof(watcher_key
));
329 _target_watcher_add(SCNetworkReachabilityRef target
,
330 xpc_connection_t connection
,
333 __block Boolean ok
= TRUE
;
336 q
= _target_queue(target
);
339 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
341 if (targetPrivate
->serverWatchers
== NULL
) {
342 ok
= SCNetworkReachabilitySetDispatchQueue(target
, q
);
345 CFSTR("<%p> target %p: _watcher_add SCNetworkReachabilitySetDispatchQueue() failed: %s"),
348 SCErrorString(SCError()));
352 targetPrivate
->serverWatchers
= CFDictionaryCreateMutable(NULL
,
354 &kCFTypeDictionaryKeyCallBacks
,
355 &kCFTypeDictionaryValueCallBacks
);
358 xpc_retain(connection
);
360 key
= _target_watcher_key_create(connection
, target_id
);
361 if (CFDictionaryContainsKey(targetPrivate
->serverWatchers
, key
)) {
363 CFSTR("<%p> target %p: watcher not added, c=0x%0llx, \"serverWatchers\" key exists"),
369 static const reach_watcher_val_t watcher_val0
= { 0 };
371 val
= CFDataCreate(NULL
, (UInt8
*)&watcher_val0
, sizeof(watcher_val0
));
372 CFDictionaryAddValue(targetPrivate
->serverWatchers
, key
, val
);
376 SCLog(TRUE
, LOG_INFO
,
377 CFSTR("<%p> target %p: watcher added, c=0x%0llx, n=%d"),
381 CFDictionaryGetCount(targetPrivate
->serverWatchers
));
392 _target_watcher_checkin(SCNetworkReachabilityRef target
,
393 xpc_connection_t connection
,
396 __block Boolean scheduled
= FALSE
;
398 dispatch_sync(_target_queue(target
), ^{
401 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
403 reach_watcher_val_t
*watcher_val
;
405 if (targetPrivate
->serverWatchers
== NULL
) {
410 key
= _target_watcher_key_create(connection
, target_id
);
411 val
= CFDictionaryGetValue(targetPrivate
->serverWatchers
, key
);
414 // if the target [for this client] was not scheduled
418 // indicate that the target was scheduled
422 * and note that the reachability flags for this target have
423 * been picked up by the client
425 /* ALIGN: CF aligns to at least >8 byte boundries */
426 watcher_val
= (reach_watcher_val_t
*)(void *)CFDataGetBytePtr(val
);
427 n
= _SC_ATOMIC_ZERO(&watcher_val
->n_changes
);
428 if (S_debug
&& (n
> 0)) {
429 SCLog(TRUE
, LOG_INFO
,
430 CFSTR("<%p> target %p: SCNetworkReachabilityGetFlags() after %d notification%s"),
434 (n
== 1) ? "" : "s");
443 _target_watcher_remove(SCNetworkReachabilityRef target
,
444 xpc_connection_t connection
,
447 __block Boolean ok
= TRUE
;
449 dispatch_sync(_target_queue(target
), ^{
452 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
454 if (targetPrivate
->serverWatchers
== NULL
) {
456 CFSTR("<%p> target %p: watcher not removed, c=0x%0llx, no \"serverWatchers\""),
464 key
= _target_watcher_key_create(connection
, target_id
);
465 if (!CFDictionaryContainsKey(targetPrivate
->serverWatchers
, key
)) {
467 CFSTR("<%p> target %p: watcher not removed, c=0x%0llx, no \"serverWatchers\" key"),
476 CFDictionaryRemoveValue(targetPrivate
->serverWatchers
, key
);
479 n
= CFDictionaryGetCount(targetPrivate
->serverWatchers
);
482 SCLog(TRUE
, LOG_INFO
,
483 CFSTR("<%p> target %p: watcher removed, c=0x%0llx, n=%d"),
491 CFRelease(targetPrivate
->serverWatchers
);
492 targetPrivate
->serverWatchers
= NULL
;
494 ok
= SCNetworkReachabilitySetDispatchQueue(target
, NULL
);
497 CFSTR("<%p> target %p: _watcher_remove SCNetworkReachabilitySetDispatchQueue() failed: %s"),
500 SCErrorString(SCError()));
504 // no more watchers, flags are no longer valid
505 if (_SC_ATOMIC_CMPXCHG(&targetPrivate
->serverInfoValid
, TRUE
, FALSE
)) {
507 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags are no longer \"valid\""),
508 targetPrivate
->log_prefix
);
513 xpc_release(connection
);
521 #pragma mark Reachability [RBT] client support
526 xpc_connection_t connection
;
528 const char *proc_name
;
529 CFMutableDictionaryRef targets
; // target_id --> SCNetworkReachabilityRef
534 _rbt_compare_transaction_nodes(void *context
, const void *n1
, const void *n2
)
536 uint64_t a
= (uintptr_t)((reach_client_t
*)n1
)->connection
;
537 uint64_t b
= (uintptr_t)((reach_client_t
*)n2
)->connection
;
544 _rbt_compare_transaction_key(void *context
, const void *n1
, const void *key
)
546 uint64_t a
= (uintptr_t)((reach_client_t
*)n1
)->connection
;
547 uint64_t b
= *(uintptr_t *)key
;
556 static dispatch_once_t once
;
557 static const rb_tree_ops_t ops
= {
558 .rbto_compare_nodes
= _rbt_compare_transaction_nodes
,
559 .rbto_compare_key
= _rbt_compare_transaction_key
,
560 .rbto_node_offset
= offsetof(reach_client_t
, rbn
),
563 static rb_tree_t rbt
;
565 dispatch_once(&once
, ^{
566 rb_tree_init(&rbt
, &ops
);
573 static dispatch_queue_t
574 _reach_clients_rbt_queue()
576 static dispatch_once_t once
;
577 static dispatch_queue_t q
;
579 dispatch_once(&once
, ^{
580 q
= dispatch_queue_create(REACH_SERVICE_NAME
".clients.rbt", NULL
);
587 static reach_client_t
*
588 _reach_client_create(xpc_connection_t connection
)
590 reach_client_t
*client
;
592 client
= calloc(1, sizeof(*client
));
593 client
->connection
= connection
;
594 client
->pid
= xpc_connection_get_pid(connection
);
595 client
->proc_name
= NULL
;
596 client
->targets
= CFDictionaryCreateMutable(NULL
,
598 &kCFTypeDictionaryKeyCallBacks
,
599 &kCFTypeDictionaryValueCallBacks
);
606 _reach_client_release(reach_client_t
*client
)
608 if (client
->proc_name
!= NULL
) {
609 free((void *)client
->proc_name
);
611 CFRelease(client
->targets
);
618 _reach_client_remove_target(const void *key
, const void *value
, void *context
)
620 xpc_connection_t connection
= (xpc_connection_t
)context
;
621 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
622 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
624 // check if we have anyone watching this target
625 if (targetPrivate
->serverWatchers
!= NULL
) {
628 n
= CFDictionaryGetCount(targetPrivate
->serverWatchers
);
631 CFDictionaryRef serverWatchers
;
632 const void * watchers_q
[32];
633 const void ** watchers
= watchers_q
;
635 serverWatchers
= CFDictionaryCreateCopy(NULL
, targetPrivate
->serverWatchers
);
637 if (n
> sizeof(watchers_q
)/sizeof(watchers
[0])) {
638 watchers
= CFAllocatorAllocate(NULL
, n
* sizeof(CFDataRef
), 0);
640 CFDictionaryGetKeysAndValues(serverWatchers
, watchers
, NULL
);
642 for (i
= 0; i
< n
; i
++) {
644 reach_watcher_key_t
*watcher_key
;
646 key
= (CFDataRef
)watchers
[i
];
647 /* ALIGN: CF aligns to >8 byte boundries */
648 watcher_key
= (reach_watcher_key_t
*)(void *)CFDataGetBytePtr(key
);
649 if (watcher_key
->connection
== connection
) {
650 // remove watcher references for THIS connection
651 _target_watcher_remove(target
,
652 watcher_key
->connection
,
653 watcher_key
->target_id
);
657 if (watchers
!= watchers_q
) {
658 CFAllocatorDeallocate(NULL
, watchers
);
661 CFRelease(serverWatchers
);
665 // remove our reference to this target
666 dispatch_sync(_server_digest_queue(), ^{
667 _target_reference_remove(target
, connection
);
675 _reach_client_remove(xpc_connection_t connection
)
677 uint64_t connection_id
= (uintptr_t)connection
;
679 dispatch_sync(_reach_clients_rbt_queue(), ^{
680 reach_client_t
*client
;
681 rb_tree_t
*rbt
= _reach_clients_rbt();
683 client
= rb_tree_find_node(rbt
, &connection_id
);
684 if (client
!= NULL
) {
685 // remove any remaining target references (for this client)
686 my_CFDictionaryApplyFunction(client
->targets
,
687 _reach_client_remove_target
,
690 rb_tree_remove_node(rbt
, client
);
691 _reach_client_release(client
);
694 CFSTR("<%p> _reach_client_remove: unexpected client"),
703 static __inline__ CFDataRef
704 _client_target_key_create(uint64_t target_id
)
706 CFDataRef target_key
;
708 target_key
= CFDataCreate(NULL
, (UInt8
*)&target_id
, sizeof(target_id
));
713 static SCNetworkReachabilityRef
714 _client_target_copy(reach_client_t
*client
, uint64_t target_id
)
716 SCNetworkReachabilityRef target
;
717 CFDataRef target_key
;
719 target_key
= _client_target_key_create(target_id
);
720 target
= CFDictionaryGetValue(client
->targets
, target_key
);
721 CFRelease(target_key
);
723 if (target
!= NULL
) {
732 _client_target_set(reach_client_t
*client
, uint64_t target_id
, SCNetworkReachabilityRef target
)
735 CFDataRef target_key
;
737 target_key
= _client_target_key_create(target_id
);
738 added
= !CFDictionaryContainsKey(client
->targets
, target_key
);
740 CFDictionarySetValue(client
->targets
, target_key
, target
);
742 CFRelease(target_key
);
749 _client_target_remove(reach_client_t
*client
, uint64_t target_id
)
751 CFDataRef target_key
;
753 target_key
= _client_target_key_create(target_id
);
754 CFDictionaryRemoveValue(client
->targets
, target_key
);
755 CFRelease(target_key
);
762 #pragma mark Reachability [XPC] server functions
765 static dispatch_queue_t
766 _reach_server_queue()
768 static dispatch_once_t once
;
769 static dispatch_queue_t q
;
771 dispatch_once(&once
, ^{
772 q
= dispatch_queue_create(REACH_SERVICE_NAME
, NULL
);
782 * Note: should be exec'd on the target queue
785 _reach_changed(SCNetworkReachabilityRef target
, SCNetworkReachabilityFlags flags
, void *info
)
789 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
790 const void * watcher_keys_q
[32];
791 const void ** watcher_keys
= watcher_keys_q
;
792 const void * watcher_vals_q
[32];
793 const void ** watcher_vals
= watcher_vals_q
;
796 SCLog(TRUE
, LOG_INFO
,
797 CFSTR("%sprocess reachability changed, flags = 0x%08x"),
798 targetPrivate
->log_prefix
,
802 if (targetPrivate
->serverWatchers
== NULL
) {
807 n
= CFDictionaryGetCount(targetPrivate
->serverWatchers
);
814 * Because we are actively watching for additional changes
815 * we mark the flags as "valid"
817 if (_SC_ATOMIC_CMPXCHG(&targetPrivate
->serverInfoValid
, FALSE
, TRUE
)) {
819 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags are now \"valid\""),
820 targetPrivate
->log_prefix
);
824 // notify all of the watchers
825 if (n
> sizeof(watcher_keys_q
)/sizeof(watcher_keys
[0])) {
826 watcher_keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFDataRef
), 0);
827 watcher_vals
= CFAllocatorAllocate(NULL
, n
* sizeof(CFDataRef
), 0);
830 CFDictionaryGetKeysAndValues(targetPrivate
->serverWatchers
,
834 for (i
= 0; i
< n
; i
++) {
835 xpc_connection_t connection
;
839 reach_watcher_key_t
*watcher_key
;
840 reach_watcher_val_t
*watcher_val
;
842 val
= (CFDataRef
)watcher_vals
[i
];
843 /* ALIGN: CF aligns to >8 byte boundries */
844 watcher_val
= (reach_watcher_val_t
*)(void *)CFDataGetBytePtr(val
);
846 if (_SC_ATOMIC_INC(&watcher_val
->n_changes
) > 0) {
847 // if we've already sent a notification
851 key
= (CFDataRef
)watcher_keys
[i
];
852 /* ALIGN: CF aligns to >8 byte boundries */
853 watcher_key
= (reach_watcher_key_t
*)(void *)CFDataGetBytePtr(key
);
855 connection
= xpc_retain(watcher_key
->connection
);
856 target_id
= watcher_key
->target_id
;
857 dispatch_async(_reach_server_queue(), ^{
860 // create our [async] notification
861 reply
= xpc_dictionary_create(NULL
, NULL
, 0);
864 xpc_dictionary_set_int64(reply
,
866 MESSAGE_REACHABILITY_STATUS
);
869 xpc_dictionary_set_uint64(reply
,
870 REACH_CLIENT_TARGET_ID
,
873 log_xpc_object(" reply [async]", reply
);
874 xpc_connection_send_message(connection
, reply
);
877 xpc_release(connection
);
881 if (n
> sizeof(watcher_keys_q
)/sizeof(watcher_keys
[0])) {
882 CFAllocatorDeallocate(NULL
, watcher_keys
);
883 CFAllocatorDeallocate(NULL
, watcher_vals
);
891 sanitize_address(const struct sockaddr
*from
, struct sockaddr
*to
)
893 switch (from
->sa_family
) {
895 /* ALIGN: cast okay, alignment not assumed. */
896 struct sockaddr_in
*from4
= (struct sockaddr_in
*)(void *)from
;
897 struct sockaddr_in
*to4
= (struct sockaddr_in
*)(void *)to
;
899 bzero(to4
, sizeof(*to4
));
900 to4
->sin_len
= sizeof(*to4
);
901 to4
->sin_family
= AF_INET
;
902 bcopy(&from4
->sin_addr
, &to4
->sin_addr
, sizeof(to4
->sin_addr
));
907 /* ALIGN: cast okay, alignment not assumed. */
908 struct sockaddr_in6
*from6
= (struct sockaddr_in6
*)(void *)from
;
909 struct sockaddr_in6
*to6
= (struct sockaddr_in6
*)(void *)to
;
911 bzero(to6
, sizeof(*to6
));
912 to6
->sin6_len
= sizeof(*to6
);
913 to6
->sin6_family
= AF_INET6
;
914 bcopy(&from6
->sin6_addr
, &to6
->sin6_addr
, sizeof(to6
->sin6_addr
));
915 to6
->sin6_scope_id
= from6
->sin6_scope_id
;
920 bcopy(from
, to
, from
->sa_len
);
929 target_add(reach_client_t
*client
, xpc_object_t request
)
932 const struct sockaddr
*localAddress
;
933 struct sockaddr_storage localAddress0
;
934 const struct sockaddr
*remoteAddress
;
935 struct sockaddr_storage remoteAddress0
;
937 const char *if_name
= NULL
;
938 bool onDemandBypass
= FALSE
;
939 bool resolverBypass
= FALSE
;
943 unsigned char bytes
[CC_SHA1_DIGEST_LENGTH
];
945 CFDataRef digest
= NULL
;
947 xpc_connection_t remote
;
949 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
952 __block Boolean ok
= TRUE
;
953 __block SCNetworkReachabilityRef target
= NULL
;
956 SCLog(TRUE
, LOG_INFO
,
957 CFSTR("<%p> create reachability target"),
959 // log_xpc_object(" create", request);
962 remote
= xpc_dictionary_get_remote_connection(request
);
963 reply
= xpc_dictionary_create_reply(request
);
966 CFSTR("<%p> target_add: xpc_dictionary_create_reply: failed"),
971 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
972 if (target_id
== 0) {
973 xpc_dictionary_set_string(reply
,
974 REACH_REQUEST_REPLY_DETAIL
,
979 // create a "digest" of the [new] target
983 name
= xpc_dictionary_get_string(request
, REACH_TARGET_NAME
);
985 CC_SHA1_Update(&ctx
, REACH_TARGET_NAME
, sizeof(REACH_TARGET_NAME
));
986 CC_SHA1_Update(&ctx
, name
, strlen(name
));
989 localAddress
= xpc_dictionary_get_data(request
, REACH_TARGET_LOCAL_ADDR
, &len
);
990 if (localAddress
!= NULL
) {
991 if ((len
== localAddress
->sa_len
) && (len
<= sizeof(struct sockaddr_storage
))) {
992 sanitize_address(localAddress
, (struct sockaddr
*)&localAddress0
);
993 CC_SHA1_Update(&ctx
, REACH_TARGET_LOCAL_ADDR
, sizeof(REACH_TARGET_LOCAL_ADDR
));
994 CC_SHA1_Update(&ctx
, &localAddress0
, len
);
996 xpc_dictionary_set_string(reply
,
997 REACH_REQUEST_REPLY_DETAIL
,
998 "local address: size error");
1003 remoteAddress
= xpc_dictionary_get_data(request
, REACH_TARGET_REMOTE_ADDR
, &len
);
1004 if (remoteAddress
!= NULL
) {
1005 if ((len
== remoteAddress
->sa_len
) && (len
<= sizeof(struct sockaddr_storage
))) {
1006 sanitize_address(remoteAddress
, (struct sockaddr
*)&remoteAddress0
);
1007 CC_SHA1_Update(&ctx
, REACH_TARGET_REMOTE_ADDR
, sizeof(REACH_TARGET_REMOTE_ADDR
));
1008 CC_SHA1_Update(&ctx
, &remoteAddress0
, len
);
1010 xpc_dictionary_set_string(reply
,
1011 REACH_REQUEST_REPLY_DETAIL
,
1012 "remote address: size error");
1017 if_index
= xpc_dictionary_get_int64(request
, REACH_TARGET_IF_INDEX
);
1018 if (if_index
!= 0) {
1019 if_name
= xpc_dictionary_get_string(request
, REACH_TARGET_IF_NAME
);
1020 if (if_name
!= NULL
) {
1021 CC_SHA1_Update(&ctx
, REACH_TARGET_IF_NAME
, sizeof(REACH_TARGET_IF_NAME
));
1022 CC_SHA1_Update(&ctx
, if_name
, strlen(if_name
));
1027 onDemandBypass
= xpc_dictionary_get_bool(request
, REACH_TARGET_ONDEMAND_BYPASS
);
1028 if (onDemandBypass
) {
1029 CC_SHA1_Update(&ctx
, REACH_TARGET_ONDEMAND_BYPASS
, sizeof(REACH_TARGET_ONDEMAND_BYPASS
));
1030 CC_SHA1_Update(&ctx
, &onDemandBypass
, sizeof(onDemandBypass
));
1033 resolverBypass
= xpc_dictionary_get_bool(request
, REACH_TARGET_RESOLVER_BYPASS
);
1034 if (resolverBypass
) {
1035 CC_SHA1_Update(&ctx
, REACH_TARGET_RESOLVER_BYPASS
, sizeof(REACH_TARGET_RESOLVER_BYPASS
));
1036 CC_SHA1_Update(&ctx
, &resolverBypass
, sizeof(resolverBypass
));
1040 CC_SHA1_Final(bytes
, &ctx
);
1041 digest
= CFDataCreate(NULL
, bytes
, sizeof(bytes
));
1044 * Check to see if we already have a SCNetworkReachability object
1045 * for this digest. If so, we'll share the existing target. If not,
1046 * create a new [shared] target.
1048 dispatch_sync(_server_digest_queue(), ^{
1049 target
= CFDictionaryGetValue(reach_digest_map
, digest
);
1050 if (target
!= NULL
) {
1054 CFMutableDictionaryRef options
;
1057 options
= CFDictionaryCreateMutable(NULL
,
1059 &kCFTypeDictionaryKeyCallBacks
,
1060 &kCFTypeDictionaryValueCallBacks
);
1062 str
= CFStringCreateWithCString(NULL
, name
, kCFStringEncodingUTF8
);
1063 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, str
);
1066 if (localAddress
!= NULL
) {
1067 data
= CFDataCreate(NULL
, (const UInt8
*)&localAddress0
, localAddress0
.ss_len
);
1068 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionLocalAddress
, data
);
1071 if (remoteAddress
!= NULL
) {
1072 data
= CFDataCreate(NULL
, (const UInt8
*)&remoteAddress0
, remoteAddress0
.ss_len
);
1073 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
, data
);
1076 if (onDemandBypass
) {
1077 CFDictionarySetValue(options
,
1078 kSCNetworkReachabilityOptionConnectionOnDemandBypass
,
1081 if (resolverBypass
) {
1082 CFDictionarySetValue(options
,
1083 kSCNetworkReachabilityOptionResolverBypass
,
1086 CFDictionarySetValue(options
,
1087 kSCNetworkReachabilityOptionServerBypass
,
1089 target
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
1091 if (target
== NULL
) {
1092 xpc_dictionary_set_string(reply
,
1093 REACH_REQUEST_REPLY_DETAIL
,
1094 "SCNetworkReachabilityCreateWithOptions failed");
1099 // because the interface name may not (no longer) be valid we set
1100 // this after we've created the SCNetworkReachabilty object
1101 if ((if_index
!= 0) && (if_name
!= NULL
)) {
1102 SCNetworkReachabilityPrivateRef targetPrivate
;
1104 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1105 targetPrivate
->if_index
= if_index
;
1106 strlcpy(targetPrivate
->if_name
, if_name
, sizeof(targetPrivate
->if_name
));
1110 ok
= SCNetworkReachabilitySetCallback(target
, _reach_changed
, NULL
);
1112 xpc_dictionary_set_string(reply
,
1113 REACH_REQUEST_REPLY_DETAIL
,
1114 "SCNetworkReachabilitySetCallback failed");
1121 // bump the number of references to this target
1122 _target_reference_add(target
, digest
, client
->connection
);
1130 * add an association for the client's target_id to the [shared]
1131 * SCNetworkReachability object.
1133 added
= _client_target_set(client
, target_id
, target
);
1135 // if we already had a reference to the target (e.g. reconnect)
1136 dispatch_sync(_server_digest_queue(), ^{
1137 _target_reference_remove(target
, client
->connection
);
1141 status
= REACH_REQUEST_REPLY_OK
;
1145 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1146 // log_xpc_object(" reply", reply);
1147 xpc_connection_send_message(remote
, reply
);
1150 if (digest
!= NULL
) CFRelease(digest
);
1151 if (target
!= NULL
) CFRelease(target
);
1157 target_remove(reach_client_t
*client
, xpc_object_t request
)
1159 xpc_connection_t remote
;
1161 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1162 SCNetworkReachabilityRef target
= NULL
;
1166 SCLog(TRUE
, LOG_INFO
,
1167 CFSTR("<%p> remove reachability target"),
1168 client
->connection
);
1169 // log_xpc_object(" remove", request);
1172 remote
= xpc_dictionary_get_remote_connection(request
);
1173 reply
= xpc_dictionary_create_reply(request
);
1174 if (reply
== NULL
) {
1175 SCLog(TRUE
, LOG_ERR
,
1176 CFSTR("<%p> target_remove: xpc_dictionary_create_reply: failed"),
1177 client
->connection
);
1181 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
1182 if (target_id
== 0) {
1183 xpc_dictionary_set_string(reply
,
1184 REACH_REQUEST_REPLY_DETAIL
,
1189 target
= _client_target_copy(client
, target_id
);
1190 if (target
== NULL
) {
1191 xpc_dictionary_set_string(reply
,
1192 REACH_REQUEST_REPLY_DETAIL
,
1194 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1199 * remove the association from the client's target_id to the [shared]
1200 * SCNetworkReachability object.
1202 _client_target_remove(client
, target_id
);
1204 // drop the number of references to this target
1205 dispatch_sync(_server_digest_queue(), ^{
1206 _target_reference_remove(target
, client
->connection
);
1209 status
= REACH_REQUEST_REPLY_OK
;
1213 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1214 // log_xpc_object(" reply", reply);
1215 xpc_connection_send_message(remote
, reply
);
1218 if (target
!= NULL
) CFRelease(target
);
1224 target_schedule(reach_client_t
*client
, xpc_object_t request
)
1227 xpc_connection_t remote
;
1229 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1230 SCNetworkReachabilityRef target
= NULL
;
1234 SCLog(TRUE
, LOG_INFO
,
1235 CFSTR("<%p> schedule reachability target"),
1236 client
->connection
);
1237 // log_xpc_object(" schedule", request);
1240 remote
= xpc_dictionary_get_remote_connection(request
);
1241 reply
= xpc_dictionary_create_reply(request
);
1242 if (reply
== NULL
) {
1243 SCLog(TRUE
, LOG_ERR
,
1244 CFSTR("<%p> target_schedule: xpc_dictionary_create_reply: failed"),
1245 client
->connection
);
1249 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
1250 if (target_id
== 0) {
1251 xpc_dictionary_set_string(reply
,
1252 REACH_REQUEST_REPLY_DETAIL
,
1257 target
= _client_target_copy(client
, target_id
);
1258 if (target
== NULL
) {
1259 xpc_dictionary_set_string(reply
,
1260 REACH_REQUEST_REPLY_DETAIL
,
1262 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1266 // enable monitoring
1267 ok
= _target_watcher_add(target
, client
->connection
, target_id
);
1269 xpc_dictionary_set_string(reply
,
1270 REACH_REQUEST_REPLY_DETAIL
,
1271 "could not add watcher");
1275 status
= REACH_REQUEST_REPLY_OK
;
1279 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1280 // log_xpc_object(" reply", reply);
1281 xpc_connection_send_message(remote
, reply
);
1284 if (target
!= NULL
) CFRelease(target
);
1290 target_status(reach_client_t
*client
, xpc_object_t request
)
1292 xpc_connection_t remote
;
1294 __block Boolean reply_now
= TRUE
;
1296 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1297 SCNetworkReachabilityRef target
= NULL
;
1301 SCLog(TRUE
, LOG_INFO
,
1302 CFSTR("<%p> get status of reachability target"),
1303 client
->connection
);
1304 // log_xpc_object(" status", request);
1307 remote
= xpc_dictionary_get_remote_connection(request
);
1308 reply
= xpc_dictionary_create_reply(request
);
1309 if (reply
== NULL
) {
1310 SCLog(TRUE
, LOG_ERR
,
1311 CFSTR("<%p> target_status: xpc_dictionary_create_reply: failed"),
1312 client
->connection
);
1316 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
1317 if (target_id
== 0) {
1318 SCLog(TRUE
, LOG_ERR
,
1319 CFSTR("<%p> target_status: no target"),
1320 client
->connection
);
1321 xpc_dictionary_set_string(reply
,
1322 REACH_REQUEST_REPLY_DETAIL
,
1327 target
= _client_target_copy(client
, target_id
);
1328 if (target
== NULL
) {
1329 SCLog(TRUE
, LOG_ERR
,
1330 CFSTR("<%p> target_status: no target (0x%0llx)"),
1333 xpc_dictionary_set_string(reply
,
1334 REACH_REQUEST_REPLY_DETAIL
,
1336 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1341 * Check to see if the target [for this client] had been "scheduled".
1343 * If so, also mark that we've picked up the current reachability
1344 * flags and that any pending notifications have been processed.
1346 scheduled
= _target_watcher_checkin(target
, client
->connection
, target_id
);
1349 * return current reachability information to the caller
1351 dispatch_sync(_target_queue(target
), ^{
1352 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1356 * The client "scheduled" this target. As such, we
1357 * know that this is an async query and that we only
1358 * need to return the "last known" flags.
1360 _target_reply_add_reachability(target
, reply
);
1361 // log_xpc_object(" reply [scheduled]", reply);
1364 * ... and if it's not a "name" query then we can mark the
1367 if (targetPrivate
->type
!= reachabilityTypeName
) {
1368 if (_SC_ATOMIC_CMPXCHG(&targetPrivate
->serverInfoValid
, FALSE
, TRUE
)) {
1370 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags are now \"valid\"."),
1371 targetPrivate
->log_prefix
);
1379 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1380 SCLog(TRUE
, LOG_INFO
,
1381 CFSTR("<%p> reply [scheduled%s], %@"),
1383 targetPrivate
->serverInfoValid
? "/valid" : "",
1389 * The client has NOT "scheduled" this target. As
1390 * such, we know that this is a sync query and that
1391 * we must return "current" flags.
1393 if (targetPrivate
->scheduled
&& targetPrivate
->serverInfoValid
) {
1395 * The server target has been "scheduled" and we
1396 * have flags that are "current".
1398 _target_reply_add_reachability(target
, reply
);
1399 // log_xpc_object(" reply [scheduled/valid]", reply);
1404 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1405 SCLog(TRUE
, LOG_INFO
,
1406 CFSTR("<%p> reply [scheduled/valid], %@"),
1412 dispatch_group_t group
;
1415 * The server target has NOT been "scheduled" or
1416 * we do not have "current" flags. This means that
1417 * we must query for the current information and
1418 * return the flags to the client when they are
1424 group
= _target_group(target
);
1425 if (_SC_ATOMIC_INC(&targetPrivate
->serverSyncQueryActive
) == 0) {
1427 dispatch_group_async(group
, _server_concurrent_queue(), ^{
1428 SCNetworkReachabilityFlags flags
;
1432 // query for the flags
1433 ok
= SCNetworkReachabilityGetFlags(target
, &flags
);
1434 flags
= targetPrivate
->info
.flags
; // get the "raw" flags
1436 SCLog(TRUE
, LOG_ERR
,
1437 CFSTR("SCNetworkReachabilityGetFlags() [sync query] failed"
1441 SCErrorString(SCError()));
1445 * if we have current flags, if the target has since been
1446 * scheduled, and this is not a "name" query, then mark as
1450 targetPrivate
->scheduled
&&
1451 targetPrivate
->type
!= reachabilityTypeName
) {
1452 if (_SC_ATOMIC_CMPXCHG(&targetPrivate
->serverInfoValid
, FALSE
, TRUE
)) {
1454 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags are now \"valid\"!"),
1455 targetPrivate
->log_prefix
);
1460 // sync query complete
1461 n
= _SC_ATOMIC_ZERO(&targetPrivate
->serverSyncQueryActive
);
1463 SCLog(TRUE
, LOG_INFO
,
1464 CFSTR("%sSCNetworkReachabilityGetFlags() [sync query] complete, n = %d"),
1465 targetPrivate
->log_prefix
,
1474 dispatch_group_notify(group
, _target_queue(target
), ^{
1475 // flags are now available
1476 _target_reply_add_reachability(target
, reply
);
1477 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, REACH_REQUEST_REPLY_OK
);
1478 // log_xpc_object(" reply [delayed]", reply);
1483 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1484 SCLog(TRUE
, LOG_INFO
,
1485 CFSTR("<%p> reply [delayed], %@"),
1491 xpc_connection_send_message(remote
, reply
);
1500 status
= REACH_REQUEST_REPLY_OK
;
1505 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1507 if (status
!= REACH_REQUEST_REPLY_OK
) {
1508 // log_xpc_object(" reply [!]", reply);
1511 SCLog(TRUE
, LOG_INFO
,
1512 CFSTR("<%p> reply [!]"),
1513 client
->connection
);
1517 xpc_connection_send_message(remote
, reply
);
1519 } else if (S_debug
) {
1522 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1523 SCLog(TRUE
, LOG_INFO
,
1524 CFSTR("<%p> no reply [yet], %@"),
1530 if (target
!= NULL
) CFRelease(target
);
1536 target_unschedule(reach_client_t
*client
, xpc_object_t request
)
1538 xpc_connection_t remote
;
1540 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1541 SCNetworkReachabilityRef target
= NULL
;
1545 SCLog(TRUE
, LOG_INFO
,
1546 CFSTR("<%p> unschedule reachability target"),
1547 client
->connection
);
1548 // log_xpc_object(" unschedule", request);
1551 remote
= xpc_dictionary_get_remote_connection(request
);
1552 reply
= xpc_dictionary_create_reply(request
);
1553 if (reply
== NULL
) {
1554 SCLog(TRUE
, LOG_ERR
,
1555 CFSTR("<%p> target_unschedule: xpc_dictionary_create_reply: failed"),
1556 client
->connection
);
1560 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
1561 if (target_id
== 0) {
1562 xpc_dictionary_set_string(reply
,
1563 REACH_REQUEST_REPLY_DETAIL
,
1568 target
= _client_target_copy(client
, target_id
);
1569 if (target
== NULL
) {
1570 xpc_dictionary_set_string(reply
,
1571 REACH_REQUEST_REPLY_DETAIL
,
1573 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1577 // disable monitoring
1578 _target_watcher_remove(target
, client
->connection
, target_id
);
1580 status
= REACH_REQUEST_REPLY_OK
;
1584 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1585 // log_xpc_object(" reply", reply);
1586 xpc_connection_send_message(remote
, reply
);
1589 if (target
!= NULL
) CFRelease(target
);
1594 #define SNAPSHOT_PATH_STATE _PATH_VARTMP "configd-reachability"
1598 _snapshot_digest_watcher(const void *key
, const void *value
, void *context
)
1600 __block reach_client_t
*client
= NULL
;
1601 FILE *f
= (FILE *)context
;
1602 static reach_client_t no_client
= {
1606 reach_watcher_key_t
*watcher_key
;
1607 reach_watcher_val_t
*watcher_val
;
1609 /* ALIGN: CF aligns to >8 byte boundries */
1610 watcher_key
= (reach_watcher_key_t
*)(void *)CFDataGetBytePtr(key
);
1611 watcher_val
= (reach_watcher_val_t
*)(void *)CFDataGetBytePtr(value
);
1613 dispatch_sync(_reach_clients_rbt_queue(), ^{
1614 uint64_t connection_id
= (uintptr_t)watcher_key
->connection
;
1615 rb_tree_t
*rbt
= _reach_clients_rbt();
1617 client
= rb_tree_find_node(rbt
, &connection_id
);
1618 if (client
== NULL
) {
1619 client
= &no_client
;
1624 CFSTR(" connection = %p, target(c) = 0x%0llx, command = %s, pid = %d, changes = %u\n"),
1625 watcher_key
->connection
,
1626 watcher_key
->target_id
,
1629 watcher_val
->n_changes
);
1636 _snapshot_digest(const void *key
, const void *value
, void *context
)
1638 FILE *f
= (FILE *)context
;
1639 CFStringRef digest
= (CFStringRef
)key
;
1641 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
1642 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1644 q
= _target_queue(target
);
1646 CFIndex nWatchers
= 0;
1648 if (targetPrivate
->serverWatchers
!= NULL
) {
1649 nWatchers
= CFDictionaryGetCount(targetPrivate
->serverWatchers
);
1652 SCPrint(TRUE
, f
, CFSTR("\n digest : %@\n"), digest
);
1653 SCPrint(TRUE
, f
, CFSTR(" %@\n"), target
);
1654 SCPrint(TRUE
, f
, CFSTR(" valid = %s, async watchers = %u, sync queries = %u, refs = %u\n"),
1655 targetPrivate
->serverInfoValid
? "Y" : "N",
1657 targetPrivate
->serverSyncQueryActive
,
1658 targetPrivate
->serverReferences
);
1660 SCPrint(TRUE
, f
, CFSTR(" network %d.%3.3d"),
1661 targetPrivate
->last_network
.tv_sec
,
1662 targetPrivate
->last_network
.tv_usec
/ 1000);
1663 #if !TARGET_OS_IPHONE
1664 SCPrint(TRUE
, f
, CFSTR(", power %d.%3.3d"),
1665 targetPrivate
->last_power
.tv_sec
,
1666 targetPrivate
->last_power
.tv_usec
/ 1000);
1667 #endif // !TARGET_OS_IPHONE
1668 if (targetPrivate
->type
== reachabilityTypeName
) {
1669 SCPrint(TRUE
, f
, CFSTR(", DNS %d.%3.3d"),
1670 targetPrivate
->last_dns
.tv_sec
,
1671 targetPrivate
->last_dns
.tv_usec
/ 1000);
1672 if (timerisset(&targetPrivate
->dnsQueryEnd
)) {
1673 struct timeval dnsQueryElapsed
;
1675 timersub(&targetPrivate
->dnsQueryEnd
,
1676 &targetPrivate
->dnsQueryStart
,
1678 SCPrint(TRUE
, f
, CFSTR(" (query %d.%3.3d / reply %d.%3.3d)"),
1679 targetPrivate
->dnsQueryStart
.tv_sec
,
1680 targetPrivate
->dnsQueryStart
.tv_usec
/ 1000,
1681 dnsQueryElapsed
.tv_sec
,
1682 dnsQueryElapsed
.tv_usec
/ 1000);
1685 if (timerisset(&targetPrivate
->last_push
)) {
1686 SCPrint(TRUE
, f
, CFSTR(", last notify %d.%3.3d"),
1687 targetPrivate
->last_push
.tv_sec
,
1688 targetPrivate
->last_push
.tv_usec
/ 1000);
1690 SCPrint(TRUE
, f
, CFSTR("\n"));
1692 if (nWatchers
> 0) {
1693 CFDictionaryApplyFunction(targetPrivate
->serverWatchers
,
1694 _snapshot_digest_watcher
,
1704 _snapshot_target(const void *key
, const void *value
, void *context
)
1706 FILE *f
= (FILE *)context
;
1707 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
1709 CFDataRef target_key
= (CFDataRef
)key
;
1711 /* ALIGN: CF aligns > 8 byte boundries */
1712 target_id
= *(uint64_t *)(void *)CFDataGetBytePtr(target_key
);
1715 CFSTR(" target(c) = 0x%0llx, target(s) = %@\n"),
1724 _snapshot(reach_client_t
*client
, xpc_object_t request
)
1730 xpc_connection_t remote
;
1734 SCLog(TRUE
, LOG_INFO
,
1735 CFSTR("<%p> snapshot"),
1736 client
->connection
);
1737 // log_xpc_object(" create", request);
1740 remote
= xpc_dictionary_get_remote_connection(request
);
1741 reply
= xpc_dictionary_create_reply(request
);
1742 if (reply
== NULL
) {
1743 SCLog(TRUE
, LOG_ERR
,
1744 CFSTR("<%p> _snapshot: xpc_dictionary_create_reply: failed"),
1745 client
->connection
);
1749 euid
= xpc_connection_get_euid(remote
);
1751 xpc_dictionary_set_string(reply
,
1752 REACH_REQUEST_REPLY_DETAIL
,
1753 "Permission denied.");
1757 // Save a snapshot of the SCNetworkReachability server "state"
1759 (void) unlink(SNAPSHOT_PATH_STATE
);
1760 fd
= open(SNAPSHOT_PATH_STATE
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_EXCL
, 0644);
1762 xpc_dictionary_set_string(reply
,
1763 REACH_REQUEST_REPLY_DETAIL
,
1767 f
= fdopen(fd
, "w");
1769 xpc_dictionary_set_string(reply
,
1770 REACH_REQUEST_REPLY_DETAIL
,
1775 // provide connection/client info
1777 dispatch_sync(_reach_clients_rbt_queue(), ^{
1778 rb_tree_t
*rbt
= _reach_clients_rbt();
1780 SCPrint(TRUE
, f
, CFSTR("Clients :\n"));
1782 if (rb_tree_count(rbt
) > 0) {
1783 reach_client_t
*client
;
1785 RB_TREE_FOREACH(client
, rbt
) {
1787 CFSTR("\n connection = %p, client = %p, command = %s, pid = %d\n"),
1790 client
->proc_name
!= NULL
? client
->proc_name
: "?",
1792 my_CFDictionaryApplyFunction(client
->targets
,
1797 SCPrint(TRUE
, f
, CFSTR(" None.\n"));
1800 SCPrint(TRUE
, f
, CFSTR("\n"));
1803 // provide "digest" info
1805 SCPrint(TRUE
, f
, CFSTR("Digests :\n"));
1806 dispatch_sync(_server_digest_queue(), ^{
1807 if (reach_digest_map
!= NULL
) {
1808 CFDictionaryApplyFunction(reach_digest_map
,
1820 xpc_dictionary_set_int64(reply
,
1821 REACH_REQUEST_REPLY
,
1822 ok
? REACH_REQUEST_REPLY_OK
: REACH_REQUEST_REPLY_FAILED
);
1823 // log_xpc_object(" reply", reply);
1824 xpc_connection_send_message(remote
, reply
);
1831 static __inline__
void
1832 _extract_client_info(reach_client_t
*client
, xpc_object_t request
)
1834 // if available/needed, save the process name
1835 if (client
->proc_name
== NULL
) {
1836 const char *proc_name
;
1838 proc_name
= xpc_dictionary_get_string(request
, REACH_CLIENT_PROC_NAME
);
1839 if (proc_name
!= NULL
) {
1840 client
->proc_name
= strdup(proc_name
);
1849 process_request(reach_client_t
*client
, xpc_object_t request
)
1853 op
= xpc_dictionary_get_int64(request
, REACH_REQUEST
);
1855 case REACH_REQUEST_CREATE
:
1856 _extract_client_info(client
, request
);
1857 target_add(client
, request
);
1859 case REACH_REQUEST_REMOVE
:
1860 target_remove(client
, request
);
1862 case REACH_REQUEST_STATUS
:
1863 target_status(client
, request
);
1865 case REACH_REQUEST_SCHEDULE
:
1866 target_schedule(client
, request
);
1868 case REACH_REQUEST_UNSCHEDULE
:
1869 target_unschedule(client
, request
);
1871 case REACH_REQUEST_SNAPSHOT
:
1872 _extract_client_info(client
, request
);
1873 _snapshot(client
, request
);
1876 SCLog(TRUE
, LOG_ERR
,
1877 CFSTR("<%p> unknown request : %d"),
1888 process_new_connection(xpc_connection_t connection
)
1890 reach_client_t
*client
;
1893 SCLog(TRUE
, LOG_INFO
, CFSTR("<%p> new reach client, pid=%d"),
1895 xpc_connection_get_pid(connection
));
1898 client
= _reach_client_create(connection
);
1899 assert(client
!= NULL
);
1901 dispatch_sync(_reach_clients_rbt_queue(), ^{
1902 rb_tree_t
*rbt
= _reach_clients_rbt();
1904 rb_tree_insert_node(rbt
, client
);
1907 xpc_connection_set_target_queue(connection
, _reach_server_queue());
1909 xpc_connection_set_event_handler(connection
, ^(xpc_object_t xobj
) {
1912 type
= xpc_get_type(xobj
);
1913 if (type
== XPC_TYPE_DICTIONARY
) {
1914 __block reach_client_t
*client
= NULL
;
1916 dispatch_sync(_reach_clients_rbt_queue(), ^{
1917 uint64_t connection_id
= (uintptr_t)connection
;
1918 rb_tree_t
*rbt
= _reach_clients_rbt();
1920 client
= rb_tree_find_node(rbt
, &connection_id
);
1923 if (client
!= NULL
) {
1924 // process the request
1925 process_request(client
, xobj
);
1929 SCLog(TRUE
, LOG_ERR
,
1930 CFSTR("<%p:%d> unexpected SCNetworkReachability request"),
1932 xpc_connection_get_pid(connection
));
1934 desc
= xpc_copy_description(xobj
);
1935 SCLog(TRUE
, LOG_ERR
,
1936 CFSTR(" request = %s"),
1940 xpc_connection_cancel(connection
);
1943 } else if (type
== XPC_TYPE_ERROR
) {
1946 desc
= xpc_dictionary_get_string(xobj
, XPC_ERROR_KEY_DESCRIPTION
);
1947 if (xobj
== XPC_ERROR_CONNECTION_INVALID
) {
1949 SCLog(TRUE
, LOG_INFO
,
1950 CFSTR("<%p:%d> %s"),
1952 xpc_connection_get_pid(connection
),
1956 _reach_client_remove(connection
);
1958 } else if (xobj
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
1959 SCLog(TRUE
, LOG_ERR
,
1960 CFSTR("<%p:%d> %s"),
1962 xpc_connection_get_pid(connection
),
1966 SCLog(TRUE
, LOG_ERR
,
1967 CFSTR("<%p:%d> Connection error: %d : %s"),
1969 xpc_connection_get_pid(connection
),
1975 SCLog(TRUE
, LOG_ERR
,
1976 CFSTR("<%p:%d> unknown event type : %x"),
1978 xpc_connection_get_pid(connection
),
1983 xpc_connection_resume(connection
);
1990 #pragma mark Reachability server "main"
1995 load_SCNetworkReachability(CFBundleRef bundle
, Boolean bundleVerbose
)
1997 xpc_connection_t connection
;
2000 S_debug
= bundleVerbose
;
2003 * create a dictionary mapping SCNetworkReachability [CFData] digests
2004 * to SCNetworkReachability objects.
2006 reach_digest_map
= CFDictionaryCreateMutable(NULL
,
2008 &kCFTypeDictionaryKeyCallBacks
,
2009 &kCFTypeDictionaryValueCallBacks
);
2011 // create XPC listener
2012 name
= getenv("REACH_SERVER");
2014 name
= REACH_SERVICE_NAME
;
2016 connection
= xpc_connection_create_mach_service(name
,
2017 _reach_server_queue(),
2018 XPC_CONNECTION_MACH_SERVICE_LISTENER
);
2020 xpc_connection_set_event_handler(connection
, ^(xpc_object_t event
) {
2023 type
= xpc_get_type(event
);
2024 if (type
== XPC_TYPE_CONNECTION
) {
2025 process_new_connection(event
);
2027 } else if (type
== XPC_TYPE_ERROR
) {
2030 desc
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
2031 if (event
== XPC_ERROR_CONNECTION_INVALID
) {
2032 SCLog(TRUE
, LOG_ERR
, CFSTR("reach server: %s"), desc
);
2033 xpc_release(connection
);
2034 } else if (event
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
2035 SCLog(TRUE
, LOG_ERR
, CFSTR("reach server: %s"), desc
);
2037 SCLog(TRUE
, LOG_ERR
,
2038 CFSTR("reach server: Connection error: %d : %s"),
2044 SCLog(TRUE
, LOG_ERR
,
2045 CFSTR("reach server: unknown event type : %x"),
2049 xpc_connection_resume(connection
);
2057 main(int argc
, char **argv
)
2060 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
2063 load_SCNetworkReachability(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);
2072 #endif // HAVE_REACHABILITY_SERVER