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
;
915 bool resolverBypass
= FALSE
;
919 unsigned char bytes
[CC_SHA1_DIGEST_LENGTH
];
921 CFDataRef digest
= NULL
;
923 xpc_connection_t remote
;
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
, REACH_TARGET_NAME
, sizeof(REACH_TARGET_NAME
));
962 CC_SHA1_Update(&ctx
, name
, strlen(name
));
965 serv
= xpc_dictionary_get_string(request
, REACH_TARGET_SERV
);
967 CC_SHA1_Update(&ctx
, REACH_TARGET_SERV
, sizeof(REACH_TARGET_SERV
));
968 CC_SHA1_Update(&ctx
, serv
, strlen(serv
));
971 localAddress
= xpc_dictionary_get_data(request
, REACH_TARGET_LOCAL_ADDR
, &len
);
972 if (localAddress
!= NULL
) {
973 if ((len
== localAddress
->sa_len
) && (len
<= sizeof(struct sockaddr_storage
))) {
974 sanitize_address(localAddress
, (struct sockaddr
*)&localAddress0
);
975 CC_SHA1_Update(&ctx
, REACH_TARGET_LOCAL_ADDR
, sizeof(REACH_TARGET_LOCAL_ADDR
));
976 CC_SHA1_Update(&ctx
, &localAddress0
, len
);
978 xpc_dictionary_set_string(reply
,
979 REACH_REQUEST_REPLY_DETAIL
,
980 "local address: size error");
985 remoteAddress
= xpc_dictionary_get_data(request
, REACH_TARGET_REMOTE_ADDR
, &len
);
986 if (remoteAddress
!= NULL
) {
987 if ((len
== remoteAddress
->sa_len
) && (len
<= sizeof(struct sockaddr_storage
))) {
988 sanitize_address(remoteAddress
, (struct sockaddr
*)&remoteAddress0
);
989 CC_SHA1_Update(&ctx
, REACH_TARGET_REMOTE_ADDR
, sizeof(REACH_TARGET_REMOTE_ADDR
));
990 CC_SHA1_Update(&ctx
, &remoteAddress0
, len
);
992 xpc_dictionary_set_string(reply
,
993 REACH_REQUEST_REPLY_DETAIL
,
994 "remote address: size error");
999 hints
= xpc_dictionary_get_data(request
, REACH_TARGET_HINTS
, &len
);
1000 if (hints
!= NULL
) {
1001 if (len
== sizeof(struct addrinfo
)) {
1002 CC_SHA1_Update(&ctx
, REACH_TARGET_HINTS
, sizeof(REACH_TARGET_HINTS
));
1003 CC_SHA1_Update(&ctx
, hints
, len
);
1005 xpc_dictionary_set_string(reply
,
1006 REACH_REQUEST_REPLY_DETAIL
,
1007 "hints: size error");
1012 if_index
= xpc_dictionary_get_int64(request
, REACH_TARGET_IF_INDEX
);
1013 if (if_index
!= 0) {
1014 if_name
= xpc_dictionary_get_string(request
, REACH_TARGET_IF_NAME
);
1015 if (if_name
!= NULL
) {
1016 CC_SHA1_Update(&ctx
, REACH_TARGET_IF_NAME
, sizeof(REACH_TARGET_IF_NAME
));
1017 CC_SHA1_Update(&ctx
, if_name
, strlen(if_name
));
1021 onDemandBypass
= xpc_dictionary_get_bool(request
, REACH_TARGET_ONDEMAND_BYPASS
);
1022 if (onDemandBypass
) {
1023 CC_SHA1_Update(&ctx
, REACH_TARGET_ONDEMAND_BYPASS
, sizeof(REACH_TARGET_ONDEMAND_BYPASS
));
1024 CC_SHA1_Update(&ctx
, &onDemandBypass
, sizeof(onDemandBypass
));
1027 resolverBypass
= xpc_dictionary_get_bool(request
, REACH_TARGET_RESOLVER_BYPASS
);
1028 if (resolverBypass
) {
1029 CC_SHA1_Update(&ctx
, REACH_TARGET_RESOLVER_BYPASS
, sizeof(REACH_TARGET_RESOLVER_BYPASS
));
1030 CC_SHA1_Update(&ctx
, &resolverBypass
, sizeof(resolverBypass
));
1034 CC_SHA1_Final(bytes
, &ctx
);
1035 digest
= CFDataCreate(NULL
, bytes
, sizeof(bytes
));
1038 * Check to see if we already have a SCNetworkReachability object
1039 * for this digest. If so, we'll share the existing target. If not,
1040 * create a new [shared] target.
1042 dispatch_sync(_server_digest_queue(), ^{
1043 target
= CFDictionaryGetValue(reach_digest_map
, digest
);
1044 if (target
!= NULL
) {
1048 CFMutableDictionaryRef options
;
1051 options
= CFDictionaryCreateMutable(NULL
,
1053 &kCFTypeDictionaryKeyCallBacks
,
1054 &kCFTypeDictionaryValueCallBacks
);
1056 str
= CFStringCreateWithCString(NULL
, name
, kCFStringEncodingUTF8
);
1057 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, str
);
1061 str
= CFStringCreateWithCString(NULL
, serv
, kCFStringEncodingUTF8
);
1062 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionServName
, str
);
1065 if (localAddress
!= NULL
) {
1066 data
= CFDataCreate(NULL
, (const UInt8
*)&localAddress0
, localAddress0
.ss_len
);
1067 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionLocalAddress
, data
);
1070 if (remoteAddress
!= NULL
) {
1071 data
= CFDataCreate(NULL
, (const UInt8
*)&remoteAddress0
, remoteAddress0
.ss_len
);
1072 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
, data
);
1075 if (hints
!= NULL
) {
1076 data
= CFDataCreate(NULL
, (const UInt8
*)hints
, sizeof(struct addrinfo
));
1077 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionHints
, data
);
1080 if (onDemandBypass
) {
1081 CFDictionarySetValue(options
,
1082 kSCNetworkReachabilityOptionConnectionOnDemandBypass
,
1085 if (resolverBypass
) {
1086 CFDictionarySetValue(options
,
1087 kSCNetworkReachabilityOptionResolverBypass
,
1090 CFDictionarySetValue(options
,
1091 kSCNetworkReachabilityOptionServerBypass
,
1093 target
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
1095 if (target
== NULL
) {
1096 xpc_dictionary_set_string(reply
,
1097 REACH_REQUEST_REPLY_DETAIL
,
1098 "SCNetworkReachabilityCreateWithOptions failed");
1103 // because the interface name may not (no longer) be valid we set
1104 // this after we've created the SCNetworkReachabilty object
1105 if ((if_index
!= 0) && (if_name
!= NULL
)) {
1106 SCNetworkReachabilityPrivateRef targetPrivate
;
1108 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1109 targetPrivate
->if_index
= if_index
;
1110 strlcpy(targetPrivate
->if_name
, if_name
, sizeof(targetPrivate
->if_name
));
1114 ok
= SCNetworkReachabilitySetCallback(target
, _reach_changed
, NULL
);
1116 xpc_dictionary_set_string(reply
,
1117 REACH_REQUEST_REPLY_DETAIL
,
1118 "SCNetworkReachabilitySetCallback failed");
1125 // bump the number of references to this target
1126 _target_reference_add(target
, digest
, client
->connection
);
1134 * add an association for the client's target_id to the [shared]
1135 * SCNetworkReachability object.
1137 added
= _client_target_set(client
, target_id
, target
);
1139 // if we already had a reference to the target (e.g. reconnect)
1140 dispatch_sync(_server_digest_queue(), ^{
1141 _target_reference_remove(target
, client
->connection
);
1145 status
= REACH_REQUEST_REPLY_OK
;
1149 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1150 // log_xpc_object(" reply", reply);
1151 xpc_connection_send_message(remote
, reply
);
1154 if (digest
!= NULL
) CFRelease(digest
);
1155 if (target
!= NULL
) CFRelease(target
);
1161 target_remove(reach_client_t
*client
, xpc_object_t request
)
1163 xpc_connection_t remote
;
1165 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1166 SCNetworkReachabilityRef target
= NULL
;
1170 SCLog(TRUE
, LOG_INFO
,
1171 CFSTR("<%p> remove reachability target"),
1172 client
->connection
);
1173 // log_xpc_object(" remove", request);
1176 remote
= xpc_dictionary_get_remote_connection(request
);
1177 reply
= xpc_dictionary_create_reply(request
);
1178 if (reply
== NULL
) {
1179 SCLog(TRUE
, LOG_ERR
,
1180 CFSTR("<%p> target_remove: xpc_dictionary_create_reply: failed"),
1181 client
->connection
);
1185 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
1186 if (target_id
== 0) {
1187 xpc_dictionary_set_string(reply
,
1188 REACH_REQUEST_REPLY_DETAIL
,
1193 target
= _client_target_copy(client
, target_id
);
1194 if (target
== NULL
) {
1195 xpc_dictionary_set_string(reply
,
1196 REACH_REQUEST_REPLY_DETAIL
,
1198 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1203 * remove the association from the client's target_id to the [shared]
1204 * SCNetworkReachability object.
1206 _client_target_remove(client
, target_id
);
1208 // drop the number of references to this target
1209 dispatch_sync(_server_digest_queue(), ^{
1210 _target_reference_remove(target
, client
->connection
);
1213 status
= REACH_REQUEST_REPLY_OK
;
1217 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1218 // log_xpc_object(" reply", reply);
1219 xpc_connection_send_message(remote
, reply
);
1222 if (target
!= NULL
) CFRelease(target
);
1228 target_schedule(reach_client_t
*client
, xpc_object_t request
)
1231 xpc_connection_t remote
;
1233 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1234 SCNetworkReachabilityRef target
= NULL
;
1238 SCLog(TRUE
, LOG_INFO
,
1239 CFSTR("<%p> schedule reachability target"),
1240 client
->connection
);
1241 // log_xpc_object(" schedule", request);
1244 remote
= xpc_dictionary_get_remote_connection(request
);
1245 reply
= xpc_dictionary_create_reply(request
);
1246 if (reply
== NULL
) {
1247 SCLog(TRUE
, LOG_ERR
,
1248 CFSTR("<%p> target_schedule: xpc_dictionary_create_reply: failed"),
1249 client
->connection
);
1253 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
1254 if (target_id
== 0) {
1255 xpc_dictionary_set_string(reply
,
1256 REACH_REQUEST_REPLY_DETAIL
,
1261 target
= _client_target_copy(client
, target_id
);
1262 if (target
== NULL
) {
1263 xpc_dictionary_set_string(reply
,
1264 REACH_REQUEST_REPLY_DETAIL
,
1266 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1270 // enable monitoring
1271 ok
= _target_watcher_add(target
, client
->connection
, target_id
);
1273 status
= REACH_REQUEST_REPLY_OK
;
1278 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1279 // log_xpc_object(" reply", reply);
1280 xpc_connection_send_message(remote
, reply
);
1283 if (target
!= NULL
) CFRelease(target
);
1289 target_status(reach_client_t
*client
, xpc_object_t request
)
1291 xpc_connection_t remote
;
1293 __block Boolean reply_now
= TRUE
;
1295 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1296 SCNetworkReachabilityRef target
= NULL
;
1300 SCLog(TRUE
, LOG_INFO
,
1301 CFSTR("<%p> get status of reachability target"),
1302 client
->connection
);
1303 // log_xpc_object(" status", request);
1306 remote
= xpc_dictionary_get_remote_connection(request
);
1307 reply
= xpc_dictionary_create_reply(request
);
1308 if (reply
== NULL
) {
1309 SCLog(TRUE
, LOG_ERR
,
1310 CFSTR("<%p> target_status: xpc_dictionary_create_reply: failed"),
1311 client
->connection
);
1315 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
1316 if (target_id
== 0) {
1317 SCLog(TRUE
, LOG_ERR
,
1318 CFSTR("<%p> target_status: no target"),
1319 client
->connection
);
1320 xpc_dictionary_set_string(reply
,
1321 REACH_REQUEST_REPLY_DETAIL
,
1326 target
= _client_target_copy(client
, target_id
);
1327 if (target
== NULL
) {
1328 SCLog(TRUE
, LOG_ERR
,
1329 CFSTR("<%p> target_status: no target (0x%0llx)"),
1332 xpc_dictionary_set_string(reply
,
1333 REACH_REQUEST_REPLY_DETAIL
,
1335 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1340 * Check to see if the target [for this client] had been "scheduled".
1342 * If so, also mark that we've picked up the current reachability
1343 * flags and that any pending notifications have been processed.
1345 scheduled
= _target_watcher_checkin(target
, client
->connection
, target_id
);
1348 * return current reachability information to the caller
1350 dispatch_sync(_target_queue(target
), ^{
1351 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1355 * The client "scheduled" this target. As such, we
1356 * know that this an async query and that we only
1357 * need to return the "last known" flags.
1359 _target_reply_add_reachability(target
, reply
);
1360 // log_xpc_object(" reply [scheduled]", reply);
1365 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1366 SCLog(TRUE
, LOG_INFO
,
1367 CFSTR("<%p> reply [scheduled], %@"),
1374 * The client has NOT "scheduled" this target. As
1375 * such, we know that this is a sync query and that
1376 * must return "current" flags.
1378 if (targetPrivate
->scheduled
&& targetPrivate
->serverInfoValid
) {
1380 * The server target has been "scheduled" and we
1381 * have flags that are "current".
1383 _target_reply_add_reachability(target
, reply
);
1384 // log_xpc_object(" reply [scheduled/valid]", reply);
1389 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1390 SCLog(TRUE
, LOG_INFO
,
1391 CFSTR("<%p> reply [scheduled/valid], %@"),
1397 dispatch_group_t group
;
1400 * The server target has NOT been "scheduled" (or
1401 * we do not have "current" flags. This means that
1402 * we must query for the current information and
1403 * return the flags to the client when they are
1409 group
= _target_group(target
);
1410 if (_SC_ATOMIC_INC(&targetPrivate
->serverQueryActive
) == 0) {
1412 dispatch_group_async(group
, _server_concurrent_queue(), ^{
1413 SCNetworkReachabilityFlags flags
;
1417 // query for the flags
1418 ok
= SCNetworkReachabilityGetFlags(target
, &flags
);
1419 flags
= targetPrivate
->info
.flags
; // get the "raw" flags
1421 SCLog(TRUE
, LOG_ERR
,
1422 CFSTR("SCNetworkReachabilityGetFlags() [sync query] failed"
1426 SCErrorString(SCError()));
1429 // flags are now available
1430 n
= _SC_ATOMIC_ZERO(&targetPrivate
->serverQueryActive
);
1432 SCLog(TRUE
, LOG_INFO
,
1433 CFSTR("%sSCNetworkReachabilityGetFlags() [sync query] complete, n = %d"),
1434 targetPrivate
->log_prefix
,
1443 dispatch_group_notify(group
, _target_queue(target
), ^{
1444 // flags are now available
1445 _target_reply_add_reachability(target
, reply
);
1446 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, REACH_REQUEST_REPLY_OK
);
1447 // log_xpc_object(" reply [delayed]", reply);
1452 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1453 SCLog(TRUE
, LOG_INFO
,
1454 CFSTR("<%p> reply [delayed], %@"),
1460 xpc_connection_send_message(remote
, reply
);
1469 status
= REACH_REQUEST_REPLY_OK
;
1474 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1476 if (status
!= REACH_REQUEST_REPLY_OK
) {
1477 // log_xpc_object(" reply [!]", reply);
1480 SCLog(TRUE
, LOG_INFO
,
1481 CFSTR("<%p> reply [!]"),
1482 client
->connection
);
1486 xpc_connection_send_message(remote
, reply
);
1488 } else if (S_debug
) {
1491 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1492 SCLog(TRUE
, LOG_INFO
,
1493 CFSTR("<%p> no reply [yet], %@"),
1499 if (target
!= NULL
) CFRelease(target
);
1505 target_unschedule(reach_client_t
*client
, xpc_object_t request
)
1508 xpc_connection_t remote
;
1510 uint64_t status
= REACH_REQUEST_REPLY_FAILED
;
1511 SCNetworkReachabilityRef target
= NULL
;
1515 SCLog(TRUE
, LOG_INFO
,
1516 CFSTR("<%p> unschedule reachability target"),
1517 client
->connection
);
1518 // log_xpc_object(" unschedule", request);
1521 remote
= xpc_dictionary_get_remote_connection(request
);
1522 reply
= xpc_dictionary_create_reply(request
);
1523 if (reply
== NULL
) {
1524 SCLog(TRUE
, LOG_ERR
,
1525 CFSTR("<%p> target_unschedule: xpc_dictionary_create_reply: failed"),
1526 client
->connection
);
1530 target_id
= xpc_dictionary_get_uint64(request
, REACH_CLIENT_TARGET_ID
);
1531 if (target_id
== 0) {
1532 xpc_dictionary_set_string(reply
,
1533 REACH_REQUEST_REPLY_DETAIL
,
1538 target
= _client_target_copy(client
, target_id
);
1539 if (target
== NULL
) {
1540 xpc_dictionary_set_string(reply
,
1541 REACH_REQUEST_REPLY_DETAIL
,
1543 status
= REACH_REQUEST_REPLY_UNKNOWN
;
1547 // disable monitoring
1548 ok
= _target_watcher_remove(target
, client
->connection
, target_id
);
1550 status
= REACH_REQUEST_REPLY_OK
;
1555 xpc_dictionary_set_int64(reply
, REACH_REQUEST_REPLY
, status
);
1556 // log_xpc_object(" reply", reply);
1557 xpc_connection_send_message(remote
, reply
);
1560 if (target
!= NULL
) CFRelease(target
);
1565 #define SNAPSHOT_PATH_STATE _PATH_VARTMP "configd-reachability"
1569 _snapshot_digest_watcher(const void *key
, const void *value
, void *context
)
1571 FILE *f
= (FILE *)context
;
1572 static reach_client_t no_client
= {
1576 struct rb_node
*rbn
;
1577 reach_client_t
*rbt_client
;
1578 reach_watcher_key_t
*watcher_key
;
1579 reach_watcher_val_t
*watcher_val
;
1581 /* ALIGN: CF aligns to >8 byte boundries */
1582 watcher_key
= (reach_watcher_key_t
*)(void *)CFDataGetBytePtr(key
);
1583 watcher_val
= (reach_watcher_val_t
*)(void *)CFDataGetBytePtr(value
);
1585 rbn
= rb_tree_find_node(_reach_clients_rbt(), &watcher_key
->connection
);
1587 rbn
= &no_client
.rbn
;
1590 rbt_client
= RBNODE_TO_REACH_CLIENT(rbn
);
1593 CFSTR(" connection = %p, target(c) = 0x%0llx, command = %s, pid = %d, changes = %u\n"),
1594 watcher_key
->connection
,
1595 watcher_key
->target_id
,
1596 rbt_client
->proc_name
,
1598 watcher_val
->n_changes
);
1605 _snapshot_digest(const void *key
, const void *value
, void *context
)
1607 FILE *f
= (FILE *)context
;
1608 CFStringRef digest
= (CFStringRef
)key
;
1610 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
1611 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1613 q
= _target_queue(target
);
1615 SCPrint(TRUE
, f
, CFSTR("\n digest : %@\n"), digest
);
1616 SCPrint(TRUE
, f
, CFSTR(" %@\n"), target
);
1617 SCPrint(TRUE
, f
, CFSTR(" valid = %s, active = %u, refs = %u\n"),
1618 targetPrivate
->serverInfoValid
? "Y" : "N",
1619 targetPrivate
->serverQueryActive
,
1620 targetPrivate
->serverReferences
);
1622 SCPrint(TRUE
, f
, CFSTR(" network %d.%3.3d"),
1623 targetPrivate
->last_network
.tv_sec
,
1624 targetPrivate
->last_network
.tv_usec
/ 1000);
1625 #if !TARGET_OS_IPHONE
1626 SCPrint(TRUE
, f
, CFSTR(", power %d.%3.3d"),
1627 targetPrivate
->last_power
.tv_sec
,
1628 targetPrivate
->last_power
.tv_usec
/ 1000);
1629 #endif // !TARGET_OS_IPHONE
1630 if (targetPrivate
->type
== reachabilityTypeName
) {
1631 SCPrint(TRUE
, f
, CFSTR(", DNS %d.%3.3d"),
1632 targetPrivate
->last_dns
.tv_sec
,
1633 targetPrivate
->last_dns
.tv_usec
/ 1000);
1634 if (timerisset(&targetPrivate
->dnsQueryEnd
)) {
1635 struct timeval dnsQueryElapsed
;
1637 timersub(&targetPrivate
->dnsQueryEnd
,
1638 &targetPrivate
->dnsQueryStart
,
1640 SCPrint(TRUE
, f
, CFSTR(" (query %d.%3.3d / reply %d.%3.3d)"),
1641 targetPrivate
->dnsQueryStart
.tv_sec
,
1642 targetPrivate
->dnsQueryStart
.tv_usec
/ 1000,
1643 dnsQueryElapsed
.tv_sec
,
1644 dnsQueryElapsed
.tv_usec
/ 1000);
1647 if (timerisset(&targetPrivate
->last_push
)) {
1648 SCPrint(TRUE
, f
, CFSTR(", last notify %d.%3.3d"),
1649 targetPrivate
->last_push
.tv_sec
,
1650 targetPrivate
->last_push
.tv_usec
/ 1000);
1652 SCPrint(TRUE
, f
, CFSTR("\n"));
1654 if (targetPrivate
->serverWatchers
!= NULL
) {
1655 CFDictionaryApplyFunction(targetPrivate
->serverWatchers
,
1656 _snapshot_digest_watcher
,
1666 _snapshot_target(const void *key
, const void *value
, void *context
)
1668 FILE *f
= (FILE *)context
;
1669 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
1671 CFDataRef target_key
= (CFDataRef
)key
;
1673 /* ALIGN: CF aligns > 8 byte boundries */
1674 target_id
= *(uint64_t *)(void *)CFDataGetBytePtr(target_key
);
1677 CFSTR(" target(c) = 0x%0llx, target(s) = %@\n"),
1686 _snapshot(reach_client_t
*client
, xpc_object_t request
)
1692 struct rb_node
*rbn
;
1693 struct rb_tree
*rbt
;
1694 xpc_connection_t remote
;
1698 SCLog(TRUE
, LOG_INFO
,
1699 CFSTR("<%p> snapshot"),
1700 client
->connection
);
1701 // log_xpc_object(" create", request);
1704 remote
= xpc_dictionary_get_remote_connection(request
);
1705 reply
= xpc_dictionary_create_reply(request
);
1706 if (reply
== NULL
) {
1707 SCLog(TRUE
, LOG_ERR
,
1708 CFSTR("<%p> _snapshot: xpc_dictionary_create_reply: failed"),
1709 client
->connection
);
1713 euid
= xpc_connection_get_euid(remote
);
1715 xpc_dictionary_set_string(reply
,
1716 REACH_REQUEST_REPLY_DETAIL
,
1717 "Permission denied.");
1721 // Save a snapshot of the SCNetworkReachability server "state"
1723 (void) unlink(SNAPSHOT_PATH_STATE
);
1724 fd
= open(SNAPSHOT_PATH_STATE
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_EXCL
, 0644);
1726 xpc_dictionary_set_string(reply
,
1727 REACH_REQUEST_REPLY_DETAIL
,
1731 f
= fdopen(fd
, "w");
1733 xpc_dictionary_set_string(reply
,
1734 REACH_REQUEST_REPLY_DETAIL
,
1739 // provide connection/client info
1741 SCPrint(TRUE
, f
, CFSTR("Clients :\n"));
1742 rbt
= _reach_clients_rbt();
1743 rbn
= rb_tree_iterate(rbt
, NULL
, RB_DIR_RIGHT
);
1745 while (rbn
!= NULL
) {
1746 reach_client_t
*rbt_client
;
1748 rbt_client
= RBNODE_TO_REACH_CLIENT(rbn
);
1750 CFSTR("\n connection = %p, client = %p, command = %s, pid = %d\n"),
1751 rbt_client
->connection
,
1753 rbt_client
->proc_name
!= NULL
? rbt_client
->proc_name
: "?",
1755 my_CFDictionaryApplyFunction(rbt_client
->targets
,
1759 rbn
= rb_tree_iterate(rbt
, rbn
, RB_DIR_LEFT
);
1762 SCPrint(TRUE
, f
, CFSTR(" None.\n"));
1764 SCPrint(TRUE
, f
, CFSTR("\n"));
1766 // provide "digest" info
1768 SCPrint(TRUE
, f
, CFSTR("Digests :\n"));
1769 dispatch_sync(_server_digest_queue(), ^{
1770 if (reach_digest_map
!= NULL
) {
1771 CFDictionaryApplyFunction(reach_digest_map
,
1783 xpc_dictionary_set_int64(reply
,
1784 REACH_REQUEST_REPLY
,
1785 ok
? REACH_REQUEST_REPLY_OK
: REACH_REQUEST_REPLY_FAILED
);
1786 // log_xpc_object(" reply", reply);
1787 xpc_connection_send_message(remote
, reply
);
1794 static __inline__
void
1795 _extract_client_info(reach_client_t
*client
, xpc_object_t request
)
1797 // if available/needed, save the process name
1798 if (client
->proc_name
== NULL
) {
1799 const char *proc_name
;
1801 proc_name
= xpc_dictionary_get_string(request
, REACH_CLIENT_PROC_NAME
);
1802 if (proc_name
!= NULL
) {
1803 client
->proc_name
= strdup(proc_name
);
1812 process_request(reach_client_t
*client
, xpc_object_t request
)
1816 op
= xpc_dictionary_get_int64(request
, REACH_REQUEST
);
1818 case REACH_REQUEST_CREATE
:
1819 _extract_client_info(client
, request
);
1820 target_add(client
, request
);
1822 case REACH_REQUEST_REMOVE
:
1823 target_remove(client
, request
);
1825 case REACH_REQUEST_STATUS
:
1826 target_status(client
, request
);
1828 case REACH_REQUEST_SCHEDULE
:
1829 target_schedule(client
, request
);
1831 case REACH_REQUEST_UNSCHEDULE
:
1832 target_unschedule(client
, request
);
1834 case REACH_REQUEST_SNAPSHOT
:
1835 _extract_client_info(client
, request
);
1836 _snapshot(client
, request
);
1839 SCLog(TRUE
, LOG_ERR
,
1840 CFSTR("<%p> unknown request : %d"),
1851 process_new_connection(xpc_connection_t connection
)
1854 SCLog(TRUE
, LOG_INFO
, CFSTR("<%p> new reach client, pid=%d"),
1856 xpc_connection_get_pid(connection
));
1859 dispatch_sync(_reach_connection_queue(), ^{
1860 reach_client_t
*client
;
1862 client
= _reach_client_create(connection
);
1863 if (client
== NULL
|| !rb_tree_insert_node(_reach_clients_rbt(), &client
->rbn
)) {
1868 xpc_connection_set_event_handler(connection
, ^(xpc_object_t xobj
) {
1871 type
= xpc_get_type(xobj
);
1872 if (type
== XPC_TYPE_DICTIONARY
) {
1873 dispatch_sync(_reach_connection_queue(), ^{
1874 struct rb_node
*rbn
;
1876 rbn
= rb_tree_find_node(_reach_clients_rbt(), &connection
);
1878 reach_client_t
*client
;
1880 // process the request
1881 client
= RBNODE_TO_REACH_CLIENT(rbn
);
1882 process_request(client
, xobj
);
1886 SCLog(TRUE
, LOG_ERR
,
1887 CFSTR("<%p:%d> unexpected SCNetworkReachability request"),
1889 xpc_connection_get_pid(connection
));
1891 desc
= xpc_copy_description(xobj
);
1892 SCLog(TRUE
, LOG_ERR
,
1893 CFSTR(" request = %s"),
1897 xpc_connection_cancel(connection
);
1901 } else if (type
== XPC_TYPE_ERROR
) {
1904 desc
= xpc_dictionary_get_string(xobj
, XPC_ERROR_KEY_DESCRIPTION
);
1905 if (xobj
== XPC_ERROR_CONNECTION_INVALID
) {
1907 SCLog(TRUE
, LOG_INFO
,
1908 CFSTR("<%p:%d> %s"),
1910 xpc_connection_get_pid(connection
),
1914 xpc_retain(connection
);
1915 dispatch_async(_reach_connection_queue(), ^{
1916 _reach_client_remove(connection
);
1917 xpc_release(connection
);
1920 } else if (xobj
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
1921 SCLog(TRUE
, LOG_ERR
,
1922 CFSTR("<%p:%d> %s"),
1924 xpc_connection_get_pid(connection
),
1928 SCLog(TRUE
, LOG_ERR
,
1929 CFSTR("<%p:%d> Connection error: %d : %s"),
1931 xpc_connection_get_pid(connection
),
1937 SCLog(TRUE
, LOG_ERR
,
1938 CFSTR("<%p:%d> unknown event type : %x"),
1940 xpc_connection_get_pid(connection
),
1945 xpc_connection_resume(connection
);
1952 #pragma mark Reachability server "main"
1957 load_SCNetworkReachability(CFBundleRef bundle
, Boolean bundleVerbose
)
1959 xpc_connection_t connection
;
1961 dispatch_queue_t reach_server_q
;
1963 S_debug
= bundleVerbose
;
1966 * create a dictionary mapping SCNetworkReachability [CFData] digests
1967 * to SCNetworkReachability objects.
1969 reach_digest_map
= CFDictionaryCreateMutable(NULL
,
1971 &kCFTypeDictionaryKeyCallBacks
,
1972 &kCFTypeDictionaryValueCallBacks
);
1975 * create dispatch queue for processing SCNetworkReachability
1978 reach_server_q
= dispatch_queue_create(REACH_SERVICE_NAME
, NULL
);
1980 // create XPC listener
1981 name
= getenv("REACH_SERVER");
1983 name
= REACH_SERVICE_NAME
;
1985 connection
= xpc_connection_create_mach_service(name
,
1987 XPC_CONNECTION_MACH_SERVICE_LISTENER
);
1989 xpc_connection_set_event_handler(connection
, ^(xpc_object_t event
) {
1992 type
= xpc_get_type(event
);
1993 if (type
== XPC_TYPE_CONNECTION
) {
1994 process_new_connection(event
);
1996 } else if (type
== XPC_TYPE_ERROR
) {
1999 desc
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
2000 if (event
== XPC_ERROR_CONNECTION_INVALID
) {
2001 SCLog(TRUE
, LOG_ERR
, CFSTR("reach server: %s"), desc
);
2002 xpc_release(connection
);
2003 } else if (event
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
2004 SCLog(TRUE
, LOG_ERR
, CFSTR("reach server: %s"), desc
);
2006 SCLog(TRUE
, LOG_ERR
,
2007 CFSTR("reach server: Connection error: %d : %s"),
2013 SCLog(TRUE
, LOG_ERR
,
2014 CFSTR("reach server: unknown event type : %x"),
2018 xpc_connection_resume(connection
);
2026 main(int argc
, char **argv
)
2029 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
2032 load_SCNetworkReachability(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);
2041 #endif // HAVE_REACHABILITY_SERVER