2 * Copyright (c) 2011-2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <CoreFoundation/CoreFoundation.h>
25 #include <SystemConfiguration/SystemConfiguration.h>
26 #include <SystemConfiguration/SCPrivate.h>
27 #include "SCNetworkReachabilityInternal.h"
29 #ifdef HAVE_REACHABILITY_SERVER
32 #include <xpc/private.h>
33 #include <sys/rbtree.h>
40 static Boolean serverAvailable
= TRUE
;
44 #pragma mark Support functions
48 log_xpc_object(const char *msg
, xpc_object_t obj
)
52 desc
= xpc_copy_description(obj
);
53 SCLog(TRUE
, LOG_DEBUG
, CFSTR("%s = %s"), msg
, desc
);
59 #pragma mark Reachability [RBT] client support
64 SCNetworkReachabilityRef target
;
69 _rbt_compare_transaction_nodes(void *context
, const void *n1
, const void *n2
)
71 uint64_t a
= (uintptr_t)(((reach_request_t
*)n1
)->target
);
72 uint64_t b
= (uintptr_t)(((reach_request_t
*)n2
)->target
);
79 _rbt_compare_transaction_key(void *context
, const void *n1
, const void *key
)
81 uint64_t a
= (uintptr_t)(((reach_request_t
*)n1
)->target
);
82 uint64_t b
= *(uint64_t *)key
;
91 static dispatch_once_t once
;
92 static const rb_tree_ops_t ops
= {
93 .rbto_compare_nodes
= _rbt_compare_transaction_nodes
,
94 .rbto_compare_key
= _rbt_compare_transaction_key
,
95 .rbto_node_offset
= offsetof(reach_request_t
, rbn
),
100 dispatch_once(&once
, ^{
101 rb_tree_init(&rbt
, &ops
);
108 static dispatch_queue_t
109 _reach_requests_rbt_queue()
111 static dispatch_once_t once
;
112 static dispatch_queue_t q
;
114 dispatch_once(&once
, ^{
115 q
= dispatch_queue_create(REACH_SERVICE_NAME
".requests.rbt", NULL
);
122 static reach_request_t
*
123 _reach_request_create(SCNetworkReachabilityRef target
)
125 reach_request_t
*request
;
127 request
= calloc(1, sizeof(*request
));
128 request
->target
= CFRetain(target
);
135 _reach_request_release(reach_request_t
*request
)
137 SCNetworkReachabilityRef target
= request
->target
;
147 _reach_request_add(SCNetworkReachabilityRef target
)
149 uint64_t target_id
= (uintptr_t)target
;
151 dispatch_sync(_reach_requests_rbt_queue(), ^{
152 rb_tree_t
*rbt
= _reach_requests_rbt();
153 reach_request_t
*request
;
155 request
= rb_tree_find_node(rbt
, &target_id
);
156 if (request
== NULL
) {
157 request
= _reach_request_create(target
);
158 if (request
== NULL
|| !rb_tree_insert_node(rbt
, request
)) {
169 _reach_request_remove(SCNetworkReachabilityRef target
)
171 uint64_t target_id
= (uintptr_t)target
;
173 dispatch_sync(_reach_requests_rbt_queue(), ^{ // FIXME ?? use dispatch_async?
174 rb_tree_t
*rbt
= _reach_requests_rbt();
175 reach_request_t
*request
;
177 request
= rb_tree_find_node(rbt
, &target_id
);
178 if (request
!= NULL
) {
179 rb_tree_remove_node(rbt
, request
);
180 _reach_request_release(request
);
188 static SCNetworkReachabilityRef
189 _reach_request_copy_target(uint64_t target_id
)
191 __block SCNetworkReachabilityRef target
= NULL
;
193 dispatch_sync(_reach_requests_rbt_queue(), ^{
194 rb_tree_t
*rbt
= _reach_requests_rbt();
195 reach_request_t
*request
;
197 request
= rb_tree_find_node(rbt
, &target_id
);
198 if (request
!= NULL
) {
199 target
= request
->target
;
209 #pragma mark Reachability [XPC] client support
213 handle_reachability_status(SCNetworkReachabilityRef target
, xpc_object_t dict
)
215 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
218 SCLog(TRUE
, LOG_INFO
, CFSTR("%sgot [async] notification"),
219 targetPrivate
->log_prefix
);
220 // log_xpc_object(" status", dict);
223 __SCNetworkReachabilityPerformConcurrent(target
);
230 handle_async_notification(SCNetworkReachabilityRef target
, xpc_object_t dict
)
233 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
235 op
= xpc_dictionary_get_int64(dict
, MESSAGE_NOTIFY
);
237 case MESSAGE_REACHABILITY_STATUS
:
238 handle_reachability_status(target
, dict
);
241 SCLog(TRUE
, LOG_ERR
, CFSTR("%sgot [async] unknown reply : %d"),
242 targetPrivate
->log_prefix
,
244 log_xpc_object(" reply", dict
);
252 static dispatch_queue_t
255 static dispatch_once_t once
;
256 static dispatch_queue_t q
;
258 dispatch_once(&once
, ^{
259 q
= dispatch_queue_create(REACH_SERVICE_NAME
".xpc", NULL
);
267 _reach_connection_reconnect(xpc_connection_t connection
);
270 static xpc_connection_t
271 _reach_connection_create()
274 #if !TARGET_IPHONE_SIMULATOR
275 const uint64_t flags
= XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
;
276 #else // !TARGET_IPHONE_SIMULATOR
277 const uint64_t flags
= 0;
278 #endif // !TARGET_IPHONE_SIMULATOR
280 dispatch_queue_t q
= _reach_xpc_queue();
282 // create XPC connection
283 name
= getenv("REACH_SERVER");
284 if ((name
== NULL
) || (issetugid() != 0)) {
285 name
= REACH_SERVICE_NAME
;
288 c
= xpc_connection_create_mach_service(name
, q
, flags
);
290 xpc_connection_set_event_handler(c
, ^(xpc_object_t xobj
) {
293 type
= xpc_get_type(xobj
);
294 if (type
== XPC_TYPE_DICTIONARY
) {
295 SCNetworkReachabilityRef target
;
298 target_id
= xpc_dictionary_get_uint64(xobj
, REACH_CLIENT_TARGET_ID
);
299 if (target_id
== 0) {
301 CFSTR("reach client %p: async reply with no target [ID]"),
303 log_xpc_object(" reply", xobj
);
307 target
= _reach_request_copy_target(target_id
);
308 if (target
== NULL
) {
309 // SCLog(TRUE, LOG_ERR,
310 // CFSTR("received unexpected target [ID] from SCNetworkReachability server"));
311 // log_xpc_object(" reply", xobj);
316 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
317 handle_async_notification(target
, xobj
);
322 } else if (type
== XPC_TYPE_ERROR
) {
323 if (xobj
== XPC_ERROR_CONNECTION_INVALID
) {
325 CFSTR("SCNetworkReachability server not available"));
326 serverAvailable
= FALSE
;
327 } else if (xobj
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
328 SCLog(TRUE
, LOG_DEBUG
,
329 CFSTR("SCNetworkReachability server failure, reconnecting"));
330 _reach_connection_reconnect(c
);
334 desc
= xpc_dictionary_get_string(xobj
, XPC_ERROR_KEY_DESCRIPTION
);
336 CFSTR("reach client %p: Connection error: %s"),
343 CFSTR("reach client %p: unknown event type : %x"),
348 xpc_connection_resume(c
);
354 static xpc_connection_t
357 static xpc_connection_t c
;
358 static dispatch_once_t once
;
359 static dispatch_queue_t q
;
361 if (!serverAvailable
) {
362 // if SCNetworkReachabilty [XPC] server not available
366 dispatch_once(&once
, ^{
367 q
= dispatch_queue_create(REACH_SERVICE_NAME
".connection", NULL
);
372 c
= _reach_connection_create();
380 typedef void (^reach_server_reply_handler_t
)(xpc_object_t reply
);
384 add_proc_name(xpc_object_t reqdict
)
386 static const char *name
= NULL
;
387 static dispatch_once_t once
;
389 // add the process name
390 dispatch_once(&once
, ^{
391 name
= getprogname();
393 xpc_dictionary_set_string(reqdict
, REACH_CLIENT_PROC_NAME
, name
);
400 _reach_server_target_reconnect(xpc_connection_t connection
, SCNetworkReachabilityRef target
, Boolean disconnect
);
404 _reach_server_target_add(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
408 xpc_object_t reqdict
;
409 Boolean retry
= FALSE
;
410 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
413 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
416 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_CREATE
);
418 // add reachability target info
419 if (targetPrivate
->name
!= NULL
) {
420 xpc_dictionary_set_string(reqdict
,
422 targetPrivate
->name
);
424 if (targetPrivate
->localAddress
!= NULL
) {
425 xpc_dictionary_set_data(reqdict
,
426 REACH_TARGET_LOCAL_ADDR
,
427 targetPrivate
->localAddress
,
428 targetPrivate
->localAddress
->sa_len
);
430 if (targetPrivate
->remoteAddress
!= NULL
) {
431 xpc_dictionary_set_data(reqdict
,
432 REACH_TARGET_REMOTE_ADDR
,
433 targetPrivate
->remoteAddress
,
434 targetPrivate
->remoteAddress
->sa_len
);
436 if (targetPrivate
->if_index
!= 0) {
437 xpc_dictionary_set_int64(reqdict
,
438 REACH_TARGET_IF_INDEX
,
439 targetPrivate
->if_index
);
440 xpc_dictionary_set_string(reqdict
,
441 REACH_TARGET_IF_NAME
,
442 targetPrivate
->if_name
);
444 if (targetPrivate
->onDemandBypass
) {
445 xpc_dictionary_set_bool(reqdict
,
446 REACH_TARGET_ONDEMAND_BYPASS
,
449 if (targetPrivate
->resolverBypass
) {
450 xpc_dictionary_set_bool(reqdict
,
451 REACH_TARGET_RESOLVER_BYPASS
,
457 // add the target [ID]
458 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
460 // add the process name (for debugging)
461 add_proc_name(reqdict
);
465 // send request to the SCNetworkReachability server
466 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
470 type
= xpc_get_type(reply
);
471 if (type
== XPC_TYPE_DICTIONARY
) {
474 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
475 ok
= (status
== REACH_REQUEST_REPLY_OK
);
476 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
478 CFSTR("SCNetworkReachability server not available"));
479 serverAvailable
= FALSE
;
480 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
481 SCLog(TRUE
, LOG_DEBUG
,
482 CFSTR("reach target %p: SCNetworkReachability server failure, retrying"),
487 CFSTR("reach target %p: _targetAdd with unexpected reply"),
489 log_xpc_object(" reply", reply
);
500 xpc_release(reqdict
);
506 _reach_server_target_remove(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
510 xpc_object_t reqdict
;
511 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
514 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
517 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_REMOVE
);
519 // add the target [ID]
520 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
522 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
526 type
= xpc_get_type(reply
);
527 if (type
== XPC_TYPE_DICTIONARY
) {
530 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
532 case REACH_REQUEST_REPLY_OK
:
535 case REACH_REQUEST_REPLY_UNKNOWN
:
536 // target not known by the server (most likely due to a
537 // SCNetworkReachability server failure), no need to
542 SCLog(TRUE
, LOG_ERR
, CFSTR("%s target remove failed"),
543 targetPrivate
->log_prefix
);
544 log_xpc_object(" reply", reply
);
547 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
549 CFSTR("SCNetworkReachability server not available"));
550 serverAvailable
= FALSE
;
552 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
553 SCLog(TRUE
, LOG_DEBUG
,
554 CFSTR("reach target %p: SCNetworkReachability server failure, no need to remove"),
559 CFSTR("reach target %p: _targetRemove with unexpected reply"),
561 log_xpc_object(" reply", reply
);
567 xpc_release(reqdict
);
573 _reach_server_target_schedule(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
577 xpc_object_t reqdict
;
578 Boolean retry
= FALSE
;
579 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
582 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
585 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_SCHEDULE
);
587 // add the target [ID]
588 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
592 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
596 type
= xpc_get_type(reply
);
597 if (type
== XPC_TYPE_DICTIONARY
) {
600 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
602 case REACH_REQUEST_REPLY_OK
:
605 case REACH_REQUEST_REPLY_UNKNOWN
:
606 // target not known by the server (most likely due to a
607 // SCNetworkReachability server failure), re-establish
608 // and retry scheduling.
612 SCLog(TRUE
, LOG_ERR
, CFSTR("%s target schedule failed"),
613 targetPrivate
->log_prefix
);
614 log_xpc_object(" reply", reply
);
621 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
623 CFSTR("SCNetworkReachability server not available"));
624 serverAvailable
= FALSE
;
625 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
626 SCLog(TRUE
, LOG_DEBUG
,
627 CFSTR("reach target %p: SCNetworkReachability server failure, retry schedule"),
632 CFSTR("reach target %p: _targetSchedule with unexpected reply"),
634 log_xpc_object(" reply", reply
);
642 _reach_server_target_reconnect(connection
, target
, FALSE
);
649 xpc_release(reqdict
);
655 _reach_reply_set_reachability(SCNetworkReachabilityRef target
,
661 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
663 targetPrivate
->serverInfo
.cycle
= xpc_dictionary_get_uint64(reply
,
666 targetPrivate
->serverInfo
.flags
= xpc_dictionary_get_uint64(reply
,
669 targetPrivate
->serverInfo
.if_index
= xpc_dictionary_get_uint64(reply
,
670 REACH_STATUS_IF_INDEX
);
672 bzero(&targetPrivate
->serverInfo
.if_name
, sizeof(targetPrivate
->serverInfo
.if_name
));
673 if_name
= (void *)xpc_dictionary_get_data(reply
,
674 REACH_STATUS_IF_NAME
,
676 if ((if_name
!= NULL
) && (len
> 0)) {
677 if (len
> sizeof(targetPrivate
->serverInfo
.if_name
)) {
678 len
= sizeof(targetPrivate
->serverInfo
.if_name
);
681 bcopy(if_name
, targetPrivate
->serverInfo
.if_name
, len
);
684 targetPrivate
->serverInfo
.sleeping
= xpc_dictionary_get_bool(reply
,
685 REACH_STATUS_SLEEPING
);
687 if (targetPrivate
->type
== reachabilityTypeName
) {
688 xpc_object_t addresses
;
690 if (targetPrivate
->resolvedAddresses
!= NULL
) {
691 CFRelease(targetPrivate
->resolvedAddresses
);
692 targetPrivate
->resolvedAddresses
= NULL
;
695 targetPrivate
->resolvedError
= xpc_dictionary_get_int64(reply
,
696 REACH_STATUS_RESOLVED_ERROR
);
698 addresses
= xpc_dictionary_get_value(reply
, REACH_STATUS_RESOLVED_ADDRESSES
);
699 if ((addresses
!= NULL
) && (xpc_get_type(addresses
) != XPC_TYPE_ARRAY
)) {
703 if ((targetPrivate
->resolvedError
== NETDB_SUCCESS
) && (addresses
!= NULL
)) {
706 CFMutableArrayRef newAddresses
;
708 newAddresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
710 n
= xpc_array_get_count(addresses
);
711 for (i
= 0; i
< n
; i
++) {
714 CFDataRef newAddress
;
716 sa
= (struct addrinfo
*)xpc_array_get_data(addresses
, i
, &len
);
717 newAddress
= CFDataCreate(NULL
, (const UInt8
*)sa
, len
);
718 CFArrayAppendValue(newAddresses
, newAddress
);
719 CFRelease(newAddress
);
722 targetPrivate
->resolvedAddresses
= newAddresses
;
724 /* save the error associated with the attempt to resolve the name */
725 targetPrivate
->resolvedAddresses
= CFRetain(kCFNull
);
727 targetPrivate
->needResolve
= FALSE
;
735 _reach_server_target_status(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
739 xpc_object_t reqdict
;
740 Boolean retry
= FALSE
;
741 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
746 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
747 SCLog(TRUE
, LOG_INFO
, CFSTR("%scheckReachability(%@)"),
748 targetPrivate
->log_prefix
,
754 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
757 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_STATUS
);
759 // add the target [ID]
760 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
764 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
768 type
= xpc_get_type(reply
);
769 if (type
== XPC_TYPE_DICTIONARY
) {
772 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
774 case REACH_REQUEST_REPLY_OK
:
777 case REACH_REQUEST_REPLY_UNKNOWN
:
778 // target not known by the server (most likely due to a
779 // SCNetworkReachability server failure), re-establish
784 SCLog(TRUE
, LOG_INFO
, CFSTR("%s target status failed"),
785 targetPrivate
->log_prefix
);
786 log_xpc_object(" reply", reply
);
790 _reach_reply_set_reachability(target
, reply
);
793 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags = 0x%08x"),
794 targetPrivate
->log_prefix
,
795 targetPrivate
->serverInfo
.flags
);
796 if (targetPrivate
->serverInfo
.if_index
!= 0) {
797 SCLog(TRUE
, LOG_INFO
, CFSTR("%s device = %s (%hu%s)"),
798 targetPrivate
->log_prefix
,
799 targetPrivate
->serverInfo
.if_name
,
800 targetPrivate
->serverInfo
.if_index
,
801 targetPrivate
->serverInfo
.sleeping
? ", z" : "");
803 if (targetPrivate
->serverInfo
.cycle
!= targetPrivate
->cycle
) {
804 SCLog(TRUE
, LOG_INFO
, CFSTR("%s forced"),
805 targetPrivate
->log_prefix
);
809 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
811 CFSTR("SCNetworkReachability server not available"));
812 serverAvailable
= FALSE
;
814 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
815 SCLog(TRUE
, LOG_DEBUG
,
816 CFSTR("reach target %p: SCNetworkReachability server failure, retry status"),
821 CFSTR("reach target %p: _targetStatus with unexpected reply"),
823 log_xpc_object(" reply", reply
);
831 _reach_server_target_reconnect(connection
, target
, FALSE
);
838 xpc_release(reqdict
);
844 _reach_server_target_unschedule(xpc_connection_t connection
, SCNetworkReachabilityRef target
)
848 xpc_object_t reqdict
;
849 Boolean retry
= FALSE
;
850 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
853 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
856 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_UNSCHEDULE
);
858 // add the target [ID]
859 xpc_dictionary_set_uint64(reqdict
, REACH_CLIENT_TARGET_ID
, (uintptr_t)target
);
861 reply
= xpc_connection_send_message_with_reply_sync(connection
, reqdict
);
865 type
= xpc_get_type(reply
);
866 if (type
== XPC_TYPE_DICTIONARY
) {
869 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
871 case REACH_REQUEST_REPLY_OK
:
874 case REACH_REQUEST_REPLY_UNKNOWN
:
875 // target not known by the server (most likely due to a
876 // SCNetworkReachability server failure), re-establish
877 // but no need to unschedule.
881 SCLog(TRUE
, LOG_INFO
, CFSTR("%s target unschedule failed"),
882 targetPrivate
->log_prefix
);
883 log_xpc_object(" reply", reply
);
885 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
887 CFSTR("SCNetworkReachability server not available"));
888 serverAvailable
= FALSE
;
890 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
891 SCLog(TRUE
, LOG_DEBUG
,
892 CFSTR("reach target %p: SCNetworkReachability server failure, re-establish (but do not re-schedule)"),
897 CFSTR("reach target %p: _targetUnschedule with unexpected reply"),
899 log_xpc_object(" reply", reply
);
907 targetPrivate
->serverScheduled
= FALSE
;
908 _reach_server_target_reconnect(connection
, target
, FALSE
);
916 xpc_release(reqdict
);
922 #pragma mark Reconnect
926 _reach_server_target_reconnect(xpc_connection_t connection
, SCNetworkReachabilityRef target
, Boolean disconnect
)
929 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
931 if (!targetPrivate
->serverActive
) {
932 // if target already removed
937 // if we should first disconnect (unschedule, remove)
938 if (targetPrivate
->serverScheduled
) {
939 (void) _reach_server_target_unschedule(connection
, target
);
942 (void) _reach_server_target_remove(connection
, target
);
944 // server has been restarted
945 targetPrivate
->cycle
= 0;
948 // re-associate with server
949 ok
= _reach_server_target_add(connection
, target
);
951 // if we could not add the target
955 if (!targetPrivate
->serverScheduled
) {
960 // ... and re-schedule with server
961 ok
= _reach_server_target_schedule(connection
, target
);
963 // if we could not reschedule the target
967 // For addresses, update our status now. For names, queries will
968 // be updated with a callback
969 if (targetPrivate
->type
!= reachabilityTypeName
) {
970 __SCNetworkReachabilityPerform(target
);
978 _reach_connection_reconnect(xpc_connection_t connection
)
980 dispatch_sync(_reach_requests_rbt_queue(), ^{
981 rb_tree_t
*rbt
= _reach_requests_rbt();
982 reach_request_t
*request
;
984 RB_TREE_FOREACH(request
, rbt
) {
985 SCNetworkReachabilityRef target
;
987 xpc_retain(connection
);
988 target
= request
->target
;
990 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
991 _reach_server_target_reconnect(connection
, target
, FALSE
);
993 xpc_release(connection
);
1003 #pragma mark SPI (exposed)
1007 _SCNetworkReachabilityServer_snapshot(void)
1012 xpc_object_t reqdict
;
1014 // initialize connection with SCNetworkReachability server
1015 c
= _reach_connection();
1021 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
1024 xpc_dictionary_set_int64(reqdict
, REACH_REQUEST
, REACH_REQUEST_SNAPSHOT
);
1026 // add the process name (for debugging)
1027 add_proc_name(reqdict
);
1032 reply
= xpc_connection_send_message_with_reply_sync(c
, reqdict
);
1033 if (reply
!= NULL
) {
1036 type
= xpc_get_type(reply
);
1037 if (type
== XPC_TYPE_DICTIONARY
) {
1040 status
= xpc_dictionary_get_int64(reply
, REACH_REQUEST_REPLY
);
1041 ok
= (status
== REACH_REQUEST_REPLY_OK
);
1042 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INVALID
)) {
1043 SCLog(TRUE
, LOG_ERR
,
1044 CFSTR("SCNetworkReachability server not available"));
1045 serverAvailable
= FALSE
;
1046 } else if ((type
== XPC_TYPE_ERROR
) && (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
)) {
1047 SCLog(TRUE
, LOG_DEBUG
,
1048 CFSTR("SCNetworkReachability server failure, retrying"));
1052 SCLog(TRUE
, LOG_ERR
,
1053 CFSTR("_snapshot with unexpected reply"));
1054 log_xpc_object(" reply", reply
);
1060 xpc_release(reqdict
);
1067 __SCNetworkReachabilityServer_targetAdd(SCNetworkReachabilityRef target
)
1071 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1073 c
= _reach_connection();
1078 ok
= _reach_server_target_add(c
, target
);
1080 _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverActive
, FALSE
, TRUE
);
1089 __SCNetworkReachabilityServer_targetRemove(SCNetworkReachabilityRef target
)
1093 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1095 if (!targetPrivate
->serverActive
) {
1100 c
= _reach_connection();
1105 ok
= _reach_server_target_remove(c
, target
);
1107 _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverActive
, TRUE
, FALSE
);
1116 __SCNetworkReachabilityServer_targetSchedule(SCNetworkReachabilityRef target
)
1120 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1122 c
= _reach_connection();
1127 _reach_request_add(target
);
1128 ok
= _reach_server_target_schedule(c
, target
);
1130 _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverScheduled
, FALSE
, TRUE
);
1132 _reach_request_remove(target
);
1141 __SCNetworkReachabilityServer_targetStatus(SCNetworkReachabilityRef target
)
1146 c
= _reach_connection();
1151 ok
= _reach_server_target_status(c
, target
);
1158 __SCNetworkReachabilityServer_targetUnschedule(SCNetworkReachabilityRef target
)
1162 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1164 if (!targetPrivate
->serverScheduled
) {
1169 c
= _reach_connection();
1174 ok
= _reach_server_target_unschedule(c
, target
);
1176 _SC_ATOMIC_CMPXCHG(&targetPrivate
->serverScheduled
, TRUE
, FALSE
);
1177 _reach_request_remove(target
);
1179 // if unschedule failed
1187 #endif // HAVE_REACHABILITY_SERVER