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