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
32 #include <xpc/private.h>
41 static const struct addrinfo hints0
= {
43 .ai_flags
= AI_PARALLEL
| AI_ADDRCONFIG
45 .ai_flags
= AI_ADDRCONFIG
50 static Boolean serverAvailable
= TRUE
;
54 #pragma mark Support functions
58 log_xpc_object(const char *msg
, xpc_object_t obj
)
62 desc
= xpc_copy_description(obj
);
63 SCLog(TRUE
, LOG_DEBUG
, CFSTR("%s = %s"), msg
, desc
);
69 #pragma mark Reachability [RBT] client support
74 SCNetworkReachabilityRef target
;
78 #define RBNODE_TO_REACH_REQUEST(node) \
79 ((reach_request_t *)((uintptr_t)node - offsetof(reach_request_t, rbn)))
83 _rbt_compare_transaction_nodes(const struct rb_node
*n1
, const struct rb_node
*n2
)
85 uint64_t a
= (uintptr_t)(RBNODE_TO_REACH_REQUEST(n1
)->target
);
86 uint64_t b
= (uintptr_t)(RBNODE_TO_REACH_REQUEST(n2
)->target
);
93 _rbt_compare_transaction_key(const struct rb_node
*n1
, const void *key
)
95 uint64_t a
= (uintptr_t)(RBNODE_TO_REACH_REQUEST(n1
)->target
);
96 uint64_t b
= *(uint64_t *)key
;
102 static struct rb_tree
*
103 _reach_requests_rbt()
105 static dispatch_once_t once
;
106 static const struct rb_tree_ops ops
= {
107 .rbto_compare_nodes
= _rbt_compare_transaction_nodes
,
108 .rbto_compare_key
= _rbt_compare_transaction_key
,
110 static struct rb_tree rbtree
;
112 dispatch_once(&once
, ^{
113 rb_tree_init(&rbtree
, &ops
);
120 static dispatch_queue_t
121 _reach_requests_rbt_queue()
123 static dispatch_once_t once
;
124 static dispatch_queue_t q
;
126 dispatch_once(&once
, ^{
127 q
= dispatch_queue_create(REACH_SERVICE_NAME
".rbt", NULL
);
134 static reach_request_t
*
135 _reach_request_create(SCNetworkReachabilityRef target
)
137 reach_request_t
*request
;
139 request
= calloc(1, sizeof(*request
));
140 request
->target
= CFRetain(target
);
147 _reach_request_release(reach_request_t
*request
)
149 SCNetworkReachabilityRef target
= request
->target
;
159 _reach_request_add(SCNetworkReachabilityRef target
)
161 uint64_t target_id
= (uintptr_t)target
;
163 dispatch_sync(_reach_requests_rbt_queue(), ^{
166 rbn
= rb_tree_find_node(_reach_requests_rbt(), &target_id
);
168 reach_request_t
*request
;
170 request
= _reach_request_create(target
);
171 if (request
== NULL
|| !rb_tree_insert_node(_reach_requests_rbt(), &request
->rbn
)) {
182 _reach_request_remove(SCNetworkReachabilityRef target
)
184 uint64_t target_id
= (uintptr_t)target
;
186 dispatch_sync(_reach_requests_rbt_queue(), ^{ // FIXME ?? use dispatch_async?
188 struct rb_tree
*rbtree
= _reach_requests_rbt();
190 rbn
= rb_tree_find_node(rbtree
, &target_id
);
192 reach_request_t
*request
= RBNODE_TO_REACH_REQUEST(rbn
);
194 rb_tree_remove_node(rbtree
, rbn
);
195 _reach_request_release(request
);
201 static SCNetworkReachabilityRef
202 _reach_request_copy_target(uint64_t target_id
)
204 __block SCNetworkReachabilityRef target
= NULL
;
206 dispatch_sync(_reach_requests_rbt_queue(), ^{
209 rbn
= rb_tree_find_node(_reach_requests_rbt(), &target_id
);
211 // handle the [async] reply
212 target
= (SCNetworkReachabilityRef
)(uintptr_t)target_id
;
222 #pragma mark Reachability [XPC] client support
226 handle_reachability_status(SCNetworkReachabilityRef target
, xpc_object_t dict
)
228 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
231 SCLog(TRUE
, LOG_INFO
, CFSTR("%sgot [async] notification"),
232 targetPrivate
->log_prefix
);
233 // log_xpc_object(" status", dict);
236 __SCNetworkReachabilityPerformNoLock(target
);
243 handle_async_notification(SCNetworkReachabilityRef target
, xpc_object_t dict
)
246 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
248 op
= xpc_dictionary_get_int64(dict
, MESSAGE_NOTIFY
);
250 case MESSAGE_REACHABILITY_STATUS
:
251 handle_reachability_status(target
, dict
);
254 SCLog(TRUE
, LOG_ERR
, CFSTR("%sgot [async] unknown reply : %d"),
255 targetPrivate
->log_prefix
,
257 log_xpc_object(" reply", dict
);
265 static dispatch_queue_t
268 static dispatch_once_t once
;
269 static dispatch_queue_t q
;
271 dispatch_once(&once
, ^{
272 q
= dispatch_queue_create(REACH_SERVICE_NAME
".xpc", NULL
);
280 _reach_connection_reconnect(xpc_connection_t connection
);
283 static xpc_connection_t
284 _reach_connection_create()
288 dispatch_queue_t q
= _reach_xpc_queue();
290 // create XPC connection
291 name
= getenv("REACH_SERVER");
292 if ((name
== NULL
) || (issetugid() != 0)) {
293 name
= REACH_SERVICE_NAME
;
296 c
= xpc_connection_create_mach_service(name
,
298 XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
);
300 xpc_connection_set_event_handler(c
, ^(xpc_object_t xobj
) {
303 type
= xpc_get_type(xobj
);
304 if (type
== XPC_TYPE_DICTIONARY
) {
305 SCNetworkReachabilityRef target
;
308 target_id
= xpc_dictionary_get_uint64(xobj
, REACH_CLIENT_TARGET_ID
);
309 if (target_id
== 0) {
311 CFSTR("reach client %p: async reply with no target [ID]"),
313 log_xpc_object(" reply", xobj
);
317 target
= _reach_request_copy_target(target_id
);
318 if (target
== NULL
) {
320 CFSTR("received unexpected target [ID] from SCNetworkReachability server"));
321 log_xpc_object(" reply", xobj
);
325 handle_async_notification(target
, xobj
);
328 } else if (type
== XPC_TYPE_ERROR
) {
329 if (xobj
== XPC_ERROR_CONNECTION_INVALID
) {
331 CFSTR("SCNetworkReachability server not available"));
332 serverAvailable
= FALSE
;
333 } else if (xobj
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
334 SCLog(TRUE
, LOG_DEBUG
,
335 CFSTR("SCNetworkReachability server failure, reconnecting"));
336 _reach_connection_reconnect(c
);
340 desc
= xpc_dictionary_get_string(xobj
, XPC_ERROR_KEY_DESCRIPTION
);
342 CFSTR("reach client %p: Connection error: %s"),
349 CFSTR("reach client %p: unknown event type : %x"),
354 xpc_connection_resume(c
);
360 static xpc_connection_t
363 static xpc_connection_t c
;
364 static dispatch_once_t once
;
365 static dispatch_queue_t q
;
367 if (!serverAvailable
) {
368 // if SCNetworkReachabilty [XPC] server not available
372 dispatch_once(&once
, ^{
373 q
= dispatch_queue_create(REACH_SERVICE_NAME
".connection", NULL
);
378 c
= _reach_connection_create();
386 typedef void (^reach_server_reply_handler_t
)(xpc_object_t reply
);
390 add_proc_name(xpc_object_t reqdict
)
392 static const char *name
= NULL
;
393 static dispatch_once_t once
;
395 // add the process name
396 dispatch_once(&once
, ^{
397 name
= getprogname();
399 xpc_dictionary_set_string(reqdict
, REACH_CLIENT_PROC_NAME
, name
);
406 _reach_server_target_reconnect(xpc_connection_t connection
, SCNetworkReachabilityRef target
);
410 _reach_server_target_add(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
414 xpc_object_t reqdict
;
415 Boolean retry
= FALSE
;
416 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
419 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
422 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_CREATE
);
424 // add reachability target info
425 if (targetPrivate
->name
!= NULL
) {
426 xpc_dictionary_set_string(reqdict
,
428 targetPrivate
->name
);
430 if (targetPrivate
->serv
!= NULL
) {
431 xpc_dictionary_set_string(reqdict
,
433 targetPrivate
->serv
);
435 if (targetPrivate
->localAddress
!= NULL
) {
436 xpc_dictionary_set_data(reqdict
,
437 REACH_TARGET_LOCAL_ADDR
,
438 targetPrivate
->localAddress
,
439 targetPrivate
->localAddress
->sa_len
);
441 if (targetPrivate
->remoteAddress
!= NULL
) {
442 xpc_dictionary_set_data(reqdict
,
443 REACH_TARGET_REMOTE_ADDR
,
444 targetPrivate
->remoteAddress
,
445 targetPrivate
->remoteAddress
->sa_len
);
447 if (bcmp(&targetPrivate
->hints
, &hints0
, sizeof(struct addrinfo
)) != 0) {
448 xpc_dictionary_set_data(reqdict
,
450 &targetPrivate
->hints
,
451 sizeof(targetPrivate
->hints
));
453 if (targetPrivate
->if_index
!= 0) {
454 xpc_dictionary_set_int64(reqdict
,
455 REACH_TARGET_IF_INDEX
,
456 targetPrivate
->if_index
);
457 xpc_dictionary_set_string(reqdict
,
458 REACH_TARGET_IF_NAME
,
459 targetPrivate
->if_name
);
461 if (targetPrivate
->onDemandBypass
) {
462 xpc_dictionary_set_bool(reqdict
,
463 REACH_TARGET_ONDEMAND_BYPASS
,
466 if (targetPrivate
->resolverBypass
) {
467 xpc_dictionary_set_bool(reqdict
,
468 REACH_TARGET_RESOLVER_BYPASS
,
473 // add the target [ID]
474 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
476 // add the process name (for debugging)
477 add_proc_name(reqdict
);
481 // send request to the SCNetworkReachability server
482 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
486 type
= xpc_get_type(reply
);
487 if (type
== XPC_TYPE_DICTIONARY
) {
490 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
491 ok
= (status
== REACH_REQUEST_REPLY_OK
);
492 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
494 CFSTR("SCNetworkReachability server not available"));
495 serverAvailable
= FALSE
;
496 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
497 SCLog(TRUE
, LOG_DEBUG
,
498 CFSTR("reach target %p: SCNetworkReachability server failure, retrying"),
503 CFSTR("reach target %p: _targetAdd with unexpected reply"),
505 log_xpc_object(" reply", reply
);
516 xpc_release(reqdict
);
522 _reach_server_target_remove(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
526 xpc_object_t reqdict
;
527 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
530 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
533 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_REMOVE
);
535 // add the target [ID]
536 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
538 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
542 type
= xpc_get_type(reply
);
543 if (type
== XPC_TYPE_DICTIONARY
) {
546 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
548 case REACH_REQUEST_REPLY_OK
:
551 case REACH_REQUEST_REPLY_UNKNOWN
:
552 SCLog(TRUE
, LOG_DEBUG
,
553 CFSTR("reach target %p: SCNetworkReachability server failure, no need to remove"),
558 SCLog(TRUE
, LOG_ERR
, CFSTR("%s target remove failed"),
559 targetPrivate
->log_prefix
);
560 log_xpc_object(" reply", reply
);
563 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
565 CFSTR("SCNetworkReachability server not available"));
566 serverAvailable
= FALSE
;
568 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
569 SCLog(TRUE
, LOG_DEBUG
,
570 CFSTR("reach target %p: SCNetworkReachability server failure, no need to remove"),
575 CFSTR("reach target %p: _targetRemove with unexpected reply"),
577 log_xpc_object(" reply", reply
);
583 xpc_release(reqdict
);
589 _reach_server_target_schedule(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
593 xpc_object_t reqdict
;
594 Boolean retry
= FALSE
;
595 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
598 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
601 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_SCHEDULE
);
603 // add the target [ID]
604 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
608 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
612 type
= xpc_get_type(reply
);
613 if (type
== XPC_TYPE_DICTIONARY
) {
616 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
618 case REACH_REQUEST_REPLY_OK
:
621 case REACH_REQUEST_REPLY_UNKNOWN
:
622 SCLog(TRUE
, LOG_DEBUG
,
623 CFSTR("reach target %p: SCNetworkReachability server failure, retry schedule"),
628 SCLog(TRUE
, LOG_ERR
, CFSTR("%s target schedule failed"),
629 targetPrivate
->log_prefix
);
630 log_xpc_object(" reply", reply
);
637 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
639 CFSTR("SCNetworkReachability server not available"));
640 serverAvailable
= FALSE
;
641 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
642 SCLog(TRUE
, LOG_DEBUG
,
643 CFSTR("reach target %p: SCNetworkReachability server failure, retry schedule"),
648 CFSTR("reach target %p: _targetSchedule with unexpected reply"),
650 log_xpc_object(" reply", reply
);
658 _reach_server_target_reconnect(connection
, target
);
665 xpc_release(reqdict
);
671 _reach_reply_set_reachability(SCNetworkReachabilityRef target
,
676 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
678 targetPrivate
->serverInfo
.cycle
= xpc_dictionary_get_uint64(reply
,
681 targetPrivate
->serverInfo
.flags
= xpc_dictionary_get_uint64(reply
,
684 targetPrivate
->serverInfo
.if_index
= xpc_dictionary_get_uint64(reply
,
685 REACH_STATUS_IF_INDEX
);
687 bzero(&targetPrivate
->serverInfo
.if_name
, sizeof(targetPrivate
->serverInfo
.if_name
));
688 if_name
= (void *)xpc_dictionary_get_data(reply
,
689 REACH_STATUS_IF_NAME
,
691 if ((if_name
!= NULL
) && (len
> 0)) {
692 if (len
> sizeof(targetPrivate
->serverInfo
.if_name
)) {
693 len
= sizeof(targetPrivate
->serverInfo
.if_name
);
696 bcopy(if_name
, targetPrivate
->serverInfo
.if_name
, len
);
699 targetPrivate
->serverInfo
.sleeping
= xpc_dictionary_get_bool(reply
,
700 REACH_STATUS_SLEEPING
);
702 if (targetPrivate
->type
== reachabilityTypeName
) {
703 xpc_object_t addresses
;
705 if (targetPrivate
->resolvedAddress
!= NULL
) {
706 CFRelease(targetPrivate
->resolvedAddress
);
707 targetPrivate
->resolvedAddress
= NULL
;
710 targetPrivate
->resolvedAddressError
= xpc_dictionary_get_int64(reply
,
711 REACH_STATUS_RESOLVED_ADDRESS_ERROR
);
713 addresses
= xpc_dictionary_get_value(reply
, REACH_STATUS_RESOLVED_ADDRESS
);
714 if ((addresses
!= NULL
) && (xpc_get_type(addresses
) != XPC_TYPE_ARRAY
)) {
718 if ((targetPrivate
->resolvedAddressError
== 0) && (addresses
!= NULL
)) {
721 CFMutableArrayRef newAddresses
;
723 newAddresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
725 n
= xpc_array_get_count(addresses
);
726 for (i
= 0; i
< n
; i
++) {
729 CFDataRef newAddress
;
731 sa
= (struct addrinfo
*)xpc_array_get_data(addresses
, i
, &len
);
732 newAddress
= CFDataCreate(NULL
, (const UInt8
*)sa
, len
);
733 CFArrayAppendValue(newAddresses
, newAddress
);
734 CFRelease(newAddress
);
737 targetPrivate
->resolvedAddress
= newAddresses
;
739 /* save the error associated with the attempt to resolve the name */
740 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
742 targetPrivate
->needResolve
= FALSE
;
751 _reach_server_target_status(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
755 xpc_object_t reqdict
;
756 Boolean retry
= FALSE
;
757 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
762 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
763 SCLog(TRUE
, LOG_INFO
, CFSTR("%scheckReachability(%@)"),
764 targetPrivate
->log_prefix
,
770 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
773 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_STATUS
);
775 // add the target [ID]
776 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
780 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
784 type
= xpc_get_type(reply
);
785 if (type
== XPC_TYPE_DICTIONARY
) {
788 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
790 case REACH_REQUEST_REPLY_OK
:
793 case REACH_REQUEST_REPLY_UNKNOWN
:
794 SCLog(TRUE
, LOG_DEBUG
,
795 CFSTR("reach target %p: SCNetworkReachability server failure, retry status"),
800 SCLog(TRUE
, LOG_INFO
, CFSTR("%s target status failed"),
801 targetPrivate
->log_prefix
);
802 log_xpc_object(" reply", reply
);
806 _reach_reply_set_reachability(target
, reply
);
809 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags = 0x%08x"),
810 targetPrivate
->log_prefix
,
811 targetPrivate
->serverInfo
.flags
);
812 if (targetPrivate
->serverInfo
.if_index
!= 0) {
813 SCLog(TRUE
, LOG_INFO
, CFSTR("%s device = %s (%hu%s)"),
814 targetPrivate
->log_prefix
,
815 targetPrivate
->serverInfo
.if_name
,
816 targetPrivate
->serverInfo
.if_index
,
817 targetPrivate
->serverInfo
.sleeping
? ", z" : "");
819 if (targetPrivate
->serverInfo
.cycle
!= targetPrivate
->cycle
) {
820 SCLog(TRUE
, LOG_INFO
, CFSTR("%s forced"),
821 targetPrivate
->log_prefix
);
825 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
827 CFSTR("SCNetworkReachability server not available"));
828 serverAvailable
= FALSE
;
830 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
831 SCLog(TRUE
, LOG_DEBUG
,
832 CFSTR("reach target %p: SCNetworkReachability server failure, retry status"),
837 CFSTR("reach target %p: _targetStatus with unexpected reply"),
839 log_xpc_object(" reply", reply
);
847 _reach_server_target_reconnect(connection
, target
);
854 xpc_release(reqdict
);
860 _reach_server_target_unschedule(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
864 xpc_object_t reqdict
;
865 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
868 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
871 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_UNSCHEDULE
);
873 // add the target [ID]
874 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
876 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
880 type
= xpc_get_type(reply
);
881 if (type
== XPC_TYPE_DICTIONARY
) {
884 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
886 case REACH_REQUEST_REPLY_OK
:
889 case REACH_REQUEST_REPLY_UNKNOWN
:
890 SCLog(TRUE
, LOG_DEBUG
,
891 CFSTR("reach target %p: SCNetworkReachability server failure, no need to unschedule"),
895 SCLog(TRUE
, LOG_INFO
, CFSTR("%s target unschedule failed"),
896 targetPrivate
->log_prefix
);
897 log_xpc_object(" reply", reply
);
903 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
905 CFSTR("SCNetworkReachability server not available"));
906 serverAvailable
= FALSE
;
908 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
909 SCLog(TRUE
, LOG_DEBUG
,
910 CFSTR("reach target %p: SCNetworkReachability server failure, no need to unschedule"),
915 CFSTR("reach target %p: _targetUnschedule with unexpected reply"),
917 log_xpc_object(" reply", reply
);
923 xpc_release(reqdict
);
929 #pragma mark Reconnect
933 _reach_server_target_reconnect(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
936 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
938 if (!targetPrivate
->serverActive
) {
939 // if target already removed
943 // server has been restarted
944 targetPrivate
->cycle
= 0;
946 // re-associate with server
947 ok
= _reach_server_target_add(connection
, target
);
949 // if we could not add the target
953 if (!targetPrivate
->serverScheduled
) {
958 // ... and re-schedule with server
959 ok
= _reach_server_target_schedule(connection
, target
);
961 // if we could not reschedule the target
965 // .. and update our status
966 __SCNetworkReachabilityPerformNoLock(target
);
973 _reach_connection_reconnect(xpc_connection_t connection
)
977 q
= _reach_requests_rbt_queue();
982 rbt
= _reach_requests_rbt();
983 rbn
= rb_tree_iterate(rbt
, NULL
, RB_DIR_RIGHT
);
984 for ( ; rbn
!= NULL
; rbn
= rb_tree_iterate(rbt
, rbn
, RB_DIR_LEFT
)) {
985 reach_request_t
*rbt_request
;
986 SCNetworkReachabilityRef target
;
988 rbt_request
= RBNODE_TO_REACH_REQUEST(rbn
);
990 target
= rbt_request
->target
;
992 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
993 _reach_server_target_reconnect(connection
, target
);
1004 #pragma mark SPI (exposed)
1008 _SCNetworkReachabilityServer_snapshot(void)
1013 xpc_object_t reqdict
;
1015 // initialize connection with SCNetworkReachability server
1016 c
= _reach_connection();
1022 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
1025 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_SNAPSHOT
);
1027 // add the process name (for debugging)
1028 add_proc_name(reqdict
);
1033 reply
= xpc_connection_send_message_with_reply_sync(c
, reqdict
);
1034 if (reply
!= NULL
) {
1037 type
= xpc_get_type(reply
);
1038 if (type
== XPC_TYPE_DICTIONARY
) {
1041 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
1042 ok
= (status
== REACH_REQUEST_REPLY_OK
);
1043 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
1044 SCLog(TRUE
, LOG_ERR
,
1045 CFSTR("SCNetworkReachability server not available"));
1046 serverAvailable
= FALSE
;
1047 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
1048 SCLog(TRUE
, LOG_DEBUG
,
1049 CFSTR("SCNetworkReachability server failure, retrying"));
1053 SCLog(TRUE
, LOG_ERR
,
1054 CFSTR("_snapshot with unexpected reply"));
1055 log_xpc_object(" reply", reply
);
1061 xpc_release(reqdict
);
1068 __SCNetworkReachabilityServer_targetAdd(SCNetworkReachabilityRef target
)
1072 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1074 c
= _reach_connection();
1079 ok
= _reach_server_target_add(c
, target
);
1081 _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverActive
, FALSE
, TRUE
);
1090 __SCNetworkReachabilityServer_targetRemove(SCNetworkReachabilityRef target
)
1094 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1096 if (!targetPrivate
->serverActive
) {
1101 c
= _reach_connection();
1106 ok
= _reach_server_target_remove(c
, target
);
1108 _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverActive
, TRUE
, FALSE
);
1117 __SCNetworkReachabilityServer_targetSchedule(SCNetworkReachabilityRef target
)
1121 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1123 c
= _reach_connection();
1128 _reach_request_add(target
);
1129 ok
= _reach_server_target_schedule(c
, target
);
1131 _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverScheduled
, FALSE
, TRUE
);
1133 _reach_request_remove(target
);
1142 __SCNetworkReachabilityServer_targetStatus(SCNetworkReachabilityRef target
)
1147 c
= _reach_connection();
1152 ok
= _reach_server_target_status(c
, target
);
1159 __SCNetworkReachabilityServer_targetUnschedule(SCNetworkReachabilityRef target
)
1163 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1165 if (!targetPrivate
->serverScheduled
) {
1170 c
= _reach_connection();
1175 ok
= _reach_server_target_unschedule(c
, target
);
1177 _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverScheduled
, TRUE
, FALSE
);
1178 _reach_request_remove(target
);
1180 // if unschedule failed
1186 #endif // HAVE_REACHABILITY_SERVER