]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/reachability/SCNetworkReachabilityServer_client.c
13196ea568d20d6d87184dfca19ccd412a2fe695
[apple/configd.git] / SystemConfiguration.fproj / reachability / SCNetworkReachabilityServer_client.c
1 /*
2 * Copyright (c) 2011-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <CoreFoundation/CoreFoundation.h>
25 #include <SystemConfiguration/SystemConfiguration.h>
26 #include <SystemConfiguration/SCPrivate.h>
27 #include "SCNetworkReachabilityInternal.h"
28
29 #ifdef HAVE_REACHABILITY_SERVER
30
31 #include <xpc/xpc.h>
32 #include <xpc/private.h>
33 #include <sys/rbtree.h>
34
35
36 #pragma mark -
37 #pragma mark Globals
38
39
40 static Boolean serverAvailable = TRUE;
41
42
43 #pragma mark -
44 #pragma mark Support functions
45
46
47 static void
48 log_xpc_object(const char *msg, xpc_object_t obj)
49 {
50 char *desc;
51
52 desc = xpc_copy_description(obj);
53 SCLog(TRUE, LOG_DEBUG, CFSTR("%s = %s"), msg, desc);
54 free(desc);
55 }
56
57
58 #pragma mark -
59 #pragma mark Reachability [RBT] client support
60
61
62 typedef struct {
63 rb_node_t rbn;
64 SCNetworkReachabilityRef target;
65 } reach_request_t;
66
67
68 static int
69 _rbt_compare_transaction_nodes(void *context, const void *n1, const void *n2)
70 {
71 uint64_t a = (uintptr_t)(((reach_request_t *)n1)->target);
72 uint64_t b = (uintptr_t)(((reach_request_t *)n2)->target);
73
74 return (a - b);
75 }
76
77
78 static int
79 _rbt_compare_transaction_key(void *context, const void *n1, const void *key)
80 {
81 uint64_t a = (uintptr_t)(((reach_request_t *)n1)->target);
82 uint64_t b = *(uint64_t *)key;
83
84 return (a - b);
85 }
86
87
88 static rb_tree_t *
89 _reach_requests_rbt()
90 {
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),
96 .rbto_context = NULL
97 };
98 static rb_tree_t rbt;
99
100 dispatch_once(&once, ^{
101 rb_tree_init(&rbt, &ops);
102 });
103
104 return &rbt;
105 }
106
107
108 static dispatch_queue_t
109 _reach_requests_rbt_queue()
110 {
111 static dispatch_once_t once;
112 static dispatch_queue_t q;
113
114 dispatch_once(&once, ^{
115 q = dispatch_queue_create(REACH_SERVICE_NAME ".requests.rbt", NULL);
116 });
117
118 return q;
119 }
120
121
122 static reach_request_t *
123 _reach_request_create(SCNetworkReachabilityRef target)
124 {
125 reach_request_t *request;
126
127 request = calloc(1, sizeof(*request));
128 request->target = CFRetain(target);
129
130 return request;
131 }
132
133
134 static void
135 _reach_request_release(reach_request_t *request)
136 {
137 SCNetworkReachabilityRef target = request->target;
138
139 CFRelease(target);
140 free(request);
141
142 return;
143 }
144
145
146 static void
147 _reach_request_add(SCNetworkReachabilityRef target)
148 {
149 uint64_t target_id = (uintptr_t)target;
150
151 dispatch_sync(_reach_requests_rbt_queue(), ^{
152 rb_tree_t *rbt = _reach_requests_rbt();
153 reach_request_t *request;
154
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)) {
159 __builtin_trap();
160 }
161 }
162 });
163
164 return;
165 }
166
167
168 static void
169 _reach_request_remove(SCNetworkReachabilityRef target)
170 {
171 uint64_t target_id = (uintptr_t)target;
172
173 dispatch_sync(_reach_requests_rbt_queue(), ^{ // FIXME ?? use dispatch_async?
174 rb_tree_t *rbt = _reach_requests_rbt();
175 reach_request_t *request;
176
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);
181 }
182 });
183
184 return;
185 }
186
187
188 static SCNetworkReachabilityRef
189 _reach_request_copy_target(uint64_t target_id)
190 {
191 __block SCNetworkReachabilityRef target = NULL;
192
193 dispatch_sync(_reach_requests_rbt_queue(), ^{
194 rb_tree_t *rbt = _reach_requests_rbt();
195 reach_request_t *request;
196
197 request = rb_tree_find_node(rbt, &target_id);
198 if (request != NULL) {
199 target = request->target;
200 CFRetain(target);
201 }
202 });
203
204 return target;
205 }
206
207
208 #pragma mark -
209 #pragma mark Reachability [XPC] client support
210
211
212 static void
213 handle_reachability_status(SCNetworkReachabilityRef target, xpc_object_t dict)
214 {
215 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
216
217 if (_sc_debug) {
218 SCLog(TRUE, LOG_INFO, CFSTR("%sgot [async] notification"),
219 targetPrivate->log_prefix);
220 // log_xpc_object(" status", dict);
221 }
222
223 __SCNetworkReachabilityPerformConcurrent(target);
224
225 return;
226 }
227
228
229 static void
230 handle_async_notification(SCNetworkReachabilityRef target, xpc_object_t dict)
231 {
232 int64_t op;
233 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
234
235 op = xpc_dictionary_get_int64(dict, MESSAGE_NOTIFY);
236 switch (op) {
237 case MESSAGE_REACHABILITY_STATUS :
238 handle_reachability_status(target, dict);
239 break;
240 default :
241 SCLog(TRUE, LOG_ERR, CFSTR("%sgot [async] unknown reply : %d"),
242 targetPrivate->log_prefix,
243 op);
244 log_xpc_object(" reply", dict);
245 break;
246 }
247
248 return;
249 }
250
251
252 static dispatch_queue_t
253 _reach_xpc_queue()
254 {
255 static dispatch_once_t once;
256 static dispatch_queue_t q;
257
258 dispatch_once(&once, ^{
259 q = dispatch_queue_create(REACH_SERVICE_NAME ".xpc", NULL);
260 });
261
262 return q;
263 }
264
265
266 static void
267 _reach_connection_reconnect(xpc_connection_t connection);
268
269
270 static xpc_connection_t
271 _reach_connection_create()
272 {
273 xpc_connection_t c;
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
279 const char *name;
280 dispatch_queue_t q = _reach_xpc_queue();
281
282 // create XPC connection
283 name = getenv("REACH_SERVER");
284 if ((name == NULL) || (issetugid() != 0)) {
285 name = REACH_SERVICE_NAME;
286 }
287
288 c = xpc_connection_create_mach_service(name, q, flags);
289
290 xpc_connection_set_event_handler(c, ^(xpc_object_t xobj) {
291 xpc_type_t type;
292
293 type = xpc_get_type(xobj);
294 if (type == XPC_TYPE_DICTIONARY) {
295 SCNetworkReachabilityRef target;
296 uint64_t target_id;
297
298 target_id = xpc_dictionary_get_uint64(xobj, REACH_CLIENT_TARGET_ID);
299 if (target_id == 0) {
300 SCLog(TRUE, LOG_ERR,
301 CFSTR("reach client %p: async reply with no target [ID]"),
302 c);
303 log_xpc_object(" reply", xobj);
304 return;
305 }
306
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);
312 return;
313 }
314
315 xpc_retain(xobj);
316 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
317 handle_async_notification(target, xobj);
318 CFRelease(target);
319 xpc_release(xobj);
320 });
321
322 } else if (type == XPC_TYPE_ERROR) {
323 if (xobj == XPC_ERROR_CONNECTION_INVALID) {
324 SCLog(TRUE, LOG_ERR,
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);
331 } else {
332 const char *desc;
333
334 desc = xpc_dictionary_get_string(xobj, XPC_ERROR_KEY_DESCRIPTION);
335 SCLog(TRUE, LOG_ERR,
336 CFSTR("reach client %p: Connection error: %s"),
337 c,
338 desc);
339 }
340
341 } else {
342 SCLog(TRUE, LOG_ERR,
343 CFSTR("reach client %p: unknown event type : %x"),
344 c,
345 type);
346 }
347 });
348 xpc_connection_resume(c);
349
350 return c;
351 }
352
353
354 static xpc_connection_t
355 _reach_connection()
356 {
357 static xpc_connection_t c;
358 static dispatch_once_t once;
359 static dispatch_queue_t q;
360
361 if (!serverAvailable) {
362 // if SCNetworkReachabilty [XPC] server not available
363 return NULL;
364 }
365
366 dispatch_once(&once, ^{
367 q = dispatch_queue_create(REACH_SERVICE_NAME ".connection", NULL);
368 });
369
370 dispatch_sync(q, ^{
371 if (c == NULL) {
372 c = _reach_connection_create();
373 }
374 });
375
376 return c;
377 }
378
379
380 typedef void (^reach_server_reply_handler_t)(xpc_object_t reply);
381
382
383 static void
384 add_proc_name(xpc_object_t reqdict)
385 {
386 static const char *name = NULL;
387 static dispatch_once_t once;
388
389 // add the process name
390 dispatch_once(&once, ^{
391 name = getprogname();
392 });
393 xpc_dictionary_set_string(reqdict, REACH_CLIENT_PROC_NAME, name);
394
395 return;
396 }
397
398
399 static void
400 _reach_server_target_reconnect(xpc_connection_t connection, SCNetworkReachabilityRef target, Boolean disconnect);
401
402
403 static Boolean
404 _reach_server_target_add(xpc_connection_t connection, SCNetworkReachabilityRef target)
405 {
406 Boolean ok = FALSE;
407 xpc_object_t reply;
408 xpc_object_t reqdict;
409 Boolean retry = FALSE;
410 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
411
412 // create message
413 reqdict = xpc_dictionary_create(NULL, NULL, 0);
414
415 // set request
416 xpc_dictionary_set_int64(reqdict, REACH_REQUEST, REACH_REQUEST_CREATE);
417
418 // add reachability target info
419 if (targetPrivate->name != NULL) {
420 xpc_dictionary_set_string(reqdict,
421 REACH_TARGET_NAME,
422 targetPrivate->name);
423 }
424 if (targetPrivate->localAddress != NULL) {
425 xpc_dictionary_set_data(reqdict,
426 REACH_TARGET_LOCAL_ADDR,
427 targetPrivate->localAddress,
428 targetPrivate->localAddress->sa_len);
429 }
430 if (targetPrivate->remoteAddress != NULL) {
431 xpc_dictionary_set_data(reqdict,
432 REACH_TARGET_REMOTE_ADDR,
433 targetPrivate->remoteAddress,
434 targetPrivate->remoteAddress->sa_len);
435 }
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);
443 }
444 if (targetPrivate->onDemandBypass) {
445 xpc_dictionary_set_bool(reqdict,
446 REACH_TARGET_ONDEMAND_BYPASS,
447 TRUE);
448 }
449 if (targetPrivate->resolverBypass) {
450 xpc_dictionary_set_bool(reqdict,
451 REACH_TARGET_RESOLVER_BYPASS,
452 TRUE);
453 }
454
455
456
457 // add the target [ID]
458 xpc_dictionary_set_uint64(reqdict, REACH_CLIENT_TARGET_ID, (uintptr_t)target);
459
460 // add the process name (for debugging)
461 add_proc_name(reqdict);
462
463 retry :
464
465 // send request to the SCNetworkReachability server
466 reply = xpc_connection_send_message_with_reply_sync(connection, reqdict);
467 if (reply != NULL) {
468 xpc_type_t type;
469
470 type = xpc_get_type(reply);
471 if (type == XPC_TYPE_DICTIONARY) {
472 int64_t status;
473
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)) {
477 SCLog(TRUE, LOG_ERR,
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"),
483 target);
484 retry = TRUE;
485 } else {
486 SCLog(TRUE, LOG_ERR,
487 CFSTR("reach target %p: _targetAdd with unexpected reply"),
488 target);
489 log_xpc_object(" reply", reply);
490 }
491
492 xpc_release(reply);
493 }
494
495 if (retry) {
496 retry = FALSE;
497 goto retry;
498 }
499
500 xpc_release(reqdict);
501 return ok;
502 }
503
504
505 static Boolean
506 _reach_server_target_remove(xpc_connection_t connection, SCNetworkReachabilityRef target)
507 {
508 Boolean ok = FALSE;
509 xpc_object_t reply;
510 xpc_object_t reqdict;
511 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
512
513 // create message
514 reqdict = xpc_dictionary_create(NULL, NULL, 0);
515
516 // set request
517 xpc_dictionary_set_int64(reqdict, REACH_REQUEST, REACH_REQUEST_REMOVE);
518
519 // add the target [ID]
520 xpc_dictionary_set_uint64(reqdict, REACH_CLIENT_TARGET_ID, (uintptr_t)target);
521
522 reply = xpc_connection_send_message_with_reply_sync(connection, reqdict);
523 if (reply != NULL) {
524 xpc_type_t type;
525
526 type = xpc_get_type(reply);
527 if (type == XPC_TYPE_DICTIONARY) {
528 int64_t status;
529
530 status = xpc_dictionary_get_int64(reply, REACH_REQUEST_REPLY);
531 switch (status) {
532 case REACH_REQUEST_REPLY_OK :
533 ok = TRUE;
534 break;
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
538 // remove.
539 ok = TRUE;
540 break;
541 default : {
542 SCLog(TRUE, LOG_ERR, CFSTR("%s target remove failed"),
543 targetPrivate->log_prefix);
544 log_xpc_object(" reply", reply);
545 }
546 }
547 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INVALID)) {
548 SCLog(TRUE, LOG_ERR,
549 CFSTR("SCNetworkReachability server not available"));
550 serverAvailable = FALSE;
551 ok = TRUE;
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"),
555 target);
556 ok = TRUE;
557 } else {
558 SCLog(TRUE, LOG_ERR,
559 CFSTR("reach target %p: _targetRemove with unexpected reply"),
560 target);
561 log_xpc_object(" reply", reply);
562 }
563
564 xpc_release(reply);
565 }
566
567 xpc_release(reqdict);
568 return ok;
569 }
570
571
572 static Boolean
573 _reach_server_target_schedule(xpc_connection_t connection, SCNetworkReachabilityRef target)
574 {
575 Boolean ok = FALSE;
576 xpc_object_t reply;
577 xpc_object_t reqdict;
578 Boolean retry = FALSE;
579 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
580
581 // create message
582 reqdict = xpc_dictionary_create(NULL, NULL, 0);
583
584 // set request
585 xpc_dictionary_set_int64(reqdict, REACH_REQUEST, REACH_REQUEST_SCHEDULE);
586
587 // add the target [ID]
588 xpc_dictionary_set_uint64(reqdict, REACH_CLIENT_TARGET_ID, (uintptr_t)target);
589
590 retry :
591
592 reply = xpc_connection_send_message_with_reply_sync(connection, reqdict);
593 if (reply != NULL) {
594 xpc_type_t type;
595
596 type = xpc_get_type(reply);
597 if (type == XPC_TYPE_DICTIONARY) {
598 int64_t status;
599
600 status = xpc_dictionary_get_int64(reply, REACH_REQUEST_REPLY);
601 switch (status) {
602 case REACH_REQUEST_REPLY_OK :
603 ok = TRUE;
604 break;
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.
609 retry = TRUE;
610 break;
611 default : {
612 SCLog(TRUE, LOG_ERR, CFSTR("%s target schedule failed"),
613 targetPrivate->log_prefix);
614 log_xpc_object(" reply", reply);
615 }
616 }
617
618 if (ok) {
619 CFRetain(target);
620 }
621 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INVALID)) {
622 SCLog(TRUE, LOG_ERR,
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"),
628 target);
629 retry = TRUE;
630 } else {
631 SCLog(TRUE, LOG_ERR,
632 CFSTR("reach target %p: _targetSchedule with unexpected reply"),
633 target);
634 log_xpc_object(" reply", reply);
635 }
636
637 xpc_release(reply);
638 }
639
640 if (retry) {
641 // reconnect
642 _reach_server_target_reconnect(connection, target, FALSE);
643
644 // and retry
645 retry = FALSE;
646 goto retry;
647 }
648
649 xpc_release(reqdict);
650 return ok;
651 }
652
653
654 static void
655 _reach_reply_set_reachability(SCNetworkReachabilityRef target,
656 xpc_object_t reply)
657 {
658 char *if_name;
659 size_t len = 0;
660
661 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
662
663 targetPrivate->serverInfo.cycle = xpc_dictionary_get_uint64(reply,
664 REACH_STATUS_CYCLE);
665
666 targetPrivate->serverInfo.flags = xpc_dictionary_get_uint64(reply,
667 REACH_STATUS_FLAGS);
668
669 targetPrivate->serverInfo.if_index = xpc_dictionary_get_uint64(reply,
670 REACH_STATUS_IF_INDEX);
671
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,
675 &len);
676 if ((if_name != NULL) && (len > 0)) {
677 if (len > sizeof(targetPrivate->serverInfo.if_name)) {
678 len = sizeof(targetPrivate->serverInfo.if_name);
679 }
680
681 bcopy(if_name, targetPrivate->serverInfo.if_name, len);
682 }
683
684 targetPrivate->serverInfo.sleeping = xpc_dictionary_get_bool(reply,
685 REACH_STATUS_SLEEPING);
686
687 if (targetPrivate->type == reachabilityTypeName) {
688 xpc_object_t addresses;
689
690 if (targetPrivate->resolvedAddresses != NULL) {
691 CFRelease(targetPrivate->resolvedAddresses);
692 targetPrivate->resolvedAddresses = NULL;
693 }
694
695 targetPrivate->resolvedError = xpc_dictionary_get_int64(reply,
696 REACH_STATUS_RESOLVED_ERROR);
697
698 addresses = xpc_dictionary_get_value(reply, REACH_STATUS_RESOLVED_ADDRESSES);
699 if ((addresses != NULL) && (xpc_get_type(addresses) != XPC_TYPE_ARRAY)) {
700 addresses = NULL;
701 }
702
703 if ((targetPrivate->resolvedError == NETDB_SUCCESS) && (addresses != NULL)) {
704 int i;
705 int n;
706 CFMutableArrayRef newAddresses;
707
708 newAddresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
709
710 n = xpc_array_get_count(addresses);
711 for (i = 0; i < n; i++) {
712 struct addrinfo *sa;
713 size_t len;
714 CFDataRef newAddress;
715
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);
720 }
721
722 targetPrivate->resolvedAddresses = newAddresses;
723 } else {
724 /* save the error associated with the attempt to resolve the name */
725 targetPrivate->resolvedAddresses = CFRetain(kCFNull);
726 }
727 targetPrivate->needResolve = FALSE;
728 }
729
730 return;
731 }
732
733
734 static Boolean
735 _reach_server_target_status(xpc_connection_t connection, SCNetworkReachabilityRef target)
736 {
737 Boolean ok = FALSE;
738 xpc_object_t reply;
739 xpc_object_t reqdict;
740 Boolean retry = FALSE;
741 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
742
743 if (_sc_debug) {
744 CFStringRef str;
745
746 str = _SCNetworkReachabilityCopyTargetDescription(target);
747 SCLog(TRUE, LOG_INFO, CFSTR("%scheckReachability(%@)"),
748 targetPrivate->log_prefix,
749 str);
750 CFRelease(str);
751 }
752
753 // create message
754 reqdict = xpc_dictionary_create(NULL, NULL, 0);
755
756 // set request
757 xpc_dictionary_set_int64(reqdict, REACH_REQUEST, REACH_REQUEST_STATUS);
758
759 // add the target [ID]
760 xpc_dictionary_set_uint64(reqdict, REACH_CLIENT_TARGET_ID, (uintptr_t)target);
761
762 retry :
763
764 reply = xpc_connection_send_message_with_reply_sync(connection, reqdict);
765 if (reply != NULL) {
766 xpc_type_t type;
767
768 type = xpc_get_type(reply);
769 if (type == XPC_TYPE_DICTIONARY) {
770 int64_t status;
771
772 status = xpc_dictionary_get_int64(reply, REACH_REQUEST_REPLY);
773 switch (status) {
774 case REACH_REQUEST_REPLY_OK :
775 ok = TRUE;
776 break;
777 case REACH_REQUEST_REPLY_UNKNOWN :
778 // target not known by the server (most likely due to a
779 // SCNetworkReachability server failure), re-establish
780 // and retry status.
781 retry = TRUE;
782 break;
783 default :
784 SCLog(TRUE, LOG_INFO, CFSTR("%s target status failed"),
785 targetPrivate->log_prefix);
786 log_xpc_object(" reply", reply);
787 }
788
789 if (ok) {
790 _reach_reply_set_reachability(target, reply);
791
792 if (_sc_debug) {
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" : "");
802 }
803 if (targetPrivate->serverInfo.cycle != targetPrivate->cycle) {
804 SCLog(TRUE, LOG_INFO, CFSTR("%s forced"),
805 targetPrivate->log_prefix);
806 }
807 }
808 }
809 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INVALID)) {
810 SCLog(TRUE, LOG_ERR,
811 CFSTR("SCNetworkReachability server not available"));
812 serverAvailable = FALSE;
813 ok = TRUE;
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"),
817 target);
818 retry = TRUE;
819 } else {
820 SCLog(TRUE, LOG_ERR,
821 CFSTR("reach target %p: _targetStatus with unexpected reply"),
822 target);
823 log_xpc_object(" reply", reply);
824 }
825
826 xpc_release(reply);
827 }
828
829 if (retry) {
830 // reconnect
831 _reach_server_target_reconnect(connection, target, FALSE);
832
833 // and retry
834 retry = FALSE;
835 goto retry;
836 }
837
838 xpc_release(reqdict);
839 return ok;
840 }
841
842
843 static Boolean
844 _reach_server_target_unschedule(xpc_connection_t connection, SCNetworkReachabilityRef target)
845 {
846 Boolean ok = FALSE;
847 xpc_object_t reply;
848 xpc_object_t reqdict;
849 Boolean retry = FALSE;
850 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
851
852 // create message
853 reqdict = xpc_dictionary_create(NULL, NULL, 0);
854
855 // set request
856 xpc_dictionary_set_int64(reqdict, REACH_REQUEST, REACH_REQUEST_UNSCHEDULE);
857
858 // add the target [ID]
859 xpc_dictionary_set_uint64(reqdict, REACH_CLIENT_TARGET_ID, (uintptr_t)target);
860
861 reply = xpc_connection_send_message_with_reply_sync(connection, reqdict);
862 if (reply != NULL) {
863 xpc_type_t type;
864
865 type = xpc_get_type(reply);
866 if (type == XPC_TYPE_DICTIONARY) {
867 int64_t status;
868
869 status = xpc_dictionary_get_int64(reply, REACH_REQUEST_REPLY);
870 switch (status) {
871 case REACH_REQUEST_REPLY_OK :
872 ok = TRUE;
873 break;
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.
878 retry = TRUE;
879 break;
880 default :
881 SCLog(TRUE, LOG_INFO, CFSTR("%s target unschedule failed"),
882 targetPrivate->log_prefix);
883 log_xpc_object(" reply", reply);
884 }
885 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INVALID)) {
886 SCLog(TRUE, LOG_ERR,
887 CFSTR("SCNetworkReachability server not available"));
888 serverAvailable = FALSE;
889 ok = TRUE;
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)"),
893 target);
894 retry = TRUE;
895 } else {
896 SCLog(TRUE, LOG_ERR,
897 CFSTR("reach target %p: _targetUnschedule with unexpected reply"),
898 target);
899 log_xpc_object(" reply", reply);
900 }
901
902 xpc_release(reply);
903 }
904
905 if (retry) {
906 // reconnect
907 targetPrivate->serverScheduled = FALSE;
908 _reach_server_target_reconnect(connection, target, FALSE);
909 ok = TRUE;
910 }
911
912 if (ok) {
913 CFRelease(target);
914 }
915
916 xpc_release(reqdict);
917 return ok;
918 }
919
920
921 #pragma mark -
922 #pragma mark Reconnect
923
924
925 static void
926 _reach_server_target_reconnect(xpc_connection_t connection, SCNetworkReachabilityRef target, Boolean disconnect)
927 {
928 Boolean ok;
929 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
930
931 if (!targetPrivate->serverActive) {
932 // if target already removed
933 return;
934 }
935
936 if (disconnect) {
937 // if we should first disconnect (unschedule, remove)
938 if (targetPrivate->serverScheduled) {
939 (void) _reach_server_target_unschedule(connection, target);
940 }
941
942 (void) _reach_server_target_remove(connection, target);
943 } else {
944 // server has been restarted
945 targetPrivate->cycle = 0;
946 }
947
948 // re-associate with server
949 ok = _reach_server_target_add(connection, target);
950 if (!ok) {
951 // if we could not add the target
952 return;
953 }
954
955 if (!targetPrivate->serverScheduled) {
956 // if not scheduled
957 return;
958 }
959
960 // ... and re-schedule with server
961 ok = _reach_server_target_schedule(connection, target);
962 if (!ok) {
963 // if we could not reschedule the target
964 return;
965 }
966
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);
971 }
972
973 return;
974 }
975
976
977 static void
978 _reach_connection_reconnect(xpc_connection_t connection)
979 {
980 dispatch_sync(_reach_requests_rbt_queue(), ^{
981 rb_tree_t *rbt = _reach_requests_rbt();
982 reach_request_t *request;
983
984 RB_TREE_FOREACH(request, rbt) {
985 SCNetworkReachabilityRef target;
986
987 xpc_retain(connection);
988 target = request->target;
989 CFRetain(target);
990 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
991 _reach_server_target_reconnect(connection, target, FALSE);
992 CFRelease(target);
993 xpc_release(connection);
994 });
995 }
996 });
997
998 return;
999 }
1000
1001
1002 #pragma mark -
1003 #pragma mark SPI (exposed)
1004
1005
1006 Boolean
1007 _SCNetworkReachabilityServer_snapshot(void)
1008 {
1009 xpc_connection_t c;
1010 Boolean ok = FALSE;
1011 xpc_object_t reply;
1012 xpc_object_t reqdict;
1013
1014 // initialize connection with SCNetworkReachability server
1015 c = _reach_connection();
1016 if (c == NULL) {
1017 return FALSE;
1018 }
1019
1020 // create message
1021 reqdict = xpc_dictionary_create(NULL, NULL, 0);
1022
1023 // set request
1024 xpc_dictionary_set_int64(reqdict, REACH_REQUEST, REACH_REQUEST_SNAPSHOT);
1025
1026 // add the process name (for debugging)
1027 add_proc_name(reqdict);
1028
1029 retry :
1030
1031 // send request
1032 reply = xpc_connection_send_message_with_reply_sync(c, reqdict);
1033 if (reply != NULL) {
1034 xpc_type_t type;
1035
1036 type = xpc_get_type(reply);
1037 if (type == XPC_TYPE_DICTIONARY) {
1038 int64_t status;
1039
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"));
1049 xpc_release(reply);
1050 goto retry;
1051 } else {
1052 SCLog(TRUE, LOG_ERR,
1053 CFSTR("_snapshot with unexpected reply"));
1054 log_xpc_object(" reply", reply);
1055 }
1056
1057 xpc_release(reply);
1058 }
1059
1060 xpc_release(reqdict);
1061 return ok;
1062 }
1063
1064
1065 __private_extern__
1066 Boolean
1067 __SCNetworkReachabilityServer_targetAdd(SCNetworkReachabilityRef target)
1068 {
1069 xpc_connection_t c;
1070 Boolean ok;
1071 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1072
1073 c = _reach_connection();
1074 if (c == NULL) {
1075 return FALSE;
1076 }
1077
1078 ok = _reach_server_target_add(c, target);
1079 if (ok) {
1080 _SC_ATOMIC_CMPXCHG(&targetPrivate->serverActive, FALSE, TRUE);
1081 }
1082
1083 return ok;
1084 }
1085
1086
1087 __private_extern__
1088 void
1089 __SCNetworkReachabilityServer_targetRemove(SCNetworkReachabilityRef target)
1090 {
1091 xpc_connection_t c;
1092 Boolean ok;
1093 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1094
1095 if (!targetPrivate->serverActive) {
1096 // if not active
1097 return;
1098 }
1099
1100 c = _reach_connection();
1101 if (c == NULL) {
1102 return;
1103 }
1104
1105 ok = _reach_server_target_remove(c, target);
1106 if (ok) {
1107 _SC_ATOMIC_CMPXCHG(&targetPrivate->serverActive, TRUE, FALSE);
1108 }
1109
1110 return;
1111 }
1112
1113
1114 __private_extern__
1115 Boolean
1116 __SCNetworkReachabilityServer_targetSchedule(SCNetworkReachabilityRef target)
1117 {
1118 xpc_connection_t c;
1119 Boolean ok;
1120 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1121
1122 c = _reach_connection();
1123 if (c == NULL) {
1124 return FALSE;
1125 }
1126
1127 _reach_request_add(target);
1128 ok = _reach_server_target_schedule(c, target);
1129 if (ok) {
1130 _SC_ATOMIC_CMPXCHG(&targetPrivate->serverScheduled, FALSE, TRUE);
1131 } else {
1132 _reach_request_remove(target);
1133 }
1134
1135 return ok;
1136 }
1137
1138
1139 __private_extern__
1140 Boolean
1141 __SCNetworkReachabilityServer_targetStatus(SCNetworkReachabilityRef target)
1142 {
1143 xpc_connection_t c;
1144 Boolean ok;
1145
1146 c = _reach_connection();
1147 if (c == NULL) {
1148 return FALSE;
1149 }
1150
1151 ok = _reach_server_target_status(c, target);
1152 return ok;
1153 }
1154
1155
1156 __private_extern__
1157 Boolean
1158 __SCNetworkReachabilityServer_targetUnschedule(SCNetworkReachabilityRef target)
1159 {
1160 xpc_connection_t c;
1161 Boolean ok;
1162 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1163
1164 if (!targetPrivate->serverScheduled) {
1165 // if not scheduled
1166 return TRUE;
1167 }
1168
1169 c = _reach_connection();
1170 if (c == NULL) {
1171 return FALSE;
1172 }
1173
1174 ok = _reach_server_target_unschedule(c, target);
1175 if (ok) {
1176 _SC_ATOMIC_CMPXCHG(&targetPrivate->serverScheduled, TRUE, FALSE);
1177 _reach_request_remove(target);
1178 } else {
1179 // if unschedule failed
1180 }
1181
1182 return ok;
1183 }
1184
1185
1186
1187 #endif // HAVE_REACHABILITY_SERVER