]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/reachability/SCNetworkReachabilityServer_server.c
configd-453.16.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 uint64_t target_id;
916
917
918 unsigned char bytes[CC_SHA1_DIGEST_LENGTH];
919 CC_SHA1_CTX ctx;
920 CFDataRef digest = NULL;
921 size_t len;
922 xpc_connection_t remote;
923 xpc_object_t reply;
924 bool resolverBypass = FALSE;
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, name, strlen(name));
962 }
963
964 serv = xpc_dictionary_get_string(request, REACH_TARGET_SERV);
965 if (serv != NULL) {
966 CC_SHA1_Update(&ctx, serv, strlen(serv));
967 }
968
969 localAddress = xpc_dictionary_get_data(request, REACH_TARGET_LOCAL_ADDR, &len);
970 if (localAddress != NULL) {
971 if ((len == localAddress->sa_len) && (len <= sizeof(struct sockaddr_storage))) {
972 sanitize_address(localAddress, (struct sockaddr *)&localAddress0);
973 CC_SHA1_Update(&ctx, &localAddress0, len);
974 } else {
975 xpc_dictionary_set_string(reply,
976 REACH_REQUEST_REPLY_DETAIL,
977 "local address: size error");
978 goto done;
979 }
980 }
981
982 remoteAddress = xpc_dictionary_get_data(request, REACH_TARGET_REMOTE_ADDR, &len);
983 if (remoteAddress != NULL) {
984 if ((len == remoteAddress->sa_len) && (len <= sizeof(struct sockaddr_storage))) {
985 sanitize_address(remoteAddress, (struct sockaddr *)&remoteAddress0);
986 CC_SHA1_Update(&ctx, &remoteAddress0, len);
987 } else {
988 xpc_dictionary_set_string(reply,
989 REACH_REQUEST_REPLY_DETAIL,
990 "remote address: size error");
991 goto done;
992 }
993 }
994
995 hints = xpc_dictionary_get_data(request, REACH_TARGET_HINTS, &len);
996 if (hints != NULL) {
997 if (len == sizeof(struct addrinfo)) {
998 CC_SHA1_Update(&ctx, hints, len);
999 } else {
1000 xpc_dictionary_set_string(reply,
1001 REACH_REQUEST_REPLY_DETAIL,
1002 "hints: size error");
1003 goto done;
1004 }
1005 }
1006
1007 if_index = xpc_dictionary_get_int64(request, REACH_TARGET_IF_INDEX);
1008 if (if_index != 0) {
1009 if_name = xpc_dictionary_get_string(request, REACH_TARGET_IF_NAME);
1010 if (if_name != NULL) {
1011 CC_SHA1_Update(&ctx, if_name, strlen(if_name));
1012 }
1013 }
1014
1015 onDemandBypass = xpc_dictionary_get_bool(request, REACH_TARGET_ONDEMAND_BYPASS);
1016 if (onDemandBypass) {
1017 CC_SHA1_Update(&ctx, &onDemandBypass, sizeof(onDemandBypass));
1018 }
1019
1020 resolverBypass = xpc_dictionary_get_bool(request, REACH_TARGET_RESOLVER_BYPASS);
1021 if (resolverBypass) {
1022 CC_SHA1_Update(&ctx, &resolverBypass, sizeof(resolverBypass));
1023 }
1024
1025
1026 CC_SHA1_Final(bytes, &ctx);
1027 digest = CFDataCreate(NULL, bytes, sizeof(bytes));
1028
1029 /*
1030 * Check to see if we already have a SCNetworkReachability object
1031 * for this digest. If so, we'll share the existing target. If not,
1032 * create a new [shared] target.
1033 */
1034 dispatch_sync(_server_digest_queue(), ^{
1035 target = CFDictionaryGetValue(reach_digest_map, digest);
1036 if (target != NULL) {
1037 CFRetain(target);
1038 } else {
1039 CFDataRef data;
1040 CFMutableDictionaryRef options;
1041 CFStringRef str;
1042
1043 options = CFDictionaryCreateMutable(NULL,
1044 0,
1045 &kCFTypeDictionaryKeyCallBacks,
1046 &kCFTypeDictionaryValueCallBacks);
1047 if (name != NULL) {
1048 str = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
1049 CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, str);
1050 CFRelease(str);
1051 }
1052 if (serv != NULL) {
1053 str = CFStringCreateWithCString(NULL, serv, kCFStringEncodingUTF8);
1054 CFDictionarySetValue(options, kSCNetworkReachabilityOptionServName, str);
1055 CFRelease(str);
1056 }
1057 if (localAddress != NULL) {
1058 data = CFDataCreate(NULL, (const UInt8 *)&localAddress0, localAddress0.ss_len);
1059 CFDictionarySetValue(options, kSCNetworkReachabilityOptionLocalAddress, data);
1060 CFRelease(data);
1061 }
1062 if (remoteAddress != NULL) {
1063 data = CFDataCreate(NULL, (const UInt8 *)&remoteAddress0, remoteAddress0.ss_len);
1064 CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data);
1065 CFRelease(data);
1066 }
1067 if (hints != NULL) {
1068 data = CFDataCreate(NULL, (const UInt8 *)hints, sizeof(struct addrinfo));
1069 CFDictionarySetValue(options, kSCNetworkReachabilityOptionHints, data);
1070 CFRelease(data);
1071 }
1072 if (onDemandBypass) {
1073 CFDictionarySetValue(options,
1074 kSCNetworkReachabilityOptionConnectionOnDemandBypass,
1075 kCFBooleanTrue);
1076 }
1077 if (resolverBypass) {
1078 CFDictionarySetValue(options,
1079 kSCNetworkReachabilityOptionResolverBypass,
1080 kCFBooleanTrue);
1081 }
1082 CFDictionarySetValue(options,
1083 kSCNetworkReachabilityOptionServerBypass,
1084 kCFBooleanTrue);
1085 target = SCNetworkReachabilityCreateWithOptions(NULL, options);
1086 CFRelease(options);
1087 if (target == NULL) {
1088 xpc_dictionary_set_string(reply,
1089 REACH_REQUEST_REPLY_DETAIL,
1090 "SCNetworkReachabilityCreateWithOptions failed");
1091 ok = FALSE;
1092 return;
1093 }
1094
1095 // because the interface name may not (no longer) be valid we set
1096 // this after we've created the SCNetworkReachabilty object
1097 if ((if_index != 0) && (if_name != NULL)) {
1098 SCNetworkReachabilityPrivateRef targetPrivate;
1099
1100 targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1101 targetPrivate->if_index = if_index;
1102 strlcpy(targetPrivate->if_name, if_name, sizeof(targetPrivate->if_name));
1103 }
1104
1105
1106 ok = SCNetworkReachabilitySetCallback(target, _reach_changed, NULL);
1107 if (!ok) {
1108 xpc_dictionary_set_string(reply,
1109 REACH_REQUEST_REPLY_DETAIL,
1110 "SCNetworkReachabilitySetCallback failed");
1111 CFRelease(target);
1112 target = NULL;
1113 return;
1114 }
1115 }
1116
1117 // bump the number of references to this target
1118 _target_reference_add(target, digest, client->connection);
1119 });
1120
1121 if (!ok) {
1122 goto done;
1123 }
1124
1125 /*
1126 * add an association for the client's target_id to the [shared]
1127 * SCNetworkReachability object.
1128 */
1129 added = _client_target_set(client, target_id, target);
1130 if (!added) {
1131 // if we already had a reference to the target (e.g. reconnect)
1132 dispatch_sync(_server_digest_queue(), ^{
1133 _target_reference_remove(target, client->connection);
1134 });
1135 }
1136
1137 status = REACH_REQUEST_REPLY_OK;
1138
1139 done :
1140
1141 xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1142 // log_xpc_object(" reply", reply);
1143 xpc_connection_send_message(remote, reply);
1144 xpc_release(reply);
1145
1146 if (digest != NULL) CFRelease(digest);
1147 if (target != NULL) CFRelease(target);
1148 return;
1149 }
1150
1151
1152 static void
1153 target_remove(reach_client_t *client, xpc_object_t request)
1154 {
1155 xpc_connection_t remote;
1156 xpc_object_t reply;
1157 uint64_t status = REACH_REQUEST_REPLY_FAILED;
1158 SCNetworkReachabilityRef target = NULL;
1159 uint64_t target_id;
1160
1161 if (S_debug) {
1162 SCLog(TRUE, LOG_INFO,
1163 CFSTR("<%p> remove reachability target"),
1164 client->connection);
1165 // log_xpc_object(" remove", request);
1166 }
1167
1168 remote = xpc_dictionary_get_remote_connection(request);
1169 reply = xpc_dictionary_create_reply(request);
1170 if (reply == NULL) {
1171 SCLog(TRUE, LOG_ERR,
1172 CFSTR("<%p> target_remove: xpc_dictionary_create_reply: failed"),
1173 client->connection);
1174 return;
1175 }
1176
1177 target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
1178 if (target_id == 0) {
1179 xpc_dictionary_set_string(reply,
1180 REACH_REQUEST_REPLY_DETAIL,
1181 "no target ID");
1182 goto done;
1183 }
1184
1185 target = _client_target_copy(client, target_id);
1186 if (target == NULL) {
1187 xpc_dictionary_set_string(reply,
1188 REACH_REQUEST_REPLY_DETAIL,
1189 "no target");
1190 status = REACH_REQUEST_REPLY_UNKNOWN;
1191 goto done;
1192 }
1193
1194 /*
1195 * remove the association from the client's target_id to the [shared]
1196 * SCNetworkReachability object.
1197 */
1198 _client_target_remove(client, target_id);
1199
1200 // drop the number of references to this target
1201 dispatch_sync(_server_digest_queue(), ^{
1202 _target_reference_remove(target, client->connection);
1203 });
1204
1205 status = REACH_REQUEST_REPLY_OK;
1206
1207 done :
1208
1209 xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1210 // log_xpc_object(" reply", reply);
1211 xpc_connection_send_message(remote, reply);
1212 xpc_release(reply);
1213
1214 if (target != NULL) CFRelease(target);
1215 return;
1216 }
1217
1218
1219 static void
1220 target_schedule(reach_client_t *client, xpc_object_t request)
1221 {
1222 Boolean ok;
1223 xpc_connection_t remote;
1224 xpc_object_t reply;
1225 uint64_t status = REACH_REQUEST_REPLY_FAILED;
1226 SCNetworkReachabilityRef target = NULL;
1227 uint64_t target_id;
1228
1229 if (S_debug) {
1230 SCLog(TRUE, LOG_INFO,
1231 CFSTR("<%p> schedule reachability target"),
1232 client->connection);
1233 // log_xpc_object(" schedule", request);
1234 }
1235
1236 remote = xpc_dictionary_get_remote_connection(request);
1237 reply = xpc_dictionary_create_reply(request);
1238 if (reply == NULL) {
1239 SCLog(TRUE, LOG_ERR,
1240 CFSTR("<%p> target_schedule: xpc_dictionary_create_reply: failed"),
1241 client->connection);
1242 return;
1243 }
1244
1245 target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
1246 if (target_id == 0) {
1247 xpc_dictionary_set_string(reply,
1248 REACH_REQUEST_REPLY_DETAIL,
1249 "no target ID");
1250 goto done;
1251 }
1252
1253 target = _client_target_copy(client, target_id);
1254 if (target == NULL) {
1255 xpc_dictionary_set_string(reply,
1256 REACH_REQUEST_REPLY_DETAIL,
1257 "no target");
1258 status = REACH_REQUEST_REPLY_UNKNOWN;
1259 goto done;
1260 }
1261
1262 // enable monitoring
1263 ok = _target_watcher_add(target, client->connection, target_id);
1264 if (ok) {
1265 status = REACH_REQUEST_REPLY_OK;
1266 }
1267
1268 done :
1269
1270 xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1271 // log_xpc_object(" reply", reply);
1272 xpc_connection_send_message(remote, reply);
1273 xpc_release(reply);
1274
1275 if (target != NULL) CFRelease(target);
1276 return;
1277 }
1278
1279
1280 static void
1281 target_status(reach_client_t *client, xpc_object_t request)
1282 {
1283 xpc_connection_t remote;
1284 xpc_object_t reply;
1285 __block Boolean reply_now = TRUE;
1286 Boolean scheduled;
1287 uint64_t status = REACH_REQUEST_REPLY_FAILED;
1288 SCNetworkReachabilityRef target = NULL;
1289 uint64_t target_id;
1290
1291 if(S_debug) {
1292 SCLog(TRUE, LOG_INFO,
1293 CFSTR("<%p> get status of reachability target"),
1294 client->connection);
1295 // log_xpc_object(" status", request);
1296 }
1297
1298 remote = xpc_dictionary_get_remote_connection(request);
1299 reply = xpc_dictionary_create_reply(request);
1300 if (reply == NULL) {
1301 SCLog(TRUE, LOG_ERR,
1302 CFSTR("<%p> target_status: xpc_dictionary_create_reply: failed"),
1303 client->connection);
1304 return;
1305 }
1306
1307 target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
1308 if (target_id == 0) {
1309 SCLog(TRUE, LOG_ERR,
1310 CFSTR("<%p> target_status: no target"),
1311 client->connection);
1312 xpc_dictionary_set_string(reply,
1313 REACH_REQUEST_REPLY_DETAIL,
1314 "no target ID");
1315 goto done;
1316 }
1317
1318 target = _client_target_copy(client, target_id);
1319 if (target == NULL) {
1320 SCLog(TRUE, LOG_ERR,
1321 CFSTR("<%p> target_status: no target (0x%0llx)"),
1322 client->connection,
1323 target_id);
1324 xpc_dictionary_set_string(reply,
1325 REACH_REQUEST_REPLY_DETAIL,
1326 "no target");
1327 status = REACH_REQUEST_REPLY_UNKNOWN;
1328 goto done;
1329 }
1330
1331 /*
1332 * Check to see if the target [for this client] had been "scheduled".
1333 *
1334 * If so, also mark that we've picked up the current reachability
1335 * flags and that any pending notifications have been processed.
1336 */
1337 scheduled = _target_watcher_checkin(target, client->connection, target_id);
1338
1339 /*
1340 * return current reachability information to the caller
1341 */
1342 dispatch_sync(_target_queue(target), ^{
1343 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1344
1345 if (scheduled) {
1346 /*
1347 * The client "scheduled" this target. As such, we
1348 * know that this an async query and that we only
1349 * need to return the "last known" flags.
1350 */
1351 _target_reply_add_reachability(target, reply);
1352 // log_xpc_object(" reply [scheduled]", reply);
1353
1354 if (S_debug) {
1355 CFStringRef str;
1356
1357 str = _SCNetworkReachabilityCopyTargetFlags(target);
1358 SCLog(TRUE, LOG_INFO,
1359 CFSTR("<%p> reply [scheduled], %@"),
1360 client->connection,
1361 str);
1362 CFRelease(str);
1363 }
1364 } else {
1365 /*
1366 * The client has NOT "scheduled" this target. As
1367 * such, we know that this is a sync query and that
1368 * must return "current" flags.
1369 */
1370 if (targetPrivate->scheduled && targetPrivate->serverInfoValid) {
1371 /*
1372 * The server target has been "scheduled" and we
1373 * have flags that are "current".
1374 */
1375 _target_reply_add_reachability(target, reply);
1376 // log_xpc_object(" reply [scheduled/valid]", reply);
1377
1378 if (S_debug) {
1379 CFStringRef str;
1380
1381 str = _SCNetworkReachabilityCopyTargetFlags(target);
1382 SCLog(TRUE, LOG_INFO,
1383 CFSTR("<%p> reply [scheduled/valid], %@"),
1384 client->connection,
1385 str);
1386 CFRelease(str);
1387 }
1388 } else {
1389 dispatch_group_t group;
1390
1391 /*
1392 * The server target has NOT been "scheduled" (or
1393 * we do not have "current" flags. This means that
1394 * we must query for the current information and
1395 * return the flags to the client when they are
1396 * available.
1397 */
1398
1399 reply_now = FALSE;
1400
1401 group = _target_group(target);
1402 if (_SC_ATOMIC_INC(&targetPrivate->serverQueryActive) == 0) {
1403 CFRetain(target);
1404 dispatch_group_async(group, _server_concurrent_queue(), ^{
1405 SCNetworkReachabilityFlags flags;
1406 unsigned int n;
1407 Boolean ok;
1408
1409 // query for the flags
1410 ok = SCNetworkReachabilityGetFlags(target, &flags);
1411 flags = targetPrivate->info.flags; // get the "raw" flags
1412 if (!ok) {
1413 SCLog(TRUE, LOG_ERR,
1414 CFSTR("SCNetworkReachabilityGetFlags() [sync query] failed"
1415 "\n target = %@"
1416 "\n status = %s"),
1417 target,
1418 SCErrorString(SCError()));
1419 }
1420
1421 // flags are now available
1422 n = _SC_ATOMIC_ZERO(&targetPrivate->serverQueryActive);
1423 if (S_debug) {
1424 SCLog(TRUE, LOG_INFO,
1425 CFSTR("%sSCNetworkReachabilityGetFlags() [sync query] complete, n = %d"),
1426 targetPrivate->log_prefix,
1427 n);
1428 }
1429
1430 CFRelease(target);
1431 });
1432 }
1433
1434 CFRetain(target);
1435 dispatch_group_notify(group, _target_queue(target), ^{
1436 // flags are now available
1437 _target_reply_add_reachability(target, reply);
1438 xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, REACH_REQUEST_REPLY_OK);
1439 // log_xpc_object(" reply [delayed]", reply);
1440
1441 if (S_debug) {
1442 CFStringRef str;
1443
1444 str = _SCNetworkReachabilityCopyTargetFlags(target);
1445 SCLog(TRUE, LOG_INFO,
1446 CFSTR("<%p> reply [delayed], %@"),
1447 client->connection,
1448 str);
1449 CFRelease(str);
1450 }
1451
1452 xpc_connection_send_message(remote, reply);
1453 xpc_release(reply);
1454
1455 CFRelease(target);
1456 });
1457 }
1458 }
1459 });
1460
1461 status = REACH_REQUEST_REPLY_OK;
1462
1463 done :
1464
1465 if (reply_now) {
1466 xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1467
1468 if (status != REACH_REQUEST_REPLY_OK) {
1469 // log_xpc_object(" reply [!]", reply);
1470
1471 if (S_debug) {
1472 SCLog(TRUE, LOG_INFO,
1473 CFSTR("<%p> reply [!]"),
1474 client->connection);
1475 }
1476 }
1477
1478 xpc_connection_send_message(remote, reply);
1479 xpc_release(reply);
1480 } else if (S_debug) {
1481 CFStringRef str;
1482
1483 str = _SCNetworkReachabilityCopyTargetFlags(target);
1484 SCLog(TRUE, LOG_INFO,
1485 CFSTR("<%p> no reply [yet], %@"),
1486 client->connection,
1487 str);
1488 CFRelease(str);
1489 }
1490
1491 if (target != NULL) CFRelease(target);
1492 return;
1493 }
1494
1495
1496 static void
1497 target_unschedule(reach_client_t *client, xpc_object_t request)
1498 {
1499 Boolean ok;
1500 xpc_connection_t remote;
1501 xpc_object_t reply;
1502 uint64_t status = REACH_REQUEST_REPLY_FAILED;
1503 SCNetworkReachabilityRef target = NULL;
1504 uint64_t target_id;
1505
1506 if (S_debug) {
1507 SCLog(TRUE, LOG_INFO,
1508 CFSTR("<%p> unschedule reachability target"),
1509 client->connection);
1510 // log_xpc_object(" unschedule", request);
1511 }
1512
1513 remote = xpc_dictionary_get_remote_connection(request);
1514 reply = xpc_dictionary_create_reply(request);
1515 if (reply == NULL) {
1516 SCLog(TRUE, LOG_ERR,
1517 CFSTR("<%p> target_unschedule: xpc_dictionary_create_reply: failed"),
1518 client->connection);
1519 return;
1520 }
1521
1522 target_id = xpc_dictionary_get_uint64(request, REACH_CLIENT_TARGET_ID);
1523 if (target_id == 0) {
1524 xpc_dictionary_set_string(reply,
1525 REACH_REQUEST_REPLY_DETAIL,
1526 "no target ID");
1527 goto done;
1528 }
1529
1530 target = _client_target_copy(client, target_id);
1531 if (target == NULL) {
1532 xpc_dictionary_set_string(reply,
1533 REACH_REQUEST_REPLY_DETAIL,
1534 "no target");
1535 status = REACH_REQUEST_REPLY_UNKNOWN;
1536 goto done;
1537 }
1538
1539 // disable monitoring
1540 ok = _target_watcher_remove(target, client->connection, target_id);
1541 if (ok) {
1542 status = REACH_REQUEST_REPLY_OK;
1543 }
1544
1545 done :
1546
1547 xpc_dictionary_set_int64(reply, REACH_REQUEST_REPLY, status);
1548 // log_xpc_object(" reply", reply);
1549 xpc_connection_send_message(remote, reply);
1550 xpc_release(reply);
1551
1552 if (target != NULL) CFRelease(target);
1553 return;
1554 }
1555
1556
1557 #define SNAPSHOT_PATH_STATE _PATH_VARTMP "configd-reachability"
1558
1559
1560 static void
1561 _snapshot_digest_watcher(const void *key, const void *value, void *context)
1562 {
1563 FILE *f = (FILE *)context;
1564 static reach_client_t no_client = {
1565 .pid = 0,
1566 .proc_name = "?",
1567 };
1568 struct rb_node *rbn;
1569 reach_client_t *rbt_client;
1570 reach_watcher_key_t *watcher_key;
1571 reach_watcher_val_t *watcher_val;
1572
1573 /* ALIGN: CF aligns to >8 byte boundries */
1574 watcher_key = (reach_watcher_key_t *)(void *)CFDataGetBytePtr(key);
1575 watcher_val = (reach_watcher_val_t *)(void *)CFDataGetBytePtr(value);
1576
1577 rbn = rb_tree_find_node(_reach_clients_rbt(), &watcher_key->connection);
1578 if (rbn == NULL) {
1579 rbn = &no_client.rbn;
1580 }
1581
1582 rbt_client = RBNODE_TO_REACH_CLIENT(rbn);
1583
1584 SCPrint(TRUE, f,
1585 CFSTR(" connection = %p, target(c) = 0x%0llx, command = %s, pid = %d, changes = %u\n"),
1586 watcher_key->connection,
1587 watcher_key->target_id,
1588 rbt_client->proc_name,
1589 rbt_client->pid,
1590 watcher_val->n_changes);
1591
1592 return;
1593 }
1594
1595
1596 static void
1597 _snapshot_digest(const void *key, const void *value, void *context)
1598 {
1599 FILE *f = (FILE *)context;
1600 CFStringRef digest = (CFStringRef)key;
1601 dispatch_queue_t q;
1602 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value;
1603 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1604
1605 q = _target_queue(target);
1606 dispatch_sync(q, ^{
1607 SCPrint(TRUE, f, CFSTR("\n digest : %@\n"), digest);
1608 SCPrint(TRUE, f, CFSTR(" %@\n"), target);
1609 SCPrint(TRUE, f, CFSTR(" valid = %s, active = %u, refs = %u\n"),
1610 targetPrivate->serverInfoValid ? "Y" : "N",
1611 targetPrivate->serverQueryActive,
1612 targetPrivate->serverReferences);
1613
1614 SCPrint(TRUE, f, CFSTR(" network %d.%3.3d"),
1615 targetPrivate->last_network.tv_sec,
1616 targetPrivate->last_network.tv_usec / 1000);
1617 #if !TARGET_OS_IPHONE
1618 SCPrint(TRUE, f, CFSTR(", power %d.%3.3d"),
1619 targetPrivate->last_power.tv_sec,
1620 targetPrivate->last_power.tv_usec / 1000);
1621 #endif // !TARGET_OS_IPHONE
1622 if (targetPrivate->type == reachabilityTypeName) {
1623 SCPrint(TRUE, f, CFSTR(", DNS %d.%3.3d"),
1624 targetPrivate->last_dns.tv_sec,
1625 targetPrivate->last_dns.tv_usec / 1000);
1626 if (timerisset(&targetPrivate->dnsQueryEnd)) {
1627 struct timeval dnsQueryElapsed;
1628
1629 timersub(&targetPrivate->dnsQueryEnd,
1630 &targetPrivate->dnsQueryStart,
1631 &dnsQueryElapsed);
1632 SCPrint(TRUE, f, CFSTR(" (query %d.%3.3d / reply %d.%3.3d)"),
1633 targetPrivate->dnsQueryStart.tv_sec,
1634 targetPrivate->dnsQueryStart.tv_usec / 1000,
1635 dnsQueryElapsed.tv_sec,
1636 dnsQueryElapsed.tv_usec / 1000);
1637 }
1638 }
1639 if (timerisset(&targetPrivate->last_push)) {
1640 SCPrint(TRUE, f, CFSTR(", last notify %d.%3.3d"),
1641 targetPrivate->last_push.tv_sec,
1642 targetPrivate->last_push.tv_usec / 1000);
1643 }
1644 SCPrint(TRUE, f, CFSTR("\n"));
1645
1646 if (targetPrivate->serverWatchers != NULL) {
1647 CFDictionaryApplyFunction(targetPrivate->serverWatchers,
1648 _snapshot_digest_watcher,
1649 f);
1650 }
1651 });
1652
1653 return;
1654 }
1655
1656
1657 static void
1658 _snapshot_target(const void *key, const void *value, void *context)
1659 {
1660 FILE *f = (FILE *)context;
1661 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value;
1662 uint64_t target_id;
1663 CFDataRef target_key = (CFDataRef)key;
1664
1665 /* ALIGN: CF aligns > 8 byte boundries */
1666 target_id = *(uint64_t *)(void *)CFDataGetBytePtr(target_key);
1667
1668 SCPrint(TRUE, f,
1669 CFSTR(" target(c) = 0x%0llx, target(s) = %@\n"),
1670 target_id,
1671 target);
1672
1673 return;
1674 }
1675
1676
1677 static void
1678 _snapshot(reach_client_t *client, xpc_object_t request)
1679 {
1680 uid_t euid;
1681 FILE *f;
1682 int fd;
1683 Boolean ok = FALSE;
1684 struct rb_node *rbn;
1685 struct rb_tree *rbt;
1686 xpc_connection_t remote;
1687 xpc_object_t reply;
1688
1689 if (S_debug) {
1690 SCLog(TRUE, LOG_INFO,
1691 CFSTR("<%p> snapshot"),
1692 client->connection);
1693 // log_xpc_object(" create", request);
1694 }
1695
1696 remote = xpc_dictionary_get_remote_connection(request);
1697 reply = xpc_dictionary_create_reply(request);
1698 if (reply == NULL) {
1699 SCLog(TRUE, LOG_ERR,
1700 CFSTR("<%p> _snapshot: xpc_dictionary_create_reply: failed"),
1701 client->connection);
1702 return;
1703 }
1704
1705 euid = xpc_connection_get_euid(remote);
1706 if (euid != 0) {
1707 xpc_dictionary_set_string(reply,
1708 REACH_REQUEST_REPLY_DETAIL,
1709 "Permission denied.");
1710 goto done;
1711 }
1712
1713 // Save a snapshot of the SCNetworkReachability server "state"
1714
1715 (void) unlink(SNAPSHOT_PATH_STATE);
1716 fd = open(SNAPSHOT_PATH_STATE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
1717 if (fd == -1) {
1718 xpc_dictionary_set_string(reply,
1719 REACH_REQUEST_REPLY_DETAIL,
1720 "open: failed");
1721 goto done;
1722 }
1723 f = fdopen(fd, "w");
1724 if (f == NULL) {
1725 xpc_dictionary_set_string(reply,
1726 REACH_REQUEST_REPLY_DETAIL,
1727 "fdopen: failed");
1728 goto done;
1729 }
1730
1731 // provide connection/client info
1732
1733 SCPrint(TRUE, f, CFSTR("Clients :\n"));
1734 rbt = _reach_clients_rbt();
1735 rbn = rb_tree_iterate(rbt, NULL, RB_DIR_RIGHT);
1736 if (rbn != NULL) {
1737 while (rbn != NULL) {
1738 reach_client_t *rbt_client;
1739
1740 rbt_client = RBNODE_TO_REACH_CLIENT(rbn);
1741 SCPrint(TRUE, f,
1742 CFSTR("\n connection = %p, client = %p, command = %s, pid = %d\n"),
1743 rbt_client->connection,
1744 rbt_client,
1745 rbt_client->proc_name != NULL ? rbt_client->proc_name : "?",
1746 rbt_client->pid);
1747 my_CFDictionaryApplyFunction(rbt_client->targets,
1748 _snapshot_target,
1749 f);
1750
1751 rbn = rb_tree_iterate(rbt, rbn, RB_DIR_LEFT);
1752 }
1753 } else {
1754 SCPrint(TRUE, f, CFSTR(" None.\n"));
1755 }
1756 SCPrint(TRUE, f, CFSTR("\n"));
1757
1758 // provide "digest" info
1759
1760 SCPrint(TRUE, f, CFSTR("Digests :\n"));
1761 dispatch_sync(_server_digest_queue(), ^{
1762 if (reach_digest_map != NULL) {
1763 CFDictionaryApplyFunction(reach_digest_map,
1764 _snapshot_digest,
1765 f);
1766 }
1767 });
1768
1769 (void) fclose(f);
1770
1771 ok = TRUE;
1772
1773 done :
1774
1775 xpc_dictionary_set_int64(reply,
1776 REACH_REQUEST_REPLY,
1777 ok ? REACH_REQUEST_REPLY_OK : REACH_REQUEST_REPLY_FAILED);
1778 // log_xpc_object(" reply", reply);
1779 xpc_connection_send_message(remote, reply);
1780 xpc_release(reply);
1781
1782 return;
1783 }
1784
1785
1786 static __inline__ void
1787 _extract_client_info(reach_client_t *client, xpc_object_t request)
1788 {
1789 // if available/needed, save the process name
1790 if (client->proc_name == NULL) {
1791 const char *proc_name;
1792
1793 proc_name = xpc_dictionary_get_string(request, REACH_CLIENT_PROC_NAME);
1794 if (proc_name != NULL) {
1795 client->proc_name = strdup(proc_name);
1796 }
1797 }
1798
1799 return;
1800 }
1801
1802
1803 static void
1804 process_request(reach_client_t *client, xpc_object_t request)
1805 {
1806 int64_t op;
1807
1808 op = xpc_dictionary_get_int64(request, REACH_REQUEST);
1809 switch (op) {
1810 case REACH_REQUEST_CREATE :
1811 _extract_client_info(client, request);
1812 target_add(client, request);
1813 break;
1814 case REACH_REQUEST_REMOVE :
1815 target_remove(client, request);
1816 break;
1817 case REACH_REQUEST_STATUS :
1818 target_status(client, request);
1819 break;
1820 case REACH_REQUEST_SCHEDULE :
1821 target_schedule(client, request);
1822 break;
1823 case REACH_REQUEST_UNSCHEDULE :
1824 target_unschedule(client, request);
1825 break;
1826 case REACH_REQUEST_SNAPSHOT :
1827 _extract_client_info(client, request);
1828 _snapshot(client, request);
1829 break;
1830 default :
1831 SCLog(TRUE, LOG_ERR,
1832 CFSTR("<%p> unknown request : %d"),
1833 client->connection,
1834 op);
1835 break;
1836 }
1837
1838 return;
1839 }
1840
1841
1842 static void
1843 process_new_connection(xpc_connection_t connection)
1844 {
1845 if (S_debug) {
1846 SCLog(TRUE, LOG_INFO, CFSTR("<%p> new reach client, pid=%d"),
1847 connection,
1848 xpc_connection_get_pid(connection));
1849 }
1850
1851 dispatch_sync(_reach_connection_queue(), ^{
1852 reach_client_t *client;
1853
1854 client = _reach_client_create(connection);
1855 if (client == NULL || !rb_tree_insert_node(_reach_clients_rbt(), &client->rbn)) {
1856 __builtin_trap();
1857 }
1858 });
1859
1860 xpc_connection_set_event_handler(connection, ^(xpc_object_t xobj) {
1861 xpc_type_t type;
1862
1863 type = xpc_get_type(xobj);
1864 if (type == XPC_TYPE_DICTIONARY) {
1865 dispatch_sync(_reach_connection_queue(), ^{
1866 struct rb_node *rbn;
1867
1868 rbn = rb_tree_find_node(_reach_clients_rbt(), &connection);
1869 if (rbn != NULL) {
1870 reach_client_t *client;
1871
1872 // process the request
1873 client = RBNODE_TO_REACH_CLIENT(rbn);
1874 process_request(client, xobj);
1875 } else {
1876 char *desc;
1877
1878 SCLog(TRUE, LOG_ERR,
1879 CFSTR("<%p:%d> unexpected SCNetworkReachability request"),
1880 connection,
1881 xpc_connection_get_pid(connection));
1882
1883 desc = xpc_copy_description(xobj);
1884 SCLog(TRUE, LOG_ERR,
1885 CFSTR(" request = %s"),
1886 desc);
1887 free(desc);
1888
1889 xpc_connection_cancel(connection);
1890 }
1891 });
1892
1893 } else if (type == XPC_TYPE_ERROR) {
1894 const char *desc;
1895
1896 desc = xpc_dictionary_get_string(xobj, XPC_ERROR_KEY_DESCRIPTION);
1897 if (xobj == XPC_ERROR_CONNECTION_INVALID) {
1898 if (S_debug) {
1899 SCLog(TRUE, LOG_INFO,
1900 CFSTR("<%p:%d> %s"),
1901 connection,
1902 xpc_connection_get_pid(connection),
1903 desc);
1904 }
1905
1906 xpc_retain(connection);
1907 dispatch_async(_reach_connection_queue(), ^{
1908 _reach_client_remove(connection);
1909 xpc_release(connection);
1910 });
1911
1912 } else if (xobj == XPC_ERROR_CONNECTION_INTERRUPTED) {
1913 SCLog(TRUE, LOG_ERR,
1914 CFSTR("<%p:%d> %s"),
1915 connection,
1916 xpc_connection_get_pid(connection),
1917 desc);
1918
1919 } else {
1920 SCLog(TRUE, LOG_ERR,
1921 CFSTR("<%p:%d> Connection error: %d : %s"),
1922 connection,
1923 xpc_connection_get_pid(connection),
1924 xobj,
1925 desc);
1926 }
1927
1928 } else {
1929 SCLog(TRUE, LOG_ERR,
1930 CFSTR("<%p:%d> unknown event type : %x"),
1931 connection,
1932 xpc_connection_get_pid(connection),
1933 type);
1934 }
1935 });
1936
1937 xpc_connection_resume(connection);
1938
1939 return;
1940 }
1941
1942
1943 #pragma mark -
1944 #pragma mark Reachability server "main"
1945
1946
1947 __private_extern__
1948 void
1949 load_SCNetworkReachability(CFBundleRef bundle, Boolean bundleVerbose)
1950 {
1951 xpc_connection_t connection;
1952 const char *name;
1953 dispatch_queue_t reach_server_q;
1954
1955 S_debug = bundleVerbose;
1956
1957 /*
1958 * create a dictionary mapping SCNetworkReachability [CFData] digests
1959 * to SCNetworkReachability objects.
1960 */
1961 reach_digest_map = CFDictionaryCreateMutable(NULL,
1962 0,
1963 &kCFTypeDictionaryKeyCallBacks,
1964 &kCFTypeDictionaryValueCallBacks);
1965
1966 /*
1967 * create dispatch queue for processing SCNetworkReachability
1968 * service requests
1969 */
1970 reach_server_q = dispatch_queue_create(REACH_SERVICE_NAME, NULL);
1971
1972 // create XPC listener
1973 name = getenv("REACH_SERVER");
1974 if (name == NULL) {
1975 name = REACH_SERVICE_NAME;
1976 }
1977 connection = xpc_connection_create_mach_service(name,
1978 reach_server_q,
1979 XPC_CONNECTION_MACH_SERVICE_LISTENER);
1980
1981 xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
1982 xpc_type_t type;
1983
1984 type = xpc_get_type(event);
1985 if (type == XPC_TYPE_CONNECTION) {
1986 process_new_connection(event);
1987
1988 } else if (type == XPC_TYPE_ERROR) {
1989 const char *desc;
1990
1991 desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
1992 if (event == XPC_ERROR_CONNECTION_INVALID) {
1993 SCLog(TRUE, LOG_ERR, CFSTR("reach server: %s"), desc);
1994 xpc_release(connection);
1995 } else if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
1996 SCLog(TRUE, LOG_ERR, CFSTR("reach server: %s"), desc);
1997 } else {
1998 SCLog(TRUE, LOG_ERR,
1999 CFSTR("reach server: Connection error: %d : %s"),
2000 event,
2001 desc);
2002 }
2003
2004 } else {
2005 SCLog(TRUE, LOG_ERR,
2006 CFSTR("reach server: unknown event type : %x"),
2007 type);
2008 }
2009 });
2010 xpc_connection_resume(connection);
2011
2012 return;
2013 }
2014
2015 #ifdef MAIN
2016
2017 int
2018 main(int argc, char **argv)
2019 {
2020 // _sc_log = FALSE;
2021 _sc_verbose = (argc > 1) ? TRUE : FALSE;
2022 _sc_debug = TRUE;
2023
2024 load_SCNetworkReachability(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
2025 CFRunLoopRun();
2026 /* not reached */
2027 exit(0);
2028 return 0;
2029 }
2030
2031 #endif /* MAIN */
2032
2033 #endif // HAVE_REACHABILITY_SERVER