2 * Copyright (c) 2011-2014 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <CoreFoundation/CoreFoundation.h>
25 #include <SystemConfiguration/SystemConfiguration.h>
26 #include <SystemConfiguration/SCPrivate.h>
27 #include "SCNetworkReachabilityInternal.h"
30 #include <xpc/private.h>
31 #include <sys/rbtree.h>
38 static Boolean serverAvailable
= TRUE
;
42 #pragma mark Support functions
46 log_xpc_object(const char *msg
, xpc_object_t obj
)
50 desc
= xpc_copy_description(obj
);
51 SCLog(TRUE
, LOG_DEBUG
, CFSTR("%s = %s"), msg
, desc
);
57 #pragma mark Reachability [RBT] client support
62 SCNetworkReachabilityRef target
;
67 _rbt_compare_transaction_nodes(void *context
, const void *n1
, const void *n2
)
69 uint64_t a
= (uintptr_t)(((reach_request_t
*)n1
)->target
);
70 uint64_t b
= (uintptr_t)(((reach_request_t
*)n2
)->target
);
83 _rbt_compare_transaction_key(void *context
, const void *n1
, const void *key
)
85 uint64_t a
= (uintptr_t)(((reach_request_t
*)n1
)->target
);
86 uint64_t b
= *(uint64_t *)key
;
101 static dispatch_once_t once
;
102 static const rb_tree_ops_t ops
= {
103 .rbto_compare_nodes
= _rbt_compare_transaction_nodes
,
104 .rbto_compare_key
= _rbt_compare_transaction_key
,
105 .rbto_node_offset
= offsetof(reach_request_t
, rbn
),
108 static rb_tree_t rbt
;
110 dispatch_once(&once
, ^{
111 rb_tree_init(&rbt
, &ops
);
118 static dispatch_queue_t
119 _reach_requests_rbt_queue()
121 static dispatch_once_t once
;
122 static dispatch_queue_t q
;
124 dispatch_once(&once
, ^{
125 q
= dispatch_queue_create(REACH_SERVICE_NAME
".requests.rbt", NULL
);
132 static reach_request_t
*
133 _reach_request_create(SCNetworkReachabilityRef target
)
135 reach_request_t
*request
;
137 request
= calloc(1, sizeof(*request
));
138 request
->target
= CFRetain(target
);
145 _reach_request_release(reach_request_t
*request
)
147 SCNetworkReachabilityRef target
= request
->target
;
157 _reach_request_add(SCNetworkReachabilityRef target
)
159 uint64_t target_id
= (uintptr_t)target
;
161 dispatch_sync(_reach_requests_rbt_queue(), ^{
162 rb_tree_t
*rbt
= _reach_requests_rbt();
163 reach_request_t
*request
;
165 request
= rb_tree_find_node(rbt
, &target_id
);
166 if (request
== NULL
) {
167 request
= _reach_request_create(target
);
168 rb_tree_insert_node(rbt
, request
);
177 _reach_request_remove(SCNetworkReachabilityRef target
)
179 uint64_t target_id
= (uintptr_t)target
;
181 dispatch_sync(_reach_requests_rbt_queue(), ^{ // FIXME ?? use dispatch_async?
182 rb_tree_t
*rbt
= _reach_requests_rbt();
183 reach_request_t
*request
;
185 request
= rb_tree_find_node(rbt
, &target_id
);
186 if (request
!= NULL
) {
187 rb_tree_remove_node(rbt
, request
);
188 _reach_request_release(request
);
196 static SCNetworkReachabilityRef
197 _reach_request_copy_target(uint64_t target_id
)
199 __block SCNetworkReachabilityRef target
= NULL
;
201 dispatch_sync(_reach_requests_rbt_queue(), ^{
202 rb_tree_t
*rbt
= _reach_requests_rbt();
203 reach_request_t
*request
;
205 request
= rb_tree_find_node(rbt
, &target_id
);
206 if (request
!= NULL
) {
207 target
= request
->target
;
217 #pragma mark Reachability [XPC] client support
221 handle_reachability_status(SCNetworkReachabilityRef target
, xpc_object_t dict
)
223 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
226 SCLog(TRUE
, LOG_INFO
, CFSTR("%sgot [async] notification"),
227 targetPrivate
->log_prefix
);
228 // log_xpc_object(" status", dict);
231 __SCNetworkReachabilityUpdateConcurrent(target
);
238 handle_async_notification(SCNetworkReachabilityRef target
, xpc_object_t dict
)
241 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
243 op
= xpc_dictionary_get_int64(dict
, MESSAGE_NOTIFY
);
245 case MESSAGE_REACHABILITY_STATUS
:
246 handle_reachability_status(target
, dict
);
249 SCLog(TRUE
, LOG_ERR
, CFSTR("%sgot [async] unknown reply : %lld"),
250 targetPrivate
->log_prefix
,
252 log_xpc_object(" reply", dict
);
260 static dispatch_queue_t
263 static dispatch_once_t once
;
264 static dispatch_queue_t q
;
266 dispatch_once(&once
, ^{
267 q
= dispatch_queue_create(REACH_SERVICE_NAME
".xpc", NULL
);
275 _reach_connection_reconnect(xpc_connection_t connection
);
278 static xpc_connection_t
279 _reach_connection_create()
282 #if !TARGET_IPHONE_SIMULATOR
283 const uint64_t flags
= XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
;
284 #else // !TARGET_IPHONE_SIMULATOR
285 const uint64_t flags
= 0;
286 #endif // !TARGET_IPHONE_SIMULATOR
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
, q
, flags
);
298 xpc_connection_set_event_handler(c
, ^(xpc_object_t xobj
) {
301 type
= xpc_get_type(xobj
);
302 if (type
== XPC_TYPE_DICTIONARY
) {
303 SCNetworkReachabilityRef target
;
306 target_id
= xpc_dictionary_get_uint64(xobj
, REACH_CLIENT_TARGET_ID
);
307 if (target_id
== 0) {
309 CFSTR("reach client %p: async reply with no target [ID]"),
311 log_xpc_object(" reply", xobj
);
315 target
= _reach_request_copy_target(target_id
);
316 if (target
== NULL
) {
317 // SCLog(TRUE, LOG_ERR,
318 // CFSTR("received unexpected target [ID] from SCNetworkReachability server"));
319 // log_xpc_object(" reply", xobj);
324 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
325 handle_async_notification(target
, xobj
);
330 } else if (type
== XPC_TYPE_ERROR
) {
331 if (xobj
== XPC_ERROR_CONNECTION_INVALID
) {
333 CFSTR("SCNetworkReachability server not available"));
334 serverAvailable
= FALSE
;
335 } else if (xobj
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
336 SCLog(TRUE
, LOG_DEBUG
,
337 CFSTR("SCNetworkReachability server failure, reconnecting"));
338 _reach_connection_reconnect(c
);
342 desc
= xpc_dictionary_get_string(xobj
, XPC_ERROR_KEY_DESCRIPTION
);
344 CFSTR("reach client %p: Connection error: %s"),
351 CFSTR("reach client %p: unknown event type : %p"),
356 xpc_connection_resume(c
);
362 static xpc_connection_t
365 static xpc_connection_t c
;
366 static dispatch_once_t once
;
367 static dispatch_queue_t q
;
369 if (!serverAvailable
) {
370 // if SCNetworkReachability [XPC] server not available
374 dispatch_once(&once
, ^{
375 q
= dispatch_queue_create(REACH_SERVICE_NAME
".connection", NULL
);
380 c
= _reach_connection_create();
388 typedef void (^reach_server_reply_handler_t
)(xpc_object_t reply
);
392 add_proc_name(xpc_object_t reqdict
)
394 static const char *name
= NULL
;
395 static dispatch_once_t once
;
397 // add the process name
398 dispatch_once(&once
, ^{
399 name
= getprogname();
401 xpc_dictionary_set_string(reqdict
, REACH_CLIENT_PROC_NAME
, name
);
408 _reach_server_target_reconnect(xpc_connection_t connection
, SCNetworkReachabilityRef target
, Boolean disconnect
);
412 _reach_server_target_add(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
416 xpc_object_t reqdict
;
417 Boolean retry
= FALSE
;
418 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
421 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
424 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_CREATE
);
426 // add reachability target info
427 switch (targetPrivate
->type
) {
428 case reachabilityTypeName
:
429 xpc_dictionary_set_string(reqdict
,
431 targetPrivate
->name
);
433 case reachabilityTypeAddress
:
434 case reachabilityTypeAddressPair
:
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
);
448 case reachabilityTypePTR
:
449 xpc_dictionary_set_data(reqdict
,
450 REACH_TARGET_PTR_ADDR
,
451 targetPrivate
->remoteAddress
,
452 targetPrivate
->remoteAddress
->sa_len
);
455 if (targetPrivate
->if_index
!= 0) {
456 xpc_dictionary_set_int64(reqdict
,
457 REACH_TARGET_IF_INDEX
,
458 targetPrivate
->if_index
);
459 xpc_dictionary_set_string(reqdict
,
460 REACH_TARGET_IF_NAME
,
461 targetPrivate
->if_name
);
463 if (targetPrivate
->onDemandBypass
) {
464 xpc_dictionary_set_bool(reqdict
,
465 REACH_TARGET_ONDEMAND_BYPASS
,
468 if (targetPrivate
->resolverBypass
) {
469 xpc_dictionary_set_bool(reqdict
,
470 REACH_TARGET_RESOLVER_BYPASS
,
476 // add the target [ID]
477 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
479 // add the process name (for debugging)
480 add_proc_name(reqdict
);
484 // send request to the SCNetworkReachability server
485 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
489 type
= xpc_get_type(reply
);
490 if (type
== XPC_TYPE_DICTIONARY
) {
493 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
494 ok
= (status
== REACH_REQUEST_REPLY_OK
);
495 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
497 CFSTR("SCNetworkReachability server not available"));
498 serverAvailable
= FALSE
;
499 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
500 SCLog(TRUE
, LOG_DEBUG
,
501 CFSTR("reach target %p: SCNetworkReachability server failure, retrying"),
506 CFSTR("reach target %p: _targetAdd with unexpected reply"),
508 log_xpc_object(" reply", reply
);
519 xpc_release(reqdict
);
525 _reach_server_target_remove(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
529 xpc_object_t reqdict
;
530 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
533 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
536 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_REMOVE
);
538 // add the target [ID]
539 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
541 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
545 type
= xpc_get_type(reply
);
546 if (type
== XPC_TYPE_DICTIONARY
) {
549 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
551 case REACH_REQUEST_REPLY_OK
:
554 case REACH_REQUEST_REPLY_UNKNOWN
:
555 // target not known by the server (most likely due to a
556 // SCNetworkReachability server failure), no need to
561 SCLog(TRUE
, LOG_ERR
, CFSTR("%s target remove failed"),
562 targetPrivate
->log_prefix
);
563 log_xpc_object(" reply", reply
);
566 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
568 CFSTR("SCNetworkReachability server not available"));
569 serverAvailable
= FALSE
;
571 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
572 SCLog(TRUE
, LOG_DEBUG
,
573 CFSTR("reach target %p: SCNetworkReachability server failure, no need to remove"),
578 CFSTR("reach target %p: _targetRemove with unexpected reply"),
580 log_xpc_object(" reply", reply
);
586 xpc_release(reqdict
);
592 _reach_server_target_schedule(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
596 xpc_object_t reqdict
;
597 Boolean retry
= FALSE
;
598 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
601 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
604 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_SCHEDULE
);
606 // add the target [ID]
607 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
611 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
615 type
= xpc_get_type(reply
);
616 if (type
== XPC_TYPE_DICTIONARY
) {
619 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
621 case REACH_REQUEST_REPLY_OK
:
624 case REACH_REQUEST_REPLY_UNKNOWN
:
625 // target not known by the server (most likely due to a
626 // SCNetworkReachability server failure), re-establish
627 // and retry scheduling.
631 SCLog(TRUE
, LOG_ERR
, CFSTR("%s target schedule failed"),
632 targetPrivate
->log_prefix
);
633 log_xpc_object(" reply", reply
);
640 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
642 CFSTR("SCNetworkReachability server not available"));
643 serverAvailable
= FALSE
;
644 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
645 SCLog(TRUE
, LOG_DEBUG
,
646 CFSTR("reach target %p: SCNetworkReachability server failure, retry schedule"),
651 CFSTR("reach target %p: _targetSchedule with unexpected reply"),
653 log_xpc_object(" reply", reply
);
661 _reach_server_target_reconnect(connection
, target
, FALSE
);
668 xpc_release(reqdict
);
674 _reach_reply_set_reachability(SCNetworkReachabilityRef target
,
680 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
682 targetPrivate
->serverInfo
.cycle
= xpc_dictionary_get_uint64(reply
,
685 targetPrivate
->serverInfo
.flags
= (SCNetworkReachabilityFlags
)xpc_dictionary_get_uint64(reply
,
688 targetPrivate
->serverInfo
.if_index
= (unsigned int)xpc_dictionary_get_uint64(reply
,
689 REACH_STATUS_IF_INDEX
);
691 bzero(&targetPrivate
->serverInfo
.if_name
, sizeof(targetPrivate
->serverInfo
.if_name
));
692 if_name
= (void *)xpc_dictionary_get_data(reply
,
693 REACH_STATUS_IF_NAME
,
695 if ((if_name
!= NULL
) && (len
> 0)) {
696 if (len
> sizeof(targetPrivate
->serverInfo
.if_name
)) {
697 len
= sizeof(targetPrivate
->serverInfo
.if_name
);
700 bcopy(if_name
, targetPrivate
->serverInfo
.if_name
, len
);
703 targetPrivate
->serverInfo
.sleeping
= xpc_dictionary_get_bool(reply
,
704 REACH_STATUS_SLEEPING
);
706 if (isReachabilityTypeName(targetPrivate
->type
)) {
707 xpc_object_t addresses
;
709 if (targetPrivate
->resolvedAddresses
!= NULL
) {
710 CFRelease(targetPrivate
->resolvedAddresses
);
711 targetPrivate
->resolvedAddresses
= NULL
;
714 targetPrivate
->resolvedError
= (int)xpc_dictionary_get_int64(reply
,
715 REACH_STATUS_RESOLVED_ERROR
);
717 addresses
= xpc_dictionary_get_value(reply
, REACH_STATUS_RESOLVED_ADDRESSES
);
718 if ((addresses
!= NULL
) && (xpc_get_type(addresses
) != XPC_TYPE_ARRAY
)) {
722 if ((targetPrivate
->resolvedError
== NETDB_SUCCESS
) && (addresses
!= NULL
)) {
725 CFMutableArrayRef newAddresses
;
727 newAddresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
729 n
= xpc_array_get_count(addresses
);
730 for (i
= 0; i
< n
; i
++) {
731 if (targetPrivate
->type
== reachabilityTypeName
) {
734 CFDataRef newAddress
;
736 sa
= (struct sockaddr
*)xpc_array_get_data(addresses
, i
, &len
);
737 newAddress
= CFDataCreate(NULL
, (const UInt8
*)sa
, len
);
738 CFArrayAppendValue(newAddresses
, newAddress
);
739 CFRelease(newAddress
);
740 } else if (targetPrivate
->type
== reachabilityTypePTR
) {
744 str
= xpc_array_get_string(addresses
, i
);
745 newName
= CFStringCreateWithCString(NULL
, str
, kCFStringEncodingUTF8
);
746 CFArrayAppendValue(newAddresses
, newName
);
751 targetPrivate
->resolvedAddresses
= newAddresses
;
753 /* save the error associated with the attempt to resolve the name */
754 targetPrivate
->resolvedAddresses
= CFRetain(kCFNull
);
757 targetPrivate
->dnsFlags
= (uint32_t)xpc_dictionary_get_uint64(reply
,
758 REACH_STATUS_DNS_FLAGS
);
760 targetPrivate
->needResolve
= FALSE
;
768 _reach_server_target_status(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
772 xpc_object_t reqdict
;
773 Boolean retry
= FALSE
;
774 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
779 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
780 SCLog(TRUE
, LOG_INFO
, CFSTR("%scheckReachability(%@)"),
781 targetPrivate
->log_prefix
,
787 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
790 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_STATUS
);
792 // add the target [ID]
793 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
797 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
801 type
= xpc_get_type(reply
);
802 if (type
== XPC_TYPE_DICTIONARY
) {
805 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
807 case REACH_REQUEST_REPLY_OK
:
810 case REACH_REQUEST_REPLY_UNKNOWN
:
811 // target not known by the server (most likely due to a
812 // SCNetworkReachability server failure), re-establish
817 SCLog(TRUE
, LOG_INFO
, CFSTR("%s target status failed"),
818 targetPrivate
->log_prefix
);
819 log_xpc_object(" reply", reply
);
823 _reach_reply_set_reachability(target
, reply
);
826 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags = 0x%08x"),
827 targetPrivate
->log_prefix
,
828 targetPrivate
->serverInfo
.flags
);
829 if (targetPrivate
->serverInfo
.if_index
!= 0) {
830 SCLog(TRUE
, LOG_INFO
, CFSTR("%s device = %s (%u%s)"),
831 targetPrivate
->log_prefix
,
832 targetPrivate
->serverInfo
.if_name
,
833 targetPrivate
->serverInfo
.if_index
,
834 targetPrivate
->serverInfo
.sleeping
? ", z" : "");
836 if (targetPrivate
->serverInfo
.cycle
!= targetPrivate
->cycle
) {
837 SCLog(TRUE
, LOG_INFO
, CFSTR("%s forced"),
838 targetPrivate
->log_prefix
);
842 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
844 CFSTR("SCNetworkReachability server not available"));
845 serverAvailable
= FALSE
;
847 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
848 SCLog(TRUE
, LOG_DEBUG
,
849 CFSTR("reach target %p: SCNetworkReachability server failure, retry status"),
854 CFSTR("reach target %p: _targetStatus with unexpected reply"),
856 log_xpc_object(" reply", reply
);
864 _reach_server_target_reconnect(connection
, target
, FALSE
);
871 xpc_release(reqdict
);
877 _reach_server_target_unschedule(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
881 xpc_object_t reqdict
;
882 Boolean retry
= FALSE
;
883 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
886 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
889 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_UNSCHEDULE
);
891 // add the target [ID]
892 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
894 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
898 type
= xpc_get_type(reply
);
899 if (type
== XPC_TYPE_DICTIONARY
) {
902 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
904 case REACH_REQUEST_REPLY_OK
:
907 case REACH_REQUEST_REPLY_UNKNOWN
:
908 // target not known by the server (most likely due to a
909 // SCNetworkReachability server failure), re-establish
910 // but no need to unschedule.
914 SCLog(TRUE
, LOG_INFO
, CFSTR("%s target unschedule failed"),
915 targetPrivate
->log_prefix
);
916 log_xpc_object(" reply", reply
);
918 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
920 CFSTR("SCNetworkReachability server not available"));
921 serverAvailable
= FALSE
;
923 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
924 SCLog(TRUE
, LOG_DEBUG
,
925 CFSTR("reach target %p: SCNetworkReachability server failure, re-establish (but do not re-schedule)"),
930 CFSTR("reach target %p: _targetUnschedule with unexpected reply"),
932 log_xpc_object(" reply", reply
);
940 targetPrivate
->serverScheduled
= FALSE
;
941 _reach_server_target_reconnect(connection
, target
, FALSE
);
949 xpc_release(reqdict
);
955 #pragma mark Reconnect
959 _reach_server_target_reconnect(xpc_connection_t connection
, SCNetworkReachabilityRef target
, Boolean disconnect
)
962 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
964 if (!targetPrivate
->serverActive
) {
965 // if target already removed
970 // if we should first disconnect (unschedule, remove)
971 if (targetPrivate
->serverScheduled
) {
972 (void) _reach_server_target_unschedule(connection
, target
);
975 (void) _reach_server_target_remove(connection
, target
);
977 // server has been restarted
978 targetPrivate
->cycle
= 0;
981 // re-associate with server
982 ok
= _reach_server_target_add(connection
, target
);
984 // if we could not add the target
988 if (!targetPrivate
->serverScheduled
) {
993 // ... and re-schedule with server
994 ok
= _reach_server_target_schedule(connection
, target
);
996 // if we could not reschedule the target
1000 // For addresses, update our status now. For names, queries will
1001 // be updated with a callback
1002 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1003 __SCNetworkReachabilityUpdate(target
);
1011 _reach_connection_reconnect(xpc_connection_t connection
)
1013 dispatch_sync(_reach_requests_rbt_queue(), ^{
1014 rb_tree_t
*rbt
= _reach_requests_rbt();
1015 reach_request_t
*request
;
1017 RB_TREE_FOREACH(request
, rbt
) {
1018 SCNetworkReachabilityRef target
;
1020 xpc_retain(connection
);
1021 target
= request
->target
;
1023 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
1024 _reach_server_target_reconnect(connection
, target
, FALSE
);
1026 xpc_release(connection
);
1036 #pragma mark SPI (exposed)
1040 _SCNetworkReachabilityServer_snapshot(void)
1045 xpc_object_t reqdict
;
1047 // initialize connection with SCNetworkReachability server
1048 c
= _reach_connection();
1054 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
1057 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_SNAPSHOT
);
1059 // add the process name (for debugging)
1060 add_proc_name(reqdict
);
1065 reply
= xpc_connection_send_message_with_reply_sync(c
, reqdict
);
1066 if (reply
!= NULL
) {
1069 type
= xpc_get_type(reply
);
1070 if (type
== XPC_TYPE_DICTIONARY
) {
1073 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
1074 ok
= (status
== REACH_REQUEST_REPLY_OK
);
1075 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
1076 SCLog(TRUE
, LOG_ERR
,
1077 CFSTR("SCNetworkReachability server not available"));
1078 serverAvailable
= FALSE
;
1079 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
1080 SCLog(TRUE
, LOG_DEBUG
,
1081 CFSTR("SCNetworkReachability server failure, retrying"));
1085 SCLog(TRUE
, LOG_ERR
,
1086 CFSTR("_snapshot with unexpected reply"));
1087 log_xpc_object(" reply", reply
);
1093 xpc_release(reqdict
);
1100 __SCNetworkReachabilityServer_targetAdd(SCNetworkReachabilityRef target
)
1104 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1106 c
= _reach_connection();
1111 ok
= _reach_server_target_add(c
, target
);
1113 _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverActive
, FALSE
, TRUE
);
1122 __SCNetworkReachabilityServer_targetRemove(SCNetworkReachabilityRef target
)
1126 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1128 if (!targetPrivate
->serverActive
) {
1133 c
= _reach_connection();
1138 ok
= _reach_server_target_remove(c
, target
);
1140 _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverActive
, TRUE
, FALSE
);
1149 __SCNetworkReachabilityServer_targetSchedule(SCNetworkReachabilityRef target
)
1153 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1155 c
= _reach_connection();
1160 _reach_request_add(target
);
1161 ok
= _reach_server_target_schedule(c
, target
);
1163 _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverScheduled
, FALSE
, TRUE
);
1165 _reach_request_remove(target
);
1174 __SCNetworkReachabilityServer_targetStatus(SCNetworkReachabilityRef target
)
1179 c
= _reach_connection();
1184 ok
= _reach_server_target_status(c
, target
);
1191 __SCNetworkReachabilityServer_targetUnschedule(SCNetworkReachabilityRef target
)
1195 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1197 if (!targetPrivate
->serverScheduled
) {
1202 c
= _reach_connection();
1207 ok
= _reach_server_target_unschedule(c
, target
);
1209 _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverScheduled
, TRUE
, FALSE
);
1210 _reach_request_remove(target
);
1212 // if unschedule failed