]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkReachability.c
configd-699.1.5.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkReachability.c
1 /*
2 * Copyright (c) 2003-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 /*
25 * Modification History
26 *
27 * April 12, 2011 Allan Nathanson <ajn@apple.com>
28 * - add SCNetworkReachability "server"
29 *
30 * March 31, 2004 Allan Nathanson <ajn@apple.com>
31 * - use [SC] DNS configuration information
32 *
33 * January 19, 2003 Allan Nathanson <ajn@apple.com>
34 * - add advanced reachability APIs
35 */
36
37 #include <Availability.h>
38 #include <TargetConditionals.h>
39 #include <sys/cdefs.h>
40 #include <dispatch/dispatch.h>
41 #include <dispatch/private.h>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include <CoreFoundation/CFRuntime.h>
44 #include <SystemConfiguration/SystemConfiguration.h>
45 #include <SystemConfiguration/SCValidation.h>
46 #include <SystemConfiguration/SCPrivate.h>
47 #include <SystemConfiguration/VPNAppLayerPrivate.h>
48 #include <pthread.h>
49 #include <libkern/OSAtomic.h>
50
51 #if !TARGET_OS_IPHONE
52 #include <IOKit/pwr_mgt/IOPMLibPrivate.h>
53 #endif // !TARGET_OS_IPHONE
54
55 #include <notify.h>
56 #include <dnsinfo.h>
57 #include <netinet/in.h>
58 #include <arpa/inet.h>
59 #include <netdb.h>
60 #include <resolv.h>
61 #include <unistd.h>
62 #include <sys/ioctl.h>
63 #include <sys/socket.h>
64 #include <net/if.h>
65 #include <net/if_dl.h>
66 #include <net/if_types.h>
67 #define KERNEL_PRIVATE
68 #include <net/route.h>
69 #undef KERNEL_PRIVATE
70
71 #ifndef s6_addr16
72 #define s6_addr16 __u6_addr.__u6_addr16
73 #endif
74
75 #include "SCNetworkConnectionInternal.h"
76 #include "SCNetworkReachabilityInternal.h"
77
78 #include <ppp/ppp_msg.h>
79 #include <ppp/PPPControllerPriv.h>
80
81 #include <network_information.h>
82
83
84
85
86
87
88
89 #define DEBUG_REACHABILITY_TYPE_NAME "create w/name"
90 #define DEBUG_REACHABILITY_TYPE_NAME_CLONE " > clone"
91 #define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options"
92
93 #define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address"
94 #define DEBUG_REACHABILITY_TYPE_ADDRESS_CLONE " > clone"
95 #define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options"
96
97 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair"
98 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_CLONE " > clone"
99 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options"
100
101 #define DEBUG_REACHABILITY_TYPE_PTR "create w/ptr"
102 #define DEBUG_REACHABILITY_TYPE_PTR_CLONE " > clone"
103 #define DEBUG_REACHABILITY_TYPE_PTR_OPTIONS " + options"
104
105 #define DNS_FLAGS_FORMAT "[%s%s%s%s%s]"
106 #define DNS_FLAGS_VALUES(t) t->dnsHaveV4 ? "4" : "", \
107 t->dnsHaveV6 ? "6" : "", \
108 t->dnsHavePTR ? "P" : "", \
109 t->dnsHaveTimeout ? "T" : "", \
110 t->dnsHaveError ? "E" : ""
111
112
113 static pthread_mutexattr_t lock_attr;
114
115 #define MUTEX_INIT(m) { \
116 int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \
117 assert(_lock_); \
118 }
119
120 #define MUTEX_LOCK(m) { \
121 int _lock_ = (pthread_mutex_lock(m) == 0); \
122 assert(_lock_); \
123 }
124
125 #define MUTEX_UNLOCK(m) { \
126 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
127 assert(_unlock_); \
128 }
129
130 #define MUTEX_ASSERT_HELD(m) { \
131 int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \
132 assert(_locked_); \
133 }
134
135
136 #define SCNETWORKREACHABILITY_TRIGGER_KEY CFSTR("com.apple.SCNetworkReachability:FORCE-CHANGE")
137
138
139 #define N_QUICK 64
140
141
142 static CFStringRef __SCNetworkReachabilityCopyDescription (CFTypeRef cf);
143 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf);
144 static void reachPerform (void *info);
145 static Boolean reachUpdate (SCNetworkReachabilityRef target);
146
147
148 static void
149 __SCNetworkReachabilityHandleChanges (SCDynamicStoreRef store,
150 CFArrayRef changedKeys,
151 void *info);
152
153 static Boolean
154 __SCNetworkReachabilityScheduleWithRunLoop (SCNetworkReachabilityRef target,
155 CFRunLoopRef runLoop,
156 CFStringRef runLoopMode,
157 dispatch_queue_t queue,
158 Boolean onDemand);
159
160 static Boolean
161 __SCNetworkReachabilityUnscheduleFromRunLoop (SCNetworkReachabilityRef target,
162 CFRunLoopRef runLoop,
163 CFStringRef runLoopMode,
164 Boolean onDemand);
165
166
167 static CFTypeID __kSCNetworkReachabilityTypeID = _kCFRuntimeNotATypeID;
168
169
170 static const CFRuntimeClass __SCNetworkReachabilityClass = {
171 0, // version
172 "SCNetworkReachability", // className
173 NULL, // init
174 NULL, // copy
175 __SCNetworkReachabilityDeallocate, // dealloc
176 NULL, // equal
177 NULL, // hash
178 NULL, // copyFormattingDesc
179 __SCNetworkReachabilityCopyDescription // copyDebugDesc
180 };
181
182
183 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
184 static const ReachabilityInfo NOT_REACHABLE = { 0, 0, 0, { 0 }, FALSE };
185 static const ReachabilityInfo NOT_REPORTED = { 0, 0xFFFFFFFF, 0, { 0 }, FALSE };
186 static int rtm_seq = 0;
187
188
189 static const struct timeval TIME_ZERO = { 0, 0 };
190
191
192 static int dnsCount = 0;
193 static int dnsGeneration = 0;
194 static DNSServiceRef dnsMain = NULL;
195 static CFMutableSetRef dnsUpdated = NULL;
196
197 static Boolean D_serverBypass = FALSE;
198
199
200
201 #if !TARGET_OS_IPHONE
202 /*
203 * Power capabilities (sleep/wake)
204 */
205 #define POWER_CAPABILITIES_NETWORK ( kIOPMCapabilityCPU \
206 | kIOPMCapabilityNetwork \
207 | kIOPMCapabilityVideo)
208 static IOPMSystemPowerStateCapabilities power_capabilities = POWER_CAPABILITIES_NETWORK;
209 #endif // !TARGET_OS_IPHONE
210
211
212 /*
213 * host "something has changed" notifications
214 */
215
216 // Note: protected by _hn_target_queue()
217 static SCDynamicStoreRef hn_store = NULL;
218 static CFMutableSetRef hn_targets = NULL;
219
220
221 static dispatch_queue_t
222 _hn_changes_queue()
223 {
224 static dispatch_once_t once;
225 static dispatch_queue_t q = NULL;
226
227 dispatch_once(&once, ^{
228 q = dispatch_queue_create("SCNetworkReachability.handleChanges", NULL);
229 });
230
231 return q;
232 }
233
234
235 static dispatch_queue_t
236 _hn_target_queue()
237 {
238 static dispatch_once_t once;
239 static dispatch_queue_t q;
240
241 dispatch_once(&once, ^{
242 q = dispatch_queue_create("SCNetworkReachability.targetManagement", NULL);
243 });
244
245 return q;
246 }
247
248
249 /*
250 * DNS configuration
251 */
252
253 typedef struct {
254 dns_config_t *config;
255 int refs;
256 } dns_configuration_t;
257
258
259 // Note: protected by "dns_lock"
260 static pthread_mutex_t dns_lock = PTHREAD_MUTEX_INITIALIZER;
261 static dns_configuration_t *dns_configuration = NULL;
262 static int dns_token;
263 static Boolean dns_token_valid = FALSE;
264
265
266
267
268 typedef enum {
269 dns_query_async,
270 dns_query_mdns,
271 dns_query_mdns_timeout,
272 } query_type;
273
274
275 static void
276 __mark_operation_start(struct timeval *queryStart,
277 struct timeval *queryEnd)
278 {
279 (void) gettimeofday(queryStart, NULL);
280 *queryEnd = TIME_ZERO;
281
282 return;
283 }
284
285
286 static void
287 __mark_operation_end(SCNetworkReachabilityRef target,
288 Boolean found,
289 query_type query_type,
290 struct timeval *queryStart,
291 struct timeval *queryEnd)
292 {
293 struct timeval queryElapsed;
294 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
295
296 (void) gettimeofday(queryEnd, NULL);
297
298 if (!_sc_debug &&
299 (query_type != dns_query_mdns_timeout)) {
300 return;
301 }
302
303 if (!timerisset(queryStart)) {
304 return;
305 }
306
307 timersub(queryEnd, queryStart, &queryElapsed);
308 switch (query_type) {
309
310 #define QUERY_TIME__FMT "%ld.%6.6d"
311 #define QUERY_TIME__DIV 1
312
313 case dns_query_async :
314 SCLog(TRUE, LOG_INFO,
315 CFSTR("%sasync DNS complete%s (query time = " QUERY_TIME__FMT ")"),
316 targetPrivate->log_prefix,
317 found ? "" : ", host not found",
318 queryElapsed.tv_sec,
319 queryElapsed.tv_usec / QUERY_TIME__DIV);
320 break;
321 case dns_query_mdns :
322 SCLog(TRUE, LOG_INFO,
323 CFSTR("%s[m]DNS query complete%s (query time = " QUERY_TIME__FMT "), " DNS_FLAGS_FORMAT),
324 targetPrivate->log_prefix,
325 found ? "" : ", host not found",
326 queryElapsed.tv_sec,
327 queryElapsed.tv_usec / QUERY_TIME__DIV,
328 DNS_FLAGS_VALUES(targetPrivate));
329 break;
330 case dns_query_mdns_timeout :
331 SCLog(TRUE, LOG_ERR,
332 CFSTR("%s[m]DNS query timeout (query time = " QUERY_TIME__FMT "), " DNS_FLAGS_FORMAT),
333 targetPrivate->log_prefix,
334 queryElapsed.tv_sec,
335 queryElapsed.tv_usec / QUERY_TIME__DIV,
336 DNS_FLAGS_VALUES(targetPrivate));
337 break;
338 }
339
340 return;
341 }
342
343
344 static __inline__ Boolean
345 __reach_changed(ReachabilityInfo *r1, ReachabilityInfo *r2)
346 {
347 if (r1->flags != r2->flags) {
348 // if the reachability flags changed
349 return TRUE;
350 }
351
352 if (r1->if_index != r2->if_index) {
353 // if the target interface changed
354 return TRUE;
355 }
356
357 if ((r1->sleeping != r2->sleeping) && !r2->sleeping) {
358 // if our sleep/wake status changed and if we
359 // are no longer sleeping
360 return TRUE;
361 }
362
363 return FALSE;
364 }
365
366
367 static __inline__ void
368 _reach_set(ReachabilityInfo *dst,
369 const ReachabilityInfo *src,
370 uint64_t cycle,
371 unsigned int requested_if_index,
372 const char *requested_if_name)
373 {
374 memcpy(dst, src, sizeof(ReachabilityInfo));
375 dst->cycle = cycle;
376
377 if (!(dst->flags & kSCNetworkReachabilityFlagsReachable) ||
378 (dst->flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
379 // if not reachable or connection required, return the
380 // requested if_index and if_name.
381 dst->if_index = requested_if_index;
382 if (requested_if_name != NULL) {
383 strlcpy(dst->if_name, requested_if_name, sizeof(dst->if_name));
384 } else {
385 dst->if_name[0] = '\0';
386 }
387 }
388
389 return;
390 }
391
392
393 #pragma mark -
394 #pragma mark SCDynamicStore info
395
396
397 typedef struct {
398 SCDynamicStoreRef store;
399 CFStringRef entity;
400 CFDictionaryRef dict;
401 CFIndex n;
402 const void ** keys;
403 const void * keys_q[N_QUICK];
404 const void ** values;
405 const void * values_q[N_QUICK];
406 } ReachabilityStoreInfo, *ReachabilityStoreInfoRef;
407
408
409 static ReachabilityStoreInfo S_storeInfo = { 0 };
410 static Boolean S_storeInfoActive = FALSE;
411
412
413 static dispatch_queue_t
414 _storeInfo_queue()
415 {
416 static dispatch_once_t once;
417 static dispatch_queue_t q;
418
419 dispatch_once(&once, ^{
420 q = dispatch_queue_create("SCNetworkReachability.storeInfo", NULL);
421 });
422
423 return q;
424 }
425
426
427 static void
428 ReachabilityStoreInfo_copy(ReachabilityStoreInfoRef src,
429 ReachabilityStoreInfoRef dst)
430 {
431 if (src->dict != NULL) {
432 dst->store = src->store;
433 CFRetain(dst->store);
434
435 dst->dict = src->dict;
436 CFRetain(dst->dict);
437
438 dst->n = src->n;
439 if (dst->n > 0) {
440 if (dst->n <= (CFIndex)(sizeof(dst->keys_q) / sizeof(CFTypeRef))) {
441 dst->keys = dst->keys_q;
442 dst->values = dst->values_q;
443 } else {
444 dst->keys = CFAllocatorAllocate(NULL, dst->n * sizeof(CFTypeRef), 0);
445 dst->values = CFAllocatorAllocate(NULL, dst->n * sizeof(CFTypeRef), 0);
446 }
447 memcpy(dst->keys, src->keys, dst->n * sizeof(CFTypeRef));
448 memcpy(dst->values, src->values, dst->n * sizeof(CFTypeRef));
449 }
450 }
451
452 return;
453 }
454
455
456 static void
457 ReachabilityStoreInfo_enable(Boolean enable)
458 {
459 dispatch_sync(_storeInfo_queue(), ^{
460 S_storeInfoActive = enable;
461 });
462
463 return;
464 }
465
466
467 static void
468 ReachabilityStoreInfo_free(ReachabilityStoreInfoRef store_info)
469 {
470 if ((store_info->n > 0) && (store_info->keys != store_info->keys_q)) {
471 CFAllocatorDeallocate(NULL, store_info->keys);
472 store_info->keys = NULL;
473
474 CFAllocatorDeallocate(NULL, store_info->values);
475 store_info->values = NULL;
476 }
477 store_info->n = 0;
478
479 if (store_info->dict != NULL) {
480 CFRelease(store_info->dict);
481 store_info->dict = NULL;
482 }
483
484 if (store_info->store != NULL) {
485 CFRelease(store_info->store);
486 store_info->store = NULL;
487 }
488
489 return;
490 }
491
492
493 static void
494 ReachabilityStoreInfo_init(ReachabilityStoreInfoRef store_info)
495 {
496 dispatch_sync(_storeInfo_queue(), ^{
497 bzero(store_info, sizeof(ReachabilityStoreInfo));
498
499 if (S_storeInfoActive && (S_storeInfo.dict != NULL)) {
500 ReachabilityStoreInfo_copy(&S_storeInfo, store_info);
501 }
502 });
503
504 return;
505 }
506
507
508 static void
509 ReachabilityStoreInfo_save(ReachabilityStoreInfoRef store_info)
510 {
511 dispatch_sync(_storeInfo_queue(), ^{
512 if ((store_info == NULL) ||
513 !_SC_CFEqual(store_info->dict, S_storeInfo.dict)) {
514 // free any old info
515 ReachabilityStoreInfo_free(&S_storeInfo);
516
517 // save new info
518 if (S_storeInfoActive &&
519 (store_info != NULL) &&
520 (store_info->dict != NULL)) {
521 ReachabilityStoreInfo_copy(store_info, &S_storeInfo);
522 }
523 }
524 });
525
526 return;
527 }
528
529
530 static void
531 ReachabilityStoreInfo_keys(CFMutableArrayRef *fill_keys, CFMutableArrayRef *fill_patterns)
532 {
533 CFStringRef key;
534 CFMutableArrayRef keys;
535 CFStringRef pattern;
536 CFMutableArrayRef patterns;
537
538 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
539 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
540
541 // get info for IPv4 services
542 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
543 kSCDynamicStoreDomainState,
544 kSCEntNetIPv4);
545 CFArrayAppendValue(keys, key);
546 CFRelease(key);
547 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
548 kSCDynamicStoreDomainSetup,
549 kSCCompAnyRegex,
550 kSCEntNetIPv4);
551 CFArrayAppendValue(patterns, pattern);
552 CFRelease(pattern);
553 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
554 kSCDynamicStoreDomainState,
555 kSCCompAnyRegex,
556 kSCEntNetIPv4);
557 CFArrayAppendValue(patterns, pattern);
558 CFRelease(pattern);
559
560 // get info for IPv6 services
561 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
562 kSCDynamicStoreDomainState,
563 kSCEntNetIPv6);
564 CFArrayAppendValue(keys, key);
565 CFRelease(key);
566 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
567 kSCDynamicStoreDomainSetup,
568 kSCCompAnyRegex,
569 kSCEntNetIPv6);
570 CFArrayAppendValue(patterns, pattern);
571 CFRelease(pattern);
572 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
573 kSCDynamicStoreDomainState,
574 kSCCompAnyRegex,
575 kSCEntNetIPv6);
576 CFArrayAppendValue(patterns, pattern);
577 CFRelease(pattern);
578
579 // get info for PPP services
580 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
581 kSCDynamicStoreDomainSetup,
582 kSCCompAnyRegex,
583 kSCEntNetPPP);
584 CFArrayAppendValue(patterns, pattern);
585 CFRelease(pattern);
586 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
587 kSCDynamicStoreDomainState,
588 kSCCompAnyRegex,
589 kSCEntNetPPP);
590 CFArrayAppendValue(patterns, pattern);
591 CFRelease(pattern);
592
593 // get info for VPN services
594 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
595 kSCDynamicStoreDomainSetup,
596 kSCCompAnyRegex,
597 kSCEntNetVPN);
598 CFArrayAppendValue(patterns, pattern);
599 CFRelease(pattern);
600 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
601 kSCDynamicStoreDomainState,
602 kSCCompAnyRegex,
603 kSCEntNetVPN);
604 CFArrayAppendValue(patterns, pattern);
605 CFRelease(pattern);
606
607 // get info for IPSec services
608 // pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
609 // kSCDynamicStoreDomainSetup,
610 // kSCCompAnyRegex,
611 // kSCEntNetIPSec);
612 // CFArrayAppendValue(patterns, pattern);
613 // CFRelease(pattern);
614 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
615 kSCDynamicStoreDomainState,
616 kSCCompAnyRegex,
617 kSCEntNetIPSec);
618 CFArrayAppendValue(patterns, pattern);
619 CFRelease(pattern);
620
621 // get info to identify "available" services
622 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
623 kSCDynamicStoreDomainSetup,
624 kSCCompAnyRegex,
625 kSCEntNetInterface);
626 CFArrayAppendValue(patterns, pattern);
627 CFRelease(pattern);
628
629
630 *fill_keys = keys;
631 *fill_patterns = patterns;
632 return;
633 }
634
635
636 static Boolean
637 ReachabilityStoreInfo_fill(ReachabilityStoreInfoRef store_info)
638 {
639 CFMutableArrayRef keys;
640 CFMutableArrayRef patterns;
641
642 // get the SCDynamicStore info
643 ReachabilityStoreInfo_keys(&keys, &patterns);
644 store_info->dict = SCDynamicStoreCopyMultiple(store_info->store, keys, patterns);
645 CFRelease(keys);
646 CFRelease(patterns);
647 if (store_info->dict == NULL) {
648 return FALSE;
649 }
650
651 // and extract the keys/values for post-processing
652 store_info->n = CFDictionaryGetCount(store_info->dict);
653 if (store_info->n > 0) {
654 if (store_info->n <= (CFIndex)(sizeof(store_info->keys_q) / sizeof(CFTypeRef))) {
655 store_info->keys = store_info->keys_q;
656 store_info->values = store_info->values_q;
657 } else {
658 store_info->keys = CFAllocatorAllocate(NULL, store_info->n * sizeof(CFTypeRef), 0);
659 store_info->values = CFAllocatorAllocate(NULL, store_info->n * sizeof(CFTypeRef), 0);
660 }
661 CFDictionaryGetKeysAndValues(store_info->dict,
662 store_info->keys,
663 store_info->values);
664 }
665
666 return TRUE;
667 }
668
669
670 static Boolean
671 ReachabilityStoreInfo_update(ReachabilityStoreInfoRef store_info,
672 SCDynamicStoreRef *storeP,
673 sa_family_t sa_family)
674 {
675 __block Boolean ok = TRUE;
676
677 switch (sa_family) {
678 case AF_UNSPEC :
679 store_info->entity = NULL;
680 break;
681 case AF_INET :
682 store_info->entity = kSCEntNetIPv4;
683 break;
684 case AF_INET6 :
685 store_info->entity = kSCEntNetIPv6;
686 break;
687 default :
688 return FALSE;
689 }
690
691 if (store_info->dict != NULL) {
692 // if info already available
693 return TRUE;
694 }
695
696 dispatch_sync(_storeInfo_queue(), ^{
697 if (S_storeInfoActive && (S_storeInfo.dict != NULL)) {
698 // free any info
699 ReachabilityStoreInfo_free(store_info);
700
701 // copy the shared/available info
702 ReachabilityStoreInfo_copy(&S_storeInfo, store_info);
703 }
704
705 if (store_info->store == NULL) {
706 store_info->store = (storeP != NULL) ? *storeP : NULL;
707 if (store_info->store != NULL) {
708 // keep a reference to the passed in SCDynamicStore
709 CFRetain(store_info->store);
710 } else {
711 store_info->store = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkReachability"), NULL, NULL);
712 if (store_info->store == NULL) {
713 SCLog(TRUE, LOG_ERR, CFSTR("ReachabilityStoreInfo_update SCDynamicStoreCreate() failed"));
714 return;
715 }
716
717 if (storeP != NULL) {
718 // and pass back a reference
719 *storeP = store_info->store;
720 CFRetain(*storeP);
721 }
722 }
723 }
724
725 if (sa_family == AF_UNSPEC) {
726 // if the address family was not specified than
727 // all we wanted, for now, was to establish the
728 // SCDynamicStore session
729 return;
730 }
731
732 if (store_info->dict != NULL) {
733 // or we have picked up the shared info
734 return;
735 }
736
737 ok = ReachabilityStoreInfo_fill(store_info);
738 if (!ok) {
739 return;
740 }
741
742 if (!_SC_CFEqual(store_info->dict, S_storeInfo.dict)) {
743 // free any old info
744 ReachabilityStoreInfo_free(&S_storeInfo);
745
746 // save new info
747 if (S_storeInfoActive &&
748 (store_info->dict != NULL)) {
749 ReachabilityStoreInfo_copy(store_info, &S_storeInfo);
750 }
751 }
752 });
753
754 return ok;
755 }
756
757
758 #pragma mark -
759 #pragma mark Reachability engine
760
761
762 #define ROUNDUP(a, size) \
763 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
764
765 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
766 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
767 sizeof(uint32_t)) :\
768 sizeof(uint32_t)))
769
770 static void
771 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
772 {
773 int i;
774
775 for (i = 0; i < RTAX_MAX; i++) {
776 if (addrs & (1 << i)) {
777 rti_info[i] = sa;
778 NEXT_SA(sa);
779 } else
780 rti_info[i] = NULL;
781 }
782 }
783
784
785 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
786
787
788 typedef struct {
789 union {
790 char bytes[BUFLEN];
791 struct rt_msghdr rtm;
792 } buf;
793 int error;
794 struct sockaddr *rti_info[RTAX_MAX];
795 struct rt_msghdr *rtm;
796 struct sockaddr_dl *sdl;
797 } route_info, *route_info_p;
798
799
800 /*
801 * route_get()
802 * returns zero if route exists and data returned, EHOSTUNREACH
803 * if no route, or errno for any other error.
804 */
805 static int
806 route_get(const struct sockaddr *address,
807 unsigned int if_index,
808 route_info *info)
809 {
810 int n;
811 int opt;
812 pid_t pid = getpid();
813 int rsock;
814 struct sockaddr *sa;
815 int32_t seq = OSAtomicIncrement32Barrier(&rtm_seq);
816 #ifndef RTM_GET_SILENT
817 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
818 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
819 int sosize = 48 * 1024;
820 #endif
821
822 bzero(info, sizeof(*info));
823
824 info->rtm = &info->buf.rtm;
825 info->rtm->rtm_msglen = sizeof(struct rt_msghdr);
826 info->rtm->rtm_version = RTM_VERSION;
827 #ifdef RTM_GET_SILENT
828 info->rtm->rtm_type = RTM_GET_SILENT;
829 #else
830 info->rtm->rtm_type = RTM_GET;
831 #endif
832 info->rtm->rtm_flags = RTF_STATIC|RTF_UP|RTF_HOST|RTF_GATEWAY;
833 info->rtm->rtm_addrs = RTA_DST|RTA_IFP; /* Both destination and device */
834 info->rtm->rtm_pid = pid;
835 info->rtm->rtm_seq = seq;
836
837 if (if_index != 0) {
838 info->rtm->rtm_flags |= RTF_IFSCOPE;
839 info->rtm->rtm_index = if_index;
840 }
841
842 switch (address->sa_family) {
843 case AF_INET6: {
844 struct sockaddr_in6 *sin6;
845
846 /* ALIGN: caller ensures that the address is aligned */
847 sin6 = (struct sockaddr_in6 *)(void *)address;
848 if ((IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
849 IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) &&
850 (sin6->sin6_scope_id != 0)) {
851 sin6->sin6_addr.s6_addr16[1] = htons(sin6->sin6_scope_id);
852 sin6->sin6_scope_id = 0;
853 }
854 break;
855 }
856 }
857
858 sa = (struct sockaddr *) (info->rtm + 1);
859 bcopy(address, sa, address->sa_len);
860 n = ROUNDUP(sa->sa_len, sizeof(uint32_t));
861 info->rtm->rtm_msglen += n;
862
863 info->sdl = (struct sockaddr_dl *) ((void *)sa + n);
864 info->sdl->sdl_family = AF_LINK;
865 info->sdl->sdl_len = sizeof (struct sockaddr_dl);
866 n = ROUNDUP(info->sdl->sdl_len, sizeof(uint32_t));
867 info->rtm->rtm_msglen += n;
868
869 #ifndef RTM_GET_SILENT
870 pthread_mutex_lock(&lock);
871 #endif
872 rsock = socket(PF_ROUTE, SOCK_RAW, PF_ROUTE);
873 if (rsock == -1) {
874 int error = errno;
875
876 #ifndef RTM_GET_SILENT
877 pthread_mutex_unlock(&lock);
878 #endif
879 SCLog(TRUE, LOG_ERR, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error));
880 return error;
881 }
882 opt = 1;
883 if (ioctl(rsock, FIONBIO, &opt) < 0) {
884 int error = errno;
885
886 (void)close(rsock);
887 #ifndef RTM_GET_SILENT
888 pthread_mutex_unlock(&lock);
889 #endif
890 SCLog(TRUE, LOG_ERR, CFSTR("ioctl(FIONBIO) failed: %s"), strerror(error));
891 return error;
892 }
893
894 #ifndef RTM_GET_SILENT
895 if (setsockopt(rsock, SOL_SOCKET, SO_RCVBUF, &sosize, sizeof(sosize)) == -1) {
896 int error = errno;
897
898 (void)close(rsock);
899 pthread_mutex_unlock(&lock);
900 SCLog(TRUE, LOG_ERR, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error));
901 return error;
902 }
903 #endif
904
905 if (write(rsock, &info->buf, info->rtm->rtm_msglen) == -1) {
906 int error = errno;
907
908 (void)close(rsock);
909 #ifndef RTM_GET_SILENT
910 pthread_mutex_unlock(&lock);
911 #endif
912 if (error != ESRCH) {
913 SCLog(TRUE, LOG_ERR, CFSTR("write() failed: %s"), strerror(error));
914 return error;
915 }
916 return EHOSTUNREACH;
917 }
918
919 /*
920 * Type, seq, pid identify our response.
921 * Routing sockets are broadcasters on input.
922 */
923 while (TRUE) {
924 ssize_t n;
925
926 n = read(rsock, &info->buf, sizeof(info->buf));
927 if (n == -1) {
928 int error = errno;
929
930 if (error == EINTR) {
931 continue;
932 }
933 (void)close(rsock);
934 #ifndef RTM_GET_SILENT
935 pthread_mutex_unlock(&lock);
936 #endif
937 SCLog(TRUE, LOG_ERR,
938 CFSTR("SCNetworkReachability: routing socket"
939 " read() failed: %s"), strerror(error));
940 return error;
941 }
942 if ((info->rtm->rtm_type == RTM_GET) &&
943 (info->rtm->rtm_seq == seq) &&
944 (info->rtm->rtm_pid == pid)) {
945 break;
946 }
947 }
948
949 (void)close(rsock);
950 #ifndef RTM_GET_SILENT
951 pthread_mutex_unlock(&lock);
952 #endif
953
954 get_rtaddrs(info->rtm->rtm_addrs, sa, info->rti_info);
955
956 //#define LOG_RTADDRS
957 #ifdef LOG_RTADDRS
958 {
959 int i;
960
961 SCLog(_sc_debug, LOG_DEBUG, CFSTR("rtm_flags = 0x%8.8x"), info->rtm->rtm_flags);
962
963 if ((info->rti_info[RTAX_NETMASK] != NULL) && (info->rti_info[RTAX_DST] != NULL)) {
964 info->rti_info[RTAX_NETMASK]->sa_family = info->rti_info[RTAX_DST]->sa_family;
965 }
966
967 for (i = 0; i < RTAX_MAX; i++) {
968 if (info->rti_info[i] != NULL) {
969 char addr[128];
970
971 _SC_sockaddr_to_string(info->rti_info[i], addr, sizeof(addr));
972 SCLog(_sc_debug, LOG_DEBUG, CFSTR("%d: %s"), i, addr);
973 }
974 }
975 }
976 #endif // LOG_RTADDRS
977
978 if ((info->rti_info[RTAX_IFP] == NULL) ||
979 (info->rti_info[RTAX_IFP]->sa_family != AF_LINK)) {
980 /* no interface info */
981 SCLog(TRUE, LOG_DEBUG, CFSTR("route_get() no interface info"));
982 return EINVAL;
983 }
984
985 /* ALIGN: accessors are retrieving byte values, cast ok. */
986 info->sdl = (struct sockaddr_dl *)(void *) info->rti_info[RTAX_IFP];
987 if ((info->sdl->sdl_nlen == 0) || (info->sdl->sdl_nlen > IFNAMSIZ)) {
988 /* no interface name */
989 return EHOSTUNREACH;
990 }
991
992 return 0;
993 }
994
995
996 static void
997 log_address(const char *str,
998 const struct sockaddr *sa,
999 unsigned int if_index,
1000 const char *log_prefix)
1001 {
1002 char addr[128];
1003 char if_name[IFNAMSIZ + 1];
1004
1005 _SC_sockaddr_to_string(sa, addr, sizeof(addr));
1006
1007 if ((if_index != 0) &&
1008 (if_indextoname(if_index, &if_name[1]) != NULL)) {
1009 if_name[0] = '%';
1010 } else {
1011 if_name[0] = '\0';
1012 }
1013
1014 SCLog(TRUE, LOG_INFO, CFSTR("%s%s(%s%s)"),
1015 log_prefix,
1016 str,
1017 addr,
1018 if_name);
1019
1020 return;
1021 }
1022
1023
1024 static int
1025 checkAddress_route(const struct sockaddr *address,
1026 unsigned int if_index,
1027 char *if_name,
1028 struct ifreq *ifr,
1029 ReachabilityInfo *reach_info,
1030 route_info *info,
1031 int *sc_status,
1032 const char *log_prefix)
1033 {
1034 int isock = -1;
1035 int ret = 0;
1036 char *statusMessage = NULL;
1037 struct sockaddr_in v4mapped;
1038
1039 switch (address->sa_family) {
1040 case AF_INET :
1041 case AF_INET6 :
1042 if (_sc_debug) {
1043 log_address("checkAddress", address, if_index, log_prefix);
1044 }
1045 break;
1046 default :
1047 /*
1048 * if no code for this address family (yet)
1049 */
1050 SCLog(TRUE, LOG_INFO,
1051 CFSTR("checkAddress(): unexpected address family %d"),
1052 address->sa_family);
1053 *sc_status = kSCStatusInvalidArgument;
1054 ret = EPERM;
1055 goto done;
1056 }
1057
1058 if (address->sa_family == AF_INET6) {
1059 /* ALIGN: sin6_addr accessed aligned, cast ok. */
1060 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(void *)address;
1061
1062 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1063 bzero(&v4mapped, sizeof(v4mapped));
1064 v4mapped.sin_len = sizeof(v4mapped);
1065 v4mapped.sin_family = AF_INET;
1066 v4mapped.sin_port = sin6->sin6_port;
1067 v4mapped.sin_addr.s_addr = sin6->sin6_addr.__u6_addr.__u6_addr32[3];
1068 address = (struct sockaddr *)&v4mapped;
1069 }
1070 }
1071
1072 ret = route_get(address, if_index, info);
1073 switch (ret) {
1074 case 0 :
1075 break;
1076 case EHOSTUNREACH :
1077 // if no route
1078 goto done;
1079 default :
1080 // if error
1081 *sc_status = ret;
1082 goto done;
1083 }
1084
1085 /* get the interface flags */
1086
1087 isock = socket(AF_INET, SOCK_DGRAM, 0);
1088 if (isock == -1) {
1089 ret = errno;
1090 SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
1091 goto done;
1092 }
1093
1094 bzero(ifr, sizeof(*ifr));
1095 bcopy(info->sdl->sdl_data, ifr->ifr_name, info->sdl->sdl_nlen);
1096
1097 if (ioctl(isock, SIOCGIFFLAGS, (char *)ifr) == -1) {
1098 ret = errno;
1099 SCLog(TRUE, LOG_ERR, CFSTR("ioctl(SIOCGIFFLAGS) failed: %s"), strerror(errno));
1100 goto done;
1101 }
1102
1103 if (!(ifr->ifr_flags & IFF_UP)) {
1104 ret = EHOSTUNREACH;
1105 goto done;
1106 }
1107
1108 statusMessage = "isReachable";
1109 reach_info->flags |= kSCNetworkReachabilityFlagsReachable;
1110
1111 if (info->rtm->rtm_flags & RTF_LOCAL) {
1112 statusMessage = "isReachable (is a local address)";
1113 reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1114 } else if (ifr->ifr_flags & IFF_LOOPBACK) {
1115 statusMessage = "isReachable (is loopback network)";
1116 reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1117 } else if ((info->rti_info[RTAX_IFA] != NULL) &&
1118 (info->rti_info[RTAX_IFA]->sa_family != AF_LINK)) {
1119 void *addr1 = (void *)address;
1120 void *addr2 = (void *)info->rti_info[RTAX_IFA];
1121 size_t len = address->sa_len;
1122
1123 if ((address->sa_family != info->rti_info[RTAX_IFA]->sa_family) &&
1124 (address->sa_len != info->rti_info[RTAX_IFA]->sa_len)) {
1125 SCLog(TRUE, LOG_NOTICE,
1126 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
1127 address->sa_family,
1128 address->sa_len,
1129 info->rti_info[RTAX_IFA]->sa_family,
1130 info->rti_info[RTAX_IFA]->sa_len);
1131 goto done;
1132 }
1133
1134 switch (address->sa_family) {
1135 case AF_INET :
1136 /* ALIGN: cast ok, because only bcmp is used. */
1137 addr1 = &((struct sockaddr_in *)(void *)address)->sin_addr;
1138 addr2 = &((struct sockaddr_in *)(void *)info->rti_info[RTAX_IFA])->sin_addr;
1139 len = sizeof(struct in_addr);
1140
1141 /*
1142 * check if 0.0.0.0
1143 */
1144 /* ALIGN: sin_addr should be aligned, cast ok. */
1145 if (((struct sockaddr_in *)(void *)address)->sin_addr.s_addr == 0) {
1146 statusMessage = "isReachable (this host)";
1147 reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1148 }
1149 break;
1150 case AF_INET6 :
1151 /* ALIGN: cast ok, because only bcmp is used. */
1152 addr1 = &((struct sockaddr_in6 *)(void *)address)->sin6_addr;
1153 addr2 = &((struct sockaddr_in6 *)(void *)info->rti_info[RTAX_IFA])->sin6_addr;
1154 len = sizeof(struct in6_addr);
1155 break;
1156 default :
1157 break;
1158 }
1159
1160 if (bcmp(addr1, addr2, len) == 0) {
1161 statusMessage = "isReachable (is interface address)";
1162 reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1163 }
1164 }
1165
1166 if (!(info->rtm->rtm_flags & RTF_GATEWAY) &&
1167 (info->rti_info[RTAX_GATEWAY] != NULL) &&
1168 (info->rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) &&
1169 !(ifr->ifr_flags & IFF_POINTOPOINT)) {
1170 reach_info->flags |= kSCNetworkReachabilityFlagsIsDirect;
1171 }
1172
1173 bzero(if_name, IFNAMSIZ);
1174 bcopy(info->sdl->sdl_data,
1175 if_name,
1176 (info->sdl->sdl_nlen <= IFNAMSIZ) ? info->sdl->sdl_nlen : IFNAMSIZ);
1177
1178 strlcpy(reach_info->if_name, if_name, sizeof(reach_info->if_name));
1179 reach_info->if_index = info->sdl->sdl_index;
1180
1181 if (_sc_debug) {
1182 SCLog(TRUE, LOG_INFO, CFSTR("%s status = %s"), log_prefix, statusMessage);
1183 SCLog(TRUE, LOG_INFO, CFSTR("%s device = %s (%hu)"), log_prefix, if_name, info->sdl->sdl_index);
1184 SCLog(TRUE, LOG_INFO, CFSTR("%s sdl_type = 0x%x"), log_prefix, info->sdl->sdl_type);
1185 SCLog(TRUE, LOG_INFO, CFSTR("%s ifr_flags = 0x%04hx"), log_prefix, ifr->ifr_flags);
1186 SCLog(TRUE, LOG_INFO, CFSTR("%s rtm_flags = 0x%08x"), log_prefix, info->rtm->rtm_flags);
1187 }
1188
1189 done :
1190 if (isock != -1) (void)close(isock);
1191 return ret;
1192 }
1193
1194
1195 static Boolean
1196 checkAddress(ReachabilityStoreInfoRef store_info,
1197 const struct sockaddr *address,
1198 unsigned int if_index,
1199 ReachabilityInfo *reach_info,
1200 const char *log_prefix)
1201 {
1202 route_info info;
1203 struct ifreq ifr;
1204 char if_name[IFNAMSIZ];
1205 nwi_ifstate_t ifstate;
1206 nwi_state_t nwi_state;
1207 int ret;
1208 int sc_status = kSCStatusReachabilityUnknown;
1209
1210 _reach_set(reach_info, &NOT_REACHABLE, reach_info->cycle, if_index, NULL);
1211
1212 nwi_state = nwi_state_copy();
1213
1214 if (address != NULL) {
1215 ret = checkAddress_route(address,
1216 if_index,
1217 if_name,
1218 &ifr,
1219 reach_info,
1220 &info,
1221 &sc_status,
1222 log_prefix);
1223 } else {
1224 /* special case: check only for available paths off the system */
1225 ret = EHOSTUNREACH;
1226 }
1227
1228 if (ret == 0) {
1229 const struct sockaddr *vpn_server_address;
1230
1231 sc_status = kSCStatusOK;
1232
1233 ifstate = nwi_state_get_ifstate(nwi_state, if_name);
1234 if (ifstate == NULL) {
1235 goto done;
1236 }
1237
1238 reach_info->flags |= nwi_ifstate_get_reachability_flags(ifstate);
1239
1240
1241 vpn_server_address = nwi_ifstate_get_vpn_server(ifstate);
1242 if (vpn_server_address != NULL) {
1243 char dst_if_name[IFNAMSIZ];
1244 route_info dst_info;
1245
1246 ret = route_get(vpn_server_address, 0, &dst_info);
1247 if (ret != 0) {
1248 goto done;
1249 }
1250
1251 bzero(&dst_if_name, sizeof(dst_if_name));
1252 bcopy(dst_info.sdl->sdl_data,
1253 dst_if_name,
1254 (dst_info.sdl->sdl_nlen <= IFNAMSIZ) ? dst_info.sdl->sdl_nlen : IFNAMSIZ);
1255 if (bcmp(if_name, dst_if_name, sizeof(if_name)) != 0) {
1256 nwi_ifstate_t ifstate;
1257
1258 ifstate = nwi_state_get_ifstate(nwi_state, dst_if_name);
1259 if (ifstate != NULL) {
1260 reach_info->flags |= nwi_ifstate_get_reachability_flags(ifstate);
1261 }
1262 }
1263 }
1264 } else if (ret == EHOSTUNREACH) {
1265 if (if_index == 0) {
1266 int af;
1267
1268 // if not "scoped" request
1269 af = (address != NULL) ? address->sa_family : AF_UNSPEC;
1270 reach_info->flags |= nwi_state_get_reachability_flags(nwi_state, af);
1271 sc_status = kSCStatusOK;
1272 } else {
1273 // if "scoped" request
1274 sc_status = kSCStatusNoKey;
1275 }
1276 }
1277
1278 done:
1279
1280 if (reach_info->flags == 0) {
1281 SCLog(_sc_debug, LOG_INFO, CFSTR("%s cannot be reached"), log_prefix);
1282 }
1283
1284 if (nwi_state != NULL) {
1285 nwi_state_release(nwi_state);
1286 }
1287
1288 if ((sc_status != kSCStatusOK) && (sc_status != kSCStatusNoKey)) {
1289 _SCErrorSet(sc_status);
1290 return FALSE;
1291 }
1292
1293 return TRUE;
1294 }
1295
1296
1297 #pragma mark -
1298 #pragma mark SCNetworkReachability APIs
1299
1300
1301 static __inline__ CFTypeRef
1302 isA_SCNetworkReachability(CFTypeRef obj)
1303 {
1304 return (isA_CFType(obj, SCNetworkReachabilityGetTypeID()));
1305 }
1306
1307
1308 static Boolean
1309 addr_to_PTR_name(const struct sockaddr *sa, char *name, size_t name_len)
1310 {
1311 int n;
1312
1313 switch (sa->sa_family) {
1314 case AF_INET : {
1315 union {
1316 in_addr_t s_addr;
1317 unsigned char b[4];
1318 } rev;
1319 /* ALIGN: assuming sa is aligned, then cast ok. */
1320 struct sockaddr_in *sin = (struct sockaddr_in *)(void *)sa;
1321
1322 /*
1323 * build "PTR" query name
1324 * NNN.NNN.NNN.NNN.in-addr.arpa.
1325 */
1326 rev.s_addr = sin->sin_addr.s_addr;
1327 n = snprintf(name, name_len, "%u.%u.%u.%u.in-addr.arpa.",
1328 rev.b[3],
1329 rev.b[2],
1330 rev.b[1],
1331 rev.b[0]);
1332 if ((n == -1) || (n >= name_len)) {
1333 return FALSE;
1334 }
1335
1336 break;
1337 }
1338
1339 case AF_INET6 : {
1340 int i;
1341 int s = 0;
1342 /* ALIGN: assume sa is aligned, cast ok. */
1343 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(void *)sa;
1344 size_t x = name_len;
1345
1346 /*
1347 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
1348 * N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.ip6.arpa.
1349 */
1350 for (i = sizeof(sin6->sin6_addr) - 1; i >= 0; i--) {
1351 n = snprintf(&name[s], x, "%x.%x.",
1352 ( sin6->sin6_addr.s6_addr[i] & 0xf),
1353 ((sin6->sin6_addr.s6_addr[i] >> 4) & 0xf));
1354 if ((n == -1) || (n >= x)) {
1355 return FALSE;
1356 }
1357
1358 s += n;
1359 x -= n;
1360 }
1361
1362 n = snprintf(&name[s], x, "ip6.arpa.");
1363 if ((n == -1) || (n >= x)) {
1364 return FALSE;
1365 }
1366
1367 break;
1368 }
1369
1370 default :
1371 return FALSE;
1372 }
1373
1374 return TRUE;
1375 }
1376
1377
1378 CFStringRef
1379 _SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target)
1380 {
1381 CFAllocatorRef allocator = CFGetAllocator(target);
1382 CFMutableStringRef str;
1383 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1384
1385 str = CFStringCreateMutable(allocator, 0);
1386 switch (targetPrivate->type) {
1387 case reachabilityTypeAddress :
1388 case reachabilityTypeAddressPair : {
1389 char buf[64];
1390
1391 if (targetPrivate->localAddress != NULL) {
1392 _SC_sockaddr_to_string(targetPrivate->localAddress, buf, sizeof(buf));
1393 CFStringAppendFormat(str, NULL, CFSTR("local address = %s"),
1394 buf);
1395 }
1396
1397 if (targetPrivate->remoteAddress != NULL) {
1398 _SC_sockaddr_to_string(targetPrivate->remoteAddress, buf, sizeof(buf));
1399 CFStringAppendFormat(str, NULL, CFSTR("%s%saddress = %s"),
1400 targetPrivate->localAddress ? ", " : "",
1401 (targetPrivate->type == reachabilityTypeAddressPair) ? "remote " : "",
1402 buf);
1403 }
1404 break;
1405 }
1406 case reachabilityTypeName : {
1407 CFStringAppendFormat(str, NULL, CFSTR("name = %s"), targetPrivate->name);
1408 break;
1409 }
1410 case reachabilityTypePTR : {
1411 CFStringAppendFormat(str, NULL, CFSTR("ptr = %s"), targetPrivate->name);
1412 break;
1413 }
1414 }
1415
1416 return str;
1417 }
1418
1419
1420 CFStringRef
1421 _SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target)
1422 {
1423 CFAllocatorRef allocator = CFGetAllocator(target);
1424 CFStringRef str;
1425 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1426
1427 str = CFStringCreateWithFormat(allocator,
1428 NULL,
1429 CFSTR("flags = 0x%08x, if_index = %u%s"),
1430 targetPrivate->info.flags,
1431 targetPrivate->info.if_index,
1432 targetPrivate->info.sleeping ? ", z" : "");
1433 return str;
1434 }
1435
1436
1437 static CFStringRef
1438 __SCNetworkReachabilityCopyDescription(CFTypeRef cf)
1439 {
1440 CFAllocatorRef allocator = CFGetAllocator(cf);
1441 CFMutableStringRef result;
1442 CFStringRef str;
1443 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf;
1444 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1445
1446 result = CFStringCreateMutable(allocator, 0);
1447 CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkReachability %p [%p]> {"), cf, allocator);
1448
1449 // add target description
1450 str = _SCNetworkReachabilityCopyTargetDescription(target);
1451 CFStringAppend(result, str);
1452 CFRelease(str);
1453
1454 // add additional "name" info
1455 if (isReachabilityTypeName(targetPrivate->type)) {
1456 if (targetPrivate->dnsActive) {
1457 CFStringAppendFormat(result, NULL, CFSTR(" (DNS query active)"));
1458 } else if (targetPrivate->serverActive &&
1459 (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending)) {
1460 CFStringAppendFormat(result, NULL, CFSTR(" (server query active)"));
1461 } else if ((targetPrivate->resolvedAddresses != NULL) || (targetPrivate->resolvedError != NETDB_SUCCESS)) {
1462 if (targetPrivate->resolvedAddresses != NULL) {
1463 if (isA_CFArray(targetPrivate->resolvedAddresses)) {
1464 CFIndex i;
1465 CFIndex n = CFArrayGetCount(targetPrivate->resolvedAddresses);
1466
1467 CFStringAppendFormat(result, NULL, CFSTR(" ("));
1468 for (i = 0; i < n; i++) {
1469 CFDataRef address;
1470
1471 CFStringAppendFormat(result, NULL, CFSTR("%s"),
1472 i > 0 ? ", " : "");
1473
1474 address = CFArrayGetValueAtIndex(targetPrivate->resolvedAddresses, i);
1475 if (isA_CFData(address)) {
1476 char buf[64];
1477 struct sockaddr *sa;
1478
1479 sa = (struct sockaddr *)CFDataGetBytePtr(address);
1480 _SC_sockaddr_to_string(sa, buf, sizeof(buf));
1481 CFStringAppendFormat(result, NULL, CFSTR("%s"), buf);
1482 } else {
1483 CFStringAppendFormat(result, NULL, CFSTR("%@"), address);
1484 }
1485 }
1486 CFStringAppendFormat(result, NULL, CFSTR(")"));
1487 } else if (CFEqual(targetPrivate->resolvedAddresses, kCFNull)) {
1488 CFStringAppendFormat(result, NULL, CFSTR(" (%s)"),
1489 gai_strerror(targetPrivate->resolvedError));
1490 } else {
1491 CFStringAppendFormat(result, NULL, CFSTR(" (no addresses)"));
1492 }
1493 } else {
1494 CFStringAppendFormat(result, NULL, CFSTR(" (%s)"),
1495 gai_strerror(targetPrivate->resolvedError));
1496 }
1497 }
1498 if (targetPrivate->dnsFlags != 0) {
1499 CFStringAppendFormat(result, NULL, CFSTR(", " DNS_FLAGS_FORMAT),
1500 DNS_FLAGS_VALUES(targetPrivate));
1501 }
1502 }
1503
1504 if (targetPrivate->onDemandBypass) {
1505 CFStringAppendFormat(result, NULL, CFSTR(", !ondemand"));
1506 }
1507
1508
1509 if (targetPrivate->resolverBypass) {
1510 CFStringAppendFormat(result, NULL, CFSTR(", !resolve"));
1511 }
1512
1513
1514 // add flags
1515 if (targetPrivate->scheduled) {
1516 str = _SCNetworkReachabilityCopyTargetFlags(target);
1517 CFStringAppendFormat(result, NULL, CFSTR(", %@"), str);
1518 CFRelease(str);
1519 }
1520
1521 CFStringAppendFormat(result, NULL, CFSTR("}"));
1522
1523 return result;
1524 }
1525
1526
1527 static void
1528 __SCNetworkReachabilityDeallocate(CFTypeRef cf)
1529 {
1530 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf;
1531 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1532
1533 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%srelease"),
1534 targetPrivate->log_prefix);
1535
1536 /* disconnect from the reachability server */
1537
1538 if (targetPrivate->serverActive) {
1539 __SCNetworkReachabilityServer_targetRemove(target);
1540 }
1541
1542 /* release resources */
1543
1544 pthread_mutex_destroy(&targetPrivate->lock);
1545
1546 if (targetPrivate->name != NULL)
1547 CFAllocatorDeallocate(NULL, (void *)targetPrivate->name);
1548
1549 if (targetPrivate->resolvedAddresses != NULL)
1550 CFRelease(targetPrivate->resolvedAddresses);
1551
1552 if (targetPrivate->localAddress != NULL) {
1553 if (targetPrivate->localAddress == targetPrivate->remoteAddress) {
1554 targetPrivate->remoteAddress = NULL;
1555 }
1556 CFAllocatorDeallocate(NULL, (void *)targetPrivate->localAddress);
1557 }
1558
1559 if (targetPrivate->remoteAddress != NULL)
1560 CFAllocatorDeallocate(NULL, (void *)targetPrivate->remoteAddress);
1561
1562 if (targetPrivate->rlsContext.release != NULL) {
1563 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
1564 }
1565
1566 if (targetPrivate->onDemandName != NULL) {
1567 CFRelease(targetPrivate->onDemandName);
1568 }
1569
1570 if (targetPrivate->onDemandRemoteAddress != NULL) {
1571 CFRelease(targetPrivate->onDemandRemoteAddress);
1572 }
1573
1574 if (targetPrivate->onDemandServer != NULL) {
1575 CFRelease(targetPrivate->onDemandServer);
1576 }
1577
1578 if (targetPrivate->onDemandServiceID != NULL) {
1579 CFRelease(targetPrivate->onDemandServiceID);
1580 }
1581
1582 if (targetPrivate->serverDigest != NULL) {
1583 CFRelease(targetPrivate->serverDigest);
1584 }
1585
1586 if (targetPrivate->serverGroup != NULL) {
1587 dispatch_release(targetPrivate->serverGroup);
1588 }
1589
1590 if (targetPrivate->serverQueue != NULL) {
1591 dispatch_release(targetPrivate->serverQueue);
1592 }
1593
1594 if (targetPrivate->serverWatchers != NULL) {
1595 CFRelease(targetPrivate->serverWatchers);
1596 }
1597
1598 if (targetPrivate->nePolicyResult) {
1599 free(targetPrivate->nePolicyResult);
1600 }
1601
1602 return;
1603 }
1604
1605
1606 static void
1607 __SCNetworkReachabilityInitialize(void)
1608 {
1609 __kSCNetworkReachabilityTypeID = _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass);
1610
1611 // provide a way to enable SCNetworkReachability logging without
1612 // having to set _sc_debug=1.
1613 if ((getenv("REACH_LOGGING") != NULL) ||
1614 (CFPreferencesGetAppBooleanValue(CFSTR("com.apple.SCNetworkReachability.debug"),
1615 kCFPreferencesCurrentApplication,
1616 NULL))) {
1617 _sc_debug = TRUE;
1618 }
1619
1620 // set per-process "bypass" of the SCNetworkReachability server
1621 if (getenv("REACH_SERVER_BYPASS") != NULL) {
1622 D_serverBypass = TRUE;
1623 }
1624
1625
1626 pthread_mutexattr_init(&lock_attr);
1627 pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_ERRORCHECK);
1628
1629 return;
1630 }
1631
1632
1633 __private_extern__
1634 dispatch_queue_t
1635 __SCNetworkReachability_concurrent_queue()
1636 {
1637 static dispatch_once_t once;
1638 static dispatch_queue_t q;
1639
1640 dispatch_once(&once, ^{
1641 q = dispatch_queue_create("SCNetworkReachability.concurrent",
1642 DISPATCH_QUEUE_CONCURRENT);
1643 });
1644
1645 return q;
1646 }
1647
1648
1649 /*
1650 * __SCNetworkReachabilityUpdateConcurrent
1651 *
1652 * Calls reachUpdate()
1653 * - caller must be holding a reference to the target
1654 * - caller must *not* be holding the target lock
1655 * - caller must be running on the __SCNetworkReachability_concurrent_queue()
1656 */
1657 __private_extern__
1658 void
1659 __SCNetworkReachabilityUpdateConcurrent(SCNetworkReachabilityRef target)
1660 {
1661 Boolean changed;
1662 unsigned int n;
1663 dispatch_queue_t queue;
1664 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1665
1666 changed = reachUpdate((void *)target);
1667 if (!changed) {
1668 return;
1669 }
1670
1671 n = _SC_ATOMIC_INC(&targetPrivate->pending);
1672 if (n > 0) {
1673 // if we already have a notification pending
1674 return;
1675 }
1676
1677 MUTEX_LOCK(&targetPrivate->lock);
1678
1679 queue = targetPrivate->dispatchQueue;
1680 if (queue != NULL) {
1681 dispatch_group_t group;
1682
1683 dispatch_retain(queue);
1684
1685 group = targetPrivate->dispatchGroup;
1686 dispatch_group_enter(group);
1687
1688 MUTEX_UNLOCK(&targetPrivate->lock);
1689
1690 dispatch_sync(queue, ^{
1691 reachPerform((void *)target);
1692 dispatch_group_leave(group);
1693 });
1694
1695 dispatch_release(queue);
1696 } else {
1697 if (targetPrivate->rls != NULL) {
1698 CFRunLoopSourceSignal(targetPrivate->rls);
1699 _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
1700 }
1701
1702 MUTEX_UNLOCK(&targetPrivate->lock);
1703 }
1704
1705 return;
1706 }
1707
1708
1709 /*
1710 * __SCNetworkReachabilityUpdate
1711 *
1712 * Calls reachUpdate() [indirectly]
1713 * - caller can be holding the target lock
1714 * - caller can be running on any dispatch queue
1715 */
1716 __private_extern__
1717 void
1718 __SCNetworkReachabilityUpdate(SCNetworkReachabilityRef target)
1719 {
1720 CFRetain(target);
1721 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
1722 __SCNetworkReachabilityUpdateConcurrent(target);
1723 CFRelease(target);
1724 });
1725
1726 return;
1727 }
1728
1729
1730 static SCNetworkReachabilityPrivateRef
1731 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator)
1732 {
1733 SCNetworkReachabilityPrivateRef targetPrivate;
1734 uint32_t size;
1735
1736 /* initialize runtime */
1737 pthread_once(&initialized, __SCNetworkReachabilityInitialize);
1738
1739 /* allocate target */
1740 size = sizeof(SCNetworkReachabilityPrivate) - sizeof(CFRuntimeBase);
1741 targetPrivate = (SCNetworkReachabilityPrivateRef)_CFRuntimeCreateInstance(allocator,
1742 __kSCNetworkReachabilityTypeID,
1743 size,
1744 NULL);
1745 if (targetPrivate == NULL) {
1746 return NULL;
1747 }
1748
1749 bzero((void *)targetPrivate + sizeof(CFRuntimeBase), size);
1750
1751 MUTEX_INIT(&targetPrivate->lock);
1752
1753 targetPrivate->cycle = 1;
1754 targetPrivate->last_notify = NOT_REPORTED;
1755 targetPrivate->serverBypass = D_serverBypass;
1756
1757
1758
1759 targetPrivate->log_prefix[0] = '\0';
1760 if (_sc_log > 0) {
1761 snprintf(targetPrivate->log_prefix,
1762 sizeof(targetPrivate->log_prefix),
1763 "[%p] ",
1764 targetPrivate);
1765 }
1766
1767 return targetPrivate;
1768 }
1769
1770
1771
1772
1773 static const struct sockaddr *
1774 is_valid_address(const struct sockaddr *address)
1775 {
1776 const struct sockaddr *valid = NULL;
1777 static Boolean warned = FALSE;
1778
1779 if ((address != NULL) &&
1780 (address->sa_len <= sizeof(struct sockaddr_storage))) {
1781 switch (address->sa_family) {
1782 case AF_INET :
1783 if (address->sa_len >= sizeof(struct sockaddr_in)) {
1784 valid = address;
1785 } else {
1786 if (!warned) {
1787 SCLog(TRUE, LOG_ERR,
1788 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu"),
1789 address->sa_len,
1790 sizeof(struct sockaddr_in));
1791 warned = TRUE;
1792 }
1793 }
1794 break;
1795 case AF_INET6 :
1796 if (address->sa_len >= sizeof(struct sockaddr_in6)) {
1797 valid = address;
1798 } else if (!warned) {
1799 SCLog(TRUE, LOG_ERR,
1800 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu"),
1801 address->sa_len,
1802 sizeof(struct sockaddr_in6));
1803 warned = TRUE;
1804 }
1805 break;
1806 default :
1807 if (!warned) {
1808 SCLog(TRUE, LOG_ERR,
1809 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"),
1810 address->sa_family);
1811 warned = TRUE;
1812 }
1813 }
1814 }
1815
1816 return valid;
1817 }
1818
1819
1820
1821
1822
1823
1824 SCNetworkReachabilityRef
1825 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator,
1826 const struct sockaddr *address)
1827 {
1828 SCNetworkReachabilityPrivateRef targetPrivate;
1829
1830 address = is_valid_address(address);
1831 if (address == NULL) {
1832 _SCErrorSet(kSCStatusInvalidArgument);
1833 return NULL;
1834 }
1835
1836 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
1837 if (targetPrivate == NULL) {
1838 return NULL;
1839 }
1840
1841 targetPrivate->type = reachabilityTypeAddress;
1842 targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, address->sa_len, 0);
1843 bcopy(address, targetPrivate->remoteAddress, address->sa_len);
1844
1845
1846
1847 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
1848 targetPrivate->log_prefix,
1849 DEBUG_REACHABILITY_TYPE_ADDRESS,
1850 targetPrivate);
1851
1852 return (SCNetworkReachabilityRef)targetPrivate;
1853 }
1854
1855
1856 static Boolean
1857 is_same_address(const struct sockaddr *a, const struct sockaddr *b)
1858 {
1859 const void *a_addr;
1860 const void *b_addr;
1861 size_t len;
1862
1863 if ((a == NULL) ||
1864 (b == NULL) ||
1865 (a->sa_family != b->sa_family) ||
1866 (a->sa_len != b->sa_len )) {
1867 return FALSE;
1868 }
1869
1870 switch (a->sa_family) {
1871 case AF_INET : {
1872 struct sockaddr_in *a_sin = (struct sockaddr_in *)(void *)a;
1873 struct sockaddr_in *b_sin = (struct sockaddr_in *)(void *)b;
1874
1875 /* ALIGN: assuming a (and b) are aligned, then cast ok. */
1876 a_addr = &a_sin->sin_addr;
1877 b_addr = &b_sin->sin_addr;
1878 len = sizeof(struct in_addr);
1879 break;
1880 }
1881
1882 case AF_INET6 : {
1883 struct sockaddr_in6 *a_sin6 = (struct sockaddr_in6 *)(void *)a;
1884 struct sockaddr_in6 *b_sin6 = (struct sockaddr_in6 *)(void *)b;
1885
1886 if (a_sin6->sin6_scope_id != b_sin6->sin6_scope_id) {
1887 return FALSE;
1888 }
1889
1890 a_addr = &a_sin6->sin6_addr;
1891 b_addr = &b_sin6->sin6_addr;
1892 len = sizeof(struct in6_addr);
1893 break;
1894 }
1895
1896 default :
1897 a_addr = a;
1898 b_addr = b;
1899 len = a->sa_len;
1900 break;
1901 }
1902
1903 return (bcmp(a_addr, b_addr, len) == 0);
1904 }
1905
1906
1907 SCNetworkReachabilityRef
1908 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator,
1909 const struct sockaddr *localAddress,
1910 const struct sockaddr *remoteAddress)
1911 {
1912 SCNetworkReachabilityPrivateRef targetPrivate;
1913
1914 if ((localAddress == NULL) && (remoteAddress == NULL)) {
1915 _SCErrorSet(kSCStatusInvalidArgument);
1916 return NULL;
1917 }
1918
1919 if (localAddress != NULL) {
1920 localAddress = is_valid_address(localAddress);
1921 if (localAddress == NULL) {
1922 _SCErrorSet(kSCStatusInvalidArgument);
1923 return NULL;
1924 }
1925 }
1926
1927 if (remoteAddress != NULL) {
1928 remoteAddress = is_valid_address(remoteAddress);
1929 if (remoteAddress == NULL) {
1930 _SCErrorSet(kSCStatusInvalidArgument);
1931 return NULL;
1932 }
1933 }
1934
1935 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
1936 if (targetPrivate == NULL) {
1937 return NULL;
1938 }
1939
1940 targetPrivate->type = reachabilityTypeAddressPair;
1941
1942 if (localAddress != NULL) {
1943 targetPrivate->localAddress = CFAllocatorAllocate(NULL, localAddress->sa_len, 0);
1944 bcopy(localAddress, targetPrivate->localAddress, localAddress->sa_len);
1945 }
1946
1947 if (remoteAddress != NULL) {
1948 if (is_same_address(localAddress, remoteAddress)) {
1949 targetPrivate->remoteAddress = targetPrivate->localAddress;
1950 } else {
1951 targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, remoteAddress->sa_len, 0);
1952 bcopy(remoteAddress, targetPrivate->remoteAddress, remoteAddress->sa_len);
1953 }
1954 }
1955
1956
1957
1958 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
1959 targetPrivate->log_prefix,
1960 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR,
1961 targetPrivate);
1962
1963 return (SCNetworkReachabilityRef)targetPrivate;
1964 }
1965
1966
1967 SCNetworkReachabilityRef
1968 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator,
1969 const char *nodename)
1970 {
1971 union {
1972 struct sockaddr sa;
1973 struct sockaddr_in sin;
1974 struct sockaddr_in6 sin6;
1975 } addr;
1976 size_t nodenameLen;
1977 SCNetworkReachabilityPrivateRef targetPrivate;
1978
1979 if (nodename == NULL) {
1980 _SCErrorSet(kSCStatusInvalidArgument);
1981 return NULL;
1982 }
1983
1984 nodenameLen = strlen(nodename);
1985 if (nodenameLen == 0) {
1986 _SCErrorSet(kSCStatusInvalidArgument);
1987 return NULL;
1988 }
1989
1990 if (nodename[nodenameLen - 1] == '.') {
1991 int dots;
1992 size_t i;
1993
1994 // trim trailing "."s
1995 do {
1996 --nodenameLen;
1997 } while ((nodenameLen > 0) && (nodename[nodenameLen - 1] == '.'));
1998
1999 if (nodenameLen == 0) {
2000 // if only trailing "."s
2001 _SCErrorSet(kSCStatusInvalidArgument);
2002 return NULL;
2003 }
2004
2005 // count the remaining "."s
2006 dots = 0;
2007 for (i = 0; i < nodenameLen; i++) {
2008 if (nodename[i] == '.') dots++;
2009 }
2010
2011 if (dots == 0) {
2012 // if only a single-label, add back the FQDN "."
2013 nodenameLen++;
2014 }
2015 }
2016
2017 if (_SC_string_to_sockaddr(nodename, AF_UNSPEC, (void *)&addr, sizeof(addr)) != NULL) {
2018 /* if this "nodename" is really an IP[v6] address in disguise */
2019 return SCNetworkReachabilityCreateWithAddress(allocator, &addr.sa);
2020 }
2021
2022 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
2023 if (targetPrivate == NULL) {
2024 return NULL;
2025 }
2026
2027 targetPrivate->type = reachabilityTypeName;
2028
2029 targetPrivate->name = CFAllocatorAllocate(NULL, nodenameLen + 1, 0);
2030 strlcpy((char *)targetPrivate->name, nodename, nodenameLen + 1);
2031
2032 targetPrivate->needResolve = TRUE;
2033 targetPrivate->info.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
2034 targetPrivate->serverInfo.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
2035
2036 {
2037 /* make sure AppLayerVPN only is in client mode */
2038 CFDictionaryRef appLayerVPNProperties;
2039
2040 appLayerVPNProperties = VPNAppLayerCopyCurrentAppProperties();
2041 if (appLayerVPNProperties != NULL) {
2042 targetPrivate->serverBypassForVPN = TRUE;
2043 targetPrivate->serverBypass = YES;
2044 CFRelease(appLayerVPNProperties);
2045 }
2046 }
2047
2048 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
2049 targetPrivate->log_prefix,
2050 DEBUG_REACHABILITY_TYPE_NAME,
2051 targetPrivate);
2052
2053 return (SCNetworkReachabilityRef)targetPrivate;
2054 }
2055
2056
2057 static SCNetworkReachabilityRef
2058 __SCNetworkReachabilityCreateWithPtr(CFAllocatorRef allocator,
2059 const char *ptrName,
2060 const struct sockaddr *ptrAddress)
2061 {
2062 SCNetworkReachabilityRef target;
2063 SCNetworkReachabilityPrivateRef targetPrivate;
2064
2065 target = SCNetworkReachabilityCreateWithName(NULL, ptrName);
2066 if (target == NULL) {
2067 return NULL;
2068 }
2069
2070 targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2071
2072 // change type
2073 targetPrivate->type = reachabilityTypePTR;
2074
2075 // and keep the address
2076 targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, ptrAddress->sa_len, 0);
2077 bcopy(ptrAddress, targetPrivate->remoteAddress, ptrAddress->sa_len);
2078
2079 return target;
2080 }
2081
2082
2083
2084
2085 SCNetworkReachabilityRef
2086 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator,
2087 CFDictionaryRef options)
2088 {
2089 const struct sockaddr *addr_l = NULL;
2090 const struct sockaddr *addr_p = NULL;
2091 const struct sockaddr *addr_r = NULL;
2092 CFDataRef data;
2093 CFStringRef interface = NULL;
2094 CFStringRef nodename;
2095 CFBooleanRef onDemandBypass;
2096 CFBooleanRef resolverBypass;
2097 CFBooleanRef serverBypass;
2098 SCNetworkReachabilityRef target;
2099 SCNetworkReachabilityPrivateRef targetPrivate;
2100
2101 if (!isA_CFDictionary(options)) {
2102 _SCErrorSet(kSCStatusInvalidArgument);
2103 return NULL;
2104 }
2105
2106 nodename = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionNodeName);
2107 if ((nodename != NULL) &&
2108 (!isA_CFString(nodename) || (CFStringGetLength(nodename) == 0))) {
2109 _SCErrorSet(kSCStatusInvalidArgument);
2110 return NULL;
2111 }
2112 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionLocalAddress);
2113 if (data != NULL) {
2114 if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
2115 _SCErrorSet(kSCStatusInvalidArgument);
2116 return NULL;
2117 }
2118 addr_l = (const struct sockaddr *)CFDataGetBytePtr(data);
2119 }
2120 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionPTRAddress);
2121 if (data != NULL) {
2122 if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
2123 _SCErrorSet(kSCStatusInvalidArgument);
2124 return NULL;
2125 }
2126 addr_p = (const struct sockaddr *)CFDataGetBytePtr(data);
2127 }
2128 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionRemoteAddress);
2129 if (data != NULL) {
2130 if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
2131 _SCErrorSet(kSCStatusInvalidArgument);
2132 return NULL;
2133 }
2134 addr_r = (const struct sockaddr *)CFDataGetBytePtr(data);
2135 }
2136 interface = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionInterface);
2137 if ((interface != NULL) &&
2138 (!isA_CFString(interface) || (CFStringGetLength(interface) == 0))) {
2139 _SCErrorSet(kSCStatusInvalidArgument);
2140 return NULL;
2141 }
2142 onDemandBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandBypass);
2143 if ((onDemandBypass != NULL) && !isA_CFBoolean(onDemandBypass)) {
2144 _SCErrorSet(kSCStatusInvalidArgument);
2145 return NULL;
2146 }
2147 resolverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionResolverBypass);
2148 if ((resolverBypass != NULL) && !isA_CFBoolean(resolverBypass)) {
2149 _SCErrorSet(kSCStatusInvalidArgument);
2150 return NULL;
2151 }
2152
2153
2154 serverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionServerBypass);
2155 if ((serverBypass != NULL) && !isA_CFBoolean(serverBypass)) {
2156 _SCErrorSet(kSCStatusInvalidArgument);
2157 return NULL;
2158 }
2159
2160
2161 if (nodename != NULL) {
2162 const char *name;
2163
2164 if ((addr_l != NULL) || (addr_r != NULL) || (addr_p != NULL)) {
2165 // can't have both a nodename and an address
2166 _SCErrorSet(kSCStatusInvalidArgument);
2167 return NULL;
2168 }
2169
2170 name = _SC_cfstring_to_cstring(nodename, NULL, 0, kCFStringEncodingUTF8);
2171 target = SCNetworkReachabilityCreateWithName(allocator, name);
2172 CFAllocatorDeallocate(NULL, (void *)name);
2173 } else if (addr_p != NULL) {
2174 char name[MAXHOSTNAMELEN];
2175
2176 if ((addr_l != NULL) || // can't have PTR and target address
2177 (addr_r != NULL) || // can't have PTR and target address
2178 !addr_to_PTR_name(addr_p, name, sizeof(name))) { // can't convert PTR
2179 _SCErrorSet(kSCStatusInvalidArgument);
2180 return NULL;
2181 }
2182
2183 target = __SCNetworkReachabilityCreateWithPtr(NULL, name, addr_p);
2184 } else {
2185 if ((addr_l != NULL) && (addr_r != NULL)) {
2186 target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, addr_r);
2187 } else if (addr_r != NULL) {
2188 target = SCNetworkReachabilityCreateWithAddress(NULL, addr_r);
2189 } else if (addr_l != NULL) {
2190 target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, NULL);
2191 } else {
2192 _SCErrorSet(kSCStatusInvalidArgument);
2193 return NULL;
2194 }
2195 }
2196 if (target == NULL) {
2197 return NULL;
2198 }
2199
2200 targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2201
2202 if (interface != NULL) {
2203 if ((_SC_cfstring_to_cstring(interface,
2204 targetPrivate->if_name,
2205 sizeof(targetPrivate->if_name),
2206 kCFStringEncodingASCII) == NULL) ||
2207 ((targetPrivate->if_index = if_nametoindex(targetPrivate->if_name)) == 0)) {
2208 CFRelease(targetPrivate);
2209 _SCErrorSet(kSCStatusInvalidArgument);
2210 return NULL;
2211 }
2212 }
2213
2214
2215 if (onDemandBypass != NULL) {
2216 targetPrivate->onDemandBypass = CFBooleanGetValue(onDemandBypass);
2217 }
2218
2219 if (resolverBypass != NULL) {
2220 targetPrivate->resolverBypass = CFBooleanGetValue(resolverBypass);
2221 }
2222
2223 /* if by name, make sure client-only VPN types stay in client mode */
2224 if (serverBypass != NULL && targetPrivate->serverBypassForVPN == FALSE) {
2225 targetPrivate->serverBypass = CFBooleanGetValue(serverBypass);
2226 }
2227
2228
2229 if (_sc_debug && (_sc_log > 0)) {
2230 const char *opt = "???";
2231
2232 switch (targetPrivate->type) {
2233 case reachabilityTypeAddress :
2234 opt = DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS;
2235 break;
2236 case reachabilityTypeAddressPair :
2237 opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS;
2238 break;
2239 case reachabilityTypeName :
2240 opt = DEBUG_REACHABILITY_TYPE_NAME_OPTIONS;
2241 break;
2242 case reachabilityTypePTR :
2243 opt = DEBUG_REACHABILITY_TYPE_PTR_OPTIONS;
2244 break;
2245 }
2246
2247 SCLog(TRUE, LOG_INFO, CFSTR("%s%s %@"),
2248 targetPrivate->log_prefix,
2249 opt,
2250 targetPrivate);
2251 }
2252
2253 return (SCNetworkReachabilityRef)targetPrivate;
2254 }
2255
2256
2257 static SCNetworkReachabilityRef
2258 __SCNetworkReachabilityCreateCopy(SCNetworkReachabilityRef target)
2259 {
2260 SCNetworkReachabilityRef clone = NULL;
2261 SCNetworkReachabilityPrivateRef clonePrivate;
2262 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2263
2264 switch (targetPrivate->type) {
2265 case reachabilityTypeAddress :
2266 clone = SCNetworkReachabilityCreateWithAddress(NULL,
2267 targetPrivate->remoteAddress);
2268 break;
2269 case reachabilityTypeAddressPair :
2270 clone = SCNetworkReachabilityCreateWithAddressPair(NULL,
2271 targetPrivate->localAddress,
2272 targetPrivate->remoteAddress);
2273 break;
2274 case reachabilityTypeName :
2275 clone = SCNetworkReachabilityCreateWithName(NULL,
2276 targetPrivate->name);
2277 break;
2278 case reachabilityTypePTR :
2279 clone = __SCNetworkReachabilityCreateWithPtr(NULL,
2280 targetPrivate->name,
2281 targetPrivate->remoteAddress);
2282 break;
2283 }
2284 if (clone == NULL) {
2285 return NULL;
2286 }
2287
2288 clonePrivate = (SCNetworkReachabilityPrivateRef)clone;
2289
2290 clonePrivate->quiet = TRUE;
2291
2292 clonePrivate->if_index = targetPrivate->if_index;
2293 bcopy(targetPrivate->if_name, clonePrivate->if_name, sizeof(clonePrivate->if_name));
2294
2295 clonePrivate->onDemandBypass = targetPrivate->onDemandBypass;
2296
2297
2298 clonePrivate->serverBypass = targetPrivate->serverBypass;
2299
2300 clonePrivate->resolverBypass = targetPrivate->resolverBypass;
2301
2302
2303 if (_sc_debug && (_sc_log > 0)) {
2304 const char *opt = "???";
2305
2306 switch (clonePrivate->type) {
2307 case reachabilityTypeAddress :
2308 opt = DEBUG_REACHABILITY_TYPE_ADDRESS_CLONE;
2309 break;
2310 case reachabilityTypeAddressPair :
2311 opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_CLONE;
2312 break;
2313 case reachabilityTypeName :
2314 opt = DEBUG_REACHABILITY_TYPE_NAME_CLONE;
2315 break;
2316 case reachabilityTypePTR :
2317 opt = DEBUG_REACHABILITY_TYPE_PTR_CLONE;
2318 break;
2319 }
2320
2321 SCLog(TRUE, LOG_INFO, CFSTR("%s%s %p %@"),
2322 clonePrivate->log_prefix,
2323 opt,
2324 targetPrivate,
2325 clone);
2326 }
2327
2328 return clone;
2329 }
2330
2331
2332 CFTypeID
2333 SCNetworkReachabilityGetTypeID(void)
2334 {
2335 pthread_once(&initialized, __SCNetworkReachabilityInitialize); /* initialize runtime */
2336 return __kSCNetworkReachabilityTypeID;
2337 }
2338
2339
2340 CFArrayRef /* CFArray[CFData], where each CFData is a (struct sockaddr *) */
2341 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target,
2342 int *error_num)
2343 {
2344 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2345
2346 if (!isA_SCNetworkReachability(target)) {
2347 _SCErrorSet(kSCStatusInvalidArgument);
2348 return NULL;
2349 }
2350
2351 if (!isReachabilityTypeName(targetPrivate->type)) {
2352 _SCErrorSet(kSCStatusInvalidArgument);
2353 return NULL;
2354 }
2355
2356 if (error_num) {
2357 *error_num = targetPrivate->resolvedError;
2358 }
2359
2360 if (targetPrivate->resolvedAddresses != NULL) {
2361 if (isA_CFArray(targetPrivate->resolvedAddresses)) {
2362 return CFRetain(targetPrivate->resolvedAddresses);
2363 } else {
2364 /* if status is known but no resolved addresses to return */
2365 _SCErrorSet(kSCStatusOK);
2366 return NULL;
2367 }
2368 }
2369
2370 _SCErrorSet(kSCStatusReachabilityUnknown);
2371 return NULL;
2372 }
2373
2374
2375 static void
2376 __SCNetworkReachabilitySetResolvedError(SCNetworkReachabilityRef target,
2377 int32_t status)
2378 {
2379 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2380
2381 MUTEX_ASSERT_HELD(&targetPrivate->lock);
2382
2383 __mark_operation_end(target,
2384 FALSE, // if successful query
2385 dns_query_async, // async
2386 &targetPrivate->dnsQueryStart, // start time
2387 &targetPrivate->dnsQueryEnd); // end time
2388
2389 if (targetPrivate->resolvedAddresses != NULL) {
2390 CFRelease(targetPrivate->resolvedAddresses);
2391 targetPrivate->resolvedAddresses = NULL;
2392 }
2393
2394 SCLog(_sc_debug, LOG_INFO, CFSTR("%scould not be resolved: %s"),
2395 targetPrivate->log_prefix,
2396 gai_strerror(status));
2397
2398 /* save the error associated with the attempt to resolve the name */
2399 targetPrivate->resolvedAddresses = CFRetain(kCFNull);
2400 targetPrivate->resolvedError = status;
2401 targetPrivate->needResolve = FALSE;
2402
2403 return;
2404 }
2405
2406
2407 /*
2408 * rankReachability()
2409 * Not reachable == 0
2410 * Connection Required == 1
2411 * Reachable == 2
2412 */
2413 static int
2414 rankReachability(SCNetworkReachabilityFlags flags)
2415 {
2416 int rank = 0;
2417
2418 if (flags & kSCNetworkReachabilityFlagsReachable) rank = 2;
2419 if (flags & kSCNetworkReachabilityFlagsConnectionRequired) rank = 1;
2420 return rank;
2421 }
2422
2423
2424 #pragma mark -
2425 #pragma mark DNS name resolution
2426
2427
2428 static void
2429 update_resolver_reachability(ReachabilityStoreInfoRef store_info,
2430 dns_resolver_t *resolver,
2431 SCNetworkReachabilityFlags *flags,
2432 Boolean *haveDNS,
2433 uint32_t *resolver_if_index,
2434 const char *log_prefix)
2435 {
2436 if (resolver_if_index) *resolver_if_index = 0;
2437
2438 if (resolver->n_nameserver > 0) {
2439 *flags = (SCNetworkReachabilityFlags)resolver->reach_flags;
2440 if (resolver_if_index != NULL) {
2441 *resolver_if_index = resolver->if_index;
2442 }
2443 *haveDNS = TRUE;
2444 } else {
2445 *flags = kSCNetworkReachabilityFlagsReachable;
2446 *haveDNS = FALSE;
2447 }
2448
2449 return;
2450 }
2451
2452
2453 static Boolean
2454 check_matching_resolvers(ReachabilityStoreInfoRef store_info,
2455 dns_config_t *dns_config,
2456 const char *fqdn,
2457 unsigned int if_index,
2458 SCNetworkReachabilityFlags *flags,
2459 Boolean *haveDNS,
2460 uint32_t *resolver_if_index,
2461 int *dns_config_index,
2462 const char *log_prefix)
2463 {
2464 int i;
2465 Boolean matched = FALSE;
2466 const char *name = fqdn;
2467 int32_t n_resolvers;
2468 dns_resolver_t **resolvers;
2469
2470 if (if_index == 0) {
2471 n_resolvers = dns_config->n_resolver;
2472 resolvers = dns_config->resolver;
2473 } else {
2474 n_resolvers = dns_config->n_scoped_resolver;
2475 resolvers = dns_config->scoped_resolver;
2476 }
2477
2478 /* In case we couldn't find a match, setting an index of -1
2479 and resolver_if_index 0 */
2480 if (dns_config_index != NULL) *dns_config_index = -1;
2481 if (resolver_if_index != NULL) *resolver_if_index = 0;
2482
2483 while (!matched && (name != NULL)) {
2484 size_t len;
2485
2486 /*
2487 * check if the provided name (or sub-component)
2488 * matches one of our resolver configurations.
2489 */
2490 len = strlen(name);
2491 for (i = 0; i < n_resolvers; i++) {
2492 char *domain;
2493 dns_resolver_t *resolver;
2494
2495 resolver = resolvers[i];
2496 if ((if_index != 0) && (if_index != resolver->if_index)) {
2497 continue;
2498 }
2499
2500 domain = resolver->domain;
2501 if (domain != NULL && (len == strlen(domain))) {
2502 if (strcasecmp(name, domain) == 0) {
2503 /*
2504 * if name matches domain
2505 */
2506 matched = TRUE;
2507 update_resolver_reachability(store_info,
2508 resolver,
2509 flags,
2510 haveDNS,
2511 resolver_if_index,
2512 log_prefix);
2513 if (dns_config_index != NULL) *dns_config_index = i;
2514 break;
2515 }
2516 }
2517 }
2518
2519 if (!matched) {
2520 /*
2521 * we have not found a matching resolver, try
2522 * a less qualified domain
2523 */
2524 name = strchr(name, '.');
2525 if ((name != NULL) && (*name != '\0')) {
2526 name++;
2527 } else {
2528 name = NULL;
2529 }
2530 }
2531 }
2532
2533 return matched;
2534 }
2535
2536
2537 static dns_resolver_t *
2538 get_default_resolver(dns_config_t *dns_config, unsigned int if_index)
2539 {
2540 int i;
2541 int32_t n_resolvers;
2542 dns_resolver_t *resolver = NULL;
2543 dns_resolver_t **resolvers;
2544
2545 if (if_index == 0) {
2546 n_resolvers = dns_config->n_resolver;
2547 resolvers = dns_config->resolver;
2548 } else {
2549 n_resolvers = dns_config->n_scoped_resolver;
2550 resolvers = dns_config->scoped_resolver;
2551 }
2552
2553 for (i = 0; i < n_resolvers; i++) {
2554 if ((if_index != 0) && (if_index != resolvers[i]->if_index)) {
2555 continue;
2556 }
2557
2558 if (((if_index == 0) && (i == 0)) ||
2559 ((if_index != 0) && (resolver == NULL))) {
2560 // if this is the first (aka default) resolver
2561 resolver = resolvers[i];
2562 } else if ((resolvers[i]->domain == NULL) &&
2563 (resolvers[i]->search_order < resolver->search_order)) {
2564 // if this is a default resolver with a lower search order
2565 resolver = resolvers[i];
2566 }
2567 }
2568
2569 return resolver;
2570 }
2571
2572
2573 static dns_configuration_t *
2574 dns_configuration_retain()
2575 {
2576 dns_configuration_t *config;
2577
2578 pthread_mutex_lock(&dns_lock);
2579
2580 if (dns_configuration != NULL) {
2581 Boolean refresh = TRUE;
2582
2583 if (dns_token_valid) {
2584 int check = 0;
2585 uint32_t status;
2586
2587 /*
2588 * check if the global [DNS] configuration snapshot needs
2589 * to be updated
2590 */
2591 status = notify_check(dns_token, &check);
2592 if (status != NOTIFY_STATUS_OK) {
2593 SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%u"), status);
2594 } else if (check == 0) {
2595 // if the snapshot does not need to be refreshed
2596 refresh = FALSE;
2597 }
2598 }
2599
2600 if (refresh) {
2601 if (dns_configuration->refs == 0) {
2602 dns_configuration_free(dns_configuration->config);
2603 CFAllocatorDeallocate(NULL, dns_configuration);
2604 }
2605 dns_configuration = NULL;
2606 }
2607 }
2608
2609 if (dns_configuration == NULL) {
2610 dns_config_t *new_config;
2611
2612 new_config = dns_configuration_copy();
2613 if (new_config != NULL) {
2614 dns_configuration = CFAllocatorAllocate(NULL, sizeof(dns_configuration_t), 0);
2615 dns_configuration->config = new_config;
2616 dns_configuration->refs = 0;
2617 }
2618 }
2619
2620 if (dns_configuration != NULL) {
2621 dns_configuration->refs++;
2622 }
2623
2624 config = dns_configuration;
2625 pthread_mutex_unlock(&dns_lock);
2626 return config;
2627 }
2628
2629
2630 static void
2631 dns_configuration_release(dns_configuration_t *config)
2632 {
2633 pthread_mutex_lock(&dns_lock);
2634
2635 config->refs--;
2636 if (config->refs == 0) {
2637 if (!dns_token_valid && (config == dns_configuration)) {
2638 dns_configuration = NULL;
2639 }
2640
2641 if (config != dns_configuration) {
2642 dns_configuration_free(config->config);
2643 CFAllocatorDeallocate(NULL, config);
2644 }
2645 }
2646
2647 pthread_mutex_unlock(&dns_lock);
2648 return;
2649 }
2650
2651
2652 static Boolean
2653 dns_configuration_watch()
2654 {
2655 int dns_check = 0;
2656 const char *dns_key;
2657 Boolean ok = FALSE;
2658 uint32_t status;
2659
2660 pthread_mutex_lock(&dns_lock);
2661
2662 dns_key = dns_configuration_notify_key();
2663 if (dns_key == NULL) {
2664 SCLog(TRUE, LOG_INFO, CFSTR("dns_configuration_notify_key() failed"));
2665 goto done;
2666 }
2667
2668 status = notify_register_check(dns_key, &dns_token);
2669 if (status == NOTIFY_STATUS_OK) {
2670 dns_token_valid = TRUE;
2671 } else {
2672 SCLog(TRUE, LOG_INFO, CFSTR("notify_register_check() failed, status=%u"), status);
2673 goto done;
2674 }
2675
2676 status = notify_check(dns_token, &dns_check);
2677 if (status != NOTIFY_STATUS_OK) {
2678 SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%u"), status);
2679 (void)notify_cancel(dns_token);
2680 dns_token_valid = FALSE;
2681 goto done;
2682 }
2683
2684 ok = TRUE;
2685
2686 done :
2687
2688 pthread_mutex_unlock(&dns_lock);
2689 return ok;
2690 }
2691
2692
2693 static void
2694 dns_configuration_unwatch()
2695 {
2696 pthread_mutex_lock(&dns_lock);
2697
2698 (void)notify_cancel(dns_token);
2699 dns_token_valid = FALSE;
2700
2701 if ((dns_configuration != NULL) && (dns_configuration->refs == 0)) {
2702 dns_configuration_free(dns_configuration->config);
2703 CFAllocatorDeallocate(NULL, dns_configuration);
2704 dns_configuration = NULL;
2705 }
2706
2707 pthread_mutex_unlock(&dns_lock);
2708 return;
2709 }
2710
2711
2712 static void
2713 _SC_R_updateResolverReachability(ReachabilityStoreInfoRef store_info,
2714 SCNetworkReachabilityFlags *flags,
2715 Boolean *haveDNS,
2716 const char *nodename,
2717 unsigned int if_index,
2718 uint32_t *resolver_if_index,
2719 int *dns_config_index,
2720 const char *log_prefix
2721 )
2722 {
2723 dns_resolver_t *default_resolver;
2724 dns_configuration_t *dns;
2725 Boolean found = FALSE;
2726 char *fqdn = (char *)nodename;
2727 int i;
2728 Boolean isFQDN = FALSE;
2729 size_t len;
2730 const int ndots = 1;
2731 Boolean useDefault = FALSE;
2732
2733 if (resolver_if_index) *resolver_if_index = 0;
2734 if (dns_config_index) *dns_config_index = -1;
2735
2736 /*
2737 * We first assume that all of the configured DNS servers
2738 * are available. Since we don't know which name server will
2739 * be consulted to resolve the specified nodename we need to
2740 * check the availability of ALL name servers. We can only
2741 * proceed if we know that our query can be answered.
2742 */
2743
2744 *flags = kSCNetworkReachabilityFlagsReachable;
2745 *haveDNS = FALSE;
2746
2747 len = (nodename != NULL) ? strlen(nodename) : 0;
2748 if (len == 0) {
2749 // if no nodename, return not reachable
2750 *flags = 0;
2751 return;
2752 }
2753
2754 dns = dns_configuration_retain();
2755 if (dns == NULL) {
2756 // if error
2757 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no configuration"), log_prefix);
2758 goto done;
2759 }
2760
2761 default_resolver = get_default_resolver(dns->config, if_index);
2762 if (default_resolver == NULL) {
2763 // if no resolver configuration
2764 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no resolvers"), log_prefix);
2765 goto done;
2766 }
2767
2768 if (fqdn[len - 1] == '.') {
2769 isFQDN = TRUE;
2770
2771 // trim trailing '.''s
2772 while ((len > 0) && (fqdn[len-1] == '.')) {
2773 if (fqdn == nodename) {
2774 fqdn = strdup(nodename);
2775 assert(fqdn != nodename);
2776 }
2777 fqdn[--len] = '\0';
2778 }
2779 }
2780
2781 /*
2782 * check if the provided name matches a supplemental domain
2783 */
2784 found = check_matching_resolvers(store_info, dns->config, fqdn, if_index,
2785 flags, haveDNS, resolver_if_index,
2786 dns_config_index, log_prefix);
2787
2788 if (!found && !isFQDN) {
2789 /*
2790 * if we did not match a supplemental domain name and if the
2791 * provided name has enough "."s then the first query will be
2792 * directed to the default resolver.
2793 */
2794 char *cp;
2795 int dots;
2796
2797 dots = 0;
2798 for (cp = fqdn; *cp != '\0'; cp++) {
2799 if (*cp == '.') dots++;
2800 }
2801
2802 /* Per KB: HT4845 */
2803 if (dots >= ndots) {
2804 useDefault = TRUE;
2805 }
2806 }
2807
2808 if (!found && !isFQDN && !useDefault && (dns->config->n_resolver > 1)) {
2809 /*
2810 * FQDN not specified, try matching w/search domains
2811 */
2812 if (default_resolver->n_search > 0) {
2813 for (i = 0; !found && (i < default_resolver->n_search); i++) {
2814 int ret;
2815 char *search_fqdn = NULL;
2816
2817 ret = asprintf(&search_fqdn, "%s.%s", fqdn, default_resolver->search[i]);
2818 if (ret == -1) {
2819 continue;
2820 }
2821
2822 // try the provided name with the search domain appended
2823 found = check_matching_resolvers(store_info,
2824 dns->config,
2825 search_fqdn,
2826 if_index,
2827 flags,
2828 haveDNS,
2829 resolver_if_index,
2830 dns_config_index,
2831 log_prefix);
2832 free(search_fqdn);
2833 }
2834 } else if (default_resolver->domain != NULL) {
2835 char *dp;
2836 int domain_parts = 0;
2837
2838 // count domain parts
2839 for (dp = default_resolver->domain; *dp != '\0'; dp++) {
2840 if (*dp == '.') {
2841 domain_parts++;
2842 }
2843 }
2844
2845 // remove trailing dots
2846 for (dp--; (dp >= default_resolver->domain) && (*dp == '.'); dp--) {
2847 *dp = '\0';
2848 domain_parts--;
2849 }
2850
2851 if (dp >= default_resolver->domain) {
2852 // dots are separators, bump # of components
2853 domain_parts++;
2854 }
2855
2856 dp = default_resolver->domain;
2857 for (i = LOCALDOMAINPARTS; !found && (i <= (domain_parts - ndots)); i++) {
2858 int ret;
2859 char *search_fqdn = NULL;
2860
2861 ret = asprintf(&search_fqdn, "%s.%s", fqdn, dp);
2862 if (ret == -1) {
2863 continue;
2864 }
2865
2866 // try the provided name with the [default] domain appended
2867 found = check_matching_resolvers(store_info,
2868 dns->config,
2869 search_fqdn,
2870 if_index,
2871 flags,
2872 haveDNS,
2873 resolver_if_index,
2874 dns_config_index,
2875 log_prefix);
2876 free(search_fqdn);
2877
2878 // move to the next component of the [default] domain
2879 dp = strchr(dp, '.') + 1;
2880 }
2881 }
2882 }
2883
2884 if (!found) {
2885 // update the reachability of the default resolver
2886 update_resolver_reachability(store_info,
2887 default_resolver,
2888 flags,
2889 haveDNS,
2890 resolver_if_index,
2891 log_prefix);
2892 if (dns_config_index != NULL) *dns_config_index = 0;
2893 }
2894
2895 done :
2896
2897 if (fqdn != nodename) free(fqdn);
2898
2899 if (dns != NULL) {
2900 dns_configuration_release(dns);
2901 }
2902
2903 return;
2904 }
2905
2906
2907 Boolean
2908 __SC_checkResolverReachabilityInternal(SCDynamicStoreRef *storeP,
2909 SCNetworkReachabilityFlags *flags,
2910 Boolean *haveDNS,
2911 const char *nodename,
2912 uint32_t *resolver_if_index,
2913 int *dns_config_index)
2914 {
2915 Boolean ok;
2916 ReachabilityStoreInfo store_info;
2917
2918 ReachabilityStoreInfo_init(&store_info);
2919 ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC);
2920 if (!ok) {
2921 goto done;
2922 }
2923
2924 _SC_R_updateResolverReachability(&store_info,
2925 flags,
2926 haveDNS,
2927 nodename,
2928 0,
2929 resolver_if_index,
2930 dns_config_index,
2931 "");
2932
2933 done :
2934
2935 ReachabilityStoreInfo_free(&store_info);
2936 return ok;
2937 }
2938
2939
2940 /*
2941 * _SC_checkResolverReachabilityByAddress()
2942 *
2943 * Given an IP address, determine whether a reverse DNS query can be issued
2944 * using the current network configuration.
2945 */
2946 Boolean
2947 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef *storeP,
2948 SCNetworkReachabilityFlags *flags,
2949 Boolean *haveDNS,
2950 struct sockaddr *sa)
2951 {
2952 Boolean ok;
2953 char ptr_name[128];
2954 ReachabilityStoreInfo store_info;
2955
2956 ReachabilityStoreInfo_init(&store_info);
2957 ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC);
2958 if (!ok) {
2959 goto done;
2960 }
2961
2962 /*
2963 * Ideally, we would have an API that given a local IP
2964 * address would return the DNS server(s) that would field
2965 * a given PTR query. Fortunately, we do have an SPI which
2966 * which will provide this information given a "name" so we
2967 * take the address, convert it into the inverse query name,
2968 * and find out which servers should be consulted.
2969 */
2970 ok = addr_to_PTR_name(sa, ptr_name, sizeof(ptr_name));
2971 if (!ok) {
2972 goto done;
2973 }
2974
2975 _SC_R_updateResolverReachability(&store_info, flags, haveDNS, ptr_name, 0, NULL, NULL, "");
2976
2977 done :
2978
2979 ReachabilityStoreInfo_free(&store_info);
2980 return ok;
2981 }
2982
2983
2984 #pragma mark -
2985 #pragma mark DNSServiceGetAddrInfo support
2986
2987
2988 /*
2989 * DNS query handling
2990 *
2991 * Notes :
2992 *
2993 * 1. We have a "contract" with discoveryd that for EVERY network
2994 * or DNS configuration change that should warrant our [re-]starting
2995 * a query, discoveryd will acknowledge the latest DNS configuration.
2996 *
2997 * 2. IPMonitor also posts a notification AFTER every network or DNS
2998 * configuration change.
2999 *
3000 * 3. We use IPMonitor's "trailing edge" as a signal to restart any
3001 * by-name queries.
3002 */
3003
3004
3005 // Note: protected by _hn_target_queue()
3006 static int dns_refresh_token;
3007 static Boolean dns_refresh_token_valid = FALSE;
3008
3009
3010 /*
3011 * dns_refresh_handler
3012 *
3013 * Called to notify/update all SCNetworkReachability by-name targets of
3014 * a network/DNS change. The change should [re-]start a DNS query to
3015 * resolve the name.
3016 * - should be exec'd on the _hn_target_queue()
3017 */
3018 static void
3019 dns_refresh_handler()
3020 {
3021 CFArrayRef changes;
3022 CFStringRef key;
3023 __block SCDynamicStoreRef store = NULL;
3024
3025 dispatch_sync(_hn_target_queue(), ^{
3026 if (dns_refresh_token_valid && (hn_store != NULL)) {
3027 store = CFRetain(hn_store);
3028 }
3029 });
3030
3031 if (store == NULL) {
3032 return;
3033 }
3034
3035 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3036 kSCDynamicStoreDomainState,
3037 kSCEntNetDNS);
3038 changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
3039 __SCNetworkReachabilityHandleChanges(store, changes, NULL);
3040 CFRelease(changes);
3041 CFRelease(key);
3042
3043 CFRelease(store);
3044 return;
3045 }
3046
3047
3048 /*
3049 * dns_refresh_enable
3050 *
3051 * Called to monitor for network/DNS changes that should restart a DNS query.
3052 * - caller must be running on the _hn_target_queue()
3053 */
3054 static Boolean
3055 dns_refresh_enable(dispatch_queue_t q)
3056 {
3057 uint32_t status;
3058
3059 status = notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE,
3060 &dns_refresh_token,
3061 q,
3062 ^(int token){
3063 dns_refresh_handler();
3064 });
3065 if (status != NOTIFY_STATUS_OK) {
3066 SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed, status=%u"), status);
3067 return FALSE;
3068 }
3069
3070 dns_refresh_token_valid = TRUE;
3071
3072 return TRUE;
3073 }
3074
3075
3076 /*
3077 * dns_refresh_disable
3078 *
3079 * Called to stop monitoring for network/DNS changes
3080 * - caller must be running on the _hn_target_queue()
3081 */
3082 static void
3083 dns_refresh_disable()
3084 {
3085 (void)notify_cancel(dns_refresh_token);
3086 dns_refresh_token_valid = FALSE;
3087 return;
3088 }
3089
3090
3091 #pragma mark -
3092 #pragma mark [m]DNS Queries
3093
3094
3095 static void
3096 dequeueDNSQuery(SCNetworkReachabilityRef target);
3097
3098
3099 static dispatch_queue_t
3100 _dns_queue()
3101 {
3102 static dispatch_once_t once;
3103 static dispatch_queue_t q;
3104
3105 dispatch_once(&once, ^{
3106 q = dispatch_queue_create("SCNetworkReachability.DNSService", NULL);
3107 });
3108
3109 return q;
3110 }
3111
3112
3113 /*
3114 * _dns_complete
3115 */
3116 static __inline__ Boolean
3117 _dns_complete(SCNetworkReachabilityRef target)
3118 {
3119 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3120
3121 if ((targetPrivate->dnsHaveV4 && targetPrivate->dnsHaveV6) ||
3122 targetPrivate->dnsHavePTR ||
3123 targetPrivate->dnsHaveError ||
3124 targetPrivate->dnsHaveTimeout) {
3125 return TRUE;
3126 }
3127
3128 return FALSE;
3129 }
3130
3131
3132 /*
3133 * _dns_notify
3134 *
3135 * Called to push out a target's DNS changes
3136 * - caller must be running on the _dns_queue()
3137 */
3138 static void
3139 _dns_notify(const void *value, void *context)
3140 {
3141 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value;
3142 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3143
3144 MUTEX_LOCK(&targetPrivate->lock);
3145
3146 if (_dns_complete(target)) {
3147 __mark_operation_end(target,
3148 (targetPrivate->resolvedError == NETDB_SUCCESS), // if successful query
3149 dns_query_mdns, // [m]DNS query
3150 &targetPrivate->dnsQueryStart, // start time
3151 &targetPrivate->dnsQueryEnd); // end time
3152
3153 // update target info
3154 if (targetPrivate->resolvedAddresses != NULL) {
3155 CFRelease(targetPrivate->resolvedAddresses);
3156 }
3157 targetPrivate->resolvedAddresses = targetPrivate->dnsAddresses;
3158 targetPrivate->dnsAddresses = NULL;
3159
3160 targetPrivate->resolvedError = targetPrivate->dnsError;
3161 targetPrivate->dnsError = NETDB_SUCCESS;
3162
3163 dequeueDNSQuery(target);
3164
3165 targetPrivate->needResolve = FALSE;
3166
3167 if (targetPrivate->scheduled) {
3168 __SCNetworkReachabilityUpdate(target);
3169 }
3170 }
3171
3172 MUTEX_UNLOCK(&targetPrivate->lock);
3173 return;
3174 }
3175
3176
3177 typedef enum {
3178 MARK_NONE,
3179 MARK_ERROR,
3180 MARK_TIMEOUT,
3181 MARK_HAVE_V4,
3182 MARK_HAVE_V6,
3183 MARK_HAVE_PTR,
3184 } _dns_mark_t;
3185
3186
3187 /*
3188 * _dns_mark
3189 */
3190 static __inline__ void
3191 _dns_mark(SCNetworkReachabilityRef target, _dns_mark_t mark)
3192 {
3193 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3194
3195 switch (mark) {
3196 case MARK_NONE :
3197 break;
3198 case MARK_ERROR :
3199 targetPrivate->dnsHaveError = TRUE;
3200 break;
3201 case MARK_TIMEOUT :
3202 targetPrivate->dnsHaveTimeout = TRUE;
3203 break;
3204 case MARK_HAVE_V4 :
3205 targetPrivate->dnsHaveV4 = TRUE;
3206 break;
3207 case MARK_HAVE_V6 :
3208 targetPrivate->dnsHaveV6 = TRUE;
3209 break;
3210 case MARK_HAVE_PTR :
3211 targetPrivate->dnsHavePTR = TRUE;
3212 break;
3213 }
3214
3215 return;
3216 }
3217
3218
3219 /*
3220 * _dns_callback
3221 *
3222 * Called to process [m]DNS query updates
3223 * - caller must be running on the _dns_queue()
3224 */
3225 static void
3226 _dns_callback(DNSServiceRef sdRef,
3227 DNSServiceFlags flags,
3228 DNSServiceErrorType errorCode,
3229 _dns_mark_t dnsMark,
3230 CFTypeRef dnsAddress, // CFData(struct sockaddr) or CFString(ptr_name)
3231 void *context)
3232 {
3233 int failures = 0;
3234 Boolean restart = FALSE;
3235 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
3236 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3237
3238 MUTEX_LOCK(&targetPrivate->lock);
3239
3240 if (sdRef != targetPrivate->dnsTarget) {
3241 // if this DNSServiceRef is no longer associated with the target
3242 MUTEX_UNLOCK(&targetPrivate->lock);
3243 return;
3244 }
3245
3246 switch (errorCode) {
3247 case kDNSServiceErr_NoError :
3248 if (dnsAddress != NULL) {
3249 CFMutableArrayRef addresses;
3250 CFIndex i;
3251
3252 _dns_mark(target, dnsMark);
3253
3254 if (targetPrivate->dnsAddresses != NULL) {
3255 if (isA_CFArray(targetPrivate->dnsAddresses)) {
3256 addresses = CFArrayCreateMutableCopy(NULL, 0, targetPrivate->dnsAddresses);
3257 } else {
3258 addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3259 }
3260
3261 CFRelease(targetPrivate->dnsAddresses);
3262 targetPrivate->dnsAddresses = NULL;
3263 } else {
3264 addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3265 }
3266
3267 i = CFArrayGetFirstIndexOfValue(addresses,
3268 CFRangeMake(0, CFArrayGetCount(addresses)),
3269 dnsAddress);
3270 if (flags & kDNSServiceFlagsAdd) {
3271 // add address
3272 if (i == kCFNotFound) {
3273 CFArrayAppendValue(addresses, dnsAddress);
3274 }
3275 #ifdef HANDLE_RMV_REQUESTS
3276 } else {
3277 // remove address
3278 if (i != kCFNotFound) {
3279 CFArrayRemoveValueAtIndex(addresses, i);
3280 }
3281 #endif // HANDLE_RMV_REQUESTS
3282 }
3283
3284 if (CFArrayGetCount(addresses) > 0) {
3285 targetPrivate->dnsAddresses = addresses;
3286 targetPrivate->dnsError = NETDB_SUCCESS;
3287 } else {
3288 // if host not found
3289 targetPrivate->dnsAddresses = CFRetain(kCFNull);
3290 targetPrivate->dnsError = EAI_NONAME;
3291 CFRelease(addresses);
3292 }
3293
3294 }
3295 break;
3296 case kDNSServiceErr_BadParam :
3297 _dns_mark(target, MARK_ERROR);
3298
3299 if (targetPrivate->dnsAddresses != NULL) {
3300 CFRelease(targetPrivate->dnsAddresses);
3301 }
3302 targetPrivate->dnsAddresses = CFRetain(kCFNull);
3303 targetPrivate->dnsError = EAI_NONAME;
3304 break;
3305 case kDNSServiceErr_NoSuchRecord :
3306 _dns_mark(target, dnsMark);
3307
3308 if (targetPrivate->dnsAddresses == NULL) {
3309 targetPrivate->dnsAddresses = CFRetain(kCFNull);
3310 targetPrivate->dnsError = EAI_NONAME;
3311 }
3312 break;
3313 case kDNSServiceErr_Timeout :
3314 _dns_mark(target, MARK_TIMEOUT);
3315
3316 if (targetPrivate->dnsAddresses == NULL) {
3317 targetPrivate->dnsAddresses = CFRetain(kCFNull);
3318 targetPrivate->dnsError = EAI_NONAME;
3319 }
3320 break;
3321 default :
3322 SCLog(TRUE, LOG_ERR,
3323 CFSTR("%sSCNetworkReachability _dns_callback w/error=%d (n=%d)"),
3324 targetPrivate->log_prefix,
3325 errorCode,
3326 targetPrivate->dnsFailures + 1);
3327 // fall through
3328 case kDNSServiceErr_ServiceNotRunning :
3329 _dns_mark(target, MARK_ERROR);
3330
3331 // bump per-target failure count
3332 failures = ++targetPrivate->dnsFailures;
3333
3334 // Check to see if we've seen too many failures for this target
3335 if (failures > 2) {
3336 // if so, there's little point in retrying over
3337 // and over again so let's just return an error
3338 // and move on.
3339 if (targetPrivate->dnsAddresses != NULL) {
3340 CFRelease(targetPrivate->dnsAddresses);
3341 }
3342 targetPrivate->dnsAddresses = CFRetain(kCFNull);
3343 targetPrivate->dnsError = EAI_NONAME;
3344 } else if (targetPrivate->dnsGeneration == dnsGeneration) {
3345 // if not, then "discoveryd" crashed or some
3346 // other/unexpected error occurred. In this
3347 // case, we'll try again with a clean slate and
3348 // restart all requests.
3349 if (dnsMain != NULL) {
3350 DNSServiceRefDeallocate(dnsMain);
3351 dnsMain = NULL;
3352 dnsCount = 0;
3353 dnsGeneration++;
3354 restart = TRUE;
3355 }
3356 }
3357 break;
3358 }
3359
3360 // update DNS failure count (and [re-]set to zero if we're OK)
3361 targetPrivate->dnsFailures = failures;
3362
3363 MUTEX_UNLOCK(&targetPrivate->lock);
3364
3365 if (restart) {
3366 SCLog(TRUE, LOG_DEBUG,
3367 CFSTR("%sreconnecting SCNetworkReachability w/\"discoveryd\" (%d)"),
3368 targetPrivate->log_prefix,
3369 dnsGeneration);
3370
3371 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC),
3372 _hn_changes_queue(),
3373 ^{
3374 dns_refresh_handler();
3375 });
3376
3377 // and flush the dnsUpdated queue as any DNS results we may have
3378 // accumulated are no longer valid.
3379 if (dnsUpdated != NULL) {
3380 CFRelease(dnsUpdated);
3381 dnsUpdated = NULL;
3382 }
3383 return;
3384 }
3385
3386 if (targetPrivate->dnsHaveTimeout) {
3387 targetPrivate->dnsNoAddressesSinceLastTimeout = TRUE;
3388 } else if (targetPrivate->dnsNoAddressesSinceLastTimeout &&
3389 isA_CFArray(targetPrivate->dnsAddresses) &&
3390 CFArrayGetCount(targetPrivate->dnsAddresses) > 0)
3391 {
3392 targetPrivate->dnsNoAddressesSinceLastTimeout = FALSE;
3393 }
3394
3395 // the "more coming" flag applies to DNSService callouts for any/all
3396 // hosts that are being watched so we need to keep track of the targets
3397 // we have updated. When we [finally] have the last callout then we
3398 // push our notifications for all of the updated targets.
3399
3400 if (dnsUpdated == NULL) {
3401 dnsUpdated = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
3402 }
3403 CFSetAddValue(dnsUpdated, target);
3404
3405 if (!(flags & kDNSServiceFlagsMoreComing)) {
3406 CFSetApplyFunction(dnsUpdated, _dns_notify, NULL);
3407 CFRelease(dnsUpdated);
3408 dnsUpdated = NULL;
3409 }
3410
3411 return;
3412 }
3413
3414
3415 /*
3416 * _dns_getaddrinfo_callback
3417 *
3418 * Called to process [m]DNS query updates
3419 * - caller must be running on the _dns_queue()
3420 */
3421 static void
3422 _dns_getaddrinfo_callback(DNSServiceRef sdRef,
3423 DNSServiceFlags flags,
3424 uint32_t interfaceIndex,
3425 DNSServiceErrorType errorCode,
3426 const char *hostname,
3427 const struct sockaddr *address,
3428 uint32_t ttl,
3429 void *context)
3430 {
3431 CFDataRef dnsAddress = NULL;
3432 _dns_mark_t dnsMark = MARK_NONE;
3433
3434 if (address != NULL) {
3435 switch (errorCode) {
3436 case kDNSServiceErr_NoError :
3437 dnsAddress = CFDataCreate(NULL, (void *)address, address->sa_len);
3438 // ... and fall through
3439 case kDNSServiceErr_NoSuchRecord :
3440 switch (address->sa_family) {
3441 case AF_INET :
3442 dnsMark = MARK_HAVE_V4;
3443 break;
3444 case AF_INET6 :
3445 dnsMark = MARK_HAVE_V6;
3446 break;
3447 }
3448 break;
3449 default :
3450 break;
3451 }
3452 }
3453
3454 _dns_callback(sdRef, flags, errorCode, dnsMark, dnsAddress, context);
3455
3456 if (dnsAddress != NULL) {
3457 CFRelease(dnsAddress);
3458 }
3459
3460 return;
3461 }
3462
3463
3464 static CFStringRef
3465 _dns_copy_domain_name(const uint8_t *rdata, uint16_t rdlen)
3466 {
3467 CFMutableStringRef domain;
3468 const uint8_t *label;
3469 uint8_t label_len;
3470
3471 domain = CFStringCreateMutable(NULL, 0);
3472
3473 label = rdata;
3474 label_len = *(label++);
3475 while (label_len != 0) {
3476 while (label_len-- > 0) {
3477 uint8_t byte = *label++;
3478
3479 if ((byte == '.') || (byte == '\\')) {
3480 // if escape needed
3481 CFStringAppendFormat(domain, NULL, CFSTR("\\%c"), byte);
3482 } else if (byte <= ' ') {
3483 CFStringAppendFormat(domain, NULL, CFSTR("\\%c%c%c"),
3484 '0' + (byte / 100),
3485 '0' + ((byte / 10) % 10),
3486 '0' + (byte % 10));
3487 } else {
3488 CFStringAppendFormat(domain, NULL, CFSTR("%c"), byte);
3489 }
3490 }
3491
3492 label_len = *(label++);
3493 if (label_len != 0) {
3494 CFStringAppendFormat(domain, NULL, CFSTR("."));
3495 }
3496 }
3497
3498 return domain;
3499 }
3500
3501
3502 static void
3503 _dns_queryrecord_callback(DNSServiceRef sdRef,
3504 DNSServiceFlags flags,
3505 uint32_t interfaceIndex,
3506 DNSServiceErrorType errorCode,
3507 const char *fullname,
3508 uint16_t rrtype,
3509 uint16_t rrclass,
3510 uint16_t rdlen,
3511 const void *rdata,
3512 uint32_t ttl,
3513 void *context)
3514 {
3515 _dns_mark_t dnsMark = MARK_NONE;
3516 CFStringRef dnsPTRName = NULL;
3517 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
3518 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3519
3520 // for now, we only support using DNSServiceQueryRecord for PTR queries
3521 assert(targetPrivate->type == reachabilityTypePTR);
3522
3523 if (rdata != NULL) {
3524 switch (errorCode) {
3525 case kDNSServiceErr_NoError :
3526 if (rrtype == kDNSServiceType_PTR) {
3527 dnsPTRName = _dns_copy_domain_name(rdata, rdlen);
3528 }
3529 // ... and fall through
3530 case kDNSServiceErr_NoSuchRecord :
3531 dnsMark = MARK_HAVE_PTR;
3532 break;
3533 default :
3534 break;
3535 }
3536 }
3537
3538 _dns_callback(sdRef, flags, errorCode, dnsMark, dnsPTRName, context);
3539
3540 if (dnsPTRName != NULL) {
3541 CFRelease(dnsPTRName);
3542 }
3543
3544 return;
3545 }
3546
3547
3548 static Boolean
3549 enqueueDNSQuery(SCNetworkReachabilityRef target)
3550 {
3551 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3552
3553 MUTEX_ASSERT_HELD(&targetPrivate->lock);
3554
3555 // clear DNS flags, mark the query active
3556 targetPrivate->dnsFlags = 0;
3557 targetPrivate->dnsActive = TRUE;
3558
3559 // track the DNS resolution time
3560 __mark_operation_start(&targetPrivate->dnsQueryStart, &targetPrivate->dnsQueryEnd);
3561
3562 CFRetain(target);
3563 dispatch_async(_dns_queue(), ^{
3564 DNSServiceErrorType err;
3565 const char *fcn = "???";
3566 DNSServiceRef sdRef = NULL;
3567
3568 if (targetPrivate->dnsTarget != NULL) {
3569 // if already running
3570 CFRelease(target);
3571 return;
3572 }
3573
3574 // if needed, start interacting with "discoveryd"
3575 if (dnsMain == NULL) {
3576 err = DNSServiceCreateConnection(&dnsMain);
3577 if (err != kDNSServiceErr_NoError) {
3578 SCLog(TRUE, LOG_ERR,
3579 CFSTR("%sDNSServiceCreateConnection(&dnsMain) failed, error = %d"),
3580 targetPrivate->log_prefix,
3581 err);
3582 goto done;
3583 }
3584
3585 err = DNSServiceSetDispatchQueue(dnsMain, _dns_queue());
3586 if (err != kDNSServiceErr_NoError) {
3587 SCLog(TRUE, LOG_ERR,
3588 CFSTR("%sDNSServiceSetDispatchQueue() failed, error = %d"),
3589 targetPrivate->log_prefix,
3590 err);
3591 DNSServiceRefDeallocate(dnsMain);
3592 dnsMain = NULL;
3593 dnsGeneration++;
3594 goto done;
3595 }
3596 }
3597
3598 // start a query for this target
3599 sdRef = dnsMain;
3600
3601 switch (targetPrivate->type) {
3602 case reachabilityTypeName :
3603 fcn = "DNSServiceGetAddrInfo";
3604 err = DNSServiceGetAddrInfo(&sdRef, // sdRef
3605 kDNSServiceFlagsReturnIntermediates // flags
3606 | kDNSServiceFlagsShareConnection
3607 | kDNSServiceFlagsSuppressUnusable
3608 | kDNSServiceFlagsTimeout,
3609 targetPrivate->if_index, // interfaceIndex
3610 0, // protocol
3611 targetPrivate->name, // hostname
3612 _dns_getaddrinfo_callback, // callback
3613 (void *)target); // context
3614 break;
3615 case reachabilityTypePTR :
3616 fcn = "DNSServiceQueryRecord";
3617 err = DNSServiceQueryRecord(&sdRef, // sdRef
3618 kDNSServiceFlagsReturnIntermediates // flags
3619 | kDNSServiceFlagsShareConnection
3620 | kDNSServiceFlagsSuppressUnusable
3621 | kDNSServiceFlagsTimeout,
3622 targetPrivate->if_index, // interfaceIndex
3623 targetPrivate->name, // fullname
3624 kDNSServiceType_PTR, // rrtype
3625 kDNSServiceClass_IN, // rrclass
3626 _dns_queryrecord_callback, // callback
3627 (void *)target); // context
3628 break;
3629 default :
3630 err = kDNSServiceErr_Unknown;
3631 break;
3632 }
3633
3634 switch (err) {
3635 case kDNSServiceErr_NoError :
3636 dnsCount++;
3637 break;
3638
3639 default :
3640 SCLog(TRUE, LOG_ERR,
3641 CFSTR("%s%s() failed, error = %d (%d)"),
3642 targetPrivate->log_prefix,
3643 fcn,
3644 err,
3645 dnsCount);
3646 // fall through
3647
3648 case kDNSServiceErr_BadParam :
3649 if (dnsCount == 0) {
3650 // if this was the first request
3651 DNSServiceRefDeallocate(dnsMain);
3652 dnsMain = NULL;
3653 dnsGeneration++;
3654 }
3655 sdRef = NULL;
3656 break;
3657 }
3658
3659 done :
3660
3661 MUTEX_LOCK(&targetPrivate->lock);
3662
3663 if (err == kDNSServiceErr_NoError) {
3664 targetPrivate->dnsGeneration = dnsGeneration;
3665 targetPrivate->dnsTarget = sdRef;
3666 } else {
3667 targetPrivate->dnsActive = FALSE;
3668
3669 // queue up the returned error
3670 dispatch_async(_dns_queue(), ^{
3671 _dns_callback(NULL, // sdRef
3672 0, // flags
3673 err, // errorCode
3674 MARK_ERROR, // dnsMark
3675 NULL, // dnsAddress
3676 (void *)target); // context
3677 CFRelease(target);
3678 });
3679 }
3680
3681 MUTEX_UNLOCK(&targetPrivate->lock);
3682
3683 return;
3684 });
3685
3686 return TRUE;
3687 }
3688
3689
3690 static void
3691 dequeueDNSQuery(SCNetworkReachabilityRef target)
3692 {
3693 DNSServiceRef sdRef;
3694 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3695
3696 MUTEX_ASSERT_HELD(&targetPrivate->lock);
3697
3698 // terminate the [target] [m]DNS query
3699 sdRef = targetPrivate->dnsTarget;
3700 targetPrivate->dnsTarget = NULL;
3701
3702 // mark the query NOT active
3703 targetPrivate->dnsActive = FALSE;
3704
3705 // don't do anything if the sdRef is not valid
3706 if (sdRef != NULL) {
3707 int generation;
3708
3709 generation = targetPrivate->dnsGeneration;
3710 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3LL * NSEC_PER_SEC),
3711 _dns_queue(),
3712 ^{
3713 if (generation == dnsGeneration) {
3714 // if we're pointing to the same DNSService
3715 // generation as the main/active session
3716 // deallocate per-target query
3717 DNSServiceRefDeallocate(sdRef);
3718 dnsCount--;
3719 if (dnsCount == 0) {
3720 // if no more queries active
3721 DNSServiceRefDeallocate(dnsMain);
3722 dnsMain = NULL;
3723 dnsGeneration++;
3724 }
3725 }
3726
3727 CFRelease(target);
3728 });
3729 }
3730
3731 if (targetPrivate->dnsAddresses != NULL) {
3732 CFRelease(targetPrivate->dnsAddresses);
3733 targetPrivate->dnsAddresses = NULL;
3734 }
3735 targetPrivate->dnsError = NETDB_SUCCESS;
3736
3737 return;
3738 }
3739
3740
3741 #pragma mark -
3742 #pragma mark Synchronous DNS query support
3743
3744
3745 #define SYNC_DNS_QUERY_TIMEOUT_NSEC 35 * NSEC_PER_SEC // 35s
3746
3747
3748 static void
3749 sync_DNS_query_callback(SCNetworkReachabilityRef clone,
3750 SCNetworkReachabilityFlags cloneFlags,
3751 void *info)
3752 {
3753 dispatch_semaphore_t s = (dispatch_semaphore_t)info;
3754
3755 dispatch_semaphore_signal(s);
3756 return;
3757 }
3758
3759
3760 static void
3761 sync_DNS_query(SCNetworkReachabilityRef target)
3762 {
3763 SCNetworkReachabilityRef clone;
3764 SCNetworkReachabilityPrivateRef clonePrivate;
3765 SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL };
3766 dispatch_queue_t q;
3767 long ret;
3768 dispatch_semaphore_t s;
3769 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3770
3771 clone = __SCNetworkReachabilityCreateCopy(target);
3772 if (clone == NULL) {
3773 return;
3774 }
3775 clonePrivate = (SCNetworkReachabilityPrivateRef)clone;
3776
3777 q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3778 s = dispatch_semaphore_create(0);
3779
3780 // start async query
3781 context.info = (void *)s;
3782 SCNetworkReachabilitySetCallback(clone, sync_DNS_query_callback, &context);
3783 SCNetworkReachabilitySetDispatchQueue(clone, q);
3784
3785 // wait for reply (or timeout)
3786 ret = dispatch_semaphore_wait(s, dispatch_time(DISPATCH_TIME_NOW,
3787 SYNC_DNS_QUERY_TIMEOUT_NSEC));
3788 if (ret != 0) {
3789 dispatch_sync(_dns_queue(), ^{
3790 // mark as both a timeout *and* an error
3791 _dns_mark(clone, MARK_TIMEOUT);
3792 _dns_mark(clone, MARK_ERROR);
3793
3794 __mark_operation_end(clone,
3795 FALSE, // if successful query
3796 dns_query_mdns_timeout, // [m]DNS query
3797 &clonePrivate->dnsQueryStart, // start time
3798 &clonePrivate->dnsQueryEnd); // end time
3799
3800 MUTEX_LOCK(&clonePrivate->lock);
3801
3802 // update target info with what's available
3803 if (clonePrivate->resolvedAddresses != NULL) {
3804 CFRelease(clonePrivate->resolvedAddresses);
3805 clonePrivate->resolvedAddresses = NULL;
3806 }
3807 if ((clonePrivate->dnsAddresses != NULL) &&
3808 isA_CFArray(clonePrivate->dnsAddresses) &&
3809 (CFArrayGetCount(clonePrivate->dnsAddresses) > 0)) {
3810 clonePrivate->resolvedAddresses = CFArrayCreateMutableCopy(NULL,
3811 0,
3812 clonePrivate->dnsAddresses);
3813 }
3814 if (clonePrivate->resolvedAddresses != NULL) {
3815 // if timeout w/partial results
3816 clonePrivate->resolvedError = NETDB_SUCCESS;
3817 } else {
3818 // if timeout w/no results
3819 clonePrivate->resolvedAddresses = CFRetain(kCFNull);
3820 clonePrivate->resolvedError = EAI_NONAME;
3821 }
3822
3823 MUTEX_UNLOCK(&clonePrivate->lock);
3824 });
3825 }
3826
3827 // cancel request
3828 SCNetworkReachabilitySetDispatchQueue(clone, NULL);
3829 SCNetworkReachabilitySetCallback(clone, NULL, NULL);
3830
3831 // transfer reply
3832 if (clonePrivate->resolvedAddresses != NULL) CFRetain(clonePrivate->resolvedAddresses);
3833 if (targetPrivate->resolvedAddresses != NULL) CFRelease(targetPrivate->resolvedAddresses);
3834 targetPrivate->resolvedAddresses = clonePrivate->resolvedAddresses;
3835 targetPrivate->resolvedError = clonePrivate->resolvedError;
3836 targetPrivate->resolverFlags = clonePrivate->resolverFlags;
3837 targetPrivate->cycle = clonePrivate->cycle;
3838 targetPrivate->dnsFlags = clonePrivate->dnsFlags;
3839 memcpy(&targetPrivate->info, &clonePrivate->info, sizeof(ReachabilityInfo));
3840 memcpy(&targetPrivate->last_notify, &clonePrivate->last_notify, sizeof(ReachabilityInfo));
3841
3842 CFRelease(clone);
3843 dispatch_release(s);
3844
3845 return;
3846 }
3847
3848
3849 #pragma mark -
3850 #pragma mark Network Information support
3851
3852
3853 // Note: protected by _hn_target_queue()
3854 static int network_changed_token;
3855 static Boolean network_changed_token_valid = FALSE;
3856
3857
3858 /*
3859 * nwi_refresh_handler
3860 *
3861 * Called to notify/update network changed events
3862 * - should be exec'd on the _hn_changes_queue()
3863 */
3864 static void
3865 nwi_refresh_handler()
3866 {
3867 CFArrayRef changes;
3868 CFStringRef key;
3869 __block SCDynamicStoreRef store = NULL;
3870
3871 dispatch_sync(_hn_target_queue(), ^{
3872 if (network_changed_token_valid && (hn_store != NULL)) {
3873 store = CFRetain(hn_store);
3874 }
3875 });
3876
3877 if (store == NULL) {
3878 return;
3879 }
3880
3881 // Fake a network change.
3882 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3883 kSCDynamicStoreDomainState,
3884 kSCEntNetIPv4);
3885 changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
3886 __SCNetworkReachabilityHandleChanges(store, changes, NULL);
3887 CFRelease(changes);
3888 CFRelease(key);
3889
3890 CFRelease(store);
3891 return;
3892 }
3893
3894
3895 /*
3896 * nwi_refresh_enable
3897 *
3898 * Called to monitor for network changes.
3899 * - caller must be running on the _hn_target_queue()
3900 * - passed in queue should be _hn_changes_queue()
3901 */
3902 static Boolean
3903 nwi_refresh_enable(dispatch_queue_t q)
3904 {
3905 uint32_t status;
3906
3907 status = notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE_NWI, // trailing nwi_state_get_notify_key()
3908 &network_changed_token,
3909 q,
3910 ^(int token){
3911 nwi_refresh_handler();
3912 });
3913 if (status != NOTIFY_STATUS_OK) {
3914 SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed for network changes, status=%u"), status);
3915 return FALSE;
3916 }
3917
3918 network_changed_token_valid = TRUE;
3919
3920 return TRUE;
3921 }
3922
3923
3924 /*
3925 * nwi_refresh_disable
3926 *
3927 * Called to stop monitoring for network changes
3928 * - caller must be running on the _hn_target_queue()
3929 */
3930 static void
3931 nwi_refresh_disable()
3932 {
3933 if (network_changed_token_valid) {
3934 (void)notify_cancel(network_changed_token);
3935 network_changed_token_valid = FALSE;
3936 }
3937
3938 return;
3939 }
3940
3941
3942 #pragma mark -
3943 #pragma mark Sleep/wake support
3944
3945
3946 #if !TARGET_OS_IPHONE
3947
3948 // Note: protected by _hn_target_queue()
3949 static IOPMConnection power_changed_connection = NULL;
3950 static const CFStringRef power_changed_key = CFSTR("*** EARLY WAKE ***");
3951
3952
3953 /*
3954 * power_refresh_handler
3955 *
3956 * Called to notify/update power capability changed events
3957 * - should be exec'd on the _hn_changes_queue()
3958 */
3959 static void
3960 power_refresh_handler(void *param,
3961 IOPMConnection connection,
3962 IOPMConnectionMessageToken token,
3963 IOPMSystemPowerStateCapabilities capabilities)
3964 {
3965 Boolean change;
3966 IOReturn ret;
3967 __block SCDynamicStoreRef store = NULL;
3968
3969 dispatch_sync(_hn_target_queue(), ^{
3970 if ((power_changed_connection != NULL) && (hn_store != NULL)) {
3971 store = CFRetain(hn_store);
3972 }
3973 });
3974
3975 if (store == NULL) {
3976 return;
3977 }
3978
3979 // check for [relevant] changes
3980 change = ((power_capabilities ^ capabilities) & POWER_CAPABILITIES_NETWORK) != 0;
3981
3982 // update capabilities
3983 power_capabilities = capabilities;
3984
3985 if (change) {
3986 CFArrayRef changes;
3987
3988 // fake a network change.
3989 changes = CFArrayCreate(NULL, (const void **)&power_changed_key, 1, &kCFTypeArrayCallBacks);
3990 __SCNetworkReachabilityHandleChanges(store, changes, NULL);
3991 CFRelease(changes);
3992 }
3993
3994 ret = IOPMConnectionAcknowledgeEvent(connection, token);
3995 if (ret != kIOReturnSuccess) {
3996 SCLog(TRUE, LOG_ERR, CFSTR("IOPMConnectionAcknowledgeEvent failed, 0x%08x"), ret);
3997 }
3998
3999 CFRelease(store);
4000 return;
4001 }
4002
4003
4004 /*
4005 * power_refresh_enable
4006 *
4007 * Called to monitor power changes.
4008 * - caller must be running on the _hn_target_queue()
4009 * - passed in queue should be _hn_changes_queue()
4010 */
4011 static Boolean
4012 power_refresh_enable(dispatch_queue_t q)
4013 {
4014 IOPMConnection connection = NULL;
4015 IOReturn ret;
4016
4017 ret = IOPMConnectionCreate(CFSTR("com.apple.SCNetworkReachability"),
4018 kIOPMEarlyWakeNotification | kIOPMSleepWakeInterest,
4019 &connection);
4020 if (ret != kIOReturnSuccess) {
4021 SCLog(TRUE, LOG_ERR, CFSTR("IOPMConnectionCreate failed, 0x%08x"), ret);
4022 goto failed;
4023 }
4024
4025 ret = IOPMConnectionSetNotification(connection, NULL, power_refresh_handler);
4026 if (ret != kIOReturnSuccess) {
4027 SCLog(TRUE, LOG_ERR, CFSTR("IOPMConnectionSetNotification failed, 0x%08x"), ret);
4028 goto failed;
4029 }
4030
4031 power_changed_connection = connection;
4032 IOPMConnectionSetDispatchQueue(connection, q);
4033 power_capabilities = IOPMConnectionGetSystemCapabilities();
4034
4035 return TRUE;
4036
4037 failed:
4038
4039 if (connection != NULL) {
4040 IOPMConnectionRelease(connection);
4041 }
4042
4043 return FALSE;
4044 }
4045
4046
4047 static void
4048 power_refresh_disable()
4049 {
4050 if (power_changed_connection != NULL) {
4051 IOPMConnectionSetDispatchQueue(power_changed_connection, NULL);
4052 IOPMConnectionRelease(power_changed_connection);
4053 power_changed_connection = NULL;
4054 }
4055
4056 return;
4057 }
4058
4059 #endif // !TARGET_OS_IPHONE
4060
4061
4062
4063
4064
4065
4066 #pragma mark -
4067 #pragma mark OnDemand
4068
4069
4070 SCNetworkServiceRef
4071 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target,
4072 CFDictionaryRef *userOptions)
4073 {
4074 SCNetworkServiceRef service = NULL;
4075 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4076
4077 if (!isA_SCNetworkReachability(target)) {
4078 _SCErrorSet(kSCStatusInvalidArgument);
4079 return NULL;
4080 }
4081
4082 if (targetPrivate->onDemandServiceID != NULL) {
4083 service = _SCNetworkServiceCopyActive(NULL, targetPrivate->onDemandServiceID);
4084 }
4085
4086 if (userOptions != NULL) {
4087 if (targetPrivate->onDemandName != NULL) {
4088 *userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
4089 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, targetPrivate->onDemandName);
4090 } else {
4091 *userOptions = NULL;
4092 }
4093 }
4094
4095 return service;
4096 }
4097
4098
4099
4100
4101 static void
4102 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer,
4103 SCNetworkReachabilityFlags onDemandFlags,
4104 void *info)
4105 {
4106 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
4107 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4108
4109 MUTEX_LOCK(&targetPrivate->lock);
4110
4111 if (!targetPrivate->scheduled) {
4112 // if not currently scheduled
4113 MUTEX_UNLOCK(&targetPrivate->lock);
4114 return;
4115 }
4116
4117 SCLog(_sc_debug, LOG_INFO, CFSTR("%sOnDemand \"server\" status changed (now 0x%08x)"),
4118 targetPrivate->log_prefix,
4119 onDemandFlags);
4120
4121 if (targetPrivate->type == reachabilityTypeName) {
4122 // make sure that we resolve the name again
4123 targetPrivate->needResolve = TRUE;
4124 }
4125
4126 __SCNetworkReachabilityUpdate(target);
4127
4128 MUTEX_UNLOCK(&targetPrivate->lock);
4129
4130 return;
4131 }
4132
4133
4134 static Boolean
4135 __SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef store_info,
4136 SCNetworkReachabilityRef target,
4137 Boolean onDemandRetry,
4138 SCNetworkReachabilityFlags *flags)
4139 {
4140 SCNetworkConnectionRef connection = NULL;
4141 SCNetworkConnectionType connectionType = kSCNetworkConnectionTypeUnknown;
4142 Boolean isAppLayerVPN = FALSE;
4143 Boolean isOnDemandService = FALSE;
4144 Boolean ok = FALSE;
4145 CFStringRef onDemandRemoteAddress = NULL;
4146 CFStringRef onDemandServiceID = NULL;
4147 SCNetworkConnectionStatus onDemandStatus = kSCNetworkConnectionInvalid;
4148 CFMutableDictionaryRef selectOptions = NULL;
4149 Boolean success = FALSE;
4150 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4151
4152 MUTEX_ASSERT_HELD(&targetPrivate->lock);
4153
4154 if (targetPrivate->onDemandName == NULL) {
4155 targetPrivate->onDemandName = CFStringCreateWithCString(NULL, targetPrivate->name, kCFStringEncodingUTF8);
4156 }
4157
4158 /*
4159 * check if an OnDemand VPN configuration matches the name.
4160 */
4161
4162 connection = SCNetworkConnectionCreate(kCFAllocatorDefault, NULL, NULL);
4163 if (connection == NULL) {
4164 goto done;
4165 }
4166
4167 /* set select options */
4168 selectOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
4169 if (selectOptions == NULL) {
4170 goto done;
4171 }
4172
4173 CFDictionaryAddValue(selectOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, targetPrivate->onDemandName);
4174 CFDictionaryAddValue(selectOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry, onDemandRetry ? kCFBooleanTrue : kCFBooleanFalse);
4175 CFDictionaryAddValue(selectOptions, kSCNetworkConnectionSelectionOptionNoUserPrefs, kCFBooleanTrue);
4176
4177 /* select service. May be On Demand or App Layer VPN */
4178 if (!SCNetworkConnectionSelectServiceWithOptions(connection, selectOptions)) {
4179 goto done;
4180 }
4181
4182 /* get reachability flags (of VPN server) */
4183 (void) SCNetworkConnectionGetReachabilityInfo(connection, flags, NULL);
4184
4185 connectionType = SCNetworkConnectionGetType(connection);
4186 if (connectionType == kSCNetworkConnectionTypeAppLayerVPN) {
4187 isAppLayerVPN = TRUE;
4188 }
4189
4190 /* get on-demand info */
4191 onDemandServiceID = SCNetworkConnectionCopyServiceID(connection);
4192 if (SCNetworkConnectionCopyOnDemandInfo(connection, &onDemandRemoteAddress, &onDemandStatus)) {
4193 if (onDemandRemoteAddress != NULL) {
4194 isOnDemandService = TRUE;
4195 ok = TRUE;
4196 }
4197 }
4198
4199 /* handle non-OnDemand App Layer VPN */
4200 if (isAppLayerVPN && !isOnDemandService) {
4201 SCLog(_sc_debug, LOG_INFO, CFSTR("%s status * = 0x%08x (App Layer VPN)"),
4202 targetPrivate->log_prefix,
4203 *flags);
4204 if (*flags & kSCNetworkReachabilityFlagsReachable) {
4205 // if VPN "server" is reachable
4206
4207 if (!(*flags & kSCNetworkReachabilityFlagsTransientConnection)) {
4208 // start w/clean flags if not already layered on a transient network
4209 *flags = kSCNetworkReachabilityFlagsReachable;
4210 }
4211
4212 *flags |= kSCNetworkReachabilityFlagsTransientConnection;
4213 if (onDemandStatus != kSCNetworkConnectionConnected) {
4214 *flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4215 }
4216
4217 SCLog(_sc_debug, LOG_INFO, CFSTR("%s status = isReachable%s"),
4218 (onDemandStatus != kSCNetworkConnectionConnected)
4219 ? " (after App Layer connect)" : "",
4220 targetPrivate->log_prefix);
4221 }
4222
4223 success = TRUE;
4224 goto done;
4225 }
4226
4227 if (!_SC_CFEqual(targetPrivate->onDemandRemoteAddress, onDemandRemoteAddress) ||
4228 !_SC_CFEqual(targetPrivate->onDemandServiceID, onDemandServiceID)) {
4229 if (targetPrivate->onDemandRemoteAddress != NULL) {
4230 CFRelease(targetPrivate->onDemandRemoteAddress);
4231 targetPrivate->onDemandRemoteAddress = NULL;
4232 }
4233
4234 if (targetPrivate->onDemandServer != NULL) {
4235 SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer, NULL, NULL);
4236 if (targetPrivate->dispatchQueue != NULL) {
4237 // unschedule
4238 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE);
4239 } else if (targetPrivate->rls != NULL) {
4240 CFIndex i;
4241 CFIndex n;
4242
4243 // unschedule
4244 n = CFArrayGetCount(targetPrivate->rlList);
4245 for (i = 0; i < n; i += 3) {
4246 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
4247 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
4248
4249 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, rl, rlMode, TRUE);
4250 }
4251 }
4252
4253 CFRelease(targetPrivate->onDemandServer);
4254 targetPrivate->onDemandServer = NULL;
4255 }
4256
4257 if (targetPrivate->onDemandServiceID != NULL) {
4258 CFRelease(targetPrivate->onDemandServiceID);
4259 targetPrivate->onDemandServiceID = NULL;
4260 }
4261 }
4262
4263 if (ok) {
4264 if (onDemandStatus != kSCNetworkConnectionConnected) {
4265 /*
4266 * if we have a VPN configuration matching the name *and* we need to
4267 * bring the VPN up. Combine our flags with those of the VPN server.
4268 */
4269 if (targetPrivate->onDemandServer == NULL) {
4270 SCNetworkReachabilityPrivateRef demandPrivate;
4271 CFMutableDictionaryRef options;
4272
4273 options = CFDictionaryCreateMutable(NULL,
4274 0,
4275 &kCFTypeDictionaryKeyCallBacks,
4276 &kCFTypeDictionaryValueCallBacks);
4277 CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, onDemandRemoteAddress);
4278 CFDictionarySetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandBypass, kCFBooleanTrue);
4279 if (targetPrivate->serverBypass) {
4280 CFDictionarySetValue(options, kSCNetworkReachabilityOptionServerBypass, kCFBooleanTrue);
4281 }
4282 targetPrivate->onDemandServer = SCNetworkReachabilityCreateWithOptions(NULL, options);
4283 CFRelease(options);
4284
4285 // indent OnDemand target
4286 demandPrivate = (SCNetworkReachabilityPrivateRef)targetPrivate->onDemandServer;
4287 strlcat(demandPrivate->log_prefix, ".... ", sizeof(demandPrivate->log_prefix));
4288
4289 if (targetPrivate->scheduled) {
4290 SCNetworkReachabilityContext context = { 0, NULL, CFRetain, CFRelease, CFCopyDescription };
4291
4292 context.info = (void *)target;
4293 SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer,
4294 __SCNetworkReachabilityOnDemandCheckCallback,
4295 &context);
4296
4297 // schedule server reachability to match that of the target
4298 if (targetPrivate->dispatchQueue != NULL) {
4299 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, NULL, NULL, targetPrivate->dispatchQueue, TRUE);
4300 } else {
4301 CFIndex i;
4302 CFIndex n;
4303
4304 n = CFArrayGetCount(targetPrivate->rlList);
4305 for (i = 0; i < n; i += 3) {
4306 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
4307 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
4308
4309 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, rl, rlMode, NULL, TRUE);
4310 }
4311 }
4312 }
4313 }
4314
4315 SCLog(_sc_debug, LOG_INFO, CFSTR("%s status * = 0x%08x"),
4316 targetPrivate->log_prefix,
4317 *flags);
4318
4319
4320 if ((*flags & kSCNetworkReachabilityFlagsReachable) && !(*flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
4321 // if VPN "server" is [still] reachable
4322
4323 if (!(*flags & kSCNetworkReachabilityFlagsTransientConnection)) {
4324 // start w/clean flags if not already layered on a transient network
4325 *flags = kSCNetworkReachabilityFlagsReachable;
4326 }
4327
4328 *flags |= kSCNetworkReachabilityFlagsTransientConnection;
4329 *flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4330 *flags |= kSCNetworkReachabilityFlagsConnectionOnDemand;
4331
4332 // set 'InterventionRequired' if the OnDemand connection is paused
4333 if (SCNetworkConnectionIsOnDemandSuspended(connection)) {
4334 *flags |= kSCNetworkReachabilityFlagsInterventionRequired;
4335 }
4336
4337 if (_sc_debug) {
4338 SCLog(TRUE, LOG_INFO, CFSTR("%s service * = %@"),
4339 targetPrivate->log_prefix,
4340 onDemandServiceID);
4341 SCLog(TRUE, LOG_INFO, CFSTR("%s status = isReachable (after OnDemand connect)"),
4342 targetPrivate->log_prefix);
4343 }
4344
4345 success = TRUE;
4346 }
4347 }
4348
4349 if (onDemandRemoteAddress != NULL) {
4350 if (targetPrivate->onDemandRemoteAddress == NULL) {
4351 targetPrivate->onDemandRemoteAddress = CFRetain(onDemandRemoteAddress);
4352 }
4353 }
4354
4355 if (onDemandServiceID != NULL) {
4356 if (targetPrivate->onDemandServiceID == NULL) {
4357 targetPrivate->onDemandServiceID = CFRetain(onDemandServiceID);
4358 }
4359 }
4360 }
4361
4362 done:
4363
4364 if (onDemandServiceID != NULL) {
4365 CFRelease(onDemandServiceID);
4366 }
4367 if (onDemandRemoteAddress != NULL) {
4368 CFRelease(onDemandRemoteAddress);
4369 }
4370 if (connection != NULL) {
4371 CFRelease(connection);
4372 }
4373 if (selectOptions != NULL) {
4374 CFRelease(selectOptions);
4375 }
4376 return success;
4377 }
4378
4379
4380 /*
4381 * OnDemand configuration handling
4382 *
4383 * Notes :
4384 *
4385 * 1. We have a "contract" with discoveryd that for EVERY network
4386 * or DNS configuration change that should warrant our [re-]starting
4387 * a query, discoveryd will acknowledge the latest DNS configuration.
4388 *
4389 * 2. IPMonitor also posts a notification AFTER every network or DNS
4390 * configuration change.
4391 *
4392 * 3. We use IPMonitor's "trailing edge" as a signal to restart any
4393 * by-name queries.
4394 */
4395
4396
4397 // Note: protected by _hn_target_queue()
4398 static int onDemand_refresh_token;
4399 static Boolean onDemand_refresh_token_valid = FALSE;
4400
4401
4402 /*
4403 * onDemand_refresh_handler
4404 *
4405 * Called to notify/update all SCNetworkReachability targets of
4406 * OnDemand changes.
4407 * - should be exec'd on the _hn_changes_queue()
4408 */
4409 static void
4410 onDemand_refresh_handler()
4411 {
4412 CFArrayRef changes;
4413 CFStringRef key;
4414 __block SCDynamicStoreRef store = NULL;
4415
4416 dispatch_sync(_hn_target_queue(), ^{
4417 if (onDemand_refresh_token_valid && (hn_store != NULL)) {
4418 store = CFRetain(hn_store);
4419 }
4420 });
4421
4422 if (store == NULL) {
4423 return;
4424 }
4425
4426 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
4427 kSCDynamicStoreDomainState,
4428 kSCEntNetOnDemand);
4429 changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
4430 __SCNetworkReachabilityHandleChanges(store, changes, NULL);
4431 CFRelease(changes);
4432 CFRelease(key);
4433
4434 CFRelease(store);
4435 return;
4436 }
4437
4438
4439 /*
4440 * onDemand_refresh_enable
4441 *
4442 * Called to monitor for OnDemand changes.
4443 * - caller must be running on the _hn_target_queue()
4444 */
4445 static Boolean
4446 onDemand_refresh_enable(dispatch_queue_t q)
4447 {
4448 uint32_t status;
4449
4450 status = notify_register_dispatch(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY,
4451 &onDemand_refresh_token,
4452 q,
4453 ^(int token){
4454 onDemand_refresh_handler();
4455 });
4456 if (status != NOTIFY_STATUS_OK) {
4457 SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed, status=%u"), status);
4458 return FALSE;
4459 }
4460
4461 onDemand_refresh_token_valid = TRUE;
4462
4463 return TRUE;
4464 }
4465
4466
4467 /*
4468 * onDemand_refresh_disable
4469 *
4470 * Called to stop monitoring for OnDemand changes
4471 * - caller must be running on the _hn_target_queue()
4472 */
4473 static void
4474 onDemand_refresh_disable()
4475 {
4476 (void)notify_cancel(onDemand_refresh_token);
4477 onDemand_refresh_token_valid = FALSE;
4478 return;
4479 }
4480
4481
4482
4483
4484 #pragma mark -
4485 #pragma mark Reachability Flags
4486
4487
4488 static Boolean
4489 __SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef store_info,
4490 SCNetworkReachabilityRef target,
4491 ReachabilityInfo *reach_info,
4492 Boolean async)
4493 {
4494 CFMutableArrayRef addresses = NULL;
4495 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4496 ReachabilityInfo my_info = NOT_REACHABLE;
4497 Boolean ok = TRUE;
4498
4499 MUTEX_ASSERT_HELD(&targetPrivate->lock);
4500
4501 _reach_set(reach_info, &NOT_REACHABLE, reach_info->cycle, targetPrivate->if_index, targetPrivate->if_name);
4502
4503 if (!isA_SCNetworkReachability(target)) {
4504 _SCErrorSet(kSCStatusInvalidArgument);
4505 return FALSE;
4506 }
4507
4508 #if TARGET_OS_IPHONE
4509 if (isReachabilityTypeName(targetPrivate->type) &&
4510 !async &&
4511 pthread_is_threaded_np() &&
4512 pthread_main_np()) {
4513 SCLog(TRUE, LOG_WARNING, CFSTR("Warning: sync SCNetworkReachability (by-name) query on main thread"));
4514 }
4515 #endif // TARGET_OS_IPHONE
4516
4517 if (!targetPrivate->serverBypass) {
4518 if (!targetPrivate->serverActive) {
4519
4520 ok = __SCNetworkReachabilityServer_targetAdd(target);
4521 if (!ok) {
4522 targetPrivate->serverBypass = TRUE;
4523 }
4524 }
4525
4526 if (targetPrivate->serverActive) {
4527 ok = __SCNetworkReachabilityServer_targetStatus(target);
4528 if (!ok) {
4529 SCLog(TRUE, LOG_DEBUG,
4530 CFSTR("__SCNetworkReachabilityGetFlags _targetStatus() failed"));
4531 _SCErrorSet(kSCStatusFailed);
4532 goto done;
4533 }
4534
4535 targetPrivate->cycle = targetPrivate->serverInfo.cycle;
4536 _reach_set(&my_info,
4537 &targetPrivate->serverInfo,
4538 targetPrivate->serverInfo.cycle,
4539 targetPrivate->if_index,
4540 targetPrivate->if_name);
4541 goto done;
4542 }
4543 }
4544
4545
4546 switch (targetPrivate->type) {
4547 case reachabilityTypeAddress :
4548 case reachabilityTypeAddressPair : {
4549 /*
4550 * Check "local" address
4551 */
4552 if (targetPrivate->localAddress != NULL) {
4553 /*
4554 * Check "local" address
4555 */
4556 ok = checkAddress(store_info,
4557 targetPrivate->localAddress,
4558 targetPrivate->if_index,
4559 &my_info,
4560 targetPrivate->log_prefix);
4561 if (!ok) {
4562 goto done2; /* not today */
4563 }
4564
4565 if (!(my_info.flags & kSCNetworkReachabilityFlagsIsLocalAddress)) {
4566 goto done2; /* not reachable, non-"local" address */
4567 }
4568 }
4569
4570 /*
4571 * Check "remote" address
4572 */
4573 if ((targetPrivate->remoteAddress != NULL) &&
4574 (targetPrivate->localAddress != targetPrivate->remoteAddress)) {
4575 /*
4576 * in cases where we have different "local" and "remote" addresses
4577 * we need to re-initialize the to-be-returned flags.
4578 */
4579 my_info = NOT_REACHABLE;
4580
4581 /*
4582 * Check "remote" address
4583 */
4584 ok = checkAddress(store_info,
4585 targetPrivate->remoteAddress,
4586 targetPrivate->if_index,
4587 &my_info,
4588 targetPrivate->log_prefix);
4589 if (!ok) {
4590 goto done2; /* not today */
4591 }
4592 }
4593
4594 break;
4595
4596 }
4597
4598 case reachabilityTypeName :
4599 case reachabilityTypePTR : {
4600 int error;
4601 int ns_dns_config = -1;
4602 SCNetworkReachabilityFlags ns_flags = 0;
4603 uint32_t ns_if_index = 0;
4604
4605 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
4606 if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
4607 /* if resolved or an error had been detected */
4608 if (!async) {
4609 /* if not an async request */
4610 goto checkResolvedAddresses;
4611 } else if (targetPrivate->dnsActive) {
4612 /* if [m]DNS query active */
4613 goto checkResolvedAddresses;
4614 } else if (!targetPrivate->needResolve) {
4615 /*
4616 * if this is an async request (i.e. someone is watching the reachability
4617 * of this target), if no query active, and if no query is needed
4618 */
4619 goto checkResolvedAddresses;
4620 }
4621 }
4622
4623 if (!targetPrivate->onDemandBypass) {
4624 Boolean onDemand;
4625 SCNetworkReachabilityFlags onDemandFlags = 0;
4626
4627 /*
4628 * before we attempt our initial DNS query, check if there is
4629 * an OnDemand configuration that we should be using.
4630 */
4631 onDemand = __SCNetworkReachabilityOnDemandCheck(store_info, target, FALSE, &onDemandFlags);
4632 if (onDemand) {
4633 /* if OnDemand connection is needed */
4634 my_info.flags = onDemandFlags;
4635 goto done;
4636 }
4637 }
4638
4639 targetPrivate->dnsBlocked = FALSE;
4640
4641 /* update the reachability of the DNS servers */
4642 _SC_R_updateResolverReachability(store_info,
4643 &ns_flags,
4644 &targetPrivate->haveDNS,
4645 targetPrivate->name,
4646 targetPrivate->if_index,
4647 &ns_if_index,
4648 &ns_dns_config,
4649 targetPrivate->log_prefix);
4650
4651
4652 // save resolver reachability flags
4653 targetPrivate->resolverFlags = ns_flags;
4654
4655 if (rankReachability(ns_flags) < 2) {
4656 /*
4657 * if DNS servers are not (or are no longer) reachable, set
4658 * flags based on the availability of configured (but not
4659 * active) services.
4660 */
4661
4662 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"),
4663 targetPrivate->log_prefix);
4664
4665 if (!targetPrivate->dnsBlocked) {
4666 ok = checkAddress(store_info,
4667 NULL,
4668 targetPrivate->if_index,
4669 &my_info,
4670 targetPrivate->log_prefix);
4671 if (!ok) {
4672 SCLog(_sc_debug, LOG_INFO, CFSTR("%sNo available networks"),
4673 targetPrivate->log_prefix);
4674 goto done2;
4675 }
4676 } else {
4677 // if not checking "available" networks
4678 my_info.flags = ns_flags;
4679 my_info.if_index = ns_if_index;
4680 }
4681
4682 if (async && targetPrivate->scheduled) {
4683 /*
4684 * return "host not found", set flags appropriately,
4685 * and schedule notification.
4686 */
4687 __SCNetworkReachabilitySetResolvedError(target, EAI_NONAME);
4688 my_info.flags |= (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending);
4689
4690 SCLog(_sc_debug, LOG_INFO, CFSTR("%sno DNS servers are reachable"),
4691 targetPrivate->log_prefix);
4692 __SCNetworkReachabilityUpdate(target);
4693 }
4694
4695 break;
4696 }
4697
4698 if (targetPrivate->resolverBypass) {
4699 if (targetPrivate->haveDNS) {
4700 /*
4701 * if we are not resolving the name, and if we have
4702 * one or more DNS resolvers, then return flags that
4703 * reflect the reachability of the resolvers (and
4704 * not the actual name).
4705 */
4706 my_info.flags = ns_flags;
4707 my_info.if_index = ns_if_index;
4708 }
4709 break;
4710 }
4711
4712 if (async) {
4713 /* for async requests we return the last known status */
4714 my_info = targetPrivate->info;
4715
4716 if (targetPrivate->dnsActive) {
4717 /* if [m]DNS query active */
4718 if (_sc_debug && !targetPrivate->quiet) {
4719 SCLog(TRUE, LOG_INFO,
4720 CFSTR("%swaiting for DNS reply"),
4721 targetPrivate->log_prefix);
4722 }
4723 if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
4724 /* updated reachability based on the previous reply */
4725 goto checkResolvedAddresses;
4726 }
4727 break;
4728 }
4729
4730 SCLog(_sc_debug, LOG_INFO,
4731 CFSTR("%sstart DNS query for name = %s"),
4732 targetPrivate->log_prefix,
4733 targetPrivate->name);
4734
4735 /*
4736 * initiate an DNS query w/DNSServiceGetAddrInfo
4737 */
4738 enqueueDNSQuery(target);
4739 break;
4740 }
4741
4742 SCLog(_sc_debug, LOG_INFO,
4743 CFSTR("%scheckName(%s)"),
4744 targetPrivate->log_prefix,
4745 targetPrivate->name);
4746
4747 /*
4748 * OK, all of the DNS name servers are available. Let's
4749 * resolve the nodename into an address.
4750 */
4751 sync_DNS_query(target);
4752
4753 if (!(targetPrivate->dnsHaveTimeout && targetPrivate->dnsHaveError)) {
4754 // if target reach info is valid
4755 memcpy(reach_info, &targetPrivate->info, sizeof(ReachabilityInfo));
4756 goto done2;
4757 }
4758
4759 if (addresses != NULL) CFRelease(addresses);
4760 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
4761
4762 checkResolvedAddresses :
4763
4764 /*
4765 * We first assume that the requested host is NOT available.
4766 * Then, check each address for accessibility and return the
4767 * best status available.
4768 */
4769 my_info = NOT_REACHABLE;
4770
4771 if ((targetPrivate->type == reachabilityTypeName) && isA_CFArray(addresses)) {
4772 CFIndex i;
4773 CFIndex n = CFArrayGetCount(addresses);
4774 struct sockaddr *sa;
4775
4776 for (i = 0; i < n; i++) {
4777 ReachabilityInfo ns_info = NOT_REACHABLE;
4778
4779 sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
4780
4781 ok = checkAddress(store_info,
4782 sa,
4783 targetPrivate->if_index,
4784 &ns_info,
4785 targetPrivate->log_prefix);
4786 if (!ok) {
4787 goto done2; /* not today */
4788 }
4789
4790 if (rankReachability(ns_info.flags) > rankReachability(my_info.flags)) {
4791 /* return the best case result */
4792 my_info = ns_info;
4793 if (rankReachability(my_info.flags) == 2) {
4794 /* can't get any better than REACHABLE */
4795 break;
4796 }
4797 }
4798 }
4799
4800 if (_sc_debug) {
4801 for (i++; i < n; i++) {
4802 sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
4803 log_address("skipAddress",
4804 sa,
4805 targetPrivate->if_index,
4806 targetPrivate->log_prefix);
4807 }
4808 }
4809 } else if ((targetPrivate->type == reachabilityTypePTR) && isA_CFArray(addresses)) {
4810 CFIndex i;
4811 CFIndex n = CFArrayGetCount(addresses);
4812
4813 my_info = NOT_REACHABLE;
4814
4815 for (i = 0; i < n; i++) {
4816 if (i == 0) {
4817 my_info.flags = kSCNetworkReachabilityFlagsReachable;
4818 }
4819
4820 if (_sc_debug) {
4821 CFStringRef ptrName;
4822
4823 ptrName = CFArrayGetValueAtIndex(addresses, i);
4824 SCLog(TRUE, LOG_INFO, CFSTR("%sPTR name(%@)"),
4825 targetPrivate->log_prefix,
4826 ptrName);
4827 }
4828 }
4829 } else {
4830 if ((error == EAI_NONAME)
4831 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
4832 || (error == EAI_NODATA)
4833 #endif
4834 ) {
4835 /*
4836 * the target host name could not be resolved
4837 */
4838 if (!targetPrivate->onDemandBypass) {
4839 Boolean onDemand;
4840 SCNetworkReachabilityFlags onDemandFlags = 0;
4841
4842 /*
4843 * our initial DNS query failed, check again to see if there
4844 * there is an OnDemand configuration that we should be using.
4845 */
4846 onDemand = __SCNetworkReachabilityOnDemandCheck(store_info, target, TRUE, &onDemandFlags);
4847 if (onDemand) {
4848 /* if OnDemand connection is needed */
4849 my_info.flags = onDemandFlags;
4850 goto done;
4851 }
4852 }
4853
4854
4855 if (!targetPrivate->haveDNS) {
4856 /*
4857 * No DNS servers are defined. Set flags based on
4858 * the availability of configured (but not active)
4859 * services.
4860 */
4861 ok = checkAddress(store_info,
4862 NULL,
4863 targetPrivate->if_index,
4864 &my_info,
4865 targetPrivate->log_prefix);
4866 if (!ok) {
4867 goto done2; /* not today */
4868 }
4869
4870 if ((my_info.flags & kSCNetworkReachabilityFlagsReachable) &&
4871 (my_info.flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
4872 /*
4873 * Since we might pick up a set of DNS servers when this connection
4874 * is established, don't reply with a "HOST NOT FOUND" error just yet.
4875 */
4876 break;
4877 }
4878
4879 /* Host not found, not reachable! */
4880 my_info = NOT_REACHABLE;
4881 }
4882 }
4883 }
4884
4885 break;
4886 }
4887 }
4888
4889 done:
4890
4891
4892 _reach_set(reach_info, &my_info, targetPrivate->cycle, targetPrivate->if_index, targetPrivate->if_name);
4893
4894 done2 :
4895
4896 if (addresses != NULL) CFRelease(addresses);
4897 return ok;
4898 }
4899
4900 int
4901 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target)
4902 {
4903 int if_index = -1;
4904 Boolean ok = TRUE;
4905 ReachabilityStoreInfo store_info;
4906 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4907
4908 if (!isA_SCNetworkReachability(target)) {
4909 _SCErrorSet(kSCStatusInvalidArgument);
4910 return if_index;
4911 }
4912
4913 ReachabilityStoreInfo_init(&store_info);
4914
4915 MUTEX_LOCK(&targetPrivate->lock);
4916
4917 if (targetPrivate->scheduled) {
4918 // if being watched, return the last known (and what should be current) status
4919 goto done;
4920 }
4921
4922
4923 ok = __SCNetworkReachabilityGetFlags(&store_info, target, &targetPrivate->info, FALSE);
4924
4925 done :
4926
4927 /* Only return the if_index if the connection is reachable not for reachable connection
4928 * required etc ... */
4929 if (ok && rankReachability(targetPrivate->info.flags) == 2) {
4930 if_index = targetPrivate->info.if_index;
4931 }
4932
4933 MUTEX_UNLOCK(&targetPrivate->lock);
4934 ReachabilityStoreInfo_free(&store_info);
4935 return if_index;
4936 }
4937
4938
4939 Boolean
4940 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target,
4941 SCNetworkReachabilityFlags *flags)
4942 {
4943 Boolean ok = TRUE;
4944 ReachabilityStoreInfo store_info;
4945 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4946
4947 if (!isA_SCNetworkReachability(target)) {
4948 _SCErrorSet(kSCStatusInvalidArgument);
4949 return FALSE;
4950 }
4951
4952 ReachabilityStoreInfo_init(&store_info);
4953
4954 MUTEX_LOCK(&targetPrivate->lock);
4955
4956 if (targetPrivate->scheduled) {
4957 // if being watched, return the last known (and what should be current) status
4958 *flags = targetPrivate->info.flags & kSCNetworkReachabilityFlagsMask;
4959
4960 if (isReachabilityTypeName(targetPrivate->type) && targetPrivate->dnsNoAddressesSinceLastTimeout) {
4961 targetPrivate->needResolve = TRUE;
4962 ReachabilityInfo tmp_reach_info = NOT_REACHABLE;
4963 __SCNetworkReachabilityGetFlags(&store_info, target, &tmp_reach_info, TRUE);
4964 }
4965
4966 goto done;
4967 }
4968
4969
4970 ok = __SCNetworkReachabilityGetFlags(&store_info, target, &targetPrivate->info, FALSE);
4971 if (_sc_debug) {
4972 SCLog(TRUE, LOG_INFO, CFSTR("%s flags = 0x%08x"), targetPrivate->log_prefix, targetPrivate->info.flags);
4973 }
4974
4975 *flags = targetPrivate->info.flags & kSCNetworkReachabilityFlagsMask;
4976
4977 done :
4978
4979 MUTEX_UNLOCK(&targetPrivate->lock);
4980 ReachabilityStoreInfo_free(&store_info);
4981 return ok;
4982 }
4983
4984
4985 #pragma mark -
4986 #pragma mark Notifications
4987
4988
4989 /*
4990 * __SCNetworkReachabilityHandleChanges
4991 *
4992 * Called to process network configuration changes and determine
4993 * if a reachability notification is warranted.
4994 * - should be exec'd on the _hn_changes_queue()
4995 */
4996 static void
4997 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store,
4998 CFArrayRef changedKeys,
4999 void *info)
5000 {
5001 Boolean dnsConfigChanged = FALSE;
5002 CFIndex i;
5003 Boolean forcedChange = FALSE;
5004 CFStringRef key;
5005 Boolean match;
5006 CFIndex nChanges;
5007 CFIndex nGlobals = 0;
5008 CFIndex nTargets;
5009 Boolean neChanged = FALSE;
5010 Boolean networkConfigChanged = FALSE;
5011 struct timeval now;
5012 Boolean onDemandConfigChanged = FALSE;
5013 #if !TARGET_OS_IPHONE
5014 Boolean powerStatusChanged = FALSE;
5015 #endif // !TARGET_OS_IPHONE
5016 ReachabilityStoreInfo store_info;
5017 const void * targets_q[N_QUICK];
5018 const void ** targets = targets_q;
5019 __block CFSetRef watchers = NULL;
5020
5021 nChanges = CFArrayGetCount(changedKeys);
5022 if (nChanges == 0) {
5023 /* if no changes */
5024 return;
5025 }
5026
5027
5028 dispatch_sync(_hn_target_queue(), ^{
5029 /* grab the currently watched targets */
5030 if (hn_targets != NULL) {
5031 watchers = CFSetCreateCopy(NULL, hn_targets);
5032 }
5033 });
5034
5035 nTargets = (watchers != NULL) ? CFSetGetCount(watchers) : 0;
5036 if (nTargets == 0) {
5037 /* if no addresses being monitored */
5038 goto done;
5039 }
5040
5041 /* grab the current time */
5042 (void)gettimeofday(&now, NULL);
5043
5044 #if !TARGET_OS_IPHONE
5045 match = CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), power_changed_key);
5046 if (match) {
5047 /* handle "early" wake notification */
5048 nGlobals++;
5049 powerStatusChanged = TRUE;
5050 }
5051 #endif // !TARGET_OS_IPHONE
5052
5053 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
5054 kSCDynamicStoreDomainState,
5055 kSCEntNetDNS);
5056 match = CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key);
5057 CFRelease(key);
5058 if (match) {
5059 nGlobals++;
5060 dnsConfigChanged = TRUE; /* the DNS server(s) have changed */
5061 }
5062
5063 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
5064 kSCDynamicStoreDomainState,
5065 kSCEntNetOnDemand);
5066 match = CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key);
5067 CFRelease(key);
5068 if (match) {
5069 nGlobals++;
5070 onDemandConfigChanged = TRUE; /* the OnDemand configuration has changed */
5071
5072 // force OnDemand configuration refresh (if SC notification arrives before BSD notify)
5073 __SCNetworkConnectionForceOnDemandConfigurationRefresh();
5074 }
5075
5076
5077
5078 match = CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), SCNETWORKREACHABILITY_TRIGGER_KEY);
5079 if (match) {
5080 nGlobals++;
5081 forcedChange = TRUE; /* an SCDynamicStore driven "network" change */
5082 }
5083
5084 if (nChanges > nGlobals) {
5085 networkConfigChanged = TRUE;
5086 }
5087
5088 if (_sc_debug) {
5089 unsigned int changes = 0;
5090 static const char *change_strings[] = {
5091 // with no "power" status change
5092 "", // 00000
5093 "network ", // 00001
5094 "DNS ", // 00010
5095 "network and DNS ", // 00011
5096 "OnDemand ", // 00100
5097 "network and OnDemand ", // 00101
5098 "DNS and OnDemand ", // 00110
5099 "network, DNS, and OnDemand ", // 00111
5100 "NE ", // 01000
5101 "network and NE ", // 01001
5102 "DNS and NE ", // 01010
5103 "network, DNS, and NE ", // 01011
5104 "OnDemand and NE ", // 01100
5105 "network, OnDemand, and NE ", // 01101
5106 "DNS, OnDemand, and NE ", // 01110
5107 "network, DNS, OnDemand, and NE ", // 01111
5108 #if !TARGET_OS_IPHONE
5109 // with "power" status change
5110 "power", // 10000
5111 "network and power ", // 10001
5112 "DNS and power ", // 10010
5113 "network, DNS, and power ", // 10011
5114 "OnDemand and power ", // 10100
5115 "network, OnDemand, and power ", // 10101
5116 "DNS, OnDemand, and power ", // 10110
5117 "network, DNS, OnDemand, and power ", // 10111
5118 "NE and power ", // 11000
5119 "network, NE, and power ", // 11001
5120 "DNS, NE, and power ", // 11010
5121 "network, DNS, NE, and power ", // 11011
5122 "OnDemand, NE, and power ", // 11100
5123 "network, OnDemand, NE, and power ", // 11101
5124 "DNS, OnDemand, NE, and power ", // 11110
5125 "network, DNS, OnDemand, NE, and power ", // 11111
5126 #endif // !TARGET_OS_IPHONE
5127 };
5128
5129 #if !TARGET_OS_IPHONE
5130 #define PWR 16
5131 if (powerStatusChanged) {
5132 changes |= PWR;
5133 }
5134 #endif // !TARGET_OS_IPHONE
5135
5136 #define NE 8
5137 if (neChanged) {
5138 changes |= NE;
5139 }
5140
5141 #define VOD 4
5142 if (onDemandConfigChanged) {
5143 changes |= VOD;
5144 }
5145
5146 #define DNS 2
5147 if (dnsConfigChanged) {
5148 changes |= DNS;
5149 }
5150
5151 #define NET 1
5152 if (networkConfigChanged) {
5153 changes |= NET;
5154 }
5155
5156 SCLog(TRUE, LOG_INFO,
5157 CFSTR("process %s%s%sconfiguration change"),
5158 forcedChange ? "[forced] " : "",
5159 change_strings[changes]);
5160 }
5161
5162 ReachabilityStoreInfo_init(&store_info);
5163
5164 if (nTargets > (CFIndex)(sizeof(targets_q) / sizeof(CFTypeRef)))
5165 targets = CFAllocatorAllocate(NULL, nTargets * sizeof(CFTypeRef), 0);
5166 CFSetGetValues(watchers, targets);
5167 for (i = 0; i < nTargets; i++) {
5168 Boolean dnsNeedsUpdate = FALSE;
5169 SCNetworkReachabilityRef target = targets[i];
5170 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5171
5172 MUTEX_LOCK(&targetPrivate->lock);
5173
5174
5175 if (dnsConfigChanged) {
5176 targetPrivate->last_dns = now;
5177 }
5178
5179 if (networkConfigChanged) {
5180 targetPrivate->last_network = now;
5181 }
5182
5183 #if !TARGET_OS_IPHONE
5184 if (powerStatusChanged) {
5185 targetPrivate->last_power = now;
5186 }
5187 #endif // !TARGET_OS_IPHONE
5188
5189 if (isReachabilityTypeName(targetPrivate->type)) {
5190 Boolean dnsChanged = (dnsConfigChanged |
5191 dnsNeedsUpdate |
5192 onDemandConfigChanged |
5193 neChanged);
5194
5195 if (!dnsChanged) {
5196 /*
5197 * if the DNS configuration didn't change we still need to
5198 * check that the DNS servers are accessible.
5199 */
5200 Boolean ns_blocked = FALSE;
5201 int ns_dns_config = -1;
5202 SCNetworkReachabilityFlags ns_flags = 0;
5203 uint32_t ns_if_index = 0;
5204 Boolean ok;
5205
5206 /* update the reachability of the DNS servers */
5207 ok = ReachabilityStoreInfo_update(&store_info, &store, AF_UNSPEC);
5208 if (ok) {
5209 _SC_R_updateResolverReachability(&store_info,
5210 &ns_flags,
5211 &targetPrivate->haveDNS,
5212 targetPrivate->name,
5213 targetPrivate->if_index,
5214 &ns_if_index,
5215 &ns_dns_config,
5216 targetPrivate->log_prefix);
5217 } else {
5218 ns_flags = kSCNetworkReachabilityFlagsReachable;
5219 dnsChanged = TRUE;
5220 }
5221
5222
5223 if (rankReachability(ns_flags) < 2) {
5224 /*
5225 * if DNS servers are not (or are no longer) reachable, set
5226 * flags based on the availability of configured (but not
5227 * active) services.
5228 */
5229 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"),
5230 targetPrivate->log_prefix);
5231 dnsChanged = TRUE;
5232 }
5233
5234 if ((targetPrivate->dnsBlocked != ns_blocked) ||
5235 (targetPrivate->resolverFlags != ns_flags)) {
5236 // if the DNS blocked or resolver reachability changed
5237 targetPrivate->dnsBlocked = ns_blocked;
5238 targetPrivate->resolverFlags = ns_flags;
5239 dnsChanged = TRUE;
5240 }
5241 }
5242
5243 if (dnsChanged) {
5244 if (targetPrivate->dnsActive) {
5245 // if we have an outstanding [m]DNS query
5246 SCLog(_sc_debug, LOG_INFO,
5247 CFSTR("%scancel [m]DNS query for name = %s"),
5248 targetPrivate->log_prefix,
5249 targetPrivate->name);
5250 dequeueDNSQuery(target);
5251 }
5252
5253 /* schedule request to resolve the name again */
5254 targetPrivate->needResolve = TRUE;
5255 }
5256 }
5257
5258 if (forcedChange) {
5259 targetPrivate->cycle++;
5260 }
5261
5262 if (targetPrivate->scheduled) {
5263 __SCNetworkReachabilityUpdate(target);
5264 }
5265
5266 MUTEX_UNLOCK(&targetPrivate->lock);
5267 }
5268 if (targets != targets_q) CFAllocatorDeallocate(NULL, targets);
5269
5270 ReachabilityStoreInfo_free(&store_info);
5271
5272 done :
5273
5274 if (watchers != NULL) CFRelease(watchers);
5275 return;
5276 }
5277
5278
5279 /*
5280 * __SCNetworkReachabilityHandleStoreChanges
5281 *
5282 * Called to process SCDynamicStore network configuration changes.
5283 * - should be exec'd on the _hn_changes_queue()
5284 */
5285 static void
5286 __SCNetworkReachabilityHandleStoreChanges(SCDynamicStoreRef store,
5287 CFArrayRef changedKeys,
5288 void *info)
5289 {
5290 nwi_state_t nwi_state;
5291
5292 if ((CFArrayGetCount(changedKeys) == 1) &&
5293 CFArrayContainsValue(changedKeys, CFRangeMake(0, 1), SCNETWORKREACHABILITY_TRIGGER_KEY)) {
5294 goto update;
5295 }
5296
5297 /* "something" [else] changed, start fresh */
5298 ReachabilityStoreInfo_save(NULL);
5299
5300 nwi_state = nwi_state_copy();
5301 if (nwi_state != NULL) {
5302 // if we have some networking
5303 nwi_state_release(nwi_state);
5304 return;
5305 }
5306
5307 // if no networking, use the [SC] changes to add/update
5308 // the kSCNetworkReachabilityFlagsConnectionRequired flag
5309
5310 update :
5311
5312 __SCNetworkReachabilityHandleChanges(store, changedKeys, info);
5313 return;
5314 }
5315
5316
5317 #if !TARGET_OS_IPHONE
5318
5319 static Boolean
5320 darkWakeNotify(SCNetworkReachabilityRef target)
5321 {
5322 return FALSE;
5323 }
5324
5325
5326 static Boolean
5327 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities)
5328 {
5329 if ((power_capabilities & POWER_CAPABILITIES_NETWORK) != POWER_CAPABILITIES_NETWORK) {
5330 // if we're not fully awake (from a networking point of view).
5331 return FALSE;
5332 }
5333
5334 return TRUE;
5335 }
5336
5337 #endif // !TARGET_OS_IPHONE
5338
5339
5340 static void
5341 reachPerform(void *info)
5342 {
5343 void *context_info;
5344 void (*context_release)(const void *);
5345 unsigned int n;
5346 ReachabilityInfo reach_info;
5347 SCNetworkReachabilityCallBack rlsFunction;
5348 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
5349 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5350
5351 n = _SC_ATOMIC_ZERO(&targetPrivate->pending);
5352 if (_sc_debug && (n > 1)) {
5353 SCLog(TRUE, LOG_DEBUG,
5354 CFSTR("%sdelivering SCNetworkReachability notifications (%u)"),
5355 targetPrivate->log_prefix,
5356 n);
5357 }
5358
5359 MUTEX_LOCK(&targetPrivate->lock);
5360
5361 if (!targetPrivate->scheduled) {
5362 // if no longer scheduled
5363 SCLog(_sc_debug, LOG_DEBUG,
5364 CFSTR("%sskipping SCNetworkReachability callback, no longer scheduled"),
5365 targetPrivate->log_prefix);
5366 MUTEX_UNLOCK(&targetPrivate->lock);
5367 return;
5368 }
5369
5370 // capture current state
5371 memcpy(&reach_info, &targetPrivate->info, sizeof(ReachabilityInfo));
5372
5373 // callout
5374 rlsFunction = targetPrivate->rlsFunction;
5375 if (targetPrivate->rlsContext.retain != NULL) {
5376 context_info = (void *)(*targetPrivate->rlsContext.retain)(targetPrivate->rlsContext.info);
5377 context_release = targetPrivate->rlsContext.release;
5378 } else {
5379 context_info = targetPrivate->rlsContext.info;
5380 context_release = NULL;
5381 }
5382
5383 // update last notification info
5384 _reach_set(&targetPrivate->last_notify, &reach_info, targetPrivate->cycle, targetPrivate->if_index, targetPrivate->if_name);
5385 (void)gettimeofday(&targetPrivate->last_push, NULL);
5386
5387 MUTEX_UNLOCK(&targetPrivate->lock);
5388
5389 if (rlsFunction != NULL) {
5390 (*rlsFunction)(target,
5391 reach_info.flags & kSCNetworkReachabilityFlagsMask,
5392 context_info);
5393 }
5394
5395 if (context_release != NULL) {
5396 (*context_release)(context_info);
5397 }
5398
5399 return;
5400 }
5401
5402
5403 /*
5404 * reachUpdate
5405 *
5406 * - caller must *not* be holding the target lock
5407 * - caller must be running on the __SCNetworkReachability_concurrent_queue()
5408 */
5409 static Boolean
5410 reachUpdate(SCNetworkReachabilityRef target)
5411 {
5412 uint64_t cycle;
5413 Boolean defer = FALSE;
5414 Boolean forced;
5415 Boolean ok;
5416 ReachabilityInfo reach_info = NOT_REACHABLE;
5417 ReachabilityStoreInfo store_info;
5418 Boolean target_debug;
5419 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5420
5421 target_debug = (_sc_debug && !targetPrivate->quiet);
5422
5423 if (target_debug) {
5424 SCLog(TRUE, LOG_INFO, CFSTR("%schecking target reachability"),
5425 targetPrivate->log_prefix);
5426 }
5427
5428
5429 MUTEX_LOCK(&targetPrivate->lock);
5430
5431 if (!targetPrivate->scheduled) {
5432 // if not currently scheduled
5433 MUTEX_UNLOCK(&targetPrivate->lock);
5434 return FALSE;
5435 }
5436
5437 /* update reachability, notify if status changed */
5438 ReachabilityStoreInfo_init(&store_info);
5439 ok = __SCNetworkReachabilityGetFlags(&store_info, target, &reach_info, TRUE);
5440 ReachabilityStoreInfo_free(&store_info);
5441 if (!ok) {
5442 /* if reachability status not available */
5443 if (target_debug) {
5444 SCLog(TRUE, LOG_INFO, CFSTR("%sflags not available"),
5445 targetPrivate->log_prefix);
5446 }
5447 reach_info = NOT_REACHABLE;
5448 }
5449
5450 #if !TARGET_OS_IPHONE
5451 /*
5452 * We want to defer the notification if this is a maintenance wake *and*
5453 * the reachability flags that we would be reporting to the application
5454 * are better than those that we last reported.
5455 */
5456 if (!systemIsAwake(power_capabilities)) {
5457 /* if this is a maintenace wake */
5458 reach_info.sleeping = TRUE;
5459
5460 if (rankReachability(reach_info.flags) >= rankReachability(targetPrivate->info.flags)) {
5461 /*
5462 * don't report the change if the new reachability flags are
5463 * the same or "better"
5464 */
5465 defer = !darkWakeNotify(target);
5466 } else if (!__reach_changed(&targetPrivate->last_notify, &reach_info)) {
5467 /*
5468 * if we have already posted this change
5469 */
5470 defer = !darkWakeNotify(target);
5471 }
5472 }
5473 #endif // !TARGET_OS_IPHONE
5474
5475 cycle = targetPrivate->cycle;
5476 forced = ((cycle != 0) && (targetPrivate->info.cycle != cycle));
5477
5478 /*
5479 * (A) (B) (C)
5480 * reach resolve
5481 * changed forced pending
5482 * ======= ======= =======
5483 * N N N No change
5484 * N N Y No change
5485 * N Y N Change (forced && !resolve pending)
5486 * N Y Y No change (suppress forced w/resolve pending)
5487 * Y N/A N/A Change
5488 * Y N/A N/A Change
5489 * Y N/A N/A Change
5490 * Y N/A N/A Change
5491 *
5492 * Change == A || (B && !C)
5493 * No Change == !(A || (B && !C))
5494 * No Change == !A && !(B && !C)
5495 * No Change == !A && (!B || C)
5496 * No Change == (!B || C) && !A
5497 */
5498 if ((!forced || (reach_info.flags == kSCNetworkReachabilityFlagsFirstResolvePending))
5499 && !__reach_changed(&targetPrivate->info, &reach_info)) {
5500 if (target_debug) {
5501 if (targetPrivate->info.sleeping == reach_info.sleeping) {
5502 SCLog(TRUE, LOG_INFO,
5503 CFSTR("%sflags/interface match (now 0x%08x/%u%s)%s%s"),
5504 targetPrivate->log_prefix,
5505 reach_info.flags,
5506 reach_info.if_index,
5507 reach_info.sleeping ? ", z" : "",
5508 defer ? ", deferred" : "",
5509 forced ? ", forced" : "");
5510 } else {
5511 SCLog(TRUE, LOG_INFO,
5512 CFSTR("%sflags/interface equiv (was 0x%08x/%u%s, now 0x%08x/%u%s)%s%s"),
5513 targetPrivate->log_prefix,
5514 targetPrivate->info.flags,
5515 targetPrivate->info.if_index,
5516 targetPrivate->info.sleeping ? ", z" : "",
5517 reach_info.flags,
5518 reach_info.if_index,
5519 reach_info.sleeping ? ", z" : "",
5520 defer ? ", deferred" : "",
5521 forced ? ", forced" : "");
5522 }
5523 }
5524 MUTEX_UNLOCK(&targetPrivate->lock);
5525 return FALSE;
5526 }
5527
5528 if (target_debug) {
5529 SCLog(TRUE, LOG_INFO,
5530 CFSTR("%sflags/interface have changed (was 0x%08x/%u%s, now 0x%08x/%u%s)%s%s"),
5531 targetPrivate->log_prefix,
5532 targetPrivate->info.flags,
5533 targetPrivate->info.if_index,
5534 targetPrivate->info.sleeping ? ", z" : "",
5535 reach_info.flags,
5536 reach_info.if_index,
5537 reach_info.sleeping ? ", z" : "",
5538 defer ? ", deferred" : "",
5539 forced ? ", forced" : "");
5540 }
5541
5542 /* update flags / interface */
5543 _reach_set(&targetPrivate->info, &reach_info, cycle, targetPrivate->if_index, targetPrivate->if_name);
5544
5545 /* as needed, defer the notification */
5546 if (defer) {
5547 MUTEX_UNLOCK(&targetPrivate->lock);
5548 return FALSE;
5549 }
5550
5551 MUTEX_UNLOCK(&targetPrivate->lock);
5552
5553 return TRUE;
5554 }
5555
5556
5557 Boolean
5558 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target,
5559 SCNetworkReachabilityCallBack callout,
5560 SCNetworkReachabilityContext *context)
5561 {
5562 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5563
5564 MUTEX_LOCK(&targetPrivate->lock);
5565
5566 if (targetPrivate->rlsContext.release != NULL) {
5567 /* let go of the current context */
5568 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
5569 }
5570
5571 targetPrivate->rlsFunction = callout;
5572 targetPrivate->rlsContext.info = NULL;
5573 targetPrivate->rlsContext.retain = NULL;
5574 targetPrivate->rlsContext.release = NULL;
5575 targetPrivate->rlsContext.copyDescription = NULL;
5576 if (context) {
5577 bcopy(context, &targetPrivate->rlsContext, sizeof(SCNetworkReachabilityContext));
5578 if (context->retain != NULL) {
5579 targetPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
5580 }
5581 }
5582
5583 MUTEX_UNLOCK(&targetPrivate->lock);
5584
5585 return TRUE;
5586 }
5587
5588
5589 static CFStringRef
5590 reachRLSCopyDescription(const void *info)
5591 {
5592 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
5593
5594 return CFStringCreateWithFormat(NULL,
5595 NULL,
5596 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
5597 target);
5598 }
5599
5600
5601 static Boolean
5602 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target,
5603 CFRunLoopRef runLoop,
5604 CFStringRef runLoopMode,
5605 dispatch_queue_t queue,
5606 Boolean onDemand)
5607 {
5608 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5609 Boolean init = FALSE;
5610 __block Boolean ok = FALSE;
5611
5612 MUTEX_LOCK(&targetPrivate->lock);
5613
5614 if ((targetPrivate->dispatchQueue != NULL) || // if we are already scheduled with a dispatch queue
5615 ((queue != NULL) && targetPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
5616 _SCErrorSet(kSCStatusInvalidArgument);
5617 goto done;
5618 }
5619
5620 if (!targetPrivate->serverBypass) {
5621 if (!targetPrivate->serverActive) {
5622
5623 ok = __SCNetworkReachabilityServer_targetAdd(target);
5624 if (!ok) {
5625 targetPrivate->serverBypass = TRUE;
5626 }
5627 }
5628
5629 if (targetPrivate->serverActive) {
5630 if (targetPrivate->scheduled) {
5631 // if already scheduled
5632 goto watch;
5633 }
5634
5635 ok = __SCNetworkReachabilityServer_targetSchedule(target);
5636 if (!ok) {
5637 SCLog(TRUE, LOG_DEBUG,
5638 CFSTR("__SCNetworkReachabilityScheduleWithRunLoop _targetMonitor() failed"));
5639 _SCErrorSet(kSCStatusFailed);
5640 goto done;
5641 }
5642
5643 goto watch;
5644 }
5645 }
5646
5647 /* schedule the SCNetworkReachability did-something-change handler */
5648
5649 dispatch_sync(_hn_target_queue(), ^{
5650 ok = FALSE;
5651
5652 if (!onDemand && (hn_store == NULL)) {
5653 CFMutableArrayRef keys;
5654 CFMutableArrayRef patterns;
5655 Boolean watch_dns_configuration = FALSE;
5656 Boolean watch_dns_changes = FALSE;
5657 Boolean watch_nwi = FALSE;
5658 Boolean watch_onDemand_networking = FALSE;
5659 #if !TARGET_OS_IPHONE
5660 Boolean watch_power = FALSE;
5661 #endif // !TARGET_OS_IPHONE
5662
5663 hn_store = SCDynamicStoreCreate(NULL,
5664 CFSTR("SCNetworkReachability"),
5665 __SCNetworkReachabilityHandleStoreChanges,
5666 NULL);
5667 if (hn_store == NULL) {
5668 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreCreate() failed"));
5669 return;
5670 }
5671
5672 ReachabilityStoreInfo_keys(&keys, &patterns);
5673 CFArrayAppendValue(keys, SCNETWORKREACHABILITY_TRIGGER_KEY); // force posting reach change
5674 ok = SCDynamicStoreSetNotificationKeys(hn_store, keys, patterns);
5675 CFRelease(keys);
5676 CFRelease(patterns);
5677 if (!ok) {
5678 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreSetNotificationKeys() failed"));
5679 CFRelease(hn_store);
5680 hn_store = NULL;
5681 return;
5682 }
5683
5684 ok = SCDynamicStoreSetDispatchQueue(hn_store, _hn_changes_queue());
5685 if (!ok) {
5686 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
5687 CFRelease(hn_store);
5688 hn_store = NULL;
5689 return;
5690 }
5691
5692 // watch for network information changes
5693 watch_nwi = nwi_refresh_enable(_hn_changes_queue());
5694 if (!watch_nwi) {
5695 goto fail;
5696 }
5697
5698 // watch for DNS configuration (resolver reachability) changes
5699 watch_dns_configuration = dns_configuration_watch();
5700 if (!watch_dns_configuration) {
5701 goto fail;
5702 }
5703
5704 // watch for changes affecting DNS queries
5705 watch_dns_changes = dns_refresh_enable(_hn_changes_queue());
5706 if (!watch_dns_changes) {
5707 goto fail;
5708 }
5709
5710 #if !TARGET_OS_IPHONE
5711 // watch for power capabilities (sleep/wake) changes
5712 watch_power = power_refresh_enable(_hn_changes_queue());
5713 if (!watch_power) {
5714 goto fail;
5715 }
5716 #endif // !TARGET_OS_IPHONE
5717
5718 // watch for OnDemand network changes
5719 watch_onDemand_networking = onDemand_refresh_enable(_hn_changes_queue());
5720 if (!watch_onDemand_networking) {
5721 goto fail;
5722 }
5723
5724
5725
5726 hn_targets = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
5727 ReachabilityStoreInfo_enable(TRUE);
5728
5729 goto scheduled;
5730
5731 fail :
5732
5733 ok = FALSE;
5734
5735
5736
5737 if (watch_onDemand_networking) {
5738 onDemand_refresh_disable();
5739 }
5740
5741 #if !TARGET_OS_IPHONE
5742 if (watch_power) {
5743 power_refresh_disable();
5744 }
5745 #endif // !TARGET_OS_IPHONE
5746
5747 if (watch_dns_changes) {
5748 dns_refresh_disable();
5749 }
5750
5751 if (watch_dns_configuration) {
5752 dns_configuration_unwatch();
5753 }
5754
5755 if (watch_nwi) {
5756 nwi_refresh_disable();
5757 }
5758
5759 SCDynamicStoreSetDispatchQueue(hn_store, NULL);
5760 CFRelease(hn_store);
5761 hn_store = NULL;
5762
5763 _SCErrorSet(kSCStatusFailed);
5764
5765 return;
5766 }
5767
5768 scheduled :
5769
5770 CFSetAddValue(hn_targets, target);
5771
5772 ok = TRUE;
5773 });
5774
5775 if (!ok) {
5776 goto done;
5777 }
5778
5779 watch :
5780
5781 if (!targetPrivate->scheduled) {
5782 CFRunLoopSourceContext context = { 0 // version
5783 , (void *)target // info
5784 , CFRetain // retain
5785 , CFRelease // release
5786 , reachRLSCopyDescription // copyDescription
5787 , CFEqual // equal
5788 , CFHash // hash
5789 , NULL // schedule
5790 , NULL // cancel
5791 , reachPerform // perform
5792 };
5793
5794 if (runLoop != NULL) {
5795 targetPrivate->rls = CFRunLoopSourceCreate(NULL, 0, &context);
5796 targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5797 }
5798
5799 if (isReachabilityTypeName(targetPrivate->type)) {
5800 /*
5801 * we're now scheduled so let's ensure that we
5802 * are starting with a clean slate before we
5803 * resolve the name
5804 */
5805 if (targetPrivate->resolvedAddresses != NULL) {
5806 CFRelease(targetPrivate->resolvedAddresses);
5807 targetPrivate->resolvedAddresses = NULL;
5808 }
5809 targetPrivate->resolvedError = NETDB_SUCCESS;
5810 targetPrivate->needResolve = TRUE;
5811 _reach_set(&targetPrivate->info,
5812 &NOT_REACHABLE,
5813 targetPrivate->info.cycle,
5814 targetPrivate->if_index,
5815 targetPrivate->if_name);
5816 targetPrivate->info.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
5817 _reach_set(&targetPrivate->serverInfo,
5818 &NOT_REACHABLE,
5819 targetPrivate->serverInfo.cycle,
5820 targetPrivate->if_index,
5821 targetPrivate->if_name);
5822 targetPrivate->serverInfo.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
5823 }
5824
5825
5826 targetPrivate->scheduled = TRUE;
5827
5828 init = TRUE;
5829 }
5830
5831 if (queue != NULL) {
5832 // retain dispatch queue
5833 dispatch_retain(queue);
5834 targetPrivate->dispatchQueue = queue;
5835
5836 //
5837 // We've taken a reference to the client's dispatch_queue and we
5838 // want to hold on to that reference until we've processed any/all
5839 // notifications. To facilitate this we create a group, dispatch
5840 // any notification blocks to via that group, and when the caller
5841 // has told us to stop the notifications (unschedule) we wait for
5842 // the group to empty and use the group's finalizer to release
5843 // our reference to the client's queue.
5844 //
5845
5846 // make sure that we have group to track any async requests
5847 targetPrivate->dispatchGroup = dispatch_group_create();
5848
5849 // retain the target ... and release it when the group is released
5850 CFRetain(target);
5851 dispatch_set_context(targetPrivate->dispatchGroup, (void *)target);
5852 dispatch_set_finalizer_f(targetPrivate->dispatchGroup, (dispatch_function_t)CFRelease);
5853 } else {
5854 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
5855 /*
5856 * if we do not already have host notifications scheduled with
5857 * this runLoop / runLoopMode
5858 */
5859 CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode);
5860 }
5861
5862 _SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
5863 }
5864
5865 if (init) {
5866 ReachabilityInfo reach_info = NOT_REACHABLE;
5867 ReachabilityStoreInfo store_info;
5868
5869 /*
5870 * if we have yet to schedule SC notifications for this address
5871 * - initialize current reachability status
5872 */
5873 ReachabilityStoreInfo_init(&store_info);
5874 if (__SCNetworkReachabilityGetFlags(&store_info, target, &reach_info, TRUE)) {
5875 /*
5876 * if reachability status available
5877 * - set flags
5878 * - schedule notification to report status via callback
5879 */
5880 reach_info.flags |= (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending);
5881 _reach_set(&targetPrivate->info,
5882 &reach_info,
5883 targetPrivate->cycle,
5884 targetPrivate->if_index,
5885 targetPrivate->if_name);
5886 __SCNetworkReachabilityUpdate(target);
5887 } else {
5888 /* if reachability status not available, async lookup started */
5889 _reach_set(&targetPrivate->info,
5890 &NOT_REACHABLE,
5891 targetPrivate->cycle,
5892 targetPrivate->if_index,
5893 targetPrivate->if_name);
5894 _reach_set(&targetPrivate->serverInfo,
5895 &NOT_REACHABLE,
5896 targetPrivate->cycle,
5897 targetPrivate->if_index,
5898 targetPrivate->if_name);
5899 }
5900 ReachabilityStoreInfo_free(&store_info);
5901 }
5902
5903 if (targetPrivate->onDemandServer != NULL) {
5904 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, runLoop, runLoopMode, queue, TRUE);
5905 }
5906
5907 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%sscheduled"),
5908 targetPrivate->log_prefix);
5909
5910 ok = TRUE;
5911
5912 done :
5913
5914 MUTEX_UNLOCK(&targetPrivate->lock);
5915 return ok;
5916 }
5917
5918
5919 static Boolean
5920 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target,
5921 CFRunLoopRef runLoop,
5922 CFStringRef runLoopMode,
5923 Boolean onDemand)
5924 {
5925 dispatch_group_t drainGroup = NULL;
5926 dispatch_queue_t drainQueue = NULL;
5927 CFIndex n = 0;
5928 Boolean ok = FALSE;
5929 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5930
5931 // hold a reference while we unschedule
5932 CFRetain(target);
5933
5934 MUTEX_LOCK(&targetPrivate->lock);
5935
5936 if (((runLoop == NULL) && (targetPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
5937 ((runLoop != NULL) && (targetPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are not)
5938 _SCErrorSet(kSCStatusInvalidArgument);
5939 goto done;
5940 }
5941
5942 if (!targetPrivate->scheduled) {
5943 // if not currently scheduled
5944 _SCErrorSet(kSCStatusInvalidArgument);
5945 goto done;
5946 }
5947
5948 // unschedule the target specific sources
5949 if (targetPrivate->dispatchQueue != NULL) {
5950 if (targetPrivate->onDemandServer != NULL) {
5951 SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer, NULL, NULL);
5952 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE);
5953 }
5954
5955 // save dispatchQueue, release reference when we've queue'd blocks complete, allow re-scheduling
5956 drainGroup = targetPrivate->dispatchGroup;
5957 targetPrivate->dispatchGroup = NULL;
5958 drainQueue = targetPrivate->dispatchQueue;
5959 targetPrivate->dispatchQueue = NULL;
5960 } else {
5961 if (!_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
5962 // if not currently scheduled
5963 _SCErrorSet(kSCStatusInvalidArgument);
5964 goto done;
5965 }
5966
5967 if (targetPrivate->onDemandServer != NULL) {
5968 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, runLoop, runLoopMode, TRUE);
5969 }
5970
5971 n = CFArrayGetCount(targetPrivate->rlList);
5972 if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
5973 // if target is no longer scheduled for this runLoop / runLoopMode
5974 CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode);
5975
5976 if (n == 0) {
5977 // if *all* notifications have been unscheduled
5978 if (targetPrivate->onDemandServer != NULL) {
5979 SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer, NULL, NULL);
5980 }
5981 CFRelease(targetPrivate->rlList);
5982 targetPrivate->rlList = NULL;
5983 CFRunLoopSourceInvalidate(targetPrivate->rls);
5984 CFRelease(targetPrivate->rls);
5985 targetPrivate->rls = NULL;
5986 }
5987 }
5988 }
5989
5990 if (n == 0) {
5991 //
5992 // Cancel our request for server monitoring
5993 //
5994 if (targetPrivate->serverActive) {
5995 ok = __SCNetworkReachabilityServer_targetUnschedule(target);
5996 if (!ok) {
5997 SCLog(TRUE, LOG_DEBUG,
5998 CFSTR("__SCNetworkReachabilityUnscheduleFromRunLoop _targetMonitor() failed"));
5999 _SCErrorSet(kSCStatusFailed);
6000 }
6001 }
6002
6003 // if *all* notifications have been unscheduled
6004 targetPrivate->scheduled = FALSE;
6005 }
6006
6007 if (targetPrivate->serverActive) {
6008 goto unwatch;
6009 }
6010
6011 if (n == 0) {
6012 if (targetPrivate->dnsActive) {
6013 // if we have an active [m]DNS query
6014 dequeueDNSQuery(target);
6015 }
6016
6017 dispatch_sync(_hn_target_queue(), ^{
6018 CFSetRemoveValue(hn_targets, target);
6019
6020 if (onDemand) {
6021 return;
6022 }
6023
6024 if (CFSetGetCount(hn_targets) > 0) {
6025 return;
6026 }
6027
6028 // if we are no longer monitoring any targets
6029 SCDynamicStoreSetDispatchQueue(hn_store, NULL);
6030 CFRelease(hn_store);
6031 hn_store = NULL;
6032 CFRelease(hn_targets);
6033 hn_targets = NULL;
6034
6035 ReachabilityStoreInfo_enable(FALSE);
6036 ReachabilityStoreInfo_save(NULL);
6037
6038 /*
6039 * until we start monitoring again, ensure that
6040 * any resources associated with watching network
6041 * and configuration changes have been released.
6042 */
6043
6044
6045 // OnDemand configuration
6046 onDemand_refresh_disable();
6047
6048 #if !TARGET_OS_IPHONE
6049 // sleep/wake & power capabilities
6050 power_refresh_disable();
6051 #endif // !TARGET_OS_IPHONE
6052
6053 // outstanding DNS queries
6054 dns_refresh_disable();
6055
6056 // DNS configuration
6057 dns_configuration_unwatch();
6058
6059 // nwi
6060 nwi_refresh_disable();
6061 });
6062 }
6063
6064 unwatch :
6065
6066 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%sunscheduled"),
6067 targetPrivate->log_prefix);
6068
6069 ok = TRUE;
6070
6071 done :
6072
6073 MUTEX_UNLOCK(&targetPrivate->lock);
6074
6075 if (drainGroup != NULL) {
6076 dispatch_group_notify(drainGroup, __SCNetworkReachability_concurrent_queue(), ^{
6077 // release group/queue references
6078 dispatch_release(drainQueue);
6079 dispatch_release(drainGroup); // releases our target reference
6080 });
6081 }
6082
6083 // release our reference
6084 CFRelease(target);
6085
6086 return ok;
6087 }
6088
6089 Boolean
6090 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target,
6091 CFRunLoopRef runLoop,
6092 CFStringRef runLoopMode)
6093 {
6094 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) {
6095 _SCErrorSet(kSCStatusInvalidArgument);
6096 return FALSE;
6097 }
6098
6099 return __SCNetworkReachabilityScheduleWithRunLoop(target, runLoop, runLoopMode, NULL, FALSE);
6100 }
6101
6102 Boolean
6103 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target,
6104 CFRunLoopRef runLoop,
6105 CFStringRef runLoopMode)
6106 {
6107 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) {
6108 _SCErrorSet(kSCStatusInvalidArgument);
6109 return FALSE;
6110 }
6111
6112 return __SCNetworkReachabilityUnscheduleFromRunLoop(target, runLoop, runLoopMode, FALSE);
6113 }
6114
6115 Boolean
6116 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target,
6117 dispatch_queue_t queue)
6118 {
6119 Boolean ok = FALSE;
6120
6121 if (!isA_SCNetworkReachability(target)) {
6122 _SCErrorSet(kSCStatusInvalidArgument);
6123 return FALSE;
6124 }
6125
6126 if (queue != NULL) {
6127 ok = __SCNetworkReachabilityScheduleWithRunLoop(target, NULL, NULL, queue, FALSE);
6128 } else {
6129 ok = __SCNetworkReachabilityUnscheduleFromRunLoop(target, NULL, NULL, FALSE);
6130 }
6131
6132 return ok;
6133 }