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