]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/reachability/SCNetworkReachabilityServer_server.c
configd-453.19.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / reachability / SCNetworkReachabilityServer_server.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 <fcntl.h>
32 #include <paths.h>
33 #include <CommonCrypto/CommonDigest.h>
34 #include <dispatch/dispatch.h>
35 #include <dispatch/private.h>
36 #include <xpc/xpc.h>
37 #include <xpc/private.h>
38
39 #include "rb.h"
40
41
42 #pragma mark -
43 #pragma mark Globals
44
45
46 /*
47 * S_debug
48 * A boolean that enables additional logging.
49 */
50 static boolean_t S_debug = FALSE;
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(S_debug, LOG_INFO, CFSTR("%s = %s"), msg, desc);
64 free(desc);
65 }
66
67
68 static __inline__ void
69 my_CFDictionaryApplyFunction(CFDictionaryRef theDict,
70 CFDictionaryApplierFunction applier,
71 void *context)
72 {
73 CFAllocatorRef myAllocator;
74 CFDictionaryRef myDict;
75
76 myAllocator = CFGetAllocator(theDict);
77 myDict = CFDictionaryCreateCopy(myAllocator, theDict);
78 CFDictionaryApplyFunction(myDict, applier, context);
79 CFRelease(myDict);
80 return;
81 }
82
83
84 #pragma mark -
85 #pragma mark SCNetworkReachability target support
86
87
88 static CFMutableDictionaryRef reach_digest_map;
89
90
91 static dispatch_queue_t
92 _server_concurrent_queue()
93 {
94 static dispatch_once_t once;
95 static dispatch_queue_t q;
96
97 dispatch_once(&once, ^{
98 q = dispatch_queue_create(REACH_SERVICE_NAME ".concurrent",
99 DISPATCH_QUEUE_CONCURRENT);
100 dispatch_queue_set_width(q, 32);
101 });
102
103 return q;
104 }
105
106
107 static dispatch_queue_t
108 _server_digest_queue()
109 {
110 static dispatch_once_t once;
111 static dispatch_queue_t q;
112
113 dispatch_once(&once, ^{
114 q = dispatch_queue_create(REACH_SERVICE_NAME ".digest", NULL);
115 });
116
117 return q;
118 }
119
120
121 static dispatch_group_t
122 _target_group(SCNetworkReachabilityRef target)
123 {
124 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
125
126 return targetPrivate->serverGroup;
127 }
128
129
130 static dispatch_queue_t
131 _target_queue(SCNetworkReachabilityRef target)
132 {
133 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
134
135 return targetPrivate->serverQueue;
136 }
137
138
139 #pragma mark -
140
141
142 /*
143 * _target_reference_add
144 *
145 * Note: use dispatch_sync(_server_digest_queue(), ^{ ... });
146 */
147 static void
148 _target_reference_add(SCNetworkReachabilityRef target, CFDataRef digest, xpc_connection_t connection)
149 {
150 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
151
152 // take a reference to the target
153 CFRetain(target);
154
155 // ensure that we have a dispatch group
156 if (targetPrivate->serverGroup == NULL) {
157 targetPrivate->serverGroup = dispatch_group_create();
158 }
159
160 // ensure that we have a dispatch queue
161 if (targetPrivate->serverQueue == NULL) {
162 char qname[256];
163
164 snprintf(qname, sizeof(qname), "com.apple.SCNetworkReachability.%p.server", target);
165 targetPrivate->serverQueue = dispatch_queue_create(qname, NULL);
166 }
167
168 // bump the reference count
169 if (_SC_ATOMIC_INC(&targetPrivate->serverReferences) == 0) {
170 // and maintain a digest-->target mapping
171 targetPrivate->serverDigest = CFRetain(digest);
172 CFDictionarySetValue(reach_digest_map, digest, target);
173 }
174
175 if (S_debug) {
176 CFStringRef str;
177
178 str = _SCNetworkReachabilityCopyTargetDescription(target);
179 SCLog(TRUE, LOG_INFO,
180 CFSTR("<%p> target %p: reference added (%@, %d)"),
181 connection,
182 target,
183 str,
184 targetPrivate->serverReferences);
185 CFRelease(str);
186 }
187
188 return;
189 }
190
191
192 /*
193 * _target_reference_remove
194 *
195 * Note: use dispatch_sync(_server_digest_queue(), ^{ ... });
196 */
197 static void
198 _target_reference_remove(SCNetworkReachabilityRef target, xpc_connection_t connection)
199 {
200 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
201
202 // drop the reference count
203 if (_SC_ATOMIC_DEC(&targetPrivate->serverReferences) == 0) {
204 /*
205 * if that was the last reference, we no longer need to
206 * keep the digest-->target mapping
207 */
208 CFDictionaryRemoveValue(reach_digest_map, targetPrivate->serverDigest);
209 CFRelease(targetPrivate->serverDigest);
210 targetPrivate->serverDigest = NULL;
211 }
212
213 if (S_debug) {
214 SCLog(TRUE, LOG_INFO,
215 CFSTR("<%p> target %p: reference removed (%d)"),
216 connection,
217 target,
218 targetPrivate->serverReferences);
219 }
220
221 // release a reference to the target
222 CFRelease(target);
223
224 return;
225 }
226
227
228 #pragma mark -
229
230
231 #define MUTEX_LOCK(m) { \
232 int _lock_ = (pthread_mutex_lock(m) == 0); \
233 assert(_lock_); \
234 }
235
236 #define MUTEX_UNLOCK(m) { \
237 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
238 assert(_unlock_); \
239 }
240
241
242 static void
243 _target_reply_add_reachability(SCNetworkReachabilityRef target,
244 xpc_object_t reply)
245 {
246 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
247
248 MUTEX_LOCK(&targetPrivate->lock);
249
250 xpc_dictionary_set_uint64(reply,
251 REACH_STATUS_CYCLE,
252 targetPrivate->info.cycle);
253 xpc_dictionary_set_uint64(reply,
254 REACH_STATUS_FLAGS,
255 targetPrivate->info.flags);
256 xpc_dictionary_set_uint64(reply,
257 REACH_STATUS_IF_INDEX,
258 targetPrivate->info.if_index);
259 xpc_dictionary_set_data (reply,
260 REACH_STATUS_IF_NAME,
261 targetPrivate->info.if_name,
262 sizeof(targetPrivate->info.if_name));
263 xpc_dictionary_set_bool (reply,
264 REACH_STATUS_SLEEPING,
265 targetPrivate->info.sleeping);
266 if (targetPrivate->type == reachabilityTypeName) {
267 if (isA_CFArray(targetPrivate->resolvedAddress)) {
268 xpc_object_t addresses;
269 CFIndex i;
270 CFIndex n;
271
272 addresses = xpc_array_create(NULL, 0);
273
274 n = CFArrayGetCount(targetPrivate->resolvedAddress);
275 for (i = 0; i < n; i++) {
276 CFDataRef address;
277
278 address = CFArrayGetValueAtIndex(targetPrivate->resolvedAddress, i);
279 xpc_array_set_data(addresses,
280 XPC_ARRAY_APPEND,
281 CFDataGetBytePtr(address),
282 CFDataGetLength(address));
283 }
284
285 xpc_dictionary_set_value(reply,
286 REACH_STATUS_RESOLVED_ADDRESS,
287 addresses);
288 xpc_release(addresses);
289 }
290 xpc_dictionary_set_int64(reply,
291 REACH_STATUS_RESOLVED_ADDRESS_ERROR,
292 targetPrivate->resolvedAddressError);
293 }
294
295 MUTEX_UNLOCK(&targetPrivate->lock);
296
297 return;
298 }
299
300
301 #pragma mark -
302
303
304 typedef struct {
305 xpc_connection_t connection;
306 uint64_t target_id;
307 } reach_watcher_key_t;
308
309 typedef struct {
310 unsigned int n_changes;
311 } reach_watcher_val_t;
312
313
314 static CFDataRef
315 _target_watcher_key_create(xpc_connection_t connection,
316 uint64_t target_id)
317 {
318 CFDataRef key;
319 reach_watcher_key_t watcher_key;
320
321 watcher_key.connection = connection;
322 watcher_key.target_id = target_id;
323
324 key = CFDataCreate(NULL, (UInt8 *)&watcher_key, sizeof(watcher_key));
325 return key;
326 }
327
328
329 static Boolean
330 _target_watcher_add(SCNetworkReachabilityRef target,
331 xpc_connection_t connection,
332 uint64_t target_id)
333 {
334 __block Boolean ok = TRUE;
335 dispatch_queue_t q;
336
337 q = _target_queue(target);
338 dispatch_sync(q, ^{
339 CFDataRef key;
340 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
341
342 if (targetPrivate->serverWatchers == NULL) {
343 ok = SCNetworkReachabilitySetDispatchQueue(target, q);
344 if (!ok) {
345 SCLog(TRUE, LOG_ERR,
346 CFSTR("<%p> target %p: _watcher_add SCNetworkReachabilitySetDispatchQueue() failed: %s"),
347 connection,
348 target,
349 SCErrorString(SCError()));
350 return;
351 }
352
353 targetPrivate->serverWatchers = CFDictionaryCreateMutable(NULL,
354 0,
355 &kCFTypeDictionaryKeyCallBacks,
356 &kCFTypeDictionaryValueCallBacks);
357 }
358
359 xpc_retain(connection);
360
361 key = _target_watcher_key_create(connection, target_id);
362 if (CFDictionaryContainsKey(targetPrivate->serverWatchers, key)) {
363 SCLog(TRUE, LOG_ERR,
364 CFSTR("<%p> target %p: watcher not added, c=0x%0llx, \"serverWatchers\" key exists"),
365 connection,
366 target,
367 target_id);
368 } else {
369 CFDataRef val;
370 static const reach_watcher_val_t watcher_val0 = { 0 };
371
372 val = CFDataCreate(NULL, (UInt8 *)&watcher_val0, sizeof(watcher_val0));
373 CFDictionaryAddValue(targetPrivate->serverWatchers, key, val);
374 CFRelease(val);
375
376 if (S_debug) {
377 SCLog(TRUE, LOG_INFO,
378 CFSTR("<%p> target %p: watcher added, c=0x%0llx, n=%d"),
379 connection,
380 target,
381 target_id,
382 CFDictionaryGetCount(targetPrivate->serverWatchers));
383 }
384 }
385 CFRelease(key);
386 });
387
388 return ok;
389 }
390
391
392 static Boolean
393 _target_watcher_checkin(SCNetworkReachabilityRef target,
394 xpc_connection_t connection,
395 uint64_t target_id)
396 {
397 __block Boolean scheduled = FALSE;
398
399 dispatch_sync(_target_queue(target), ^{
400 CFDataRef key;
401 unsigned int n;
402 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
403 CFDataRef val;
404 reach_watcher_val_t *watcher_val;
405
406 if (targetPrivate->serverWatchers == NULL) {
407 // if no watchers
408 return;
409 }
410
411 key = _target_watcher_key_create(connection, target_id);
412 val = CFDictionaryGetValue(targetPrivate->serverWatchers, key);
413 CFRelease(key);
414 if (val == NULL) {
415 // if the target [for this client] was not scheduled
416 return;
417 }
418
419 // indicate that the target was scheduled
420 scheduled = TRUE;
421
422 /*
423 * and note that the reachability flags for this target have
424 * been picked up by the client
425 */
426 /* ALIGN: CF aligns to at least >8 byte boundries */
427 watcher_val = (reach_watcher_val_t *)(void *)CFDataGetBytePtr(val);
428 n = _SC_ATOMIC_ZERO(&watcher_val->n_changes);
429 if (S_debug && (n > 0)) {
430 SCLog(TRUE, LOG_INFO,
431 CFSTR("<%p> target %p: SCNetworkReachabilityGetFlags() after %d notification%s"),
432 connection,
433 target,
434 n,
435 (n == 1) ? "" : "s");
436 }
437 });
438
439 return scheduled;
440 }
441
442
443 static Boolean
444 _target_watcher_remove(SCNetworkReachabilityRef target,
445 xpc_connection_t connection,
446 uint64_t target_id)
447 {
448 __block Boolean ok = TRUE;
449
450 dispatch_sync(_target_queue(target), ^{
451 CFDataRef key;
452 CFIndex n;
453 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
454
455 if (targetPrivate->serverWatchers == NULL) {
456 SCLog(TRUE, LOG_ERR,
457 CFSTR("<%p> target %p: watcher not removed, c=0x%0llx, no \"serverWatchers\""),
458 connection,
459 target,
460 target_id);
461 return;
462 }
463
464 key = _target_watcher_key_create(connection, target_id);
465 if (!CFDictionaryContainsKey(targetPrivate->serverWatchers, key)) {
466 SCLog(TRUE, LOG_ERR,
467 CFSTR("<%p> target %p: watcher not removed, c=0x%0llx, no \"serverWatchers\" key"),
468 connection,
469 target,
470 target_id);
471 CFRelease(key);
472 return;
473 }
474
475 CFDictionaryRemoveValue(targetPrivate->serverWatchers, key);
476 xpc_release(connection);
477 CFRelease(key);
478
479 n = CFDictionaryGetCount(targetPrivate->serverWatchers);
480
481 if (S_debug) {
482 SCLog(TRUE, LOG_INFO,
483 CFSTR("<%p> target %p: watcher removed, c=0x%0llx, n=%d"),
484 connection,
485 target, // server
486 target_id, // client
487 n);
488 }
489
490 if (n == 0) {
491 CFRelease(targetPrivate->serverWatchers);
492 targetPrivate->serverWatchers = NULL;
493
494 ok = SCNetworkReachabilitySetDispatchQueue(target, NULL);
495 if (!ok) {
496 SCLog(TRUE, LOG_ERR,
497 CFSTR("<%p> target %p: _watcher_remove SCNetworkReachabilitySetDispatchQueue() failed: %s"),
498 connection,
499 target,
500 SCErrorString(SCError()));
501 return;
502 }
503
504 // no more watchers, flags are no longer valid
505 (void) _SC_ATOMIC_CMPXCHG(&targetPrivate->serverInfoValid, TRUE, FALSE);
506 }
507 });
508
509 return ok;
510 }
511
512
513 #pragma mark -
514 #pragma mark Reachability [RBT] client support
515
516
517 typedef struct {
518 struct rb_node rbn;
519 xpc_connection_t connection;
520 pid_t pid;
521 const char *proc_name;
522 CFMutableDictionaryRef targets; // target_id --> SCNetworkReachabilityRef
523 } reach_client_t;
524
525
526 #define RBNODE_TO_REACH_CLIENT(node) \
527 ((reach_client_t *)((uintptr_t)node - offsetof(reach_client_t, rbn)))
528
529
530 static int
531 _rbt_compare_transaction_nodes(const struct rb_node *n1, const struct rb_node *n2)
532 {
533 uint64_t a = (uintptr_t)RBNODE_TO_REACH_CLIENT(n1)->connection;
534 uint64_t b = (uintptr_t)RBNODE_TO_REACH_CLIENT(n2)->connection;
535
536 return (a - b);
537 }
538
539
540 static int
541 _rbt_compare_transaction_key(const struct rb_node *n1, const void *key)
542 {
543 uint64_t a = (uintptr_t)RBNODE_TO_REACH_CLIENT(n1)->connection;
544 uint64_t b = *(uintptr_t *)key;
545
546 return (a - b);
547 }
548
549
550 static struct rb_tree *
551 _reach_clients_rbt()
552 {
553 static dispatch_once_t once;
554 static const struct rb_tree_ops ops = {
555 .rbto_compare_nodes = _rbt_compare_transaction_nodes,
556 .rbto_compare_key = _rbt_compare_transaction_key,
557 };
558 static struct rb_tree rbtree;
559
560 dispatch_once(&once, ^{
561 rb_tree_init(&rbtree, &ops);
562 });
563
564 return &rbtree;
565 }
566
567
568 static dispatch_queue_t
569 _reach_connection_queue()
570 {
571 static dispatch_once_t once;
572 static dispatch_queue_t q;
573
574 dispatch_once(&once, ^{
575 q = dispatch_queue_create(REACH_SERVICE_NAME ".connection", NULL);
576 });
577
578 return q;
579 }
580
581
582 static reach_client_t *
583 _reach_client_create(xpc_connection_t connection)
584 {
585 reach_client_t *client;
586
587 client = calloc(1, sizeof(*client));
588 client->connection = connection;
589 client->pid = xpc_connection_get_pid(connection);
590 client->proc_name = NULL;
591 client->targets = CFDictionaryCreateMutable(NULL,
592 0,
593 &kCFTypeDictionaryKeyCallBacks,
594 &kCFTypeDictionaryValueCallBacks);
595
596 return client;
597 }
598
599
600 static void
601 _reach_client_release(reach_client_t *client)
602 {
603 if (client->proc_name != NULL) {
604 free((void *)client->proc_name);
605 }
606 CFRelease(client->targets);
607 free(client);
608 return;
609 }
610
611
612 static void
613 _reach_client_remove_target(const void *key, const void *value, void *context)
614 {
615 xpc_connection_t connection = (xpc_connection_t)context;
616 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value;
617 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
618
619 // check if we have anyone watching this target
620 if (targetPrivate->serverWatchers != NULL) {
621 CFIndex n;
622
623 n = CFDictionaryGetCount(targetPrivate->serverWatchers);
624 if (n > 0) {
625 CFIndex i;
626 const void * watchers_q[32];
627 const void ** watchers = watchers_q;
628
629 if (n > sizeof(watchers_q)/sizeof(watchers[0])) {
630 watchers = CFAllocatorAllocate(NULL, n * sizeof(CFDataRef), 0);
631 }
632 CFDictionaryGetKeysAndValues(targetPrivate->serverWatchers, watchers, NULL);
633
634 for (i = 0; i < n; i++) {
635 CFDataRef key;
636 reach_watcher_key_t *watcher_key;
637
638 key = (CFDataRef)watchers[i];
639 /* ALIGN: CF aligns to >8 byte boundries */
640 watcher_key = (reach_watcher_key_t *)(void *)CFDataGetBytePtr(key);
641 if (watcher_key->connection == connection) {
642 // remove watcher references for THIS connection
643 _target_watcher_remove(target,
644 watcher_key->connection,
645 watcher_key->target_id);
646 }
647 }
648
649 if (watchers != watchers_q) {
650 CFAllocatorDeallocate(NULL, watchers);
651 }
652 }
653 }
654
655 // remove our reference to this target
656 dispatch_sync(_server_digest_queue(), ^{
657 _target_reference_remove(target, connection);
658 });
659
660 return;
661 }
662
663
664 static void
665 _reach_client_remove(xpc_connection_t connection)
666 {
667 struct rb_tree *rbtree = _reach_clients_rbt();
668 struct rb_node *rbn;
669
670 rbn = rb_tree_find_node(rbtree, &connection);
671 if (rbn != NULL) {
672 reach_client_t *client;
673
674 client = RBNODE_TO_REACH_CLIENT(rbn);
675
676 // remove any remaining target references (for this client)
677 my_CFDictionaryApplyFunction(client->targets,
678 _reach_client_remove_target,
679 (void *)connection);
680
681 rb_tree_remove_node(rbtree, rbn);
682 _reach_client_release(client);
683 } else {
684 SCLog(TRUE, LOG_ERR,
685 CFSTR("<%p> _reach_client_remove: unexpected client"),
686 connection);
687 }
688
689 return;
690 }
691
692
693 static __inline__ CFDataRef
694 _client_target_key_create(uint64_t target_id)
695 {
696 CFDataRef target_key;
697
698 target_key = CFDataCreate(NULL, (UInt8 *)&target_id, sizeof(target_id));
699 return target_key;
700 }
701
702
703 static SCNetworkReachabilityRef
704 _client_target_copy(reach_client_t *client, uint64_t target_id)
705 {
706 SCNetworkReachabilityRef target;
707 CFDataRef target_key;
708
709 target_key = _client_target_key_create(target_id);
710 target = CFDictionaryGetValue(client->targets, target_key);
711 CFRelease(target_key);
712
713 if (target != NULL) {
714 CFRetain(target);
715 }
716
717 return target;
718 }
719
720
721 static Boolean
722 _client_target_set(reach_client_t *client, uint64_t target_id, SCNetworkReachabilityRef target)
723 {
724 Boolean added;
725 CFDataRef target_key;
726
727 target_key = _client_target_key_create(target_id);
728 added = !CFDictionaryContainsKey(client->targets, target_key);
729 if (added) {
730 CFDictionarySetValue(client->targets, target_key, target);
731 }
732 CFRelease(target_key);
733
734 return added;
735 }
736
737
738 static void
739 _client_target_remove(reach_client_t *client, uint64_t target_id)
740 {
741 CFDataRef target_key;
742
743 target_key = _client_target_key_create(target_id);
744 CFDictionaryRemoveValue(client->targets, target_key);
745 CFRelease(target_key);
746
747 return;
748 }
749
750
751 #pragma mark -
752 #pragma mark Reachability [XPC] server functions
753
754 /*
755 * _reach_changed
756 *
757 * Note: should be exec'd on the target queue
758 */
759 static void
760 _reach_changed(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
761 {
762 CFIndex i;
763 CFIndex n;
764 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
765 const void * watcher_keys_q[32];
766 const void ** watcher_keys = watcher_keys_q;
767 const void * watcher_vals_q[32];
768 const void ** watcher_vals = watcher_vals_q;
769
770 if (S_debug) {
771 SCLog(TRUE, LOG_INFO,
772 CFSTR("%sprocess reachability changed, flags = 0x%08x"),
773 targetPrivate->log_prefix,
774 flags);
775 }
776
777 if (targetPrivate->serverWatchers == NULL) {
778 // if no watchers
779 return;
780 }
781
782 n = CFDictionaryGetCount(targetPrivate->serverWatchers);
783 if (n == 0) {
784 // if no watchers
785 return;
786 }
787
788 /*
789 * Because we are actively watching for additional changes
790 * we mark the flags as "valid"
791 */
792 if (_SC_ATOMIC_CMPXCHG(&targetPrivate->serverInfoValid, FALSE, TRUE)) {
793 if (S_debug) {
794 SCLog(TRUE, LOG_INFO, CFSTR(" flags are now \"valid\""));
795 }
796 }
797
798 // notify all of the watchers
799 if (n > sizeof(watcher_keys_q)/sizeof(watcher_keys[0])) {
800 watcher_keys = CFAllocatorAllocate(NULL, n * sizeof(CFDataRef), 0);
801 watcher_vals = CFAllocatorAllocate(NULL, n * sizeof(CFDataRef), 0);
802 }
803
804 CFDictionaryGetKeysAndValues(targetPrivate->serverWatchers,
805 watcher_keys,
806 watcher_vals);
807
808 for (i = 0; i < n; i++) {
809 xpc_connection_t connection;
810 CFDataRef key;
811 uint64_t target_id;
812 CFDataRef val;
813 reach_watcher_key_t *watcher_key;
814 reach_watcher_val_t *watcher_val;
815
816 val = (CFDataRef)watcher_vals[i];
817 /* ALIGN: CF aligns to >8 byte boundries */
818 watcher_val = (reach_watcher_val_t *)(void *)CFDataGetBytePtr(val);
819
820 if (_SC_ATOMIC_INC(&watcher_val->n_changes) > 0) {
821 // if we've already sent a notification
822 continue;
823 }
824
825 key = (CFDataRef)watcher_keys[i];
826 /* ALIGN: CF aligns to >8 byte boundries */
827 watcher_key = (reach_watcher_key_t *)(void *)CFDataGetBytePtr(key);
828
829 connection = xpc_retain(watcher_key->connection);
830 target_id = watcher_key->target_id;
831 dispatch_async(_reach_connection_queue(), ^{
832 xpc_object_t reply;
833
834 // create our [async] notification
835 reply = xpc_dictionary_create(NULL, NULL, 0);
836
837 // set notification
838 xpc_dictionary_set_int64(reply,
839 MESSAGE_NOTIFY,
840 MESSAGE_REACHABILITY_STATUS);
841
842 // set target ID
843 xpc_dictionary_set_uint64(reply,
844 REACH_CLIENT_TARGET_ID,
845 target_id);
846
847 log_xpc_object(" reply [async]", reply);
848 xpc_connection_send_message(connection, reply);
849
850 xpc_release(reply);
851 xpc_release(connection);
852 });
853 }
854
855 if (n > sizeof(watcher_keys_q)/sizeof(watcher_keys[0])) {
856 CFAllocatorDeallocate(NULL, watcher_keys);
857 CFAllocatorDeallocate(NULL, watcher_vals);
858 }
859
860 return;
861 }
862
863
864 static void
865 sanitize_address(const struct sockaddr *from, struct sockaddr *to)
866 {
867 switch (from->sa_family) {
868 case AF_INET : {
869 /* ALIGN: cast okay, alignment not assumed. */
870 struct sockaddr_in *from4 = (struct sockaddr_in *)(void *)from;
871 struct sockaddr_in *to4 = (struct sockaddr_in *)(void *)to;
872
873 bzero(to4, sizeof(*to4));
874 to4->sin_len = sizeof(*to4);
875 to4->sin_family = AF_INET;
876 bcopy(&from4->sin_addr, &to4->sin_addr, sizeof(to4->sin_addr));
877 break;
878 }
879
880 case AF_INET6 : {
881 /* ALIGN: cast okay, alignment not assumed. */
882 struct sockaddr_in6 *from6 = (struct sockaddr_in6 *)(void *)from;
883 struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)(void *)to;
884
885 bzero(to6, sizeof(*to6));
886 to6->sin6_len = sizeof(*to6);
887 to6->sin6_family = AF_INET6;
888 bcopy(&from6->sin6_addr, &to6->sin6_addr, sizeof(to6->sin6_addr));
889 to6->sin6_scope_id = from6->sin6_scope_id;
890 break;
891 }
892
893 default:
894 bcopy(from, to, from->sa_len);
895 break;
896 }
897
898 return;
899 }
900
901
902 static void
903 target_add(reach_client_t *client, xpc_object_t request)
904 {
905 const char *name;
906 const char *serv;
907 const struct sockaddr *localAddress;
908 struct sockaddr_storage localAddress0;
909 const struct sockaddr *remoteAddress;
910 struct sockaddr_storage remoteAddress0;
911 const struct addrinfo *hints;
912 int64_t if_index;
913 const char *if_name = NULL;
914 bool onDemandBypass = FALSE;
915 bool resolverBypass = FALSE;
916 uint64_t target_id;
917
918
919 unsigned char bytes[CC_SHA1_DIGEST_LENGTH];
920 CC_SHA1_CTX ctx;
921 CFDataRef digest = NULL;
922 size_t len;
923 xpc_connection_t remote;
924 xpc_object_t reply;
925 uint64_t status = REACH_REQUEST_REPLY_FAILED;
926
927 Boolean added;
928 __block Boolean ok = TRUE;
929 __block SCNetworkReachabilityRef target = NULL;
930
931 if (S_debug) {
932 SCLog(TRUE, LOG_INFO,
933 CFSTR("<%p> create reachability target"),
934 client->connection);
935 // log_xpc_object(" create", request);
936 }
937
938 remote = xpc_dictionary_get_remote_connection(request);
939 reply = xpc_dictionary_create_reply(request);
940 if (reply == NULL) {
941 SCLog(TRUE, LOG_ERR,
942 CFSTR("<%p> target_add: xpc_dictionary_create_reply: failed"),
943 client->connection);
944 return;
945 }
946
947 target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
948 if (target_id == 0) {
949 xpc_dictionary_set_string(reply,
950 REACH_REQUEST_REPLY_DETAIL,
951 "no target ID");
952 goto done;
953 }
954
955 // create a "digest" of the [new] target
956
957 CC_SHA1_Init(&ctx);
958
959 name = xpc_dictionary_get_string(request, REACH_TARGET_NAME);
960 if (name != NULL) {
961 CC_SHA1_Update(&ctx, REACH_TARGET_NAME, sizeof(REACH_TARGET_NAME));
962 CC_SHA1_Update(&ctx, name, strlen(name));
963 }
964
965 serv = xpc_dictionary_get_string(request, REACH_TARGET_SERV);
966 if (serv != NULL) {
967 CC_SHA1_Update(&ctx, REACH_TARGET_SERV, sizeof(REACH_TARGET_SERV));
968 CC_SHA1_Update(&ctx, serv, strlen(serv));
969 }
970
971 localAddress = xpc_dictionary_get_data(request, REACH_TARGET_LOCAL_ADDR, &len);
972 if (localAddress != NULL) {
973 if ((len == localAddress->sa_len) && (len <= sizeof(struct sockaddr_storage))) {
974 sanitize_address(localAddress, (struct sockaddr *)&localAddress0);
975 CC_SHA1_Update(&ctx, REACH_TARGET_LOCAL_ADDR, sizeof(REACH_TARGET_LOCAL_ADDR));
976 CC_SHA1_Update(&ctx, &localAddress0, len);
977 } else {
978 xpc_dictionary_set_string(reply,
979 REACH_REQUEST_REPLY_DETAIL,
980 "local address: size error");
981 goto done;
982 }
983 }
984
985 remoteAddress = xpc_dictionary_get_data(request, REACH_TARGET_REMOTE_ADDR, &len);
986 if (remoteAddress != NULL) {
987 if ((len == remoteAddress->sa_len) && (len <= sizeof(struct sockaddr_storage))) {
988 sanitize_address(remoteAddress, (struct sockaddr *)&remoteAddress0);
989 CC_SHA1_Update(&ctx, REACH_TARGET_REMOTE_ADDR, sizeof(REACH_TARGET_REMOTE_ADDR));
990 CC_SHA1_Update(&ctx, &remoteAddress0, len);
991 } else {
992 xpc_dictionary_set_string(reply,
993 REACH_REQUEST_REPLY_DETAIL,
994 "remote address: size error");
995 goto done;
996 }
997 }
998
999 hints = xpc_dictionary_get_data(request, REACH_TARGET_HINTS, &len);
1000 if (hints != NULL) {
1001 if (len == sizeof(struct addrinfo)) {
1002 CC_SHA1_Update(&ctx, REACH_TARGET_HINTS, sizeof(REACH_TARGET_HINTS));
1003 CC_SHA1_Update(&ctx, hints, len);
1004 } else {
1005 xpc_dictionary_set_string(reply,
1006 REACH_REQUEST_REPLY_DETAIL,
1007 "hints: size error");
1008 goto done;
1009 }
1010 }
1011
1012 if_index = xpc_dictionary_get_int64(request, REACH_TARGET_IF_INDEX);
1013 if (if_index != 0) {
1014 if_name = xpc_dictionary_get_string(request, REACH_TARGET_IF_NAME);
1015 if (if_name != NULL) {
1016 CC_SHA1_Update(&ctx, REACH_TARGET_IF_NAME, sizeof(REACH_TARGET_IF_NAME));
1017 CC_SHA1_Update(&ctx, if_name, strlen(if_name));
1018 }
1019 }
1020
1021 onDemandBypass = xpc_dictionary_get_bool(request, REACH_TARGET_ONDEMAND_BYPASS);
1022 if (onDemandBypass) {
1023 CC_SHA1_Update(&ctx, REACH_TARGET_ONDEMAND_BYPASS, sizeof(REACH_TARGET_ONDEMAND_BYPASS));
1024 CC_SHA1_Update(&ctx, &onDemandBypass, sizeof(onDemandBypass));
1025 }
1026
1027 resolverBypass = xpc_dictionary_get_bool(request, REACH_TARGET_RESOLVER_BYPASS);
1028 if (resolverBypass) {
1029 CC_SHA1_Update(&ctx, REACH_TARGET_RESOLVER_BYPASS, sizeof(REACH_TARGET_RESOLVER_BYPASS));
1030 CC_SHA1_Update(&ctx, &resolverBypass, sizeof(resolverBypass));
1031 }
1032
1033
1034 CC_SHA1_Final(bytes, &ctx);
1035 digest = CFDataCreate(NULL, bytes, sizeof(bytes));
1036
1037 /*
1038 * Check to see if we already have a SCNetworkReachability object
1039 * for this digest. If so, we'll share the existing target. If not,
1040 * create a new [shared] target.
1041 */
1042 dispatch_sync(_server_digest_queue(), ^{
1043 target = CFDictionaryGetValue(reach_digest_map, digest);
1044 if (target != NULL) {
1045 CFRetain(target);
1046 } else {
1047 CFDataRef data;
1048 CFMutableDictionaryRef options;
1049 CFStringRef str;
1050
1051 options = CFDictionaryCreateMutable(NULL,
1052 0,
1053 &kCFTypeDictionaryKeyCallBacks,
1054 &kCFTypeDictionaryValueCallBacks);
1055 if (name != NULL) {
1056 str = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
1057 CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, str);
1058 CFRelease(str);
1059 }
1060 if (serv != NULL) {
1061 str = CFStringCreateWithCString(NULL, serv, kCFStringEncodingUTF8);
1062 CFDictionarySetValue(options, kSCNetworkReachabilityOptionServName, str);
1063 CFRelease(str);
1064 }
1065 if (localAddress != NULL) {
1066 data = CFDataCreate(NULL, (const UInt8 *)&localAddress0, localAddress0.ss_len);
1067 CFDictionarySetValue(options, kSCNetworkReachabilityOptionLocalAddress, data);
1068 CFRelease(data);
1069 }
1070 if (remoteAddress != NULL) {
1071 data = CFDataCreate(NULL, (const UInt8 *)&remoteAddress0, remoteAddress0.ss_len);
1072 CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data);
1073 CFRelease(data);
1074 }
1075 if (hints != NULL) {
1076 data = CFDataCreate(NULL, (const UInt8 *)hints, sizeof(struct addrinfo));
1077 CFDictionarySetValue(options, kSCNetworkReachabilityOptionHints, data);
1078 CFRelease(data);
1079 }
1080 if (onDemandBypass) {
1081 CFDictionarySetValue(options,
1082 kSCNetworkReachabilityOptionConnectionOnDemandBypass,
1083 kCFBooleanTrue);
1084 }
1085 if (resolverBypass) {
1086 CFDictionarySetValue(options,
1087 kSCNetworkReachabilityOptionResolverBypass,
1088 kCFBooleanTrue);
1089 }
1090 CFDictionarySetValue(options,
1091 kSCNetworkReachabilityOptionServerBypass,
1092 kCFBooleanTrue);
1093 target = SCNetworkReachabilityCreateWithOptions(NULL, options);
1094 CFRelease(options);
1095 if (target == NULL) {
1096 xpc_dictionary_set_string(reply,
1097 REACH_REQUEST_REPLY_DETAIL,
1098 "SCNetworkReachabilityCreateWithOptions failed");
1099 ok = FALSE;
1100 return;
1101 }
1102
1103 // because the interface name may not (no longer) be valid we set
1104 // this after we've created the SCNetworkReachabilty object
1105 if ((if_index != 0) && (if_name != NULL)) {
1106 SCNetworkReachabilityPrivateRef targetPrivate;
1107
1108 targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1109 targetPrivate->if_index = if_index;
1110 strlcpy(targetPrivate->if_name, if_name, sizeof(targetPrivate->if_name));
1111 }
1112
1113
1114 ok = SCNetworkReachabilitySetCallback(target, _reach_changed, NULL);
1115 if (!ok) {
1116 xpc_dictionary_set_string(reply,
1117 REACH_REQUEST_REPLY_DETAIL,
1118 "SCNetworkReachabilitySetCallback failed");
1119 CFRelease(target);
1120 target = NULL;
1121 return;
1122 }
1123 }
1124
1125 // bump the number of references to this target
1126 _target_reference_add(target, digest, client->connection);
1127 });
1128
1129 if (!ok) {
1130 goto done;
1131 }
1132
1133 /*
1134 * add an association for the client's target_id to the [shared]
1135 * SCNetworkReachability object.
1136 */
1137 added = _client_target_set(client, target_id, target);
1138 if (!added) {
1139 // if we already had a reference to the target (e.g. reconnect)
1140 dispatch_sync(_server_digest_queue(), ^{
1141 _target_reference_remove(target, client->connection);
1142 });
1143 }
1144
1145 status = REACH_REQUEST_REPLY_OK;
1146
1147 done :
1148
1149 xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1150 // log_xpc_object(" reply", reply);
1151 xpc_connection_send_message(remote, reply);
1152 xpc_release(reply);
1153
1154 if (digest != NULL) CFRelease(digest);
1155 if (target != NULL) CFRelease(target);
1156 return;
1157 }
1158
1159
1160 static void
1161 target_remove(reach_client_t *client, xpc_object_t request)
1162 {
1163 xpc_connection_t remote;
1164 xpc_object_t reply;
1165 uint64_t status = REACH_REQUEST_REPLY_FAILED;
1166 SCNetworkReachabilityRef target = NULL;
1167 uint64_t target_id;
1168
1169 if (S_debug) {
1170 SCLog(TRUE, LOG_INFO,
1171 CFSTR("<%p> remove reachability target"),
1172 client->connection);
1173 // log_xpc_object(" remove", request);
1174 }
1175
1176 remote = xpc_dictionary_get_remote_connection(request);
1177 reply = xpc_dictionary_create_reply(request);
1178 if (reply == NULL) {
1179 SCLog(TRUE, LOG_ERR,
1180 CFSTR("<%p> target_remove: xpc_dictionary_create_reply: failed"),
1181 client->connection);
1182 return;
1183 }
1184
1185 target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
1186 if (target_id == 0) {
1187 xpc_dictionary_set_string(reply,
1188 REACH_REQUEST_REPLY_DETAIL,
1189 "no target ID");
1190 goto done;
1191 }
1192
1193 target = _client_target_copy(client, target_id);
1194 if (target == NULL) {
1195 xpc_dictionary_set_string(reply,
1196 REACH_REQUEST_REPLY_DETAIL,
1197 "no target");
1198 status = REACH_REQUEST_REPLY_UNKNOWN;
1199 goto done;
1200 }
1201
1202 /*
1203 * remove the association from the client's target_id to the [shared]
1204 * SCNetworkReachability object.
1205 */
1206 _client_target_remove(client, target_id);
1207
1208 // drop the number of references to this target
1209 dispatch_sync(_server_digest_queue(), ^{
1210 _target_reference_remove(target, client->connection);
1211 });
1212
1213 status = REACH_REQUEST_REPLY_OK;
1214
1215 done :
1216
1217 xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1218 // log_xpc_object(" reply", reply);
1219 xpc_connection_send_message(remote, reply);
1220 xpc_release(reply);
1221
1222 if (target != NULL) CFRelease(target);
1223 return;
1224 }
1225
1226
1227 static void
1228 target_schedule(reach_client_t *client, xpc_object_t request)
1229 {
1230 Boolean ok;
1231 xpc_connection_t remote;
1232 xpc_object_t reply;
1233 uint64_t status = REACH_REQUEST_REPLY_FAILED;
1234 SCNetworkReachabilityRef target = NULL;
1235 uint64_t target_id;
1236
1237 if (S_debug) {
1238 SCLog(TRUE, LOG_INFO,
1239 CFSTR("<%p> schedule reachability target"),
1240 client->connection);
1241 // log_xpc_object(" schedule", request);
1242 }
1243
1244 remote = xpc_dictionary_get_remote_connection(request);
1245 reply = xpc_dictionary_create_reply(request);
1246 if (reply == NULL) {
1247 SCLog(TRUE, LOG_ERR,
1248 CFSTR("<%p> target_schedule: xpc_dictionary_create_reply: failed"),
1249 client->connection);
1250 return;
1251 }
1252
1253 target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
1254 if (target_id == 0) {
1255 xpc_dictionary_set_string(reply,
1256 REACH_REQUEST_REPLY_DETAIL,
1257 "no target ID");
1258 goto done;
1259 }
1260
1261 target = _client_target_copy(client, target_id);
1262 if (target == NULL) {
1263 xpc_dictionary_set_string(reply,
1264 REACH_REQUEST_REPLY_DETAIL,
1265 "no target");
1266 status = REACH_REQUEST_REPLY_UNKNOWN;
1267 goto done;
1268 }
1269
1270 // enable monitoring
1271 ok = _target_watcher_add(target, client->connection, target_id);
1272 if (ok) {
1273 status = REACH_REQUEST_REPLY_OK;
1274 }
1275
1276 done :
1277
1278 xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1279 // log_xpc_object(" reply", reply);
1280 xpc_connection_send_message(remote, reply);
1281 xpc_release(reply);
1282
1283 if (target != NULL) CFRelease(target);
1284 return;
1285 }
1286
1287
1288 static void
1289 target_status(reach_client_t *client, xpc_object_t request)
1290 {
1291 xpc_connection_t remote;
1292 xpc_object_t reply;
1293 __block Boolean reply_now = TRUE;
1294 Boolean scheduled;
1295 uint64_t status = REACH_REQUEST_REPLY_FAILED;
1296 SCNetworkReachabilityRef target = NULL;
1297 uint64_t target_id;
1298
1299 if(S_debug) {
1300 SCLog(TRUE, LOG_INFO,
1301 CFSTR("<%p> get status of reachability target"),
1302 client->connection);
1303 // log_xpc_object(" status", request);
1304 }
1305
1306 remote = xpc_dictionary_get_remote_connection(request);
1307 reply = xpc_dictionary_create_reply(request);
1308 if (reply == NULL) {
1309 SCLog(TRUE, LOG_ERR,
1310 CFSTR("<%p> target_status: xpc_dictionary_create_reply: failed"),
1311 client->connection);
1312 return;
1313 }
1314
1315 target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
1316 if (target_id == 0) {
1317 SCLog(TRUE, LOG_ERR,
1318 CFSTR("<%p> target_status: no target"),
1319 client->connection);
1320 xpc_dictionary_set_string(reply,
1321 REACH_REQUEST_REPLY_DETAIL,
1322 "no target ID");
1323 goto done;
1324 }
1325
1326 target = _client_target_copy(client, target_id);
1327 if (target == NULL) {
1328 SCLog(TRUE, LOG_ERR,
1329 CFSTR("<%p> target_status: no target (0x%0llx)"),
1330 client->connection,
1331 target_id);
1332 xpc_dictionary_set_string(reply,
1333 REACH_REQUEST_REPLY_DETAIL,
1334 "no target");
1335 status = REACH_REQUEST_REPLY_UNKNOWN;
1336 goto done;
1337 }
1338
1339 /*
1340 * Check to see if the target [for this client] had been "scheduled".
1341 *
1342 * If so, also mark that we've picked up the current reachability
1343 * flags and that any pending notifications have been processed.
1344 */
1345 scheduled = _target_watcher_checkin(target, client->connection, target_id);
1346
1347 /*
1348 * return current reachability information to the caller
1349 */
1350 dispatch_sync(_target_queue(target), ^{
1351 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1352
1353 if (scheduled) {
1354 /*
1355 * The client "scheduled" this target. As such, we
1356 * know that this an async query and that we only
1357 * need to return the "last known" flags.
1358 */
1359 _target_reply_add_reachability(target, reply);
1360 // log_xpc_object(" reply [scheduled]", reply);
1361
1362 if (S_debug) {
1363 CFStringRef str;
1364
1365 str = _SCNetworkReachabilityCopyTargetFlags(target);
1366 SCLog(TRUE, LOG_INFO,
1367 CFSTR("<%p> reply [scheduled], %@"),
1368 client->connection,
1369 str);
1370 CFRelease(str);
1371 }
1372 } else {
1373 /*
1374 * The client has NOT "scheduled" this target. As
1375 * such, we know that this is a sync query and that
1376 * must return "current" flags.
1377 */
1378 if (targetPrivate->scheduled && targetPrivate->serverInfoValid) {
1379 /*
1380 * The server target has been "scheduled" and we
1381 * have flags that are "current".
1382 */
1383 _target_reply_add_reachability(target, reply);
1384 // log_xpc_object(" reply [scheduled/valid]", reply);
1385
1386 if (S_debug) {
1387 CFStringRef str;
1388
1389 str = _SCNetworkReachabilityCopyTargetFlags(target);
1390 SCLog(TRUE, LOG_INFO,
1391 CFSTR("<%p> reply [scheduled/valid], %@"),
1392 client->connection,
1393 str);
1394 CFRelease(str);
1395 }
1396 } else {
1397 dispatch_group_t group;
1398
1399 /*
1400 * The server target has NOT been "scheduled" (or
1401 * we do not have "current" flags. This means that
1402 * we must query for the current information and
1403 * return the flags to the client when they are
1404 * available.
1405 */
1406
1407 reply_now = FALSE;
1408
1409 group = _target_group(target);
1410 if (_SC_ATOMIC_INC(&targetPrivate->serverQueryActive) == 0) {
1411 CFRetain(target);
1412 dispatch_group_async(group, _server_concurrent_queue(), ^{
1413 SCNetworkReachabilityFlags flags;
1414 unsigned int n;
1415 Boolean ok;
1416
1417 // query for the flags
1418 ok = SCNetworkReachabilityGetFlags(target, &flags);
1419 flags = targetPrivate->info.flags; // get the "raw" flags
1420 if (!ok) {
1421 SCLog(TRUE, LOG_ERR,
1422 CFSTR("SCNetworkReachabilityGetFlags() [sync query] failed"
1423 "\n target = %@"
1424 "\n status = %s"),
1425 target,
1426 SCErrorString(SCError()));
1427 }
1428
1429 // flags are now available
1430 n = _SC_ATOMIC_ZERO(&targetPrivate->serverQueryActive);
1431 if (S_debug) {
1432 SCLog(TRUE, LOG_INFO,
1433 CFSTR("%sSCNetworkReachabilityGetFlags() [sync query] complete, n = %d"),
1434 targetPrivate->log_prefix,
1435 n);
1436 }
1437
1438 CFRelease(target);
1439 });
1440 }
1441
1442 CFRetain(target);
1443 dispatch_group_notify(group, _target_queue(target), ^{
1444 // flags are now available
1445 _target_reply_add_reachability(target, reply);
1446 xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, REACH_REQUEST_REPLY_OK);
1447 // log_xpc_object(" reply [delayed]", reply);
1448
1449 if (S_debug) {
1450 CFStringRef str;
1451
1452 str = _SCNetworkReachabilityCopyTargetFlags(target);
1453 SCLog(TRUE, LOG_INFO,
1454 CFSTR("<%p> reply [delayed], %@"),
1455 client->connection,
1456 str);
1457 CFRelease(str);
1458 }
1459
1460 xpc_connection_send_message(remote, reply);
1461 xpc_release(reply);
1462
1463 CFRelease(target);
1464 });
1465 }
1466 }
1467 });
1468
1469 status = REACH_REQUEST_REPLY_OK;
1470
1471 done :
1472
1473 if (reply_now) {
1474 xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1475
1476 if (status != REACH_REQUEST_REPLY_OK) {
1477 // log_xpc_object(" reply [!]", reply);
1478
1479 if (S_debug) {
1480 SCLog(TRUE, LOG_INFO,
1481 CFSTR("<%p> reply [!]"),
1482 client->connection);
1483 }
1484 }
1485
1486 xpc_connection_send_message(remote, reply);
1487 xpc_release(reply);
1488 } else if (S_debug) {
1489 CFStringRef str;
1490
1491 str = _SCNetworkReachabilityCopyTargetFlags(target);
1492 SCLog(TRUE, LOG_INFO,
1493 CFSTR("<%p> no reply [yet], %@"),
1494 client->connection,
1495 str);
1496 CFRelease(str);
1497 }
1498
1499 if (target != NULL) CFRelease(target);
1500 return;
1501 }
1502
1503
1504 static void
1505 target_unschedule(reach_client_t *client, xpc_object_t request)
1506 {
1507 Boolean ok;
1508 xpc_connection_t remote;
1509 xpc_object_t reply;
1510 uint64_t status = REACH_REQUEST_REPLY_FAILED;
1511 SCNetworkReachabilityRef target = NULL;
1512 uint64_t target_id;
1513
1514 if (S_debug) {
1515 SCLog(TRUE, LOG_INFO,
1516 CFSTR("<%p> unschedule reachability target"),
1517 client->connection);
1518 // log_xpc_object(" unschedule", request);
1519 }
1520
1521 remote = xpc_dictionary_get_remote_connection(request);
1522 reply = xpc_dictionary_create_reply(request);
1523 if (reply == NULL) {
1524 SCLog(TRUE, LOG_ERR,
1525 CFSTR("<%p> target_unschedule: xpc_dictionary_create_reply: failed"),
1526 client->connection);
1527 return;
1528 }
1529
1530 target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
1531 if (target_id == 0) {
1532 xpc_dictionary_set_string(reply,
1533 REACH_REQUEST_REPLY_DETAIL,
1534 "no target ID");
1535 goto done;
1536 }
1537
1538 target = _client_target_copy(client, target_id);
1539 if (target == NULL) {
1540 xpc_dictionary_set_string(reply,
1541 REACH_REQUEST_REPLY_DETAIL,
1542 "no target");
1543 status = REACH_REQUEST_REPLY_UNKNOWN;
1544 goto done;
1545 }
1546
1547 // disable monitoring
1548 ok = _target_watcher_remove(target, client->connection, target_id);
1549 if (ok) {
1550 status = REACH_REQUEST_REPLY_OK;
1551 }
1552
1553 done :
1554
1555 xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1556 // log_xpc_object(" reply", reply);
1557 xpc_connection_send_message(remote, reply);
1558 xpc_release(reply);
1559
1560 if (target != NULL) CFRelease(target);
1561 return;
1562 }
1563
1564
1565 #define SNAPSHOT_PATH_STATE _PATH_VARTMP "configd-reachability"
1566
1567
1568 static void
1569 _snapshot_digest_watcher(const void *key, const void *value, void *context)
1570 {
1571 FILE *f = (FILE *)context;
1572 static reach_client_t no_client = {
1573 .pid = 0,
1574 .proc_name = "?",
1575 };
1576 struct rb_node *rbn;
1577 reach_client_t *rbt_client;
1578 reach_watcher_key_t *watcher_key;
1579 reach_watcher_val_t *watcher_val;
1580
1581 /* ALIGN: CF aligns to >8 byte boundries */
1582 watcher_key = (reach_watcher_key_t *)(void *)CFDataGetBytePtr(key);
1583 watcher_val = (reach_watcher_val_t *)(void *)CFDataGetBytePtr(value);
1584
1585 rbn = rb_tree_find_node(_reach_clients_rbt(), &watcher_key->connection);
1586 if (rbn == NULL) {
1587 rbn = &no_client.rbn;
1588 }
1589
1590 rbt_client = RBNODE_TO_REACH_CLIENT(rbn);
1591
1592 SCPrint(TRUE, f,
1593 CFSTR(" connection = %p, target(c) = 0x%0llx, command = %s, pid = %d, changes = %u\n"),
1594 watcher_key->connection,
1595 watcher_key->target_id,
1596 rbt_client->proc_name,
1597 rbt_client->pid,
1598 watcher_val->n_changes);
1599
1600 return;
1601 }
1602
1603
1604 static void
1605 _snapshot_digest(const void *key, const void *value, void *context)
1606 {
1607 FILE *f = (FILE *)context;
1608 CFStringRef digest = (CFStringRef)key;
1609 dispatch_queue_t q;
1610 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value;
1611 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1612
1613 q = _target_queue(target);
1614 dispatch_sync(q, ^{
1615 SCPrint(TRUE, f, CFSTR("\n digest : %@\n"), digest);
1616 SCPrint(TRUE, f, CFSTR(" %@\n"), target);
1617 SCPrint(TRUE, f, CFSTR(" valid = %s, active = %u, refs = %u\n"),
1618 targetPrivate->serverInfoValid ? "Y" : "N",
1619 targetPrivate->serverQueryActive,
1620 targetPrivate->serverReferences);
1621
1622 SCPrint(TRUE, f, CFSTR(" network %d.%3.3d"),
1623 targetPrivate->last_network.tv_sec,
1624 targetPrivate->last_network.tv_usec / 1000);
1625 #if !TARGET_OS_IPHONE
1626 SCPrint(TRUE, f, CFSTR(", power %d.%3.3d"),
1627 targetPrivate->last_power.tv_sec,
1628 targetPrivate->last_power.tv_usec / 1000);
1629 #endif // !TARGET_OS_IPHONE
1630 if (targetPrivate->type == reachabilityTypeName) {
1631 SCPrint(TRUE, f, CFSTR(", DNS %d.%3.3d"),
1632 targetPrivate->last_dns.tv_sec,
1633 targetPrivate->last_dns.tv_usec / 1000);
1634 if (timerisset(&targetPrivate->dnsQueryEnd)) {
1635 struct timeval dnsQueryElapsed;
1636
1637 timersub(&targetPrivate->dnsQueryEnd,
1638 &targetPrivate->dnsQueryStart,
1639 &dnsQueryElapsed);
1640 SCPrint(TRUE, f, CFSTR(" (query %d.%3.3d / reply %d.%3.3d)"),
1641 targetPrivate->dnsQueryStart.tv_sec,
1642 targetPrivate->dnsQueryStart.tv_usec / 1000,
1643 dnsQueryElapsed.tv_sec,
1644 dnsQueryElapsed.tv_usec / 1000);
1645 }
1646 }
1647 if (timerisset(&targetPrivate->last_push)) {
1648 SCPrint(TRUE, f, CFSTR(", last notify %d.%3.3d"),
1649 targetPrivate->last_push.tv_sec,
1650 targetPrivate->last_push.tv_usec / 1000);
1651 }
1652 SCPrint(TRUE, f, CFSTR("\n"));
1653
1654 if (targetPrivate->serverWatchers != NULL) {
1655 CFDictionaryApplyFunction(targetPrivate->serverWatchers,
1656 _snapshot_digest_watcher,
1657 f);
1658 }
1659 });
1660
1661 return;
1662 }
1663
1664
1665 static void
1666 _snapshot_target(const void *key, const void *value, void *context)
1667 {
1668 FILE *f = (FILE *)context;
1669 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value;
1670 uint64_t target_id;
1671 CFDataRef target_key = (CFDataRef)key;
1672
1673 /* ALIGN: CF aligns > 8 byte boundries */
1674 target_id = *(uint64_t *)(void *)CFDataGetBytePtr(target_key);
1675
1676 SCPrint(TRUE, f,
1677 CFSTR(" target(c) = 0x%0llx, target(s) = %@\n"),
1678 target_id,
1679 target);
1680
1681 return;
1682 }
1683
1684
1685 static void
1686 _snapshot(reach_client_t *client, xpc_object_t request)
1687 {
1688 uid_t euid;
1689 FILE *f;
1690 int fd;
1691 Boolean ok = FALSE;
1692 struct rb_node *rbn;
1693 struct rb_tree *rbt;
1694 xpc_connection_t remote;
1695 xpc_object_t reply;
1696
1697 if (S_debug) {
1698 SCLog(TRUE, LOG_INFO,
1699 CFSTR("<%p> snapshot"),
1700 client->connection);
1701 // log_xpc_object(" create", request);
1702 }
1703
1704 remote = xpc_dictionary_get_remote_connection(request);
1705 reply = xpc_dictionary_create_reply(request);
1706 if (reply == NULL) {
1707 SCLog(TRUE, LOG_ERR,
1708 CFSTR("<%p> _snapshot: xpc_dictionary_create_reply: failed"),
1709 client->connection);
1710 return;
1711 }
1712
1713 euid = xpc_connection_get_euid(remote);
1714 if (euid != 0) {
1715 xpc_dictionary_set_string(reply,
1716 REACH_REQUEST_REPLY_DETAIL,
1717 "Permission denied.");
1718 goto done;
1719 }
1720
1721 // Save a snapshot of the SCNetworkReachability server "state"
1722
1723 (void) unlink(SNAPSHOT_PATH_STATE);
1724 fd = open(SNAPSHOT_PATH_STATE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
1725 if (fd == -1) {
1726 xpc_dictionary_set_string(reply,
1727 REACH_REQUEST_REPLY_DETAIL,
1728 "open: failed");
1729 goto done;
1730 }
1731 f = fdopen(fd, "w");
1732 if (f == NULL) {
1733 xpc_dictionary_set_string(reply,
1734 REACH_REQUEST_REPLY_DETAIL,
1735 "fdopen: failed");
1736 goto done;
1737 }
1738
1739 // provide connection/client info
1740
1741 SCPrint(TRUE, f, CFSTR("Clients :\n"));
1742 rbt = _reach_clients_rbt();
1743 rbn = rb_tree_iterate(rbt, NULL, RB_DIR_RIGHT);
1744 if (rbn != NULL) {
1745 while (rbn != NULL) {
1746 reach_client_t *rbt_client;
1747
1748 rbt_client = RBNODE_TO_REACH_CLIENT(rbn);
1749 SCPrint(TRUE, f,
1750 CFSTR("\n connection = %p, client = %p, command = %s, pid = %d\n"),
1751 rbt_client->connection,
1752 rbt_client,
1753 rbt_client->proc_name != NULL ? rbt_client->proc_name : "?",
1754 rbt_client->pid);
1755 my_CFDictionaryApplyFunction(rbt_client->targets,
1756 _snapshot_target,
1757 f);
1758
1759 rbn = rb_tree_iterate(rbt, rbn, RB_DIR_LEFT);
1760 }
1761 } else {
1762 SCPrint(TRUE, f, CFSTR(" None.\n"));
1763 }
1764 SCPrint(TRUE, f, CFSTR("\n"));
1765
1766 // provide "digest" info
1767
1768 SCPrint(TRUE, f, CFSTR("Digests :\n"));
1769 dispatch_sync(_server_digest_queue(), ^{
1770 if (reach_digest_map != NULL) {
1771 CFDictionaryApplyFunction(reach_digest_map,
1772 _snapshot_digest,
1773 f);
1774 }
1775 });
1776
1777 (void) fclose(f);
1778
1779 ok = TRUE;
1780
1781 done :
1782
1783 xpc_dictionary_set_int64(reply,
1784 REACH_REQUEST_REPLY,
1785 ok ? REACH_REQUEST_REPLY_OK : REACH_REQUEST_REPLY_FAILED);
1786 // log_xpc_object(" reply", reply);
1787 xpc_connection_send_message(remote, reply);
1788 xpc_release(reply);
1789
1790 return;
1791 }
1792
1793
1794 static __inline__ void
1795 _extract_client_info(reach_client_t *client, xpc_object_t request)
1796 {
1797 // if available/needed, save the process name
1798 if (client->proc_name == NULL) {
1799 const char *proc_name;
1800
1801 proc_name = xpc_dictionary_get_string(request, REACH_CLIENT_PROC_NAME);
1802 if (proc_name != NULL) {
1803 client->proc_name = strdup(proc_name);
1804 }
1805 }
1806
1807 return;
1808 }
1809
1810
1811 static void
1812 process_request(reach_client_t *client, xpc_object_t request)
1813 {
1814 int64_t op;
1815
1816 op = xpc_dictionary_get_int64(request, REACH_REQUEST);
1817 switch (op) {
1818 case REACH_REQUEST_CREATE :
1819 _extract_client_info(client, request);
1820 target_add(client, request);
1821 break;
1822 case REACH_REQUEST_REMOVE :
1823 target_remove(client, request);
1824 break;
1825 case REACH_REQUEST_STATUS :
1826 target_status(client, request);
1827 break;
1828 case REACH_REQUEST_SCHEDULE :
1829 target_schedule(client, request);
1830 break;
1831 case REACH_REQUEST_UNSCHEDULE :
1832 target_unschedule(client, request);
1833 break;
1834 case REACH_REQUEST_SNAPSHOT :
1835 _extract_client_info(client, request);
1836 _snapshot(client, request);
1837 break;
1838 default :
1839 SCLog(TRUE, LOG_ERR,
1840 CFSTR("<%p> unknown request : %d"),
1841 client->connection,
1842 op);
1843 break;
1844 }
1845
1846 return;
1847 }
1848
1849
1850 static void
1851 process_new_connection(xpc_connection_t connection)
1852 {
1853 if (S_debug) {
1854 SCLog(TRUE, LOG_INFO, CFSTR("<%p> new reach client, pid=%d"),
1855 connection,
1856 xpc_connection_get_pid(connection));
1857 }
1858
1859 dispatch_sync(_reach_connection_queue(), ^{
1860 reach_client_t *client;
1861
1862 client = _reach_client_create(connection);
1863 if (client == NULL || !rb_tree_insert_node(_reach_clients_rbt(), &client->rbn)) {
1864 __builtin_trap();
1865 }
1866 });
1867
1868 xpc_connection_set_event_handler(connection, ^(xpc_object_t xobj) {
1869 xpc_type_t type;
1870
1871 type = xpc_get_type(xobj);
1872 if (type == XPC_TYPE_DICTIONARY) {
1873 dispatch_sync(_reach_connection_queue(), ^{
1874 struct rb_node *rbn;
1875
1876 rbn = rb_tree_find_node(_reach_clients_rbt(), &connection);
1877 if (rbn != NULL) {
1878 reach_client_t *client;
1879
1880 // process the request
1881 client = RBNODE_TO_REACH_CLIENT(rbn);
1882 process_request(client, xobj);
1883 } else {
1884 char *desc;
1885
1886 SCLog(TRUE, LOG_ERR,
1887 CFSTR("<%p:%d> unexpected SCNetworkReachability request"),
1888 connection,
1889 xpc_connection_get_pid(connection));
1890
1891 desc = xpc_copy_description(xobj);
1892 SCLog(TRUE, LOG_ERR,
1893 CFSTR(" request = %s"),
1894 desc);
1895 free(desc);
1896
1897 xpc_connection_cancel(connection);
1898 }
1899 });
1900
1901 } else if (type == XPC_TYPE_ERROR) {
1902 const char *desc;
1903
1904 desc = xpc_dictionary_get_string(xobj, XPC_ERROR_KEY_DESCRIPTION);
1905 if (xobj == XPC_ERROR_CONNECTION_INVALID) {
1906 if (S_debug) {
1907 SCLog(TRUE, LOG_INFO,
1908 CFSTR("<%p:%d> %s"),
1909 connection,
1910 xpc_connection_get_pid(connection),
1911 desc);
1912 }
1913
1914 xpc_retain(connection);
1915 dispatch_async(_reach_connection_queue(), ^{
1916 _reach_client_remove(connection);
1917 xpc_release(connection);
1918 });
1919
1920 } else if (xobj == XPC_ERROR_CONNECTION_INTERRUPTED) {
1921 SCLog(TRUE, LOG_ERR,
1922 CFSTR("<%p:%d> %s"),
1923 connection,
1924 xpc_connection_get_pid(connection),
1925 desc);
1926
1927 } else {
1928 SCLog(TRUE, LOG_ERR,
1929 CFSTR("<%p:%d> Connection error: %d : %s"),
1930 connection,
1931 xpc_connection_get_pid(connection),
1932 xobj,
1933 desc);
1934 }
1935
1936 } else {
1937 SCLog(TRUE, LOG_ERR,
1938 CFSTR("<%p:%d> unknown event type : %x"),
1939 connection,
1940 xpc_connection_get_pid(connection),
1941 type);
1942 }
1943 });
1944
1945 xpc_connection_resume(connection);
1946
1947 return;
1948 }
1949
1950
1951 #pragma mark -
1952 #pragma mark Reachability server "main"
1953
1954
1955 __private_extern__
1956 void
1957 load_SCNetworkReachability(CFBundleRef bundle, Boolean bundleVerbose)
1958 {
1959 xpc_connection_t connection;
1960 const char *name;
1961 dispatch_queue_t reach_server_q;
1962
1963 S_debug = bundleVerbose;
1964
1965 /*
1966 * create a dictionary mapping SCNetworkReachability [CFData] digests
1967 * to SCNetworkReachability objects.
1968 */
1969 reach_digest_map = CFDictionaryCreateMutable(NULL,
1970 0,
1971 &kCFTypeDictionaryKeyCallBacks,
1972 &kCFTypeDictionaryValueCallBacks);
1973
1974 /*
1975 * create dispatch queue for processing SCNetworkReachability
1976 * service requests
1977 */
1978 reach_server_q = dispatch_queue_create(REACH_SERVICE_NAME, NULL);
1979
1980 // create XPC listener
1981 name = getenv("REACH_SERVER");
1982 if (name == NULL) {
1983 name = REACH_SERVICE_NAME;
1984 }
1985 connection = xpc_connection_create_mach_service(name,
1986 reach_server_q,
1987 XPC_CONNECTION_MACH_SERVICE_LISTENER);
1988
1989 xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
1990 xpc_type_t type;
1991
1992 type = xpc_get_type(event);
1993 if (type == XPC_TYPE_CONNECTION) {
1994 process_new_connection(event);
1995
1996 } else if (type == XPC_TYPE_ERROR) {
1997 const char *desc;
1998
1999 desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
2000 if (event == XPC_ERROR_CONNECTION_INVALID) {
2001 SCLog(TRUE, LOG_ERR, CFSTR("reach server: %s"), desc);
2002 xpc_release(connection);
2003 } else if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
2004 SCLog(TRUE, LOG_ERR, CFSTR("reach server: %s"), desc);
2005 } else {
2006 SCLog(TRUE, LOG_ERR,
2007 CFSTR("reach server: Connection error: %d : %s"),
2008 event,
2009 desc);
2010 }
2011
2012 } else {
2013 SCLog(TRUE, LOG_ERR,
2014 CFSTR("reach server: unknown event type : %x"),
2015 type);
2016 }
2017 });
2018 xpc_connection_resume(connection);
2019
2020 return;
2021 }
2022
2023 #ifdef MAIN
2024
2025 int
2026 main(int argc, char **argv)
2027 {
2028 // _sc_log = FALSE;
2029 _sc_verbose = (argc > 1) ? TRUE : FALSE;
2030 _sc_debug = TRUE;
2031
2032 load_SCNetworkReachability(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
2033 CFRunLoopRun();
2034 /* not reached */
2035 exit(0);
2036 return 0;
2037 }
2038
2039 #endif /* MAIN */
2040
2041 #endif // HAVE_REACHABILITY_SERVER