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