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