2 * Copyright (c) 2011, 2012 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>
48 * A boolean that enables additional logging.
50 static boolean_t S_debug
= FALSE
;
54 #pragma mark Support functions
58 log_xpc_object(const char *msg
, xpc_object_t obj
)
62 desc
= xpc_copy_description(obj
);
63 SCLog(S_debug
, LOG_INFO
, CFSTR("%s = %s"), msg
, desc
);
68 static __inline__
void
69 my_CFDictionaryApplyFunction(CFDictionaryRef theDict
,
70 CFDictionaryApplierFunction applier
,
73 CFAllocatorRef myAllocator
;
74 CFDictionaryRef myDict
;
76 myAllocator
= CFGetAllocator(theDict
);
77 myDict
= CFDictionaryCreateCopy(myAllocator
, theDict
);
78 CFDictionaryApplyFunction(myDict
, applier
, context
);
85 #pragma mark SCNetworkReachability target support
88 static CFMutableDictionaryRef reach_digest_map
;
91 static dispatch_queue_t
92 _server_concurrent_queue()
94 static dispatch_once_t once
;
95 static dispatch_queue_t q
;
97 dispatch_once(&once
, ^{
98 q
= dispatch_queue_create(REACH_SERVICE_NAME
".concurrent",
99 DISPATCH_QUEUE_CONCURRENT
);
100 dispatch_queue_set_width(q
, 32);
107 static dispatch_queue_t
108 _server_digest_queue()
110 static dispatch_once_t once
;
111 static dispatch_queue_t q
;
113 dispatch_once(&once
, ^{
114 q
= dispatch_queue_create(REACH_SERVICE_NAME
".digest", NULL
);
121 static dispatch_group_t
122 _target_group(SCNetworkReachabilityRef target
)
124 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
126 return targetPrivate
->serverGroup
;
130 static dispatch_queue_t
131 _target_queue(SCNetworkReachabilityRef target
)
133 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
135 return targetPrivate
->serverQueue
;
143 * _target_reference_add
145 * Note: use dispatch_sync(_server_digest_queue(), ^{ ... });
148 _target_reference_add(SCNetworkReachabilityRef target
, CFDataRef digest
, xpc_connection_t connection
)
150 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
152 // take a reference to the target
155 // ensure that we have a dispatch group
156 if (targetPrivate
->serverGroup
== NULL
) {
157 targetPrivate
->serverGroup
= dispatch_group_create();
160 // ensure that we have a dispatch queue
161 if (targetPrivate
->serverQueue
== NULL
) {
164 snprintf(qname
, sizeof(qname
), "com.apple.SCNetworkReachability.%p.server", target
);
165 targetPrivate
->serverQueue
= dispatch_queue_create(qname
, NULL
);
168 // bump the reference count
169 if (_SC_ATOMIC_INC(&targetPrivate
->serverReferences
) == 0) {
170 // and maintain a digest-->target mapping
171 targetPrivate
->serverDigest
= CFRetain(digest
);
172 CFDictionarySetValue(reach_digest_map
, digest
, target
);
178 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
179 SCLog(TRUE
, LOG_INFO
,
180 CFSTR("<%p> target %p: reference added (%@, %d)"),
184 targetPrivate
->serverReferences
);
193 * _target_reference_remove
195 * Note: use dispatch_sync(_server_digest_queue(), ^{ ... });
198 _target_reference_remove(SCNetworkReachabilityRef target
, xpc_connection_t connection
)
200 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
202 // drop the reference count
203 if (_SC_ATOMIC_DEC(&targetPrivate
->serverReferences
) == 0) {
205 * if that was the last reference, we no longer need to
206 * keep the digest-->target mapping
208 CFDictionaryRemoveValue(reach_digest_map
, targetPrivate
->serverDigest
);
209 CFRelease(targetPrivate
->serverDigest
);
210 targetPrivate
->serverDigest
= NULL
;
214 SCLog(TRUE
, LOG_INFO
,
215 CFSTR("<%p> target %p: reference removed (%d)"),
218 targetPrivate
->serverReferences
);
221 // release a reference to the target
231 #define MUTEX_LOCK(m) { \
232 int _lock_ = (pthread_mutex_lock(m) == 0); \
236 #define MUTEX_UNLOCK(m) { \
237 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
243 _target_reply_add_reachability(SCNetworkReachabilityRef target
,
246 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
248 MUTEX_LOCK(&targetPrivate
->lock
);
250 xpc_dictionary_set_uint64(reply
,
252 targetPrivate
->info
.cycle
);
253 xpc_dictionary_set_uint64(reply
,
255 targetPrivate
->info
.flags
);
256 xpc_dictionary_set_uint64(reply
,
257 REACH_STATUS_IF_INDEX
,
258 targetPrivate
->info
.if_index
);
259 xpc_dictionary_set_data (reply
,
260 REACH_STATUS_IF_NAME
,
261 targetPrivate
->info
.if_name
,
262 sizeof(targetPrivate
->info
.if_name
));
263 xpc_dictionary_set_bool (reply
,
264 REACH_STATUS_SLEEPING
,
265 targetPrivate
->info
.sleeping
);
266 if (targetPrivate
->type
== reachabilityTypeName
) {
267 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
268 xpc_object_t addresses
;
272 addresses
= xpc_array_create(NULL
, 0);
274 n
= CFArrayGetCount(targetPrivate
->resolvedAddress
);
275 for (i
= 0; i
< n
; i
++) {
278 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
);
279 xpc_array_set_data(addresses
,
281 CFDataGetBytePtr(address
),
282 CFDataGetLength(address
));
285 xpc_dictionary_set_value(reply
,
286 REACH_STATUS_RESOLVED_ADDRESS
,
288 xpc_release(addresses
);
290 xpc_dictionary_set_int64(reply
,
291 REACH_STATUS_RESOLVED_ADDRESS_ERROR
,
292 targetPrivate
->resolvedAddressError
);
295 MUTEX_UNLOCK(&targetPrivate
->lock
);
305 xpc_connection_t connection
;
307 } reach_watcher_key_t
;
310 unsigned int n_changes
;
311 } reach_watcher_val_t
;
315 _target_watcher_key_create(xpc_connection_t connection
,
319 reach_watcher_key_t watcher_key
;
321 watcher_key
.connection
= connection
;
322 watcher_key
.target_id
= target_id
;
324 key
= CFDataCreate(NULL
, (UInt8
*)&watcher_key
, sizeof(watcher_key
));
330 _target_watcher_add(SCNetworkReachabilityRef target
,
331 xpc_connection_t connection
,
334 __block Boolean ok
= TRUE
;
337 q
= _target_queue(target
);
340 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
342 if (targetPrivate
->serverWatchers
== NULL
) {
343 ok
= SCNetworkReachabilitySetDispatchQueue(target
, q
);
346 CFSTR("<%p> target %p: _watcher_add SCNetworkReachabilitySetDispatchQueue() failed: %s"),
349 SCErrorString(SCError()));
353 targetPrivate
->serverWatchers
= CFDictionaryCreateMutable(NULL
,
355 &kCFTypeDictionaryKeyCallBacks
,
356 &kCFTypeDictionaryValueCallBacks
);
359 xpc_retain(connection
);
361 key
= _target_watcher_key_create(connection
, target_id
);
362 if (CFDictionaryContainsKey(targetPrivate
->serverWatchers
, key
)) {
364 CFSTR("<%p> target %p: watcher not added, c=0x%0llx, \"serverWatchers\" key exists"),
370 static const reach_watcher_val_t watcher_val0
= { 0 };
372 val
= CFDataCreate(NULL
, (UInt8
*)&watcher_val0
, sizeof(watcher_val0
));
373 CFDictionaryAddValue(targetPrivate
->serverWatchers
, key
, val
);
377 SCLog(TRUE
, LOG_INFO
,
378 CFSTR("<%p> target %p: watcher added, c=0x%0llx, n=%d"),
382 CFDictionaryGetCount(targetPrivate
->serverWatchers
));
393 _target_watcher_checkin(SCNetworkReachabilityRef target
,
394 xpc_connection_t connection
,
397 __block Boolean scheduled
= FALSE
;
399 dispatch_sync(_target_queue(target
), ^{
402 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
404 reach_watcher_val_t
*watcher_val
;
406 if (targetPrivate
->serverWatchers
== NULL
) {
411 key
= _target_watcher_key_create(connection
, target_id
);
412 val
= CFDictionaryGetValue(targetPrivate
->serverWatchers
, key
);
415 // if the target [for this client] was not scheduled
419 // indicate that the target was scheduled
423 * and note that the reachability flags for this target have
424 * been picked up by the client
426 /* ALIGN: CF aligns to at least >8 byte boundries */
427 watcher_val
= (reach_watcher_val_t
*)(void *)CFDataGetBytePtr(val
);
428 n
= _SC_ATOMIC_ZERO(&watcher_val
->n_changes
);
429 if (S_debug
&& (n
> 0)) {
430 SCLog(TRUE
, LOG_INFO
,
431 CFSTR("<%p> target %p: SCNetworkReachabilityGetFlags() after %d notification%s"),
435 (n
== 1) ? "" : "s");
444 _target_watcher_remove(SCNetworkReachabilityRef target
,
445 xpc_connection_t connection
,
448 __block Boolean ok
= TRUE
;
450 dispatch_sync(_target_queue(target
), ^{
453 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
455 if (targetPrivate
->serverWatchers
== NULL
) {
457 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"),
475 CFDictionaryRemoveValue(targetPrivate
->serverWatchers
, key
);
476 xpc_release(connection
);
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 (void) _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverInfoValid
, TRUE
, FALSE
);
514 #pragma mark Reachability [RBT] client support
519 xpc_connection_t connection
;
521 const char *proc_name
;
522 CFMutableDictionaryRef targets
; // target_id --> SCNetworkReachabilityRef
526 #define RBNODE_TO_REACH_CLIENT(node) \
527 ((reach_client_t *)((uintptr_t)node - offsetof(reach_client_t, rbn)))
531 _rbt_compare_transaction_nodes(const struct rb_node
*n1
, const struct rb_node
*n2
)
533 uint64_t a
= (uintptr_t)RBNODE_TO_REACH_CLIENT(n1
)->connection
;
534 uint64_t b
= (uintptr_t)RBNODE_TO_REACH_CLIENT(n2
)->connection
;
541 _rbt_compare_transaction_key(const struct rb_node
*n1
, const void *key
)
543 uint64_t a
= (uintptr_t)RBNODE_TO_REACH_CLIENT(n1
)->connection
;
544 uint64_t b
= *(uintptr_t *)key
;
550 static struct rb_tree
*
553 static dispatch_once_t once
;
554 static const struct rb_tree_ops ops
= {
555 .rbto_compare_nodes
= _rbt_compare_transaction_nodes
,
556 .rbto_compare_key
= _rbt_compare_transaction_key
,
558 static struct rb_tree rbtree
;
560 dispatch_once(&once
, ^{
561 rb_tree_init(&rbtree
, &ops
);
568 static dispatch_queue_t
569 _reach_connection_queue()
571 static dispatch_once_t once
;
572 static dispatch_queue_t q
;
574 dispatch_once(&once
, ^{
575 q
= dispatch_queue_create(REACH_SERVICE_NAME
".connection", NULL
);
582 static reach_client_t
*
583 _reach_client_create(xpc_connection_t connection
)
585 reach_client_t
*client
;
587 client
= calloc(1, sizeof(*client
));
588 client
->connection
= connection
;
589 client
->pid
= xpc_connection_get_pid(connection
);
590 client
->proc_name
= NULL
;
591 client
->targets
= CFDictionaryCreateMutable(NULL
,
593 &kCFTypeDictionaryKeyCallBacks
,
594 &kCFTypeDictionaryValueCallBacks
);
601 _reach_client_release(reach_client_t
*client
)
603 if (client
->proc_name
!= NULL
) {
604 free((void *)client
->proc_name
);
606 CFRelease(client
->targets
);
613 _reach_client_remove_target(const void *key
, const void *value
, void *context
)
615 xpc_connection_t connection
= (xpc_connection_t
)context
;
616 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
617 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
619 // check if we have anyone watching this target
620 if (targetPrivate
->serverWatchers
!= NULL
) {
623 n
= CFDictionaryGetCount(targetPrivate
->serverWatchers
);
626 const void * watchers_q
[32];
627 const void ** watchers
= watchers_q
;
629 if (n
> sizeof(watchers_q
)/sizeof(watchers
[0])) {
630 watchers
= CFAllocatorAllocate(NULL
, n
* sizeof(CFDataRef
), 0);
632 CFDictionaryGetKeysAndValues(targetPrivate
->serverWatchers
, watchers
, NULL
);
634 for (i
= 0; i
< n
; i
++) {
636 reach_watcher_key_t
*watcher_key
;
638 key
= (CFDataRef
)watchers
[i
];
639 /* ALIGN: CF aligns to >8 byte boundries */
640 watcher_key
= (reach_watcher_key_t
*)(void *)CFDataGetBytePtr(key
);
641 if (watcher_key
->connection
== connection
) {
642 // remove watcher references for THIS connection
643 _target_watcher_remove(target
,
644 watcher_key
->connection
,
645 watcher_key
->target_id
);
649 if (watchers
!= watchers_q
) {
650 CFAllocatorDeallocate(NULL
, watchers
);
655 // remove our reference to this target
656 dispatch_sync(_server_digest_queue(), ^{
657 _target_reference_remove(target
, connection
);
665 _reach_client_remove(xpc_connection_t connection
)
667 struct rb_tree
*rbtree
= _reach_clients_rbt();
670 rbn
= rb_tree_find_node(rbtree
, &connection
);
672 reach_client_t
*client
;
674 client
= RBNODE_TO_REACH_CLIENT(rbn
);
676 // remove any remaining target references (for this client)
677 my_CFDictionaryApplyFunction(client
->targets
,
678 _reach_client_remove_target
,
681 rb_tree_remove_node(rbtree
, rbn
);
682 _reach_client_release(client
);
685 CFSTR("<%p> _reach_client_remove: unexpected client"),
693 static __inline__ CFDataRef
694 _client_target_key_create(uint64_t target_id
)
696 CFDataRef target_key
;
698 target_key
= CFDataCreate(NULL
, (UInt8
*)&target_id
, sizeof(target_id
));
703 static SCNetworkReachabilityRef
704 _client_target_copy(reach_client_t
*client
, uint64_t target_id
)
706 SCNetworkReachabilityRef target
;
707 CFDataRef target_key
;
709 target_key
= _client_target_key_create(target_id
);
710 target
= CFDictionaryGetValue(client
->targets
, target_key
);
711 CFRelease(target_key
);
713 if (target
!= NULL
) {
722 _client_target_set(reach_client_t
*client
, uint64_t target_id
, SCNetworkReachabilityRef target
)
725 CFDataRef target_key
;
727 target_key
= _client_target_key_create(target_id
);
728 added
= !CFDictionaryContainsKey(client
->targets
, target_key
);
730 CFDictionarySetValue(client
->targets
, target_key
, target
);
732 CFRelease(target_key
);
739 _client_target_remove(reach_client_t
*client
, uint64_t target_id
)
741 CFDataRef target_key
;
743 target_key
= _client_target_key_create(target_id
);
744 CFDictionaryRemoveValue(client
->targets
, target_key
);
745 CFRelease(target_key
);
752 #pragma mark Reachability [XPC] server functions
757 * Note: should be exec'd on the target queue
760 _reach_changed(SCNetworkReachabilityRef target
, SCNetworkReachabilityFlags flags
, void *info
)
764 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
765 const void * watcher_keys_q
[32];
766 const void ** watcher_keys
= watcher_keys_q
;
767 const void * watcher_vals_q
[32];
768 const void ** watcher_vals
= watcher_vals_q
;
771 SCLog(TRUE
, LOG_INFO
,
772 CFSTR("%sprocess reachability changed, flags = 0x%08x"),
773 targetPrivate
->log_prefix
,
777 if (targetPrivate
->serverWatchers
== NULL
) {
782 n
= CFDictionaryGetCount(targetPrivate
->serverWatchers
);
789 * Because we are actively watching for additional changes
790 * we mark the flags as "valid"
792 if (_SC_ATOMIC_CMPXCHG(&targetPrivate
->serverInfoValid
, FALSE
, TRUE
)) {
794 SCLog(TRUE
, LOG_INFO
, CFSTR(" flags are now \"valid\""));
798 // notify all of the watchers
799 if (n
> sizeof(watcher_keys_q
)/sizeof(watcher_keys
[0])) {
800 watcher_keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFDataRef
), 0);
801 watcher_vals
= CFAllocatorAllocate(NULL
, n
* sizeof(CFDataRef
), 0);
804 CFDictionaryGetKeysAndValues(targetPrivate
->serverWatchers
,
808 for (i
= 0; i
< n
; i
++) {
809 xpc_connection_t connection
;
813 reach_watcher_key_t
*watcher_key
;
814 reach_watcher_val_t
*watcher_val
;
816 val
= (CFDataRef
)watcher_vals
[i
];
817 /* ALIGN: CF aligns to >8 byte boundries */
818 watcher_val
= (reach_watcher_val_t
*)(void *)CFDataGetBytePtr(val
);
820 if (_SC_ATOMIC_INC(&watcher_val
->n_changes
) > 0) {
821 // if we've already sent a notification
825 key
= (CFDataRef
)watcher_keys
[i
];
826 /* ALIGN: CF aligns to >8 byte boundries */
827 watcher_key
= (reach_watcher_key_t
*)(void *)CFDataGetBytePtr(key
);
829 connection
= xpc_retain(watcher_key
->connection
);
830 target_id
= watcher_key
->target_id
;
831 dispatch_async(_reach_connection_queue(), ^{
834 // create our [async] notification
835 reply
= xpc_dictionary_create(NULL
, NULL
, 0);
838 xpc_dictionary_set_int64(reply
,
840 MESSAGE_REACHABILITY_STATUS
);
843 xpc_dictionary_set_uint64(reply
,
844 REACH_CLIENT_TARGET_ID
,
847 log_xpc_object(" reply [async]", reply
);
848 xpc_connection_send_message(connection
, reply
);
851 xpc_release(connection
);
855 if (n
> sizeof(watcher_keys_q
)/sizeof(watcher_keys
[0])) {
856 CFAllocatorDeallocate(NULL
, watcher_keys
);
857 CFAllocatorDeallocate(NULL
, watcher_vals
);
865 sanitize_address(const struct sockaddr
*from
, struct sockaddr
*to
)
867 switch (from
->sa_family
) {
869 /* ALIGN: cast okay, alignment not assumed. */
870 struct sockaddr_in
*from4
= (struct sockaddr_in
*)(void *)from
;
871 struct sockaddr_in
*to4
= (struct sockaddr_in
*)(void *)to
;
873 bzero(to4
, sizeof(*to4
));
874 to4
->sin_len
= sizeof(*to4
);
875 to4
->sin_family
= AF_INET
;
876 bcopy(&from4
->sin_addr
, &to4
->sin_addr
, sizeof(to4
->sin_addr
));
881 /* ALIGN: cast okay, alignment not assumed. */
882 struct sockaddr_in6
*from6
= (struct sockaddr_in6
*)(void *)from
;
883 struct sockaddr_in6
*to6
= (struct sockaddr_in6
*)(void *)to
;
885 bzero(to6
, sizeof(*to6
));
886 to6
->sin6_len
= sizeof(*to6
);
887 to6
->sin6_family
= AF_INET6
;
888 bcopy(&from6
->sin6_addr
, &to6
->sin6_addr
, sizeof(to6
->sin6_addr
));
889 to6
->sin6_scope_id
= from6
->sin6_scope_id
;
894 bcopy(from
, to
, from
->sa_len
);
903 target_add(reach_client_t
*client
, xpc_object_t request
)
907 const struct sockaddr
*localAddress
;
908 struct sockaddr_storage localAddress0
;
909 const struct sockaddr
*remoteAddress
;
910 struct sockaddr_storage remoteAddress0
;
911 const struct addrinfo
*hints
;
913 const char *if_name
= NULL
;
914 bool onDemandBypass
= FALSE
;
918 unsigned char bytes
[CC_SHA1_DIGEST_LENGTH
];
920 CFDataRef digest
= NULL
;
922 xpc_connection_t remote
;
924 bool resolverBypass
= FALSE
;
925 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
928 __block Boolean ok
= TRUE
;
929 __block SCNetworkReachabilityRef target
= NULL
;
932 SCLog(TRUE
, LOG_INFO
,
933 CFSTR("<%p> create reachability target"),
935 // log_xpc_object(" create", request);
938 remote
= xpc_dictionary_get_remote_connection(request
);
939 reply
= xpc_dictionary_create_reply(request
);
942 CFSTR("<%p> target_add: xpc_dictionary_create_reply: failed"),
947 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
948 if (target_id
== 0) {
949 xpc_dictionary_set_string(reply
,
950 REACH_REQUEST_REPLY_DETAIL
,
955 // create a "digest" of the [new] target
959 name
= xpc_dictionary_get_string(request
, REACH_TARGET_NAME
);
961 CC_SHA1_Update(&ctx
, name
, strlen(name
));
964 serv
= xpc_dictionary_get_string(request
, REACH_TARGET_SERV
);
966 CC_SHA1_Update(&ctx
, serv
, strlen(serv
));
969 localAddress
= xpc_dictionary_get_data(request
, REACH_TARGET_LOCAL_ADDR
, &len
);
970 if (localAddress
!= NULL
) {
971 if ((len
== localAddress
->sa_len
) && (len
<= sizeof(struct sockaddr_storage
))) {
972 sanitize_address(localAddress
, (struct sockaddr
*)&localAddress0
);
973 CC_SHA1_Update(&ctx
, &localAddress0
, len
);
975 xpc_dictionary_set_string(reply
,
976 REACH_REQUEST_REPLY_DETAIL
,
977 "local address: size error");
982 remoteAddress
= xpc_dictionary_get_data(request
, REACH_TARGET_REMOTE_ADDR
, &len
);
983 if (remoteAddress
!= NULL
) {
984 if ((len
== remoteAddress
->sa_len
) && (len
<= sizeof(struct sockaddr_storage
))) {
985 sanitize_address(remoteAddress
, (struct sockaddr
*)&remoteAddress0
);
986 CC_SHA1_Update(&ctx
, &remoteAddress0
, len
);
988 xpc_dictionary_set_string(reply
,
989 REACH_REQUEST_REPLY_DETAIL
,
990 "remote address: size error");
995 hints
= xpc_dictionary_get_data(request
, REACH_TARGET_HINTS
, &len
);
997 if (len
== sizeof(struct addrinfo
)) {
998 CC_SHA1_Update(&ctx
, hints
, len
);
1000 xpc_dictionary_set_string(reply
,
1001 REACH_REQUEST_REPLY_DETAIL
,
1002 "hints: size error");
1007 if_index
= xpc_dictionary_get_int64(request
, REACH_TARGET_IF_INDEX
);
1008 if (if_index
!= 0) {
1009 if_name
= xpc_dictionary_get_string(request
, REACH_TARGET_IF_NAME
);
1010 if (if_name
!= NULL
) {
1011 CC_SHA1_Update(&ctx
, if_name
, strlen(if_name
));
1015 onDemandBypass
= xpc_dictionary_get_bool(request
, REACH_TARGET_ONDEMAND_BYPASS
);
1016 if (onDemandBypass
) {
1017 CC_SHA1_Update(&ctx
, &onDemandBypass
, sizeof(onDemandBypass
));
1020 resolverBypass
= xpc_dictionary_get_bool(request
, REACH_TARGET_RESOLVER_BYPASS
);
1021 if (resolverBypass
) {
1022 CC_SHA1_Update(&ctx
, &resolverBypass
, sizeof(resolverBypass
));
1026 CC_SHA1_Final(bytes
, &ctx
);
1027 digest
= CFDataCreate(NULL
, bytes
, sizeof(bytes
));
1030 * Check to see if we already have a SCNetworkReachability object
1031 * for this digest. If so, we'll share the existing target. If not,
1032 * create a new [shared] target.
1034 dispatch_sync(_server_digest_queue(), ^{
1035 target
= CFDictionaryGetValue(reach_digest_map
, digest
);
1036 if (target
!= NULL
) {
1040 CFMutableDictionaryRef options
;
1043 options
= CFDictionaryCreateMutable(NULL
,
1045 &kCFTypeDictionaryKeyCallBacks
,
1046 &kCFTypeDictionaryValueCallBacks
);
1048 str
= CFStringCreateWithCString(NULL
, name
, kCFStringEncodingUTF8
);
1049 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, str
);
1053 str
= CFStringCreateWithCString(NULL
, serv
, kCFStringEncodingUTF8
);
1054 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionServName
, str
);
1057 if (localAddress
!= NULL
) {
1058 data
= CFDataCreate(NULL
, (const UInt8
*)&localAddress0
, localAddress0
.ss_len
);
1059 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionLocalAddress
, data
);
1062 if (remoteAddress
!= NULL
) {
1063 data
= CFDataCreate(NULL
, (const UInt8
*)&remoteAddress0
, remoteAddress0
.ss_len
);
1064 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
, data
);
1067 if (hints
!= NULL
) {
1068 data
= CFDataCreate(NULL
, (const UInt8
*)hints
, sizeof(struct addrinfo
));
1069 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionHints
, data
);
1072 if (onDemandBypass
) {
1073 CFDictionarySetValue(options
,
1074 kSCNetworkReachabilityOptionConnectionOnDemandBypass
,
1077 if (resolverBypass
) {
1078 CFDictionarySetValue(options
,
1079 kSCNetworkReachabilityOptionResolverBypass
,
1082 CFDictionarySetValue(options
,
1083 kSCNetworkReachabilityOptionServerBypass
,
1085 target
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
1087 if (target
== NULL
) {
1088 xpc_dictionary_set_string(reply
,
1089 REACH_REQUEST_REPLY_DETAIL
,
1090 "SCNetworkReachabilityCreateWithOptions failed");
1095 // because the interface name may not (no longer) be valid we set
1096 // this after we've created the SCNetworkReachabilty object
1097 if ((if_index
!= 0) && (if_name
!= NULL
)) {
1098 SCNetworkReachabilityPrivateRef targetPrivate
;
1100 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1101 targetPrivate
->if_index
= if_index
;
1102 strlcpy(targetPrivate
->if_name
, if_name
, sizeof(targetPrivate
->if_name
));
1106 ok
= SCNetworkReachabilitySetCallback(target
, _reach_changed
, NULL
);
1108 xpc_dictionary_set_string(reply
,
1109 REACH_REQUEST_REPLY_DETAIL
,
1110 "SCNetworkReachabilitySetCallback failed");
1117 // bump the number of references to this target
1118 _target_reference_add(target
, digest
, client
->connection
);
1126 * add an association for the client's target_id to the [shared]
1127 * SCNetworkReachability object.
1129 added
= _client_target_set(client
, target_id
, target
);
1131 // if we already had a reference to the target (e.g. reconnect)
1132 dispatch_sync(_server_digest_queue(), ^{
1133 _target_reference_remove(target
, client
->connection
);
1137 status
= REACH_REQUEST_REPLY_OK
;
1141 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1142 // log_xpc_object(" reply", reply);
1143 xpc_connection_send_message(remote
, reply
);
1146 if (digest
!= NULL
) CFRelease(digest
);
1147 if (target
!= NULL
) CFRelease(target
);
1153 target_remove(reach_client_t
*client
, xpc_object_t request
)
1155 xpc_connection_t remote
;
1157 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1158 SCNetworkReachabilityRef target
= NULL
;
1162 SCLog(TRUE
, LOG_INFO
,
1163 CFSTR("<%p> remove reachability target"),
1164 client
->connection
);
1165 // log_xpc_object(" remove", request);
1168 remote
= xpc_dictionary_get_remote_connection(request
);
1169 reply
= xpc_dictionary_create_reply(request
);
1170 if (reply
== NULL
) {
1171 SCLog(TRUE
, LOG_ERR
,
1172 CFSTR("<%p> target_remove: xpc_dictionary_create_reply: failed"),
1173 client
->connection
);
1177 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
1178 if (target_id
== 0) {
1179 xpc_dictionary_set_string(reply
,
1180 REACH_REQUEST_REPLY_DETAIL
,
1185 target
= _client_target_copy(client
, target_id
);
1186 if (target
== NULL
) {
1187 xpc_dictionary_set_string(reply
,
1188 REACH_REQUEST_REPLY_DETAIL
,
1190 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1195 * remove the association from the client's target_id to the [shared]
1196 * SCNetworkReachability object.
1198 _client_target_remove(client
, target_id
);
1200 // drop the number of references to this target
1201 dispatch_sync(_server_digest_queue(), ^{
1202 _target_reference_remove(target
, client
->connection
);
1205 status
= REACH_REQUEST_REPLY_OK
;
1209 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1210 // log_xpc_object(" reply", reply);
1211 xpc_connection_send_message(remote
, reply
);
1214 if (target
!= NULL
) CFRelease(target
);
1220 target_schedule(reach_client_t
*client
, xpc_object_t request
)
1223 xpc_connection_t remote
;
1225 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1226 SCNetworkReachabilityRef target
= NULL
;
1230 SCLog(TRUE
, LOG_INFO
,
1231 CFSTR("<%p> schedule reachability target"),
1232 client
->connection
);
1233 // log_xpc_object(" schedule", request);
1236 remote
= xpc_dictionary_get_remote_connection(request
);
1237 reply
= xpc_dictionary_create_reply(request
);
1238 if (reply
== NULL
) {
1239 SCLog(TRUE
, LOG_ERR
,
1240 CFSTR("<%p> target_schedule: xpc_dictionary_create_reply: failed"),
1241 client
->connection
);
1245 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
1246 if (target_id
== 0) {
1247 xpc_dictionary_set_string(reply
,
1248 REACH_REQUEST_REPLY_DETAIL
,
1253 target
= _client_target_copy(client
, target_id
);
1254 if (target
== NULL
) {
1255 xpc_dictionary_set_string(reply
,
1256 REACH_REQUEST_REPLY_DETAIL
,
1258 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1262 // enable monitoring
1263 ok
= _target_watcher_add(target
, client
->connection
, target_id
);
1265 status
= REACH_REQUEST_REPLY_OK
;
1270 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1271 // log_xpc_object(" reply", reply);
1272 xpc_connection_send_message(remote
, reply
);
1275 if (target
!= NULL
) CFRelease(target
);
1281 target_status(reach_client_t
*client
, xpc_object_t request
)
1283 xpc_connection_t remote
;
1285 __block Boolean reply_now
= TRUE
;
1287 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1288 SCNetworkReachabilityRef target
= NULL
;
1292 SCLog(TRUE
, LOG_INFO
,
1293 CFSTR("<%p> get status of reachability target"),
1294 client
->connection
);
1295 // log_xpc_object(" status", request);
1298 remote
= xpc_dictionary_get_remote_connection(request
);
1299 reply
= xpc_dictionary_create_reply(request
);
1300 if (reply
== NULL
) {
1301 SCLog(TRUE
, LOG_ERR
,
1302 CFSTR("<%p> target_status: xpc_dictionary_create_reply: failed"),
1303 client
->connection
);
1307 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
1308 if (target_id
== 0) {
1309 SCLog(TRUE
, LOG_ERR
,
1310 CFSTR("<%p> target_status: no target"),
1311 client
->connection
);
1312 xpc_dictionary_set_string(reply
,
1313 REACH_REQUEST_REPLY_DETAIL
,
1318 target
= _client_target_copy(client
, target_id
);
1319 if (target
== NULL
) {
1320 SCLog(TRUE
, LOG_ERR
,
1321 CFSTR("<%p> target_status: no target (0x%0llx)"),
1324 xpc_dictionary_set_string(reply
,
1325 REACH_REQUEST_REPLY_DETAIL
,
1327 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1332 * Check to see if the target [for this client] had been "scheduled".
1334 * If so, also mark that we've picked up the current reachability
1335 * flags and that any pending notifications have been processed.
1337 scheduled
= _target_watcher_checkin(target
, client
->connection
, target_id
);
1340 * return current reachability information to the caller
1342 dispatch_sync(_target_queue(target
), ^{
1343 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1347 * The client "scheduled" this target. As such, we
1348 * know that this an async query and that we only
1349 * need to return the "last known" flags.
1351 _target_reply_add_reachability(target
, reply
);
1352 // log_xpc_object(" reply [scheduled]", reply);
1357 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1358 SCLog(TRUE
, LOG_INFO
,
1359 CFSTR("<%p> reply [scheduled], %@"),
1366 * The client has NOT "scheduled" this target. As
1367 * such, we know that this is a sync query and that
1368 * must return "current" flags.
1370 if (targetPrivate
->scheduled
&& targetPrivate
->serverInfoValid
) {
1372 * The server target has been "scheduled" and we
1373 * have flags that are "current".
1375 _target_reply_add_reachability(target
, reply
);
1376 // log_xpc_object(" reply [scheduled/valid]", reply);
1381 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1382 SCLog(TRUE
, LOG_INFO
,
1383 CFSTR("<%p> reply [scheduled/valid], %@"),
1389 dispatch_group_t group
;
1392 * The server target has NOT been "scheduled" (or
1393 * we do not have "current" flags. This means that
1394 * we must query for the current information and
1395 * return the flags to the client when they are
1401 group
= _target_group(target
);
1402 if (_SC_ATOMIC_INC(&targetPrivate
->serverQueryActive
) == 0) {
1404 dispatch_group_async(group
, _server_concurrent_queue(), ^{
1405 SCNetworkReachabilityFlags flags
;
1409 // query for the flags
1410 ok
= SCNetworkReachabilityGetFlags(target
, &flags
);
1411 flags
= targetPrivate
->info
.flags
; // get the "raw" flags
1413 SCLog(TRUE
, LOG_ERR
,
1414 CFSTR("SCNetworkReachabilityGetFlags() [sync query] failed"
1418 SCErrorString(SCError()));
1421 // flags are now available
1422 n
= _SC_ATOMIC_ZERO(&targetPrivate
->serverQueryActive
);
1424 SCLog(TRUE
, LOG_INFO
,
1425 CFSTR("%sSCNetworkReachabilityGetFlags() [sync query] complete, n = %d"),
1426 targetPrivate
->log_prefix
,
1435 dispatch_group_notify(group
, _target_queue(target
), ^{
1436 // flags are now available
1437 _target_reply_add_reachability(target
, reply
);
1438 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, REACH_REQUEST_REPLY_OK
);
1439 // log_xpc_object(" reply [delayed]", reply);
1444 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1445 SCLog(TRUE
, LOG_INFO
,
1446 CFSTR("<%p> reply [delayed], %@"),
1452 xpc_connection_send_message(remote
, reply
);
1461 status
= REACH_REQUEST_REPLY_OK
;
1466 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1468 if (status
!= REACH_REQUEST_REPLY_OK
) {
1469 // log_xpc_object(" reply [!]", reply);
1472 SCLog(TRUE
, LOG_INFO
,
1473 CFSTR("<%p> reply [!]"),
1474 client
->connection
);
1478 xpc_connection_send_message(remote
, reply
);
1480 } else if (S_debug
) {
1483 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1484 SCLog(TRUE
, LOG_INFO
,
1485 CFSTR("<%p> no reply [yet], %@"),
1491 if (target
!= NULL
) CFRelease(target
);
1497 target_unschedule(reach_client_t
*client
, xpc_object_t request
)
1500 xpc_connection_t remote
;
1502 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1503 SCNetworkReachabilityRef target
= NULL
;
1507 SCLog(TRUE
, LOG_INFO
,
1508 CFSTR("<%p> unschedule reachability target"),
1509 client
->connection
);
1510 // log_xpc_object(" unschedule", request);
1513 remote
= xpc_dictionary_get_remote_connection(request
);
1514 reply
= xpc_dictionary_create_reply(request
);
1515 if (reply
== NULL
) {
1516 SCLog(TRUE
, LOG_ERR
,
1517 CFSTR("<%p> target_unschedule: xpc_dictionary_create_reply: failed"),
1518 client
->connection
);
1522 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
1523 if (target_id
== 0) {
1524 xpc_dictionary_set_string(reply
,
1525 REACH_REQUEST_REPLY_DETAIL
,
1530 target
= _client_target_copy(client
, target_id
);
1531 if (target
== NULL
) {
1532 xpc_dictionary_set_string(reply
,
1533 REACH_REQUEST_REPLY_DETAIL
,
1535 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1539 // disable monitoring
1540 ok
= _target_watcher_remove(target
, client
->connection
, target_id
);
1542 status
= REACH_REQUEST_REPLY_OK
;
1547 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1548 // log_xpc_object(" reply", reply);
1549 xpc_connection_send_message(remote
, reply
);
1552 if (target
!= NULL
) CFRelease(target
);
1557 #define SNAPSHOT_PATH_STATE _PATH_VARTMP "configd-reachability"
1561 _snapshot_digest_watcher(const void *key
, const void *value
, void *context
)
1563 FILE *f
= (FILE *)context
;
1564 static reach_client_t no_client
= {
1568 struct rb_node
*rbn
;
1569 reach_client_t
*rbt_client
;
1570 reach_watcher_key_t
*watcher_key
;
1571 reach_watcher_val_t
*watcher_val
;
1573 /* ALIGN: CF aligns to >8 byte boundries */
1574 watcher_key
= (reach_watcher_key_t
*)(void *)CFDataGetBytePtr(key
);
1575 watcher_val
= (reach_watcher_val_t
*)(void *)CFDataGetBytePtr(value
);
1577 rbn
= rb_tree_find_node(_reach_clients_rbt(), &watcher_key
->connection
);
1579 rbn
= &no_client
.rbn
;
1582 rbt_client
= RBNODE_TO_REACH_CLIENT(rbn
);
1585 CFSTR(" connection = %p, target(c) = 0x%0llx, command = %s, pid = %d, changes = %u\n"),
1586 watcher_key
->connection
,
1587 watcher_key
->target_id
,
1588 rbt_client
->proc_name
,
1590 watcher_val
->n_changes
);
1597 _snapshot_digest(const void *key
, const void *value
, void *context
)
1599 FILE *f
= (FILE *)context
;
1600 CFStringRef digest
= (CFStringRef
)key
;
1602 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
1603 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1605 q
= _target_queue(target
);
1607 SCPrint(TRUE
, f
, CFSTR("\n digest : %@\n"), digest
);
1608 SCPrint(TRUE
, f
, CFSTR(" %@\n"), target
);
1609 SCPrint(TRUE
, f
, CFSTR(" valid = %s, active = %u, refs = %u\n"),
1610 targetPrivate
->serverInfoValid
? "Y" : "N",
1611 targetPrivate
->serverQueryActive
,
1612 targetPrivate
->serverReferences
);
1614 SCPrint(TRUE
, f
, CFSTR(" network %d.%3.3d"),
1615 targetPrivate
->last_network
.tv_sec
,
1616 targetPrivate
->last_network
.tv_usec
/ 1000);
1617 #if !TARGET_OS_IPHONE
1618 SCPrint(TRUE
, f
, CFSTR(", power %d.%3.3d"),
1619 targetPrivate
->last_power
.tv_sec
,
1620 targetPrivate
->last_power
.tv_usec
/ 1000);
1621 #endif // !TARGET_OS_IPHONE
1622 if (targetPrivate
->type
== reachabilityTypeName
) {
1623 SCPrint(TRUE
, f
, CFSTR(", DNS %d.%3.3d"),
1624 targetPrivate
->last_dns
.tv_sec
,
1625 targetPrivate
->last_dns
.tv_usec
/ 1000);
1626 if (timerisset(&targetPrivate
->dnsQueryEnd
)) {
1627 struct timeval dnsQueryElapsed
;
1629 timersub(&targetPrivate
->dnsQueryEnd
,
1630 &targetPrivate
->dnsQueryStart
,
1632 SCPrint(TRUE
, f
, CFSTR(" (query %d.%3.3d / reply %d.%3.3d)"),
1633 targetPrivate
->dnsQueryStart
.tv_sec
,
1634 targetPrivate
->dnsQueryStart
.tv_usec
/ 1000,
1635 dnsQueryElapsed
.tv_sec
,
1636 dnsQueryElapsed
.tv_usec
/ 1000);
1639 if (timerisset(&targetPrivate
->last_push
)) {
1640 SCPrint(TRUE
, f
, CFSTR(", last notify %d.%3.3d"),
1641 targetPrivate
->last_push
.tv_sec
,
1642 targetPrivate
->last_push
.tv_usec
/ 1000);
1644 SCPrint(TRUE
, f
, CFSTR("\n"));
1646 if (targetPrivate
->serverWatchers
!= NULL
) {
1647 CFDictionaryApplyFunction(targetPrivate
->serverWatchers
,
1648 _snapshot_digest_watcher
,
1658 _snapshot_target(const void *key
, const void *value
, void *context
)
1660 FILE *f
= (FILE *)context
;
1661 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
1663 CFDataRef target_key
= (CFDataRef
)key
;
1665 /* ALIGN: CF aligns > 8 byte boundries */
1666 target_id
= *(uint64_t *)(void *)CFDataGetBytePtr(target_key
);
1669 CFSTR(" target(c) = 0x%0llx, target(s) = %@\n"),
1678 _snapshot(reach_client_t
*client
, xpc_object_t request
)
1684 struct rb_node
*rbn
;
1685 struct rb_tree
*rbt
;
1686 xpc_connection_t remote
;
1690 SCLog(TRUE
, LOG_INFO
,
1691 CFSTR("<%p> snapshot"),
1692 client
->connection
);
1693 // log_xpc_object(" create", request);
1696 remote
= xpc_dictionary_get_remote_connection(request
);
1697 reply
= xpc_dictionary_create_reply(request
);
1698 if (reply
== NULL
) {
1699 SCLog(TRUE
, LOG_ERR
,
1700 CFSTR("<%p> _snapshot: xpc_dictionary_create_reply: failed"),
1701 client
->connection
);
1705 euid
= xpc_connection_get_euid(remote
);
1707 xpc_dictionary_set_string(reply
,
1708 REACH_REQUEST_REPLY_DETAIL
,
1709 "Permission denied.");
1713 // Save a snapshot of the SCNetworkReachability server "state"
1715 (void) unlink(SNAPSHOT_PATH_STATE
);
1716 fd
= open(SNAPSHOT_PATH_STATE
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_EXCL
, 0644);
1718 xpc_dictionary_set_string(reply
,
1719 REACH_REQUEST_REPLY_DETAIL
,
1723 f
= fdopen(fd
, "w");
1725 xpc_dictionary_set_string(reply
,
1726 REACH_REQUEST_REPLY_DETAIL
,
1731 // provide connection/client info
1733 SCPrint(TRUE
, f
, CFSTR("Clients :\n"));
1734 rbt
= _reach_clients_rbt();
1735 rbn
= rb_tree_iterate(rbt
, NULL
, RB_DIR_RIGHT
);
1737 while (rbn
!= NULL
) {
1738 reach_client_t
*rbt_client
;
1740 rbt_client
= RBNODE_TO_REACH_CLIENT(rbn
);
1742 CFSTR("\n connection = %p, client = %p, command = %s, pid = %d\n"),
1743 rbt_client
->connection
,
1745 rbt_client
->proc_name
!= NULL
? rbt_client
->proc_name
: "?",
1747 my_CFDictionaryApplyFunction(rbt_client
->targets
,
1751 rbn
= rb_tree_iterate(rbt
, rbn
, RB_DIR_LEFT
);
1754 SCPrint(TRUE
, f
, CFSTR(" None.\n"));
1756 SCPrint(TRUE
, f
, CFSTR("\n"));
1758 // provide "digest" info
1760 SCPrint(TRUE
, f
, CFSTR("Digests :\n"));
1761 dispatch_sync(_server_digest_queue(), ^{
1762 if (reach_digest_map
!= NULL
) {
1763 CFDictionaryApplyFunction(reach_digest_map
,
1775 xpc_dictionary_set_int64(reply
,
1776 REACH_REQUEST_REPLY
,
1777 ok
? REACH_REQUEST_REPLY_OK
: REACH_REQUEST_REPLY_FAILED
);
1778 // log_xpc_object(" reply", reply);
1779 xpc_connection_send_message(remote
, reply
);
1786 static __inline__
void
1787 _extract_client_info(reach_client_t
*client
, xpc_object_t request
)
1789 // if available/needed, save the process name
1790 if (client
->proc_name
== NULL
) {
1791 const char *proc_name
;
1793 proc_name
= xpc_dictionary_get_string(request
, REACH_CLIENT_PROC_NAME
);
1794 if (proc_name
!= NULL
) {
1795 client
->proc_name
= strdup(proc_name
);
1804 process_request(reach_client_t
*client
, xpc_object_t request
)
1808 op
= xpc_dictionary_get_int64(request
, REACH_REQUEST
);
1810 case REACH_REQUEST_CREATE
:
1811 _extract_client_info(client
, request
);
1812 target_add(client
, request
);
1814 case REACH_REQUEST_REMOVE
:
1815 target_remove(client
, request
);
1817 case REACH_REQUEST_STATUS
:
1818 target_status(client
, request
);
1820 case REACH_REQUEST_SCHEDULE
:
1821 target_schedule(client
, request
);
1823 case REACH_REQUEST_UNSCHEDULE
:
1824 target_unschedule(client
, request
);
1826 case REACH_REQUEST_SNAPSHOT
:
1827 _extract_client_info(client
, request
);
1828 _snapshot(client
, request
);
1831 SCLog(TRUE
, LOG_ERR
,
1832 CFSTR("<%p> unknown request : %d"),
1843 process_new_connection(xpc_connection_t connection
)
1846 SCLog(TRUE
, LOG_INFO
, CFSTR("<%p> new reach client, pid=%d"),
1848 xpc_connection_get_pid(connection
));
1851 dispatch_sync(_reach_connection_queue(), ^{
1852 reach_client_t
*client
;
1854 client
= _reach_client_create(connection
);
1855 if (client
== NULL
|| !rb_tree_insert_node(_reach_clients_rbt(), &client
->rbn
)) {
1860 xpc_connection_set_event_handler(connection
, ^(xpc_object_t xobj
) {
1863 type
= xpc_get_type(xobj
);
1864 if (type
== XPC_TYPE_DICTIONARY
) {
1865 dispatch_sync(_reach_connection_queue(), ^{
1866 struct rb_node
*rbn
;
1868 rbn
= rb_tree_find_node(_reach_clients_rbt(), &connection
);
1870 reach_client_t
*client
;
1872 // process the request
1873 client
= RBNODE_TO_REACH_CLIENT(rbn
);
1874 process_request(client
, xobj
);
1878 SCLog(TRUE
, LOG_ERR
,
1879 CFSTR("<%p:%d> unexpected SCNetworkReachability request"),
1881 xpc_connection_get_pid(connection
));
1883 desc
= xpc_copy_description(xobj
);
1884 SCLog(TRUE
, LOG_ERR
,
1885 CFSTR(" request = %s"),
1889 xpc_connection_cancel(connection
);
1893 } else if (type
== XPC_TYPE_ERROR
) {
1896 desc
= xpc_dictionary_get_string(xobj
, XPC_ERROR_KEY_DESCRIPTION
);
1897 if (xobj
== XPC_ERROR_CONNECTION_INVALID
) {
1899 SCLog(TRUE
, LOG_INFO
,
1900 CFSTR("<%p:%d> %s"),
1902 xpc_connection_get_pid(connection
),
1906 xpc_retain(connection
);
1907 dispatch_async(_reach_connection_queue(), ^{
1908 _reach_client_remove(connection
);
1909 xpc_release(connection
);
1912 } else if (xobj
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
1913 SCLog(TRUE
, LOG_ERR
,
1914 CFSTR("<%p:%d> %s"),
1916 xpc_connection_get_pid(connection
),
1920 SCLog(TRUE
, LOG_ERR
,
1921 CFSTR("<%p:%d> Connection error: %d : %s"),
1923 xpc_connection_get_pid(connection
),
1929 SCLog(TRUE
, LOG_ERR
,
1930 CFSTR("<%p:%d> unknown event type : %x"),
1932 xpc_connection_get_pid(connection
),
1937 xpc_connection_resume(connection
);
1944 #pragma mark Reachability server "main"
1949 load_SCNetworkReachability(CFBundleRef bundle
, Boolean bundleVerbose
)
1951 xpc_connection_t connection
;
1953 dispatch_queue_t reach_server_q
;
1955 S_debug
= bundleVerbose
;
1958 * create a dictionary mapping SCNetworkReachability [CFData] digests
1959 * to SCNetworkReachability objects.
1961 reach_digest_map
= CFDictionaryCreateMutable(NULL
,
1963 &kCFTypeDictionaryKeyCallBacks
,
1964 &kCFTypeDictionaryValueCallBacks
);
1967 * create dispatch queue for processing SCNetworkReachability
1970 reach_server_q
= dispatch_queue_create(REACH_SERVICE_NAME
, NULL
);
1972 // create XPC listener
1973 name
= getenv("REACH_SERVER");
1975 name
= REACH_SERVICE_NAME
;
1977 connection
= xpc_connection_create_mach_service(name
,
1979 XPC_CONNECTION_MACH_SERVICE_LISTENER
);
1981 xpc_connection_set_event_handler(connection
, ^(xpc_object_t event
) {
1984 type
= xpc_get_type(event
);
1985 if (type
== XPC_TYPE_CONNECTION
) {
1986 process_new_connection(event
);
1988 } else if (type
== XPC_TYPE_ERROR
) {
1991 desc
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
1992 if (event
== XPC_ERROR_CONNECTION_INVALID
) {
1993 SCLog(TRUE
, LOG_ERR
, CFSTR("reach server: %s"), desc
);
1994 xpc_release(connection
);
1995 } else if (event
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
1996 SCLog(TRUE
, LOG_ERR
, CFSTR("reach server: %s"), desc
);
1998 SCLog(TRUE
, LOG_ERR
,
1999 CFSTR("reach server: Connection error: %d : %s"),
2005 SCLog(TRUE
, LOG_ERR
,
2006 CFSTR("reach server: unknown event type : %x"),
2010 xpc_connection_resume(connection
);
2018 main(int argc
, char **argv
)
2021 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
2024 load_SCNetworkReachability(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);
2033 #endif // HAVE_REACHABILITY_SERVER