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