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