]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkReachability.c
f13c2a713e4fe7e27bc87038b44a5ef44ebffc55
[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
1982 // add flags
1983 if (targetPrivate->scheduled) {
1984 str = _SCNetworkReachabilityCopyTargetFlags(target);
1985 CFStringAppendFormat(result, NULL, CFSTR(", %@"), str);
1986 CFRelease(str);
1987 }
1988
1989 CFStringAppendFormat(result, NULL, CFSTR("}"));
1990
1991 return result;
1992 }
1993
1994
1995 static void
1996 __SCNetworkReachabilityDeallocate(CFTypeRef cf)
1997 {
1998 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf;
1999 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2000
2001 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%srelease"),
2002 targetPrivate->log_prefix);
2003
2004 #ifdef HAVE_REACHABILITY_SERVER
2005 /* disconnect from the reachability server */
2006
2007 if (targetPrivate->serverActive) {
2008 __SCNetworkReachabilityServer_targetRemove(target);
2009 }
2010 #endif // HAVE_REACHABILITY_SERVER
2011
2012 /* release resources */
2013
2014 pthread_mutex_destroy(&targetPrivate->lock);
2015
2016 if (targetPrivate->name != NULL)
2017 CFAllocatorDeallocate(NULL, (void *)targetPrivate->name);
2018
2019 if (targetPrivate->serv != NULL)
2020 CFAllocatorDeallocate(NULL, (void *)targetPrivate->serv);
2021
2022 if (targetPrivate->resolvedAddress != NULL)
2023 CFRelease(targetPrivate->resolvedAddress);
2024
2025 if (targetPrivate->localAddress != NULL)
2026 CFAllocatorDeallocate(NULL, (void *)targetPrivate->localAddress);
2027
2028 if (targetPrivate->remoteAddress != NULL)
2029 CFAllocatorDeallocate(NULL, (void *)targetPrivate->remoteAddress);
2030
2031 if (targetPrivate->rlsContext.release != NULL) {
2032 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
2033 }
2034
2035 if (targetPrivate->onDemandName != NULL) {
2036 CFRelease(targetPrivate->onDemandName);
2037 }
2038
2039 if (targetPrivate->onDemandRemoteAddress != NULL) {
2040 CFRelease(targetPrivate->onDemandRemoteAddress);
2041 }
2042
2043 if (targetPrivate->onDemandServer != NULL) {
2044 CFRelease(targetPrivate->onDemandServer);
2045 }
2046
2047 if (targetPrivate->onDemandServiceID != NULL) {
2048 CFRelease(targetPrivate->onDemandServiceID);
2049 }
2050
2051 #ifdef HAVE_REACHABILITY_SERVER
2052 if (targetPrivate->serverDigest != NULL) {
2053 CFRelease(targetPrivate->serverDigest);
2054 }
2055
2056 if (targetPrivate->serverGroup != NULL) {
2057 dispatch_release(targetPrivate->serverGroup);
2058 }
2059
2060 if (targetPrivate->serverQueue != NULL) {
2061 dispatch_release(targetPrivate->serverQueue);
2062 }
2063
2064 if (targetPrivate->serverWatchers != NULL) {
2065 CFRelease(targetPrivate->serverWatchers);
2066 }
2067 #endif // HAVE_REACHABILITY_SERVER
2068
2069 return;
2070 }
2071
2072
2073 static void
2074 __SCNetworkReachabilityInitialize(void)
2075 {
2076 __kSCNetworkReachabilityTypeID = _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass);
2077
2078 // provide a way to enable SCNetworkReachability logging without
2079 // having to set _sc_debug=1.
2080 if (getenv("REACH_LOGGING") != NULL) {
2081 _sc_debug = TRUE;
2082 }
2083
2084 // set per-process "bypass" of the SCNetworkReachability server
2085 if (getenv("LONG_LIVED_QUERY_BYPASS") != NULL) {
2086 D_llqBypass = TRUE;
2087 }
2088
2089 #ifdef HAVE_REACHABILITY_SERVER
2090 // set per-process "bypass" of the SCNetworkReachability server
2091 if (getenv("REACH_SERVER_BYPASS") != NULL) {
2092 D_serverBypass = TRUE;
2093 }
2094 #endif // HAVE_REACHABILITY_SERVER
2095
2096 pthread_mutexattr_init(&lock_attr);
2097 pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_ERRORCHECK);
2098
2099 return;
2100 }
2101
2102
2103 __private_extern__
2104 dispatch_queue_t
2105 __SCNetworkReachability_concurrent_queue()
2106 {
2107 static dispatch_once_t once;
2108 static dispatch_queue_t q;
2109
2110 dispatch_once(&once, ^{
2111 q = dispatch_queue_create("SCNetworkReachabilty.concurrent",
2112 DISPATCH_QUEUE_CONCURRENT);
2113 dispatch_queue_set_width(q, 32);
2114 });
2115
2116 return q;
2117 }
2118
2119
2120 /*
2121 * __SCNetworkReachabilityPerformInlineNoLock
2122 *
2123 * Calls reachPerform()
2124 * - caller must be holding a reference to the target
2125 * - caller must *not* be holding the target lock
2126 * - caller must be running on the __SCNetworkReachability_concurrent_queue()
2127 */
2128 static __inline__ void
2129 __SCNetworkReachabilityPerformInlineNoLock(SCNetworkReachabilityRef target, Boolean needResolve)
2130 {
2131 dispatch_queue_t queue;
2132 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2133
2134 MUTEX_LOCK(&targetPrivate->lock);
2135
2136 if (needResolve) {
2137 // allow the DNS query to be [re-]started
2138 targetPrivate->needResolve = TRUE;
2139 }
2140
2141 queue = targetPrivate->dispatchQueue;
2142 if (queue != NULL) {
2143 dispatch_group_t group;
2144
2145 dispatch_retain(queue);
2146
2147 group = targetPrivate->dispatchGroup;
2148 dispatch_group_enter(group);
2149
2150 MUTEX_UNLOCK(&targetPrivate->lock);
2151
2152 dispatch_sync(queue, ^{
2153 reachPerform((void *)target);
2154 dispatch_group_leave(group);
2155 });
2156 dispatch_release(queue);
2157 } else {
2158 if (targetPrivate->rls != NULL) {
2159 CFRunLoopSourceSignal(targetPrivate->rls);
2160 _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
2161 }
2162
2163 MUTEX_UNLOCK(&targetPrivate->lock);
2164 }
2165
2166 return;
2167 }
2168
2169
2170 #ifdef HAVE_REACHABILITY_SERVER
2171 /*
2172 * __SCNetworkReachabilityPerformNoLock
2173 *
2174 * Calls reachPerform()
2175 * - caller must *not* be holding the target lock
2176 * - caller must *not* running on the __SCNetworkReachability_concurrent_queue()
2177 */
2178 __private_extern__
2179 void
2180 __SCNetworkReachabilityPerformNoLock(SCNetworkReachabilityRef target)
2181 {
2182 CFRetain(target);
2183 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
2184 __SCNetworkReachabilityPerformInlineNoLock(target, FALSE);
2185 CFRelease(target);
2186 });
2187
2188 return;
2189 }
2190 #endif // HAVE_REACHABILITY_SERVER
2191
2192
2193 /*
2194 * __SCNetworkReachabilityPerformConcurrent
2195 *
2196 * Calls reachPerform()
2197 * - caller must be holding the target lock
2198 * - caller running on the __SCNetworkReachability_concurrent_queue()
2199 */
2200 static __inline__ void
2201 __SCNetworkReachabilityPerformConcurrent(SCNetworkReachabilityRef target)
2202 {
2203 dispatch_queue_t queue;
2204 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2205
2206 MUTEX_ASSERT_HELD(&targetPrivate->lock);
2207
2208 queue = targetPrivate->dispatchQueue;
2209 if (queue != NULL) {
2210 CFRetain(target);
2211 dispatch_group_async(targetPrivate->dispatchGroup, queue, ^{
2212 reachPerform((void *)target);
2213 CFRelease(target);
2214 });
2215 } else {
2216 if (targetPrivate->rls != NULL) {
2217 CFRunLoopSourceSignal(targetPrivate->rls);
2218 _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
2219 }
2220 }
2221
2222 return;
2223 }
2224
2225
2226 /*
2227 * __SCNetworkReachabilityPerform
2228 *
2229 * Calls reachPerform()
2230 * - caller must be holding the target lock
2231 * - caller not running on the __SCNetworkReachability_concurrent_queue()
2232 */
2233 static void
2234 __SCNetworkReachabilityPerform(SCNetworkReachabilityRef target)
2235 {
2236 dispatch_queue_t queue;
2237 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2238
2239 MUTEX_ASSERT_HELD(&targetPrivate->lock);
2240
2241 queue = targetPrivate->dispatchQueue;
2242 if (queue != NULL) {
2243 dispatch_retain(queue);
2244 CFRetain(target);
2245 dispatch_group_async(targetPrivate->dispatchGroup, __SCNetworkReachability_concurrent_queue(), ^{
2246 dispatch_sync(queue, ^{
2247 reachPerform((void *)target);
2248 CFRelease(target);
2249 });
2250 dispatch_release(queue);
2251 });
2252 } else if (targetPrivate->rls != NULL) {
2253 CFRunLoopSourceSignal(targetPrivate->rls);
2254 _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
2255 }
2256
2257 return;
2258 }
2259
2260
2261 static SCNetworkReachabilityPrivateRef
2262 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator)
2263 {
2264 SCNetworkReachabilityPrivateRef targetPrivate;
2265 uint32_t size;
2266
2267 /* initialize runtime */
2268 pthread_once(&initialized, __SCNetworkReachabilityInitialize);
2269
2270 /* allocate target */
2271 size = sizeof(SCNetworkReachabilityPrivate) - sizeof(CFRuntimeBase);
2272 targetPrivate = (SCNetworkReachabilityPrivateRef)_CFRuntimeCreateInstance(allocator,
2273 __kSCNetworkReachabilityTypeID,
2274 size,
2275 NULL);
2276 if (targetPrivate == NULL) {
2277 return NULL;
2278 }
2279
2280 MUTEX_INIT(&targetPrivate->lock);
2281
2282 targetPrivate->name = NULL;
2283 targetPrivate->serv = NULL;
2284 targetPrivate->hints = HINTS_DEFAULT;
2285 targetPrivate->needResolve = FALSE;
2286 targetPrivate->resolvedAddress = NULL;
2287 targetPrivate->resolvedAddressError = NETDB_SUCCESS;
2288
2289 targetPrivate->if_index = 0;
2290
2291 targetPrivate->localAddress = NULL;
2292 targetPrivate->remoteAddress = NULL;
2293
2294 targetPrivate->cycle = 1;
2295 targetPrivate->info = NOT_REACHABLE;
2296 targetPrivate->last_notify = NOT_REPORTED;
2297
2298 targetPrivate->scheduled = FALSE;
2299 targetPrivate->rls = NULL;
2300 targetPrivate->rlsFunction = NULL;
2301 targetPrivate->rlsContext.info = NULL;
2302 targetPrivate->rlsContext.retain = NULL;
2303 targetPrivate->rlsContext.release = NULL;
2304 targetPrivate->rlsContext.copyDescription = NULL;
2305 targetPrivate->rlList = NULL;
2306
2307 targetPrivate->haveDNS = FALSE;
2308 targetPrivate->dnsMP = MACH_PORT_NULL;
2309 targetPrivate->dnsPort = NULL;
2310 targetPrivate->dnsRLS = NULL;
2311 targetPrivate->dnsSource = NULL;
2312 targetPrivate->dnsQueryStart = TIME_ZERO;
2313 targetPrivate->dnsQueryEnd = TIME_ZERO;
2314 targetPrivate->dnsRetry = NULL;
2315 targetPrivate->dnsRetryCount = 0;
2316
2317 targetPrivate->last_dns = TIME_ZERO;
2318 targetPrivate->last_network = TIME_ZERO;
2319 #if !TARGET_OS_IPHONE
2320 targetPrivate->last_power = TIME_ZERO;
2321 #endif // !TARGET_OS_IPHONE
2322 targetPrivate->last_push = TIME_ZERO;
2323
2324 targetPrivate->onDemandBypass = FALSE;
2325 targetPrivate->onDemandName = NULL;
2326 targetPrivate->onDemandRemoteAddress = NULL;
2327 targetPrivate->onDemandServer = NULL;
2328 targetPrivate->onDemandServiceID = NULL;
2329
2330 #ifdef HAVE_REACHABILITY_SERVER
2331 targetPrivate->serverBypass = D_serverBypass;
2332 #endif // HAVE_REACHABILITY_SERVER
2333
2334
2335 targetPrivate->llqActive = FALSE;
2336 targetPrivate->llqBypass = D_llqBypass;
2337 targetPrivate->llqTarget = NULL;
2338 targetPrivate->llqTimer = NULL;
2339
2340 #ifdef HAVE_REACHABILITY_SERVER
2341 targetPrivate->serverActive = FALSE;
2342 targetPrivate->serverScheduled = FALSE;
2343 targetPrivate->serverInfo = NOT_REACHABLE;
2344
2345 targetPrivate->serverDigest = NULL;
2346 targetPrivate->serverGroup = NULL;
2347 targetPrivate->serverInfoValid = FALSE;
2348 targetPrivate->serverQueryActive = 0;
2349 targetPrivate->serverQueue = NULL;
2350 targetPrivate->serverReferences = 0;
2351 targetPrivate->serverWatchers = NULL;
2352 #endif // HAVE_REACHABILITY_SERVER
2353
2354 targetPrivate->log_prefix[0] = '\0';
2355 if (_sc_log > 0) {
2356 snprintf(targetPrivate->log_prefix,
2357 sizeof(targetPrivate->log_prefix),
2358 "[%p] ",
2359 targetPrivate);
2360 }
2361
2362 return targetPrivate;
2363 }
2364
2365
2366
2367
2368 static const struct sockaddr *
2369 is_valid_address(const struct sockaddr *address)
2370 {
2371 const struct sockaddr *valid = NULL;
2372 static Boolean warned = FALSE;
2373
2374 if ((address != NULL) &&
2375 (address->sa_len <= sizeof(struct sockaddr_storage))) {
2376 switch (address->sa_family) {
2377 case AF_INET :
2378 if (address->sa_len >= sizeof(struct sockaddr_in)) {
2379 valid = address;
2380 } else {
2381 if (!warned) {
2382 SCLog(TRUE, LOG_ERR,
2383 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"),
2384 address->sa_len,
2385 sizeof(struct sockaddr_in));
2386 warned = TRUE;
2387 }
2388 }
2389 break;
2390 case AF_INET6 :
2391 if (address->sa_len >= sizeof(struct sockaddr_in6)) {
2392 valid = address;
2393 } else if (!warned) {
2394 SCLog(TRUE, LOG_ERR,
2395 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"),
2396 address->sa_len,
2397 sizeof(struct sockaddr_in6));
2398 warned = TRUE;
2399 }
2400 break;
2401 default :
2402 if (!warned) {
2403 SCLog(TRUE, LOG_ERR,
2404 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"),
2405 address->sa_family);
2406 warned = TRUE;
2407 }
2408 }
2409 }
2410
2411 return valid;
2412 }
2413
2414
2415 SCNetworkReachabilityRef
2416 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator,
2417 const struct sockaddr *address)
2418 {
2419 SCNetworkReachabilityPrivateRef targetPrivate;
2420
2421 address = is_valid_address(address);
2422 if (address == NULL) {
2423 _SCErrorSet(kSCStatusInvalidArgument);
2424 return NULL;
2425 }
2426
2427 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
2428 if (targetPrivate == NULL) {
2429 return NULL;
2430 }
2431
2432 targetPrivate->type = reachabilityTypeAddress;
2433 targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, address->sa_len, 0);
2434 bcopy(address, targetPrivate->remoteAddress, address->sa_len);
2435
2436 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
2437 targetPrivate->log_prefix,
2438 DEBUG_REACHABILITY_TYPE_ADDRESS,
2439 targetPrivate);
2440
2441 return (SCNetworkReachabilityRef)targetPrivate;
2442 }
2443
2444
2445 SCNetworkReachabilityRef
2446 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator,
2447 const struct sockaddr *localAddress,
2448 const struct sockaddr *remoteAddress)
2449 {
2450 SCNetworkReachabilityPrivateRef targetPrivate;
2451
2452 if ((localAddress == NULL) && (remoteAddress == NULL)) {
2453 _SCErrorSet(kSCStatusInvalidArgument);
2454 return NULL;
2455 }
2456
2457 if (localAddress != NULL) {
2458 localAddress = is_valid_address(localAddress);
2459 if (localAddress == NULL) {
2460 _SCErrorSet(kSCStatusInvalidArgument);
2461 return NULL;
2462 }
2463 }
2464
2465 if (remoteAddress != NULL) {
2466 remoteAddress = is_valid_address(remoteAddress);
2467 if (remoteAddress == NULL) {
2468 _SCErrorSet(kSCStatusInvalidArgument);
2469 return NULL;
2470 }
2471 }
2472
2473 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
2474 if (targetPrivate == NULL) {
2475 return NULL;
2476 }
2477
2478 targetPrivate->type = reachabilityTypeAddressPair;
2479
2480 if (localAddress != NULL) {
2481 targetPrivate->localAddress = CFAllocatorAllocate(NULL, localAddress->sa_len, 0);
2482 bcopy(localAddress, targetPrivate->localAddress, localAddress->sa_len);
2483 }
2484
2485 if (remoteAddress != NULL) {
2486 targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, remoteAddress->sa_len, 0);
2487 bcopy(remoteAddress, targetPrivate->remoteAddress, remoteAddress->sa_len);
2488 }
2489
2490 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
2491 targetPrivate->log_prefix,
2492 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR,
2493 targetPrivate);
2494
2495 return (SCNetworkReachabilityRef)targetPrivate;
2496 }
2497
2498
2499 SCNetworkReachabilityRef
2500 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator,
2501 const char *nodename)
2502 {
2503 union {
2504 struct sockaddr sa;
2505 struct sockaddr_in sin;
2506 struct sockaddr_in6 sin6;
2507 } addr;
2508 int nodenameLen;
2509 SCNetworkReachabilityPrivateRef targetPrivate;
2510
2511 if (nodename == NULL) {
2512 _SCErrorSet(kSCStatusInvalidArgument);
2513 return NULL;
2514 }
2515
2516 nodenameLen = strlen(nodename);
2517 if (nodenameLen == 0) {
2518 _SCErrorSet(kSCStatusInvalidArgument);
2519 return NULL;
2520 }
2521
2522 if (_SC_string_to_sockaddr(nodename, AF_UNSPEC, (void *)&addr, sizeof(addr)) != NULL) {
2523 /* if this "nodename" is really an IP[v6] address in disguise */
2524 return SCNetworkReachabilityCreateWithAddress(allocator, &addr.sa);
2525 }
2526
2527 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
2528 if (targetPrivate == NULL) {
2529 return NULL;
2530 }
2531
2532 targetPrivate->type = reachabilityTypeName;
2533
2534 targetPrivate->name = CFAllocatorAllocate(NULL, nodenameLen + 1, 0);
2535 strlcpy((char *)targetPrivate->name, nodename, nodenameLen + 1);
2536
2537 targetPrivate->needResolve = TRUE;
2538 targetPrivate->info.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
2539 #ifdef HAVE_REACHABILITY_SERVER
2540 targetPrivate->serverInfo.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
2541 #endif // HAVE_REACHABILITY_SERVER
2542
2543 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"),
2544 targetPrivate->log_prefix,
2545 DEBUG_REACHABILITY_TYPE_NAME,
2546 targetPrivate);
2547
2548 return (SCNetworkReachabilityRef)targetPrivate;
2549 }
2550
2551
2552
2553
2554 SCNetworkReachabilityRef
2555 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator,
2556 CFDictionaryRef options)
2557 {
2558 const struct sockaddr *addr_l = NULL;
2559 const struct sockaddr *addr_r = NULL;
2560 CFDataRef data;
2561 struct addrinfo *hints = NULL;
2562 CFStringRef interface = NULL;
2563 CFBooleanRef llqBypass;
2564 CFStringRef nodename;
2565 CFBooleanRef onDemandBypass;
2566 CFBooleanRef resolverBypass;
2567 #ifdef HAVE_REACHABILITY_SERVER
2568 CFBooleanRef serverBypass;
2569 #endif // HAVE_REACHABILITY_SERVER
2570 CFStringRef servname;
2571 SCNetworkReachabilityRef target;
2572 SCNetworkReachabilityPrivateRef targetPrivate;
2573
2574 if (!isA_CFDictionary(options)) {
2575 _SCErrorSet(kSCStatusInvalidArgument);
2576 return NULL;
2577 }
2578
2579 nodename = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionNodeName);
2580 if ((nodename != NULL) &&
2581 (!isA_CFString(nodename) || (CFStringGetLength(nodename) == 0))) {
2582 _SCErrorSet(kSCStatusInvalidArgument);
2583 return NULL;
2584 }
2585 servname = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionServName);
2586 if ((servname != NULL) &&
2587 (!isA_CFString(servname) || (CFStringGetLength(servname) == 0))) {
2588 _SCErrorSet(kSCStatusInvalidArgument);
2589 return NULL;
2590 }
2591 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionLocalAddress);
2592 if (data != NULL) {
2593 if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
2594 _SCErrorSet(kSCStatusInvalidArgument);
2595 return NULL;
2596 }
2597 addr_l = (const struct sockaddr *)CFDataGetBytePtr(data);
2598 }
2599 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionRemoteAddress);
2600 if (data != NULL) {
2601 if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
2602 _SCErrorSet(kSCStatusInvalidArgument);
2603 return NULL;
2604 }
2605 addr_r = (const struct sockaddr *)CFDataGetBytePtr(data);
2606 }
2607 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionHints);
2608 if (data != NULL) {
2609 if (!isA_CFData(data) || (CFDataGetLength(data) != sizeof(targetPrivate->hints))) {
2610 _SCErrorSet(kSCStatusInvalidArgument);
2611 return NULL;
2612 }
2613
2614 /* ALIGN: CF aligns to >8 byte boundries */
2615 hints = (struct addrinfo *)(void *)CFDataGetBytePtr(data);
2616 if ((hints->ai_addrlen != 0) ||
2617 (hints->ai_addr != NULL) ||
2618 (hints->ai_canonname != NULL) ||
2619 (hints->ai_next != NULL)) {
2620 _SCErrorSet(kSCStatusInvalidArgument);
2621 return NULL;
2622 }
2623 }
2624 interface = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionInterface);
2625 if ((interface != NULL) &&
2626 (!isA_CFString(interface) || (CFStringGetLength(interface) == 0))) {
2627 _SCErrorSet(kSCStatusInvalidArgument);
2628 return NULL;
2629 }
2630 onDemandBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandBypass);
2631 if ((onDemandBypass != NULL) && !isA_CFBoolean(onDemandBypass)) {
2632 _SCErrorSet(kSCStatusInvalidArgument);
2633 return NULL;
2634 }
2635 resolverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionResolverBypass);
2636 if ((resolverBypass != NULL) && !isA_CFBoolean(resolverBypass)) {
2637 _SCErrorSet(kSCStatusInvalidArgument);
2638 return NULL;
2639 }
2640
2641
2642 llqBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionLongLivedQueryBypass);
2643 if ((llqBypass != NULL) && !isA_CFBoolean(llqBypass)) {
2644 _SCErrorSet(kSCStatusInvalidArgument);
2645 return NULL;
2646 }
2647
2648 #ifdef HAVE_REACHABILITY_SERVER
2649 serverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionServerBypass);
2650 if ((serverBypass != NULL) && !isA_CFBoolean(serverBypass)) {
2651 _SCErrorSet(kSCStatusInvalidArgument);
2652 return NULL;
2653 }
2654 #endif // HAVE_REACHABILITY_SERVER
2655
2656 if ((nodename != NULL) || (servname != NULL)) {
2657 const char *name;
2658
2659 if ((addr_l != NULL) || (addr_r != NULL)) {
2660 // can't have both a name/serv and an address
2661 _SCErrorSet(kSCStatusInvalidArgument);
2662 return NULL;
2663 }
2664
2665 name = _SC_cfstring_to_cstring(nodename, NULL, 0, kCFStringEncodingUTF8);
2666 target = SCNetworkReachabilityCreateWithName(allocator, name);
2667 CFAllocatorDeallocate(NULL, (void *)name);
2668 } else {
2669 if ((addr_l != NULL) && (addr_r != NULL)) {
2670 target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, addr_r);
2671 } else if (addr_r != NULL) {
2672 target = SCNetworkReachabilityCreateWithAddress(NULL, addr_r);
2673 } else if (addr_l != NULL) {
2674 target = SCNetworkReachabilityCreateWithAddress(NULL, addr_l);
2675 } else {
2676 _SCErrorSet(kSCStatusInvalidArgument);
2677 return NULL;
2678 }
2679 }
2680 if (target == NULL) {
2681 return NULL;
2682 }
2683
2684 targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2685 if (targetPrivate->type == reachabilityTypeName) {
2686 if (servname != NULL) {
2687 targetPrivate->serv = _SC_cfstring_to_cstring(servname, NULL, 0, kCFStringEncodingUTF8);
2688 }
2689 if (hints != NULL) {
2690 bcopy(hints, &targetPrivate->hints, sizeof(targetPrivate->hints));
2691 }
2692 }
2693
2694 if (interface != NULL) {
2695 if ((_SC_cfstring_to_cstring(interface,
2696 targetPrivate->if_name,
2697 sizeof(targetPrivate->if_name),
2698 kCFStringEncodingASCII) == NULL) ||
2699 ((targetPrivate->if_index = if_nametoindex(targetPrivate->if_name)) == 0)) {
2700 CFRelease(targetPrivate);
2701 _SCErrorSet(kSCStatusInvalidArgument);
2702 return NULL;
2703 }
2704 }
2705
2706
2707 if (llqBypass != NULL) {
2708 targetPrivate->llqBypass = CFBooleanGetValue(llqBypass);
2709 }
2710
2711 if (onDemandBypass != NULL) {
2712 targetPrivate->onDemandBypass = CFBooleanGetValue(onDemandBypass);
2713 }
2714
2715 if (resolverBypass != NULL) {
2716 targetPrivate->resolverBypass = CFBooleanGetValue(resolverBypass);
2717 }
2718
2719 #ifdef HAVE_REACHABILITY_SERVER
2720 if (serverBypass != NULL) {
2721 targetPrivate->serverBypass = CFBooleanGetValue(serverBypass);
2722 }
2723 #endif // HAVE_REACHABILITY_SERVER
2724
2725 if (_sc_debug && (_sc_log > 0)) {
2726 const char *opt;
2727
2728 switch (targetPrivate->type) {
2729 case reachabilityTypeName :
2730 opt = DEBUG_REACHABILITY_TYPE_NAME_OPTIONS;
2731 break;
2732 case reachabilityTypeAddress :
2733 opt = DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS;
2734 break;
2735 case reachabilityTypeAddressPair :
2736 opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS;
2737 break;
2738 default :
2739 opt = "???";
2740 break;
2741 }
2742
2743 SCLog(TRUE, LOG_INFO, CFSTR("%s%s %@"),
2744 targetPrivate->log_prefix,
2745 opt,
2746 targetPrivate);
2747 }
2748
2749 return (SCNetworkReachabilityRef)targetPrivate;
2750 }
2751
2752
2753 CFTypeID
2754 SCNetworkReachabilityGetTypeID(void)
2755 {
2756 pthread_once(&initialized, __SCNetworkReachabilityInitialize); /* initialize runtime */
2757 return __kSCNetworkReachabilityTypeID;
2758 }
2759
2760
2761 CFArrayRef /* CFArray[CFData], where each CFData is a (struct sockaddr *) */
2762 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target,
2763 int *error_num)
2764 {
2765 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2766
2767 if (!isA_SCNetworkReachability(target)) {
2768 _SCErrorSet(kSCStatusInvalidArgument);
2769 return NULL;
2770 }
2771
2772 if (targetPrivate->type != reachabilityTypeName) {
2773 _SCErrorSet(kSCStatusInvalidArgument);
2774 return NULL;
2775 }
2776
2777 if (error_num) {
2778 *error_num = targetPrivate->resolvedAddressError;
2779 }
2780
2781 if (targetPrivate->resolvedAddress != NULL) {
2782 if (isA_CFArray(targetPrivate->resolvedAddress)) {
2783 return CFRetain(targetPrivate->resolvedAddress);
2784 } else {
2785 /* if status is known but no resolved addresses to return */
2786 _SCErrorSet(kSCStatusOK);
2787 return NULL;
2788 }
2789 }
2790
2791 _SCErrorSet(kSCStatusReachabilityUnknown);
2792 return NULL;
2793 }
2794
2795
2796 static void
2797 __SCNetworkReachabilitySetResolvedAddress(int32_t status,
2798 struct addrinfo *res,
2799 SCNetworkReachabilityRef target)
2800 {
2801 struct addrinfo *resP;
2802 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2803
2804 MUTEX_ASSERT_HELD(&targetPrivate->lock);
2805
2806 if (targetPrivate->resolvedAddress != NULL) {
2807 CFRelease(targetPrivate->resolvedAddress);
2808 targetPrivate->resolvedAddress = NULL;
2809 }
2810
2811 if ((status == 0) && (res != NULL)) {
2812 CFMutableArrayRef addresses;
2813
2814 addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2815
2816 for (resP = res; resP; resP = resP->ai_next) {
2817 CFIndex n;
2818 CFDataRef newAddress;
2819
2820 newAddress = CFDataCreate(NULL, (void *)resP->ai_addr, resP->ai_addr->sa_len);
2821 n = CFArrayGetCount(addresses);
2822 if ((n == 0) ||
2823 !CFArrayContainsValue(addresses, CFRangeMake(0, n), newAddress)) {
2824 CFArrayAppendValue(addresses, newAddress);
2825 }
2826 CFRelease(newAddress);
2827 }
2828
2829 /* save the resolved address[es] */
2830 targetPrivate->resolvedAddress = addresses;
2831 targetPrivate->resolvedAddressError = NETDB_SUCCESS;
2832 } else {
2833 SCLog(_sc_debug, LOG_INFO, CFSTR("%sgetaddrinfo() failed: %s"),
2834 targetPrivate->log_prefix,
2835 gai_strerror(status));
2836
2837 /* save the error associated with the attempt to resolve the name */
2838 targetPrivate->resolvedAddress = CFRetain(kCFNull);
2839 targetPrivate->resolvedAddressError = status;
2840 }
2841 targetPrivate->needResolve = FALSE;
2842
2843 if (res != NULL) freeaddrinfo(res);
2844
2845 if (targetPrivate->scheduled) {
2846 __SCNetworkReachabilityPerform(target);
2847 }
2848
2849 return;
2850 }
2851
2852
2853 static void
2854 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status, struct addrinfo *res, void *context)
2855 {
2856 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
2857 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2858
2859 __dns_query_end(target,
2860 ((status == 0) && (res != NULL)), // if successful query
2861 dns_query_async, // async
2862 &targetPrivate->dnsQueryStart, // start time
2863 &targetPrivate->dnsQueryEnd); // end time
2864
2865 __SCNetworkReachabilitySetResolvedAddress(status, res, target);
2866 return;
2867 }
2868
2869
2870 /*
2871 * rankReachability()
2872 * Not reachable == 0
2873 * Connection Required == 1
2874 * Reachable == 2
2875 */
2876 static int
2877 rankReachability(SCNetworkReachabilityFlags flags)
2878 {
2879 int rank = 0;
2880
2881 if (flags & kSCNetworkReachabilityFlagsReachable) rank = 2;
2882 if (flags & kSCNetworkReachabilityFlagsConnectionRequired) rank = 1;
2883 return rank;
2884 }
2885
2886
2887 #pragma mark -
2888 #pragma mark DNS name resolution
2889
2890
2891 static CFStringRef
2892 replyMPCopyDescription(const void *info)
2893 {
2894 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
2895 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2896
2897 return CFStringCreateWithFormat(NULL,
2898 NULL,
2899 CFSTR("<getaddrinfo_async_start reply MP> {%s%s%s%s%s, target = %p}"),
2900 targetPrivate->name != NULL ? "name = " : "",
2901 targetPrivate->name != NULL ? targetPrivate->name : "",
2902 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
2903 targetPrivate->serv != NULL ? "serv = " : "",
2904 targetPrivate->serv != NULL ? targetPrivate->serv : "",
2905 target);
2906 }
2907
2908
2909 static void
2910 getaddrinfo_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info);
2911
2912
2913 static Boolean
2914 enqueueAsyncDNSQuery_dispatch(SCNetworkReachabilityRef target)
2915 {
2916 mach_port_t mp;
2917 dispatch_source_t source;
2918 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2919
2920 MUTEX_ASSERT_HELD(&targetPrivate->lock);
2921
2922 mp = targetPrivate->dnsMP;
2923
2924 // mach_port context <-- NULL (no msg received)
2925 mach_port_set_context(mach_task_self(), mp, (mach_vm_address_t)(uintptr_t)NULL);
2926
2927 // create dispatch source to handle DNS reply
2928 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
2929 mp,
2930 0,
2931 __SCNetworkReachability_concurrent_queue());
2932 if (source == NULL) {
2933 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability dispatch_source_create() failed"));
2934 return FALSE;
2935 }
2936
2937 //
2938 // We created the dispatch_source to listen for (and process) the mach IPC
2939 // reply to our async DNS query. Because the source handler runs asychronously
2940 // we need to ensure that we're holding a reference to the target. Here, we take
2941 // a reference and setup the dispatch_source finalizer to drop it.
2942 //
2943 CFRetain(target);
2944 dispatch_set_context(source, (void *)target);
2945 dispatch_set_finalizer_f(source, (dispatch_function_t)CFRelease);
2946
2947 dispatch_source_set_event_handler(source, ^{
2948 mach_msg_size_t msg_size = 8192;
2949 const mach_msg_options_t options = MACH_RCV_MSG
2950 | MACH_RCV_LARGE
2951 | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX)
2952 | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
2953
2954 while (TRUE) {
2955 kern_return_t kr;
2956 mach_msg_header_t *msg = (mach_msg_header_t *)malloc(msg_size);
2957
2958 kr = mach_msg(msg, /* msg */
2959 options, /* options */
2960 0, /* send_size */
2961 msg_size, /* rcv_size */
2962 mp, /* rcv_name */
2963 MACH_MSG_TIMEOUT_NONE, /* timeout */
2964 MACH_PORT_NULL); /* notify */
2965 if (kr == KERN_SUCCESS) {
2966 // mach_port context <-- msg
2967 mach_port_set_context(mach_task_self(),
2968 mp,
2969 (mach_vm_address_t)(uintptr_t)msg);
2970 } else if (kr == MACH_RCV_TOO_LARGE) {
2971 msg_size *= 2;
2972 free(msg);
2973 continue;
2974 } else {
2975 SCLog(TRUE, LOG_ERR,
2976 CFSTR("SCNetworkReachability async DNS handler, kr=0x%x"),
2977 kr);
2978 free(msg);
2979 }
2980 break;
2981 }
2982
2983 dispatch_source_cancel(source);
2984 });
2985
2986 dispatch_source_set_cancel_handler(source, ^{
2987 #if !TARGET_OS_EMBEDDED
2988 mach_vm_address_t context;
2989 #else // !TARGET_OS_EMBEDDED
2990 mach_port_context_t context;
2991 #endif // !TARGET_OS_EMBEDDED
2992 kern_return_t kr;
2993 mach_port_t mp;
2994
2995 // get the [async DNS query] mach port
2996 mp = (mach_port_t)dispatch_source_get_handle(source);
2997
2998 // check if we have a received message
2999 kr = mach_port_get_context(mach_task_self(), mp, &context);
3000 if (kr == KERN_SUCCESS) {
3001 void *msg;
3002
3003 msg = (void *)(uintptr_t)context;
3004 if (msg != NULL) {
3005 MUTEX_LOCK(&targetPrivate->lock);
3006 getaddrinfo_async_handle_reply(msg);
3007 targetPrivate->dnsSource = NULL;
3008 targetPrivate->dnsMP = MACH_PORT_NULL;
3009 MUTEX_UNLOCK(&targetPrivate->lock);
3010 free(msg);
3011 } else {
3012 getaddrinfo_async_cancel(mp);
3013 }
3014 }
3015
3016 dispatch_release(source);
3017 });
3018
3019 targetPrivate->dnsSource = source;
3020 dispatch_resume(source);
3021
3022 return TRUE;
3023 }
3024
3025
3026 static Boolean
3027 enqueueAsyncDNSQuery_CF(SCNetworkReachabilityRef target)
3028 {
3029 CFMachPortContext context = { 0
3030 , (void *)target
3031 , CFRetain
3032 , CFRelease
3033 , replyMPCopyDescription
3034 };
3035 CFIndex i;
3036 mach_port_t mp;
3037 CFIndex n;
3038 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3039
3040 MUTEX_ASSERT_HELD(&targetPrivate->lock);
3041
3042 mp = targetPrivate->dnsMP;
3043
3044 targetPrivate->dnsPort = _SC_CFMachPortCreateWithPort("SCNetworkReachability",
3045 mp,
3046 getaddrinfo_async_handleCFReply,
3047 &context);
3048 if (targetPrivate->dnsPort == NULL) {
3049 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability CFMachPortCreateWithPort() failed"));
3050 goto fail;
3051 }
3052
3053 targetPrivate->dnsRLS = CFMachPortCreateRunLoopSource(NULL, targetPrivate->dnsPort, 0);
3054 if (targetPrivate->dnsRLS == NULL) {
3055 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability CFMachPortCreateRunLoopSource() failed"));
3056 goto fail;
3057 }
3058
3059 n = CFArrayGetCount(targetPrivate->rlList);
3060 for (i = 0; i < n; i += 3) {
3061 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
3062 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
3063
3064 CFRunLoopAddSource(rl, targetPrivate->dnsRLS, rlMode);
3065 }
3066
3067 return TRUE;
3068
3069 fail :
3070
3071 if (targetPrivate->dnsRLS != NULL) {
3072 CFRunLoopSourceInvalidate(targetPrivate->dnsRLS);
3073 CFRelease(targetPrivate->dnsRLS);
3074 targetPrivate->dnsRLS = NULL;
3075 }
3076 if (targetPrivate->dnsPort != NULL) {
3077 CFMachPortInvalidate(targetPrivate->dnsPort);
3078 CFRelease(targetPrivate->dnsPort);
3079 targetPrivate->dnsPort = NULL;
3080 }
3081
3082 return FALSE;
3083 }
3084
3085
3086 static Boolean
3087 enqueueAsyncDNSQuery(SCNetworkReachabilityRef target, mach_port_t mp)
3088 {
3089 Boolean ok = FALSE;
3090 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3091
3092 MUTEX_ASSERT_HELD(&targetPrivate->lock);
3093
3094 targetPrivate->dnsMP = mp;
3095
3096 if (targetPrivate->dispatchQueue != NULL) {
3097 ok = enqueueAsyncDNSQuery_dispatch(target);
3098 } else if (targetPrivate->rls != NULL) {
3099 ok = enqueueAsyncDNSQuery_CF(target);
3100 }
3101
3102 if (!ok) {
3103 targetPrivate->dnsMP = MACH_PORT_NULL;
3104 _SCErrorSet(kSCStatusFailed);
3105 return FALSE;
3106 }
3107
3108 return TRUE;
3109 }
3110
3111
3112 static void
3113 dequeueAsyncDNSQuery(SCNetworkReachabilityRef target, Boolean cancel)
3114 {
3115 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3116
3117 MUTEX_ASSERT_HELD(&targetPrivate->lock);
3118
3119 if (targetPrivate->dnsPort != NULL) {
3120 CFMachPortInvalidate(targetPrivate->dnsPort);
3121 CFRelease(targetPrivate->dnsPort);
3122 targetPrivate->dnsPort = NULL;
3123 }
3124
3125 if (targetPrivate->dnsRLS != NULL) {
3126 CFRelease(targetPrivate->dnsRLS);
3127 targetPrivate->dnsRLS = NULL;
3128 }
3129
3130 if (targetPrivate->dnsSource != NULL) {
3131 dispatch_source_cancel(targetPrivate->dnsSource);
3132 targetPrivate->dnsSource = NULL;
3133 cancel = FALSE; // the cancellation handler does the work
3134 }
3135
3136 if (targetPrivate->dnsMP != MACH_PORT_NULL) {
3137 if (cancel) {
3138 getaddrinfo_async_cancel(targetPrivate->dnsMP);
3139 }
3140 targetPrivate->dnsMP = MACH_PORT_NULL;
3141 }
3142
3143 return;
3144 }
3145
3146
3147 static void
3148 getaddrinfo_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info)
3149 {
3150 mach_port_t mp = CFMachPortGetPort(port);
3151 int32_t status;
3152 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
3153 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3154
3155 MUTEX_LOCK(&targetPrivate->lock);
3156
3157 if (mp != targetPrivate->dnsMP) {
3158 // we've received a callback on the async DNS port but since the
3159 // associated CFMachPort doesn't match than the request must have
3160 // already been cancelled.
3161 SCLog(TRUE, LOG_ERR, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP"));
3162 MUTEX_UNLOCK(&targetPrivate->lock);
3163 return;
3164 }
3165
3166 dequeueAsyncDNSQuery(target, FALSE);
3167 status = getaddrinfo_async_handle_reply(msg);
3168 if ((status == 0) &&
3169 (targetPrivate->resolvedAddress == NULL) && (targetPrivate->resolvedAddressError == NETDB_SUCCESS)) {
3170 Boolean ok;
3171
3172 // if the request is not complete and needs to be re-queued
3173 ok = enqueueAsyncDNSQuery(target, mp);
3174 if (!ok) {
3175 SCLog(TRUE, LOG_ERR, CFSTR("processAsyncDNSReply enqueueAsyncDNSQuery() failed"));
3176 }
3177 }
3178
3179 MUTEX_UNLOCK(&targetPrivate->lock);
3180
3181 return;
3182 }
3183
3184
3185 static Boolean
3186 check_resolver_reachability(ReachabilityStoreInfoRef store_info,
3187 dns_resolver_t *resolver,
3188 SCNetworkReachabilityFlags *flags,
3189 Boolean *haveDNS,
3190 uint32_t *resolver_if_index,
3191 const char *log_prefix)
3192 {
3193 Boolean ok = TRUE;
3194
3195 if (resolver_if_index) *resolver_if_index = 0;
3196
3197 if (resolver->n_nameserver > 0) {
3198 #if !TARGET_IPHONE_SIMULATOR
3199 *flags = (SCNetworkReachabilityFlags)resolver->reach_flags;
3200 if (resolver_if_index != NULL) {
3201 *resolver_if_index = resolver->if_index;
3202 }
3203 #else // !TARGET_IPHONE_SIMULATOR
3204 int i;
3205
3206 *flags = kSCNetworkReachabilityFlagsReachable;
3207
3208 for (i = 0; i < resolver->n_nameserver; i++) {
3209 struct sockaddr *address = resolver->nameserver[i];
3210 ReachabilityInfo ns_info;
3211
3212 ok = checkAddress(store_info, address, resolver->if_index, &ns_info, log_prefix);
3213 if (!ok) {
3214 /* not today */
3215 break;
3216 }
3217
3218 if ((i == 0) ||
3219 (rankReachability(ns_info.flags) < rankReachability(*flags))) {
3220 /* return the worst case result */
3221 *flags = ns_info.flags;
3222 if (resolver_if_index != NULL) {
3223 *resolver_if_index = ns_info.if_index;
3224 }
3225 }
3226 }
3227 #endif // !TARGET_IPHONE_SIMULATOR
3228 *haveDNS = TRUE;
3229 } else {
3230 *flags = kSCNetworkReachabilityFlagsReachable;
3231 *haveDNS = FALSE;
3232 }
3233
3234 return ok;
3235 }
3236
3237
3238 static Boolean
3239 check_matching_resolvers(ReachabilityStoreInfoRef store_info,
3240 dns_config_t *dns_config,
3241 const char *fqdn,
3242 unsigned int if_index,
3243 SCNetworkReachabilityFlags *flags,
3244 Boolean *haveDNS,
3245 uint32_t *resolver_if_index,
3246 int *dns_config_index,
3247 const char *log_prefix)
3248 {
3249 int i;
3250 Boolean matched = FALSE;
3251 const char *name = fqdn;
3252 int32_t n_resolvers;
3253 dns_resolver_t **resolvers;
3254
3255 if (if_index == 0) {
3256 n_resolvers = dns_config->n_resolver;
3257 resolvers = dns_config->resolver;
3258 } else {
3259 n_resolvers = dns_config->n_scoped_resolver;
3260 resolvers = dns_config->scoped_resolver;
3261 }
3262
3263 /* In case we couldn't find a match, setting an index of -1
3264 and resolver_if_index 0 */
3265 if (dns_config_index != NULL) *dns_config_index = -1;
3266 if (resolver_if_index != NULL) *resolver_if_index = 0;
3267
3268 while (!matched && (name != NULL)) {
3269 int len;
3270
3271 /*
3272 * check if the provided name (or sub-component)
3273 * matches one of our resolver configurations.
3274 */
3275 len = strlen(name);
3276 for (i = 0; i < n_resolvers; i++) {
3277 char *domain;
3278 dns_resolver_t *resolver;
3279
3280 resolver = resolvers[i];
3281 if ((if_index != 0) && (if_index != resolver->if_index)) {
3282 continue;
3283 }
3284
3285 domain = resolver->domain;
3286 if (domain != NULL && (len == strlen(domain))) {
3287 if (strcasecmp(name, domain) == 0) {
3288 Boolean ok;
3289
3290 /*
3291 * if name matches domain
3292 */
3293 matched = TRUE;
3294 ok = check_resolver_reachability(store_info, resolver, flags, haveDNS,
3295 resolver_if_index, log_prefix);
3296 if (!ok) {
3297 /* not today */
3298 return FALSE;
3299 }
3300 if (dns_config_index != NULL) *dns_config_index = i;
3301 }
3302 }
3303 }
3304
3305 if (!matched) {
3306 /*
3307 * we have not found a matching resolver, try
3308 * a less qualified domain
3309 */
3310 name = strchr(name, '.');
3311 if ((name != NULL) && (*name != '\0')) {
3312 name++;
3313 } else {
3314 name = NULL;
3315 }
3316 }
3317 }
3318
3319 return matched;
3320 }
3321
3322
3323 static dns_resolver_t *
3324 get_default_resolver(dns_config_t *dns_config, unsigned int if_index)
3325 {
3326 int i;
3327 int32_t n_resolvers;
3328 dns_resolver_t *resolver = NULL;
3329 dns_resolver_t **resolvers;
3330
3331 if (if_index == 0) {
3332 n_resolvers = dns_config->n_resolver;
3333 resolvers = dns_config->resolver;
3334 } else {
3335 n_resolvers = dns_config->n_scoped_resolver;
3336 resolvers = dns_config->scoped_resolver;
3337 }
3338
3339 for (i = 0; i < n_resolvers; i++) {
3340 if ((if_index != 0) && (if_index != resolvers[i]->if_index)) {
3341 continue;
3342 }
3343
3344 if (((if_index == 0) && (i == 0)) ||
3345 ((if_index != 0) && (resolver == NULL))) {
3346 // if this is the first (aka default) resolver
3347 resolver = resolvers[i];
3348 } else if ((resolvers[i]->domain == NULL) &&
3349 (resolvers[i]->search_order < resolver->search_order)) {
3350 // if this is a default resolver with a lower search order
3351 resolver = resolvers[i];
3352 }
3353 }
3354
3355 return resolver;
3356 }
3357
3358
3359 static dns_configuration_t *
3360 dns_configuration_retain()
3361 {
3362 pthread_mutex_lock(&dns_lock);
3363
3364 if ((dns_configuration != NULL) && dns_token_valid) {
3365 int check = 0;
3366 uint32_t status;
3367
3368 /*
3369 * check if the global [DNS] configuration snapshot needs
3370 * to be updated
3371 */
3372 status = notify_check(dns_token, &check);
3373 if (status != NOTIFY_STATUS_OK) {
3374 SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%lu"), status);
3375 }
3376
3377 if ((status != NOTIFY_STATUS_OK) || (check != 0)) {
3378 /*
3379 * if the snapshot needs to be refreshed
3380 */
3381 if (dns_configuration->refs == 0) {
3382 dns_configuration_free(dns_configuration->config);
3383 CFAllocatorDeallocate(NULL, dns_configuration);
3384 }
3385 dns_configuration = NULL;
3386 }
3387 }
3388
3389 if (dns_configuration == NULL) {
3390 dns_config_t *new_config;
3391
3392 new_config = dns_configuration_copy();
3393 if (new_config != NULL) {
3394 dns_configuration = CFAllocatorAllocate(NULL, sizeof(dns_configuration_t), 0);
3395 dns_configuration->config = new_config;
3396 dns_configuration->refs = 0;
3397 }
3398 }
3399
3400 if (dns_configuration != NULL) {
3401 dns_configuration->refs++;
3402 }
3403
3404 pthread_mutex_unlock(&dns_lock);
3405 return dns_configuration;
3406 }
3407
3408
3409 static void
3410 dns_configuration_release(dns_configuration_t *config)
3411 {
3412 pthread_mutex_lock(&dns_lock);
3413
3414 config->refs--;
3415 if (config->refs == 0) {
3416 if ((dns_configuration != config)) {
3417 dns_configuration_free(config->config);
3418 CFAllocatorDeallocate(NULL, config);
3419 }
3420 }
3421
3422 pthread_mutex_unlock(&dns_lock);
3423 return;
3424 }
3425
3426
3427 static Boolean
3428 dns_configuration_watch()
3429 {
3430 int dns_check = 0;
3431 const char *dns_key;
3432 Boolean ok = FALSE;
3433 uint32_t status;
3434
3435 pthread_mutex_lock(&dns_lock);
3436
3437 dns_key = dns_configuration_notify_key();
3438 if (dns_key == NULL) {
3439 SCLog(TRUE, LOG_INFO, CFSTR("dns_configuration_notify_key() failed"));
3440 goto done;
3441 }
3442
3443 status = notify_register_check(dns_key, &dns_token);
3444 if (status == NOTIFY_STATUS_OK) {
3445 dns_token_valid = TRUE;
3446 } else {
3447 SCLog(TRUE, LOG_INFO, CFSTR("notify_register_check() failed, status=%lu"), status);
3448 goto done;
3449 }
3450
3451 status = notify_check(dns_token, &dns_check);
3452 if (status != NOTIFY_STATUS_OK) {
3453 SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%lu"), status);
3454 (void)notify_cancel(dns_token);
3455 dns_token_valid = FALSE;
3456 goto done;
3457 }
3458
3459 ok = TRUE;
3460
3461 done :
3462
3463 pthread_mutex_unlock(&dns_lock);
3464 return ok;
3465 }
3466
3467
3468 static void
3469 dns_configuration_unwatch()
3470 {
3471 pthread_mutex_lock(&dns_lock);
3472
3473 (void)notify_cancel(dns_token);
3474 dns_token_valid = FALSE;
3475
3476 if ((dns_configuration != NULL) && (dns_configuration->refs == 0)) {
3477 dns_configuration_free(dns_configuration->config);
3478 CFAllocatorDeallocate(NULL, dns_configuration);
3479 dns_configuration = NULL;
3480 }
3481
3482 pthread_mutex_unlock(&dns_lock);
3483 return;
3484 }
3485
3486
3487 static Boolean
3488 _SC_R_checkResolverReachability(ReachabilityStoreInfoRef store_info,
3489 SCNetworkReachabilityFlags *flags,
3490 Boolean *haveDNS,
3491 const char *nodename,
3492 const char *servname,
3493 unsigned int if_index,
3494 uint32_t *resolver_if_index,
3495 int *dns_config_index,
3496 const char *log_prefix
3497 )
3498 {
3499 dns_resolver_t *default_resolver;
3500 dns_configuration_t *dns;
3501 Boolean found = FALSE;
3502 char *fqdn = (char *)nodename;
3503 int i;
3504 Boolean isFQDN = FALSE;
3505 uint32_t len;
3506 int ndots = 1;
3507 Boolean ok = TRUE;
3508 Boolean useDefault = FALSE;
3509
3510 if (resolver_if_index) *resolver_if_index = 0;
3511 if (dns_config_index) *dns_config_index = -1;
3512
3513 /*
3514 * We first assume that all of the configured DNS servers
3515 * are available. Since we don't know which name server will
3516 * be consulted to resolve the specified nodename we need to
3517 * check the availability of ALL name servers. We can only
3518 * proceed if we know that our query can be answered.
3519 */
3520
3521 *flags = kSCNetworkReachabilityFlagsReachable;
3522 *haveDNS = FALSE;
3523
3524 len = (nodename != NULL) ? strlen(nodename) : 0;
3525 if (len == 0) {
3526 if ((servname == NULL) || (strlen(servname) == 0)) {
3527 // if no nodename or servname, return not reachable
3528 *flags = 0;
3529 }
3530 return ok;
3531 }
3532
3533 dns = dns_configuration_retain();
3534 if (dns == NULL) {
3535 // if error
3536 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no configuration"), log_prefix);
3537 goto done;
3538 }
3539
3540 if (dns->config->n_resolver == 0) {
3541 // if no resolver configuration
3542 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no resolvers"), log_prefix);
3543 goto done;
3544 }
3545
3546 if (fqdn[len - 1] == '.') {
3547 isFQDN = TRUE;
3548
3549 // trim trailing '.''s
3550 while ((len > 0) && (fqdn[len-1] == '.')) {
3551 if (fqdn == nodename) {
3552 fqdn = strdup(nodename);
3553 }
3554 fqdn[--len] = '\0';
3555 }
3556 }
3557
3558 default_resolver = get_default_resolver(dns->config, if_index);
3559
3560 /*
3561 * check if the provided name matches a supplemental domain
3562 */
3563 found = check_matching_resolvers(store_info, dns->config, fqdn, if_index,
3564 flags, haveDNS, resolver_if_index,
3565 dns_config_index, log_prefix);
3566
3567 if (!found && !isFQDN) {
3568 /*
3569 * if we did not match a supplemental domain name and if the
3570 * provided name has enough "."s then the first query will be
3571 * directed to the default resolver.
3572 */
3573 char *cp;
3574 int dots;
3575
3576 #define NDOTS_OPT "ndots="
3577 #define NDOTS_OPT_LEN (sizeof("ndots=") - 1)
3578
3579 if (default_resolver->options != NULL) {
3580 cp = strstr(default_resolver->options, NDOTS_OPT);
3581 if ((cp != NULL) &&
3582 ((cp == default_resolver->options) || isspace(cp[-1])) &&
3583 ((cp[NDOTS_OPT_LEN] != '\0') && isdigit(cp[NDOTS_OPT_LEN]))) {
3584 char *end;
3585 long val;
3586
3587 cp += NDOTS_OPT_LEN;
3588 errno = 0;
3589 val = strtol(cp, &end, 10);
3590 if ((*cp != '\0') && (cp != end) && (errno == 0) &&
3591 ((*end == '\0') || isspace(*end))) {
3592 ndots = val;
3593 }
3594 }
3595 }
3596
3597 dots = 0;
3598 for (cp = fqdn; *cp != '\0'; cp++) {
3599 if (*cp == '.') dots++;
3600 }
3601
3602 if (dots > ndots) {
3603 useDefault = TRUE;
3604 }
3605 }
3606
3607 if (!found && !isFQDN && !useDefault && (dns->config->n_resolver > 1)) {
3608 /*
3609 * FQDN not specified, try matching w/search domains
3610 */
3611 if (default_resolver->n_search > 0) {
3612 for (i = 0; !found && (i < default_resolver->n_search); i++) {
3613 int ret;
3614 char *search_fqdn = NULL;
3615
3616 ret = asprintf(&search_fqdn, "%s.%s", fqdn, default_resolver->search[i]);
3617 if (ret == -1) {
3618 continue;
3619 }
3620
3621 // try the provided name with the search domain appended
3622 found = check_matching_resolvers(store_info,
3623 dns->config,
3624 search_fqdn,
3625 if_index,
3626 flags,
3627 haveDNS,
3628 resolver_if_index,
3629 dns_config_index,
3630 log_prefix);
3631 free(search_fqdn);
3632 }
3633 } else if (default_resolver->domain != NULL) {
3634 char *dp;
3635 int domain_parts = 0;
3636
3637 // count domain parts
3638 for (dp = default_resolver->domain; *dp != '\0'; dp++) {
3639 if (*dp == '.') {
3640 domain_parts++;
3641 }
3642 }
3643
3644 // remove trailing dots
3645 for (dp--; (dp >= default_resolver->domain) && (*dp == '.'); dp--) {
3646 *dp = '\0';
3647 domain_parts--;
3648 }
3649
3650 if (dp >= default_resolver->domain) {
3651 // dots are separators, bump # of components
3652 domain_parts++;
3653 }
3654
3655 dp = default_resolver->domain;
3656 for (i = LOCALDOMAINPARTS; !found && (i <= (domain_parts - ndots)); i++) {
3657 int ret;
3658 char *search_fqdn = NULL;
3659
3660 ret = asprintf(&search_fqdn, "%s.%s", fqdn, dp);
3661 if (ret == -1) {
3662 continue;
3663 }
3664
3665 // try the provided name with the [default] domain appended
3666 found = check_matching_resolvers(store_info,
3667 dns->config,
3668 search_fqdn,
3669 if_index,
3670 flags,
3671 haveDNS,
3672 resolver_if_index,
3673 dns_config_index,
3674 log_prefix);
3675 free(search_fqdn);
3676
3677 // move to the next component of the [default] domain
3678 dp = strchr(dp, '.') + 1;
3679 }
3680 }
3681 }
3682
3683 if (!found) {
3684 /*
3685 * check the reachability of the default resolver
3686 */
3687 ok = check_resolver_reachability(store_info, default_resolver, flags, haveDNS,
3688 resolver_if_index, log_prefix);
3689 if (ok && dns_config_index != NULL) *dns_config_index = 0;
3690 }
3691
3692 if (fqdn != nodename) free(fqdn);
3693
3694 done :
3695
3696 if (dns != NULL) {
3697 dns_configuration_release(dns);
3698 }
3699
3700 return ok;
3701 }
3702
3703
3704 Boolean
3705 _SC_checkResolverReachability(SCDynamicStoreRef *storeP,
3706 SCNetworkReachabilityFlags *flags,
3707 Boolean *haveDNS,
3708 const char *nodename,
3709 const char *servname)
3710 {
3711 Boolean ok;
3712 ReachabilityStoreInfo store_info;
3713
3714 ReachabilityStoreInfo_init(&store_info);
3715 ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC);
3716 if (!ok) {
3717 goto done;
3718 }
3719
3720 ok = _SC_R_checkResolverReachability(&store_info, flags, haveDNS, nodename,
3721 servname, 0, NULL, NULL, "");
3722
3723 done :
3724
3725 ReachabilityStoreInfo_free(&store_info);
3726 return ok;
3727 }
3728
3729 Boolean
3730 __SC_checkResolverReachabilityInternal(SCDynamicStoreRef *storeP,
3731 SCNetworkReachabilityFlags *flags,
3732 Boolean *haveDNS,
3733 const char *nodename,
3734 const char *servname,
3735 uint32_t *resolver_if_index,
3736 int *dns_config_index)
3737 {
3738 Boolean ok;
3739 ReachabilityStoreInfo store_info;
3740
3741 ReachabilityStoreInfo_init(&store_info);
3742 ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC);
3743 if (!ok) {
3744 goto done;
3745 }
3746
3747 ok = _SC_R_checkResolverReachability(&store_info, flags, haveDNS, nodename,
3748 servname, 0, resolver_if_index, dns_config_index, "");
3749
3750 done :
3751
3752 ReachabilityStoreInfo_free(&store_info);
3753 return ok;
3754 }
3755
3756 /*
3757 * _SC_checkResolverReachabilityByAddress()
3758 *
3759 * Given an IP address, determine whether a reverse DNS query can be issued
3760 * using the current network configuration.
3761 */
3762 Boolean
3763 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef *storeP,
3764 SCNetworkReachabilityFlags *flags,
3765 Boolean *haveDNS,
3766 struct sockaddr *sa)
3767 {
3768 int i;
3769 Boolean ok;
3770 char ptr_name[128];
3771 ReachabilityStoreInfo store_info;
3772
3773 ReachabilityStoreInfo_init(&store_info);
3774 ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC);
3775 if (!ok) {
3776 goto done;
3777 }
3778
3779 /*
3780 * Ideally, we would have an API that given a local IP
3781 * address would return the DNS server(s) that would field
3782 * a given PTR query. Fortunately, we do have an SPI which
3783 * which will provide this information given a "name" so we
3784 * take the address, convert it into the inverse query name,
3785 * and find out which servers should be consulted.
3786 */
3787
3788 switch (sa->sa_family) {
3789 case AF_INET : {
3790 union {
3791 in_addr_t s_addr;
3792 unsigned char b[4];
3793 } rev;
3794 /* ALIGN: assuming sa is aligned, then cast ok. */
3795 struct sockaddr_in *sin = (struct sockaddr_in *)(void *)sa;
3796
3797 /*
3798 * build "PTR" query name
3799 * NNN.NNN.NNN.NNN.in-addr.arpa.
3800 */
3801 rev.s_addr = sin->sin_addr.s_addr;
3802 (void) snprintf(ptr_name, sizeof(ptr_name), "%u.%u.%u.%u.in-addr.arpa.",
3803 rev.b[3],
3804 rev.b[2],
3805 rev.b[1],
3806 rev.b[0]);
3807
3808 break;
3809 }
3810
3811 case AF_INET6 : {
3812 int s = 0;
3813 /* ALIGN: assume sa is aligned, cast ok. */
3814 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(void *)sa;
3815 int x = sizeof(ptr_name);
3816 int n;
3817
3818 /*
3819 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
3820 * 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.
3821 */
3822 for (i = sizeof(sin6->sin6_addr) - 1; i >= 0; i--) {
3823 n = snprintf(&ptr_name[s], x, "%x.%x.",
3824 ( sin6->sin6_addr.s6_addr[i] & 0xf),
3825 ((sin6->sin6_addr.s6_addr[i] >> 4) & 0xf));
3826 if ((n == -1) || (n >= x)) {
3827 goto done;
3828 }
3829
3830 s += n;
3831 x -= n;
3832 }
3833
3834 n = snprintf(&ptr_name[s], x, "ip6.arpa.");
3835 if ((n == -1) || (n >= x)) {
3836 goto done;
3837 }
3838
3839 break;
3840 }
3841
3842 default :
3843 goto done;
3844 }
3845
3846 ok = _SC_R_checkResolverReachability(&store_info, flags, haveDNS, ptr_name, NULL, 0, NULL, NULL, "");
3847
3848 done :
3849
3850 ReachabilityStoreInfo_free(&store_info);
3851 return ok;
3852 }
3853
3854
3855 static Boolean
3856 startAsyncDNSQuery(SCNetworkReachabilityRef target)
3857 {
3858 int error = 0;
3859 mach_port_t mp = MACH_PORT_NULL;
3860 Boolean ok;
3861 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3862
3863 __dns_query_start(&targetPrivate->dnsQueryStart, &targetPrivate->dnsQueryEnd);
3864
3865 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3866 if (targetPrivate->if_index == 0) {
3867 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
3868 error = getaddrinfo_async_start(&mp,
3869 targetPrivate->name,
3870 targetPrivate->serv,
3871 &targetPrivate->hints,
3872 __SCNetworkReachabilityCallbackSetResolvedAddress,
3873 (void *)target);
3874 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3875 } else {
3876 mp = _getaddrinfo_interface_async_call(targetPrivate->name,
3877 targetPrivate->serv,
3878 &targetPrivate->hints,
3879 targetPrivate->if_name,
3880 __SCNetworkReachabilityCallbackSetResolvedAddress,
3881 (void *)target);
3882 if (mp == MACH_PORT_NULL) {
3883 error = EAI_SYSTEM;
3884 }
3885 }
3886 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
3887 if (error != 0) {
3888 /* save the error associated with the attempt to resolve the name */
3889 __SCNetworkReachabilityCallbackSetResolvedAddress(error, NULL, (void *)target);
3890 return FALSE;
3891 }
3892
3893 ok = enqueueAsyncDNSQuery(target, mp);
3894 return ok;
3895 }
3896
3897
3898 #pragma mark -
3899
3900
3901 static Boolean
3902 enqueueAsyncDNSRetry(SCNetworkReachabilityRef target)
3903 {
3904 int64_t delay;
3905 dispatch_source_t source;
3906 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3907
3908 MUTEX_ASSERT_HELD(&targetPrivate->lock);
3909
3910 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
3911 0,
3912 0,
3913 __SCNetworkReachability_concurrent_queue());
3914 if (source == NULL) {
3915 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability retry dispatch_source_create() failed"));
3916 return FALSE;
3917 }
3918
3919 // retain the target ... and release it when the [timer] source is released
3920 CFRetain(target);
3921 dispatch_set_context(source, (void *)target);
3922 dispatch_set_finalizer_f(source, (dispatch_function_t)CFRelease);
3923
3924 dispatch_source_set_event_handler(source, ^(void) {
3925 __SCNetworkReachabilityPerformInlineNoLock(target, TRUE);
3926 });
3927
3928 // start a one-shot timer
3929 delay = targetPrivate->dnsRetryCount * EAI_NONAME_RETRY_DELAY_USEC * NSEC_PER_USEC;
3930 dispatch_source_set_timer(source,
3931 dispatch_time(DISPATCH_TIME_NOW, delay), // start
3932 0, // interval
3933 10 * NSEC_PER_MSEC); // leeway
3934
3935 targetPrivate->dnsRetry = source;
3936 dispatch_resume(source);
3937
3938 return TRUE;
3939 }
3940
3941
3942 static void
3943 dequeueAsyncDNSRetry(SCNetworkReachabilityRef target)
3944 {
3945 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3946
3947 MUTEX_ASSERT_HELD(&targetPrivate->lock);
3948
3949 if (targetPrivate->dnsRetry != NULL) {
3950 dispatch_source_cancel(targetPrivate->dnsRetry);
3951 dispatch_release(targetPrivate->dnsRetry);
3952 targetPrivate->dnsRetry = NULL;
3953 }
3954
3955 return;
3956 }
3957
3958
3959 #pragma mark -
3960
3961
3962 static dispatch_queue_t
3963 _llq_queue()
3964 {
3965 static dispatch_once_t once;
3966 static dispatch_queue_t q;
3967
3968 dispatch_once(&once, ^{
3969 q = dispatch_queue_create("SCNetworkReachabilty.longLivedQueries", NULL);
3970 });
3971
3972 return q;
3973 }
3974
3975
3976 /*
3977 * _llq_notify
3978 *
3979 * Called to push out a target's DNS changes
3980 * - caller must be running on the _llq_queue()
3981 */
3982 static void
3983 _llq_notify(const void *value, void *context)
3984 {
3985 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value;
3986 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3987
3988 MUTEX_LOCK(&targetPrivate->lock);
3989
3990 __dns_query_end(target,
3991 (targetPrivate->resolvedAddressError == NETDB_SUCCESS), // if successful query
3992 dns_query_llq, // long-lived-query
3993 &targetPrivate->dnsQueryStart, // start time
3994 &targetPrivate->dnsQueryEnd); // end time
3995
3996 if (targetPrivate->scheduled) {
3997 __SCNetworkReachabilityPerform(target);
3998 }
3999
4000 // last long-lived-query end time is new start time
4001 targetPrivate->dnsQueryStart = targetPrivate->dnsQueryEnd;
4002
4003 MUTEX_UNLOCK(&targetPrivate->lock);
4004 return;
4005 }
4006
4007
4008 /*
4009 * _llq_callback
4010 *
4011 * Called to process mDNSResponder long-lived-query updates
4012 * - caller must be running on the _llq_queue()
4013 */
4014 static void
4015 _llq_callback(DNSServiceRef sdRef,
4016 DNSServiceFlags flags,
4017 uint32_t interfaceIndex,
4018 DNSServiceErrorType errorCode,
4019 const char *hostname,
4020 const struct sockaddr *address,
4021 uint32_t ttl,
4022 void *context)
4023 {
4024 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
4025 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4026
4027 MUTEX_LOCK(&targetPrivate->lock);
4028
4029 if (targetPrivate->llqTimer != NULL) {
4030 dispatch_source_cancel(targetPrivate->llqTimer);
4031 dispatch_release(targetPrivate->llqTimer);
4032 targetPrivate->llqTimer = NULL;
4033 }
4034
4035 switch (errorCode) {
4036 case kDNSServiceErr_NoError :
4037 if (address != NULL) {
4038 CFMutableArrayRef addresses;
4039 CFDataRef llqAddress;
4040
4041 if (targetPrivate->resolvedAddress != NULL) {
4042 if (isA_CFArray(targetPrivate->resolvedAddress)) {
4043 addresses = CFArrayCreateMutableCopy(NULL, 0, targetPrivate->resolvedAddress);
4044 } else {
4045 addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
4046 }
4047
4048 CFRelease(targetPrivate->resolvedAddress);
4049 targetPrivate->resolvedAddress = NULL;
4050 } else {
4051 addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
4052 }
4053
4054 llqAddress = CFDataCreate(NULL, (void *)address, address->sa_len);
4055 if (flags & kDNSServiceFlagsAdd) {
4056 // add address
4057 CFArrayAppendValue(addresses, llqAddress);
4058 } else {
4059 CFIndex i;
4060
4061 // remove address
4062 i = CFArrayGetFirstIndexOfValue(addresses,
4063 CFRangeMake(0, CFArrayGetCount(addresses)),
4064 llqAddress);
4065 if (i != kCFNotFound) {
4066 CFArrayRemoveValueAtIndex(addresses, i);
4067 }
4068 }
4069 CFRelease(llqAddress);
4070
4071 if (CFArrayGetCount(addresses) > 0) {
4072 targetPrivate->resolvedAddress = addresses;
4073 targetPrivate->resolvedAddressError = NETDB_SUCCESS;
4074 } else {
4075 // if host not found
4076 targetPrivate->resolvedAddress = CFRetain(kCFNull);
4077 targetPrivate->resolvedAddressError = EAI_NONAME;
4078 CFRelease(addresses);
4079 }
4080
4081 targetPrivate->needResolve = FALSE;
4082 }
4083 break;
4084 case kDNSServiceErr_NoSuchRecord :
4085 if (address != NULL) {
4086 // no IPv4/IPv6 address for name (NXDOMAIN)
4087 if (targetPrivate->resolvedAddress == NULL) {
4088 targetPrivate->resolvedAddress = CFRetain(kCFNull);
4089 targetPrivate->resolvedAddressError = EAI_NONAME;
4090 }
4091 targetPrivate->needResolve = FALSE;
4092 }
4093 break;
4094 case kDNSServiceErr_Timeout :
4095 if (targetPrivate->resolvedAddress == NULL) {
4096 targetPrivate->resolvedAddress = CFRetain(kCFNull);
4097 targetPrivate->resolvedAddressError = EAI_NONAME;
4098 }
4099 targetPrivate->needResolve = FALSE;
4100 break;
4101 default :
4102 SCLog(TRUE, LOG_ERR,
4103 CFSTR("%sSCNetworkReachability _llq_callback w/error=%d"),
4104 targetPrivate->log_prefix,
4105 errorCode);
4106 break;
4107 }
4108
4109 MUTEX_UNLOCK(&targetPrivate->lock);
4110
4111 // the "more coming" flag applies to DNSService callouts for any/all
4112 // hosts that are being watched so we need to keep track of the targets
4113 // we have updated. When we [finally] have the last callout then we
4114 // push our notifications for all of the updated targets.
4115
4116 if (llqUpdated == NULL) {
4117 llqUpdated = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
4118 }
4119 CFSetAddValue(llqUpdated, target);
4120
4121 if (!(flags & kDNSServiceFlagsMoreComing)) {
4122 CFSetApplyFunction(llqUpdated, _llq_notify, NULL);
4123 CFRelease(llqUpdated);
4124 llqUpdated = NULL;
4125 }
4126
4127 return;
4128 }
4129
4130
4131 static Boolean
4132 enqueueLongLivedQuery(SCNetworkReachabilityRef target)
4133 {
4134 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4135
4136 MUTEX_ASSERT_HELD(&targetPrivate->lock);
4137
4138 if (targetPrivate->serv != NULL) {
4139 // if "serv" provided, can't use DNSServiceGetAddrInfo
4140 return FALSE;
4141 }
4142
4143 if (memcmp(&targetPrivate->hints, &HINTS_DEFAULT, sizeof(targetPrivate->hints)) != 0) {
4144 // non-default "hints" provided, can't use DNSServiceGetAddrInfo
4145 return FALSE;
4146 }
4147
4148 // mark the long lived query active
4149 targetPrivate->llqActive = TRUE;
4150
4151 // track the DNS resolution time
4152 __dns_query_start(&targetPrivate->dnsQueryStart, &targetPrivate->dnsQueryEnd);
4153
4154 CFRetain(target);
4155 dispatch_async(_llq_queue(), ^{
4156 DNSServiceErrorType err;
4157 dispatch_source_t source;
4158
4159 MUTEX_LOCK(&targetPrivate->lock);
4160
4161 if (targetPrivate->llqTarget != NULL) {
4162 // if already running
4163 MUTEX_UNLOCK(&targetPrivate->lock);
4164 CFRelease(target);
4165 return;
4166 }
4167
4168 // if needed, start interacting with mDNSResponder
4169
4170 if (llqMain == NULL) {
4171 err = DNSServiceCreateConnection(&llqMain);
4172 if (err != kDNSServiceErr_NoError) {
4173 SCLog(TRUE, LOG_ERR,
4174 CFSTR("DNSServiceCreateConnection(&llqMain) failed, error = %d"),
4175 err);
4176
4177 targetPrivate->llqActive = FALSE;
4178
4179 MUTEX_UNLOCK(&targetPrivate->lock);
4180 CFRelease(target);
4181 return;
4182 }
4183
4184 err = DNSServiceSetDispatchQueue(llqMain, _llq_queue());
4185 if (err != kDNSServiceErr_NoError) {
4186 SCLog(TRUE, LOG_ERR,
4187 CFSTR("DNSServiceSetDispatchQueue() failed, error = %d"),
4188 err);
4189 DNSServiceRefDeallocate(llqMain);
4190 llqMain = NULL;
4191
4192 targetPrivate->llqActive = FALSE;
4193
4194 MUTEX_UNLOCK(&targetPrivate->lock);
4195 CFRelease(target);
4196 return;
4197 }
4198 }
4199
4200 // start a long-lived-query for this target
4201
4202 targetPrivate->llqTarget = llqMain;
4203 err = DNSServiceGetAddrInfo(&targetPrivate->llqTarget, // sdRef
4204 kDNSServiceFlagsReturnIntermediates // flags
4205 | kDNSServiceFlagsShareConnection,
4206 targetPrivate->if_index, // interfaceIndex
4207 0, // protocol
4208 targetPrivate->name, // hostname
4209 _llq_callback, // callback
4210 (void *)target); // context
4211 if (err != kDNSServiceErr_NoError) {
4212 SCLog(TRUE, LOG_ERR,
4213 CFSTR("DNSServiceGetAddrInfo() failed, error = %d"),
4214 err);
4215 targetPrivate->llqTarget = NULL;
4216 if (llqCount == 0) {
4217 // if this was the first request
4218 DNSServiceRefDeallocate(llqMain);
4219 llqMain = NULL;
4220 }
4221
4222 targetPrivate->llqActive = FALSE;
4223
4224 MUTEX_UNLOCK(&targetPrivate->lock);
4225 CFRelease(target);
4226 return;
4227 }
4228
4229 llqCount++;
4230
4231 // if case we don't get any callbacks from our long-lived-query (this
4232 // could happen if the DNS servers do not respond), we start a timer
4233 // to ensure that we fire off at least one reachability callback.
4234
4235 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
4236 0,
4237 0,
4238 _llq_queue());
4239 if (source != NULL) {
4240 // retain the target ... and release it when the [timer] source is released
4241 CFRetain(target);
4242 dispatch_set_context(source, (void *)target);
4243 dispatch_set_finalizer_f(source, (dispatch_function_t)CFRelease);
4244
4245 dispatch_source_set_event_handler(source, ^(void) {
4246 _llq_callback(NULL, // sdRef
4247 0, // flags
4248 0, // interfaceIndex
4249 kDNSServiceErr_Timeout, // errorCode
4250 NULL, // hostname
4251 NULL, // address
4252 0, // ttl
4253 (void *)target); // context
4254 });
4255
4256 dispatch_source_set_timer(source,
4257 dispatch_time(DISPATCH_TIME_NOW,
4258 LLQ_TIMEOUT_NSEC), // start
4259 0, // interval
4260 10 * NSEC_PER_MSEC); // leeway
4261
4262 targetPrivate->llqTimer = source;
4263 dispatch_resume(source);
4264 } else {
4265 SCLog(TRUE, LOG_ERR,
4266 CFSTR("SCNetworkReachability llq dispatch_source_create(no-reply) failed"));
4267 }
4268
4269 MUTEX_UNLOCK(&targetPrivate->lock);
4270 return;
4271 });
4272
4273 return TRUE;
4274 }
4275
4276
4277 static void
4278 dequeueLongLivedQuery(SCNetworkReachabilityRef target)
4279 {
4280 DNSServiceRef sdRef;
4281 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4282
4283 MUTEX_ASSERT_HELD(&targetPrivate->lock);
4284
4285 // terminate the [target] llq timer
4286 if (targetPrivate->llqTimer != NULL) {
4287 dispatch_source_cancel(targetPrivate->llqTimer);
4288 dispatch_release(targetPrivate->llqTimer);
4289 targetPrivate->llqTimer = NULL;
4290 }
4291
4292 // terminate the [target] long lived query
4293 sdRef = targetPrivate->llqTarget;
4294 targetPrivate->llqTarget = NULL;
4295
4296 // mark the long lived query NOT active
4297 targetPrivate->llqActive = FALSE;
4298
4299 if (sdRef != NULL) {
4300 dispatch_async(_llq_queue(), ^{
4301 DNSServiceRefDeallocate(sdRef);
4302 CFRelease(target);
4303
4304 llqCount--;
4305 if (llqCount == 0) {
4306 // if no more queries active
4307 DNSServiceRefDeallocate(llqMain);
4308 llqMain = NULL;
4309 }
4310 });
4311 }
4312
4313 return;
4314 }
4315
4316
4317 #pragma mark -
4318 #pragma mark OnDemand
4319
4320
4321 SCNetworkServiceRef
4322 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target,
4323 CFDictionaryRef *userOptions)
4324 {
4325 SCNetworkServiceRef service = NULL;
4326 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4327
4328 if (!isA_SCNetworkReachability(target)) {
4329 _SCErrorSet(kSCStatusInvalidArgument);
4330 return NULL;
4331 }
4332
4333 if (targetPrivate->onDemandServiceID != NULL) {
4334 service = _SCNetworkServiceCopyActive(NULL, targetPrivate->onDemandServiceID);
4335 }
4336
4337 if (userOptions != NULL) {
4338 if (targetPrivate->onDemandName != NULL) {
4339 *userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
4340 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, targetPrivate->onDemandName);
4341 } else {
4342 *userOptions = NULL;
4343 }
4344 }
4345
4346 return service;
4347 }
4348
4349
4350 static void
4351 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer,
4352 SCNetworkReachabilityFlags onDemandFlags,
4353 void *info)
4354 {
4355 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
4356 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4357
4358 MUTEX_LOCK(&targetPrivate->lock);
4359
4360 if (!targetPrivate->scheduled) {
4361 // if not currently scheduled
4362 MUTEX_UNLOCK(&targetPrivate->lock);
4363 return;
4364 }
4365
4366 SCLog(_sc_debug, LOG_INFO, CFSTR("%sOnDemand \"server\" status changed"),
4367 targetPrivate->log_prefix);
4368 __SCNetworkReachabilityPerform(target);
4369
4370 MUTEX_UNLOCK(&targetPrivate->lock);
4371
4372 return;
4373 }
4374
4375
4376 static Boolean
4377 __SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef store_info,
4378 SCNetworkReachabilityRef target,
4379 Boolean onDemandRetry,
4380 SCNetworkReachabilityFlags *flags)
4381 {
4382 Boolean ok;
4383 Boolean onDemand = FALSE;
4384 CFStringRef onDemandRemoteAddress = NULL;
4385 CFStringRef onDemandServiceID = NULL;
4386 SCNetworkConnectionStatus onDemandStatus;
4387 SCDynamicStoreRef store;
4388 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4389
4390 MUTEX_ASSERT_HELD(&targetPrivate->lock);
4391
4392 // SCLog(_sc_debug, LOG_INFO,
4393 // CFSTR("%s__SCNetworkReachabilityOnDemandCheck %s"),
4394 // targetPrivate->log_prefix,
4395 // onDemandRetry ? "after" : "before");
4396
4397 if (targetPrivate->onDemandName == NULL) {
4398 targetPrivate->onDemandName = CFStringCreateWithCString(NULL, targetPrivate->name, kCFStringEncodingUTF8);
4399 }
4400
4401 /*
4402 * check if an OnDemand VPN configuration matches the name.
4403 */
4404 store = store_info->store;
4405 ok = __SCNetworkConnectionCopyOnDemandInfoWithName(&store,
4406 targetPrivate->onDemandName,
4407 onDemandRetry,
4408 &onDemandServiceID,
4409 &onDemandStatus,
4410 &onDemandRemoteAddress);
4411 if ((store_info->store == NULL) && (store != NULL)) {
4412 // if an SCDynamicStore session was added, keep it
4413 store_info->store = store;
4414 }
4415 if (!_SC_CFEqual(targetPrivate->onDemandRemoteAddress, onDemandRemoteAddress) ||
4416 !_SC_CFEqual(targetPrivate->onDemandServiceID, onDemandServiceID)) {
4417 if (targetPrivate->onDemandRemoteAddress != NULL) {
4418 CFRelease(targetPrivate->onDemandRemoteAddress);
4419 targetPrivate->onDemandRemoteAddress = NULL;
4420 }
4421
4422 if (targetPrivate->onDemandServer != NULL) {
4423 if (targetPrivate->dispatchQueue != NULL) {
4424 // unschedule
4425 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE);
4426 } else if (targetPrivate->rls != NULL) {
4427 CFIndex i;
4428 CFIndex n;
4429
4430 // unschedule
4431 n = CFArrayGetCount(targetPrivate->rlList);
4432 for (i = 0; i < n; i += 3) {
4433 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
4434 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
4435
4436 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, rl, rlMode, TRUE);
4437 }
4438 }
4439
4440 CFRelease(targetPrivate->onDemandServer);
4441 targetPrivate->onDemandServer = NULL;
4442 }
4443
4444 if (targetPrivate->onDemandServiceID != NULL) {
4445 CFRelease(targetPrivate->onDemandServiceID);
4446 targetPrivate->onDemandServiceID = NULL;
4447 }
4448 }
4449 if (ok) {
4450 if (onDemandStatus != kSCNetworkConnectionConnected) {
4451 /*
4452 * if we have a VPN configuration matching the name *and* we need to
4453 * bring the VPN up. Combine our flags with those of the VPN server.
4454 */
4455 if (targetPrivate->onDemandServer == NULL) {
4456 CFMutableDictionaryRef options;
4457
4458 options = CFDictionaryCreateMutable(NULL,
4459 0,
4460 &kCFTypeDictionaryKeyCallBacks,
4461 &kCFTypeDictionaryValueCallBacks);
4462 CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, onDemandRemoteAddress);
4463 CFDictionarySetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandBypass, kCFBooleanTrue);
4464 #ifdef HAVE_REACHABILITY_SERVER
4465 CFDictionarySetValue(options, kSCNetworkReachabilityOptionServerBypass, kCFBooleanTrue);
4466 #endif // HAVE_REACHABILITY_SERVER
4467 targetPrivate->onDemandServer = SCNetworkReachabilityCreateWithOptions(NULL, options);
4468 CFRelease(options);
4469
4470 if (targetPrivate->scheduled) {
4471 SCNetworkReachabilityContext context = { 0, NULL, CFRetain, CFRelease, CFCopyDescription };
4472
4473 context.info = (void *)target;
4474 SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer,
4475 __SCNetworkReachabilityOnDemandCheckCallback,
4476 &context);
4477
4478 // schedule server reachability to match that of the target
4479 if (targetPrivate->dispatchQueue != NULL) {
4480 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, NULL, NULL, targetPrivate->dispatchQueue, TRUE);
4481 } else {
4482 CFIndex i;
4483 CFIndex n;
4484
4485 n = CFArrayGetCount(targetPrivate->rlList);
4486 for (i = 0; i < n; i += 3) {
4487 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
4488 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
4489
4490 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, rl, rlMode, NULL, TRUE);
4491 }
4492 }
4493 }
4494 }
4495
4496 ok = SCNetworkReachabilityGetFlags(targetPrivate->onDemandServer, flags);
4497 SCLog(_sc_debug, LOG_INFO, CFSTR("%s status * = 0x%08x"),
4498 targetPrivate->log_prefix,
4499 *flags);
4500 if (ok && (*flags & kSCNetworkReachabilityFlagsReachable)) {
4501 if (!(*flags & kSCNetworkReachabilityFlagsTransientConnection)) {
4502 // start clean if not already layered on a transient network
4503 *flags = 0;
4504 }
4505 *flags |= kSCNetworkReachabilityFlagsReachable;
4506 *flags |= kSCNetworkReachabilityFlagsTransientConnection;
4507 *flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4508 *flags |= kSCNetworkReachabilityFlagsConnectionOnDemand;
4509
4510 if (_sc_debug) {
4511 SCLog(TRUE, LOG_INFO, CFSTR("%s service * = %@"),
4512 targetPrivate->log_prefix,
4513 onDemandServiceID);
4514 SCLog(TRUE, LOG_INFO, CFSTR("%s status = isReachable (after OnDemand connect)"),
4515 targetPrivate->log_prefix);
4516 }
4517
4518 onDemand = TRUE;
4519 }
4520 }
4521
4522 if (onDemandRemoteAddress != NULL) {
4523 if (targetPrivate->onDemandRemoteAddress == NULL) {
4524 targetPrivate->onDemandRemoteAddress = onDemandRemoteAddress;
4525 } else {
4526 CFRelease(onDemandRemoteAddress);
4527 }
4528 }
4529
4530 if (onDemandServiceID != NULL) {
4531 if (targetPrivate->onDemandServiceID == NULL) {
4532 targetPrivate->onDemandServiceID = onDemandServiceID;
4533 } else {
4534 CFRelease(onDemandServiceID);
4535 }
4536 }
4537 }
4538
4539 return onDemand;
4540 }
4541
4542
4543 #pragma mark -
4544 #pragma mark Reachability Flags
4545
4546
4547 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
4548 typedef struct {
4549 int status;
4550 struct addrinfo *res;
4551 } reply_info;
4552
4553
4554 static void
4555 reply_callback(int32_t status, struct addrinfo *res, void *context)
4556 {
4557 reply_info *reply = (reply_info *)context;
4558
4559 reply->status = status;
4560 reply->res = res;
4561 return;
4562 }
4563
4564
4565 static int
4566 getaddrinfo_interface_sync(const char *nodename,
4567 const char *servname,
4568 const struct addrinfo *hints,
4569 const char *interface,
4570 struct addrinfo **res)
4571 {
4572 mach_port_t mp;
4573 reply_info reply = { NETDB_SUCCESS, NULL };
4574
4575 mp = _getaddrinfo_interface_async_call(nodename,
4576 servname,
4577 hints,
4578 interface,
4579 reply_callback,
4580 (void *)&reply);
4581 if (mp == MACH_PORT_NULL) {
4582 return EAI_SYSTEM;
4583 }
4584
4585 while (TRUE) {
4586 int g_status;
4587 union {
4588 u_int8_t buf[8192];
4589 mach_msg_empty_rcv_t msg;
4590 } m_reply;
4591 kern_return_t m_status;
4592
4593 m_status = mach_msg(&m_reply.msg.header, /* msg */
4594 MACH_RCV_MSG, /* options */
4595 0, /* send_size */
4596 sizeof(m_reply), /* rcv_size */
4597 mp, /* rcv_name */
4598 MACH_MSG_TIMEOUT_NONE, /* timeout */
4599 MACH_PORT_NULL); /* notify */
4600 if (m_status != KERN_SUCCESS) {
4601 return EAI_SYSTEM;
4602 }
4603
4604 g_status = getaddrinfo_async_handle_reply((void *)m_reply.buf);
4605 if (g_status != 0) {
4606 if (reply.res != NULL) {
4607 freeaddrinfo(reply.res);
4608 reply.res = NULL;
4609 }
4610 return EAI_SYSTEM;
4611 }
4612
4613 if ((reply.res != NULL) || (reply.status != NETDB_SUCCESS)) {
4614 // if we have a reply or an error
4615 break;
4616 }
4617
4618 // if the request is not complete and needs to be re-queued
4619 }
4620
4621 *res = reply.res;
4622 return reply.status;
4623 }
4624 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
4625
4626
4627 static Boolean
4628 __SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef store_info,
4629 SCNetworkReachabilityRef target,
4630 ReachabilityInfo *reach_info,
4631 Boolean async)
4632 {
4633 CFMutableArrayRef addresses = NULL;
4634 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
4635 ReachabilityInfo my_info = NOT_REACHABLE;
4636 Boolean ok = TRUE;
4637
4638 MUTEX_ASSERT_HELD(&targetPrivate->lock);
4639
4640 _reach_set(reach_info, &NOT_REACHABLE, reach_info->cycle);
4641
4642 if (!isA_SCNetworkReachability(target)) {
4643 _SCErrorSet(kSCStatusInvalidArgument);
4644 return FALSE;
4645 }
4646
4647 #ifdef HAVE_REACHABILITY_SERVER
4648 if (!targetPrivate->serverBypass) {
4649 if (!targetPrivate->serverActive) {
4650 ok = __SCNetworkReachabilityServer_targetAdd(target);
4651 if (!ok) {
4652 targetPrivate->serverBypass = TRUE;
4653 }
4654 }
4655
4656 if (targetPrivate->serverActive) {
4657 ok = __SCNetworkReachabilityServer_targetStatus(target);
4658 if (!ok) {
4659 SCLog(TRUE, LOG_DEBUG,
4660 CFSTR("__SCNetworkReachabilityGetFlags _targetStatus() failed"));
4661 _SCErrorSet(kSCStatusFailed);
4662 goto done;
4663 }
4664
4665 targetPrivate->cycle = targetPrivate->serverInfo.cycle;
4666 _reach_set(&my_info, &targetPrivate->serverInfo, targetPrivate->cycle);
4667 goto done;
4668 }
4669 }
4670 #endif // HAVE_REACHABILITY_SERVER
4671
4672 switch (targetPrivate->type) {
4673 case reachabilityTypeAddress :
4674 case reachabilityTypeAddressPair : {
4675 /*
4676 * Check "local" address
4677 */
4678 if (targetPrivate->localAddress != NULL) {
4679 /*
4680 * Check "local" address
4681 */
4682 ok = checkAddress(store_info,
4683 targetPrivate->localAddress,
4684 targetPrivate->if_index,
4685 &my_info,
4686 targetPrivate->log_prefix);
4687 if (!ok) {
4688 goto error; /* not today */
4689 }
4690
4691 if (!(my_info.flags & kSCNetworkReachabilityFlagsIsLocalAddress)) {
4692 goto error; /* not reachable, non-"local" address */
4693 }
4694 }
4695
4696 /*
4697 * Check "remote" address
4698 */
4699 if (targetPrivate->remoteAddress != NULL) {
4700 /*
4701 * in cases where we have "local" and "remote" addresses
4702 * we need to re-initialize the to-be-returned flags.
4703 */
4704 my_info = NOT_REACHABLE;
4705
4706 /*
4707 * Check "remote" address
4708 */
4709 ok = checkAddress(store_info,
4710 targetPrivate->remoteAddress,
4711 targetPrivate->if_index,
4712 &my_info,
4713 targetPrivate->log_prefix);
4714 if (!ok) {
4715 goto error; /* not today */
4716 }
4717 }
4718
4719 break;
4720
4721 }
4722
4723 case reachabilityTypeName : {
4724 struct timeval dnsQueryStart;
4725 struct timeval dnsQueryEnd;
4726 int error;
4727 SCNetworkReachabilityFlags ns_flags;
4728 uint32_t ns_if_index;
4729 struct addrinfo *res;
4730
4731 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
4732 if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
4733 /* if resolved or an error had been detected */
4734 if (!async) {
4735 /* if not an async request */
4736 goto checkResolvedAddress;
4737 } else if (targetPrivate->llqActive) {
4738 /* if long-lived-query active */
4739 goto checkResolvedAddress;
4740 } else if ((targetPrivate->dnsMP == MACH_PORT_NULL) && !targetPrivate->needResolve) {
4741 struct timeval elapsed;
4742 const struct timeval retry_limit = { EAI_NONAME_RETRY_LIMIT_USEC / USEC_PER_SEC,
4743 EAI_NONAME_RETRY_LIMIT_USEC % USEC_PER_SEC };
4744
4745 /*
4746 * if this is an async request (i.e. someone is watching the reachability
4747 * of this target), if no query active, and if no query is needed
4748 */
4749
4750 if ((error != EAI_NONAME)
4751 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
4752 && (error != EAI_NODATA)
4753 #endif
4754 ) {
4755 /* if not "host not found" */
4756 goto checkResolvedAddress;
4757 }
4758
4759 /*
4760 * if our last DNS query returned EAI_NONAME then we
4761 * "may" want to retry.
4762 *
4763 * Specifically, if the [DNS] configuration was updated a while
4764 * back then we'll trust the EAI_NONAME reply. Otherwise, we
4765 * want to try again to ensure that we didn't get caught in a
4766 * race between the time when the configuration was changed and
4767 * when mDNSResponder is really ready to handle the query.
4768 *
4769 * Retry handling details :
4770 *
4771 * Compare the time when the DNS configuration was last changed and
4772 * when our DNS reply was started (->last_dns vs ->dnsQueryStart).
4773 *
4774 * Expected: 0 < last_dns (t1) < dnsQueryStart (t2)
4775 *
4776 * last start end description action
4777 * ==== ===== ==== ================================= ========
4778 * 0 N/A N/A no change, query error no retry
4779 * 0 N/A N/A no change, query complete no retry
4780 * N/A N/A 0 changed, query in-flight or error no retry
4781 * t1 > t2 query started, then [DNS] changed no retry
4782 * t1 == t2 changed & query started together no retry
4783 * t1 < t2 changed, then query started retry
4784 */
4785
4786 if (!timerisset(&targetPrivate->last_dns)) {
4787 /*
4788 * if we have not yet seen a DNS configuration
4789 * change
4790 */
4791 goto checkResolvedAddress;
4792 }
4793
4794 if (!timerisset(&targetPrivate->dnsQueryEnd)) {
4795 /*
4796 * if no query end time (new request in flight)
4797 */
4798 goto checkResolvedAddress;
4799 }
4800
4801 if (timercmp(&targetPrivate->last_dns,
4802 &targetPrivate->dnsQueryStart,
4803 >=)) {
4804 /*
4805 * if our DNS query started and then, a
4806 * short time later, the DNS configuration
4807 * was changed we don't need to retry
4808 * because we will be re-issuing (and not
4809 * retrying) the query.
4810 */
4811 goto checkResolvedAddress;
4812 }
4813
4814 timersub(&targetPrivate->dnsQueryStart,
4815 &targetPrivate->last_dns,
4816 &elapsed);
4817 if (timercmp(&elapsed, &retry_limit, >)) {
4818 /*
4819 * if the DNS query started after mDNSResponder
4820 * had a chance to apply the last configuration
4821 * then we should trust the EAI_NONAME reply.
4822 */
4823 goto checkResolvedAddress;
4824 }
4825
4826 /* retry the DNS query */
4827
4828 if (targetPrivate->dnsRetry != NULL) {
4829 // no need to schedule if we already have a
4830 // retry query in flight
4831 break;
4832 }
4833
4834 targetPrivate->dnsRetryCount++;
4835
4836 SCLog(_sc_debug, LOG_INFO,
4837 CFSTR("%sretry [%d] DNS query for %s%s%s%s%s"),
4838 targetPrivate->log_prefix,
4839 targetPrivate->dnsRetryCount,
4840 targetPrivate->name != NULL ? "name = " : "",
4841 targetPrivate->name != NULL ? targetPrivate->name : "",
4842 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
4843 targetPrivate->serv != NULL ? "serv = " : "",
4844 targetPrivate->serv != NULL ? targetPrivate->serv : "");
4845
4846 enqueueAsyncDNSRetry(target);
4847 break;
4848 }
4849 }
4850
4851 if (!targetPrivate->onDemandBypass) {
4852 Boolean onDemand;
4853
4854 /*
4855 * before we attempt our initial DNS query, check if there is
4856 * an OnDemand configuration that we should be using.
4857 */
4858 onDemand = __SCNetworkReachabilityOnDemandCheck(store_info, target, FALSE, &my_info.flags);
4859 if (onDemand) {
4860 /* if OnDemand connection is needed */
4861 goto done;
4862 }
4863 }
4864
4865 /* check the reachability of the DNS servers */
4866 ok = _SC_R_checkResolverReachability(store_info,
4867 &ns_flags,
4868 &targetPrivate->haveDNS,
4869 targetPrivate->name,
4870 targetPrivate->serv,
4871 targetPrivate->if_index,
4872 &ns_if_index,
4873 NULL,
4874 targetPrivate->log_prefix);
4875 if (!ok) {
4876 /* if we could not get DNS server info */
4877 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server reachability unknown"),
4878 targetPrivate->log_prefix);
4879 goto error;
4880 } else if (rankReachability(ns_flags) < 2) {
4881 /*
4882 * if DNS servers are not (or are no longer) reachable, set
4883 * flags based on the availability of configured (but not
4884 * active) services.
4885 */
4886
4887 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"),
4888 targetPrivate->log_prefix);
4889
4890 ok = checkAddress(store_info,
4891 NULL,
4892 targetPrivate->if_index,
4893 &my_info,
4894 targetPrivate->log_prefix);
4895 if (!ok) {
4896 SCLog(_sc_debug, LOG_INFO, CFSTR("%sNo available networks"),
4897 targetPrivate->log_prefix);
4898 goto error;
4899 }
4900
4901 if (async && targetPrivate->scheduled) {
4902 /*
4903 * return "host not found", set flags appropriately,
4904 * and schedule notification.
4905 */
4906 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NONAME,
4907 NULL,
4908 (void *)target);
4909 my_info.flags |= (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending);
4910
4911 SCLog(_sc_debug, LOG_INFO, CFSTR("%sno DNS servers are reachable"),
4912 targetPrivate->log_prefix);
4913 __SCNetworkReachabilityPerform(target);
4914 }
4915 break;
4916 }
4917
4918 if (targetPrivate->resolverBypass) {
4919 /* if we are not resolving the name,
4920 * set the flags of the resolvers */
4921 my_info.flags = ns_flags;
4922 my_info.if_index = ns_if_index;
4923 break;
4924 }
4925
4926 if (async) {
4927 /* for async requests we return the last known status */
4928 my_info = targetPrivate->info;
4929
4930 if (targetPrivate->dnsMP != MACH_PORT_NULL) {
4931 /* if request already in progress */
4932 SCLog(_sc_debug, LOG_INFO,
4933 CFSTR("%swaiting for DNS reply"),
4934 targetPrivate->log_prefix);
4935 if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
4936 /* updated reachability based on the previous reply */
4937 goto checkResolvedAddress;
4938 }
4939 break;
4940 }
4941
4942 if (targetPrivate->dnsRetry != NULL) {
4943 /* if we already have a "retry" queued */
4944 break;
4945 }
4946
4947 if (targetPrivate->llqActive) {
4948 /* if long-lived-query active */
4949 SCLog(_sc_debug, LOG_INFO,
4950 CFSTR("%swaiting for DNS updates"),
4951 targetPrivate->log_prefix);
4952 if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
4953 /* updated reachability based on the previous reply */
4954 goto checkResolvedAddress;
4955 }
4956 break;
4957 }
4958
4959 if (!targetPrivate->llqBypass) {
4960 SCLog(_sc_debug, LOG_INFO,
4961 CFSTR("%sstart long-lived DNS query for %s%s%s%s%s"),
4962 targetPrivate->log_prefix,
4963 targetPrivate->name != NULL ? "name = " : "",
4964 targetPrivate->name != NULL ? targetPrivate->name : "",
4965 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
4966 targetPrivate->serv != NULL ? "serv = " : "",
4967 targetPrivate->serv != NULL ? targetPrivate->serv : "");
4968
4969 /*
4970 * initiate an long-lived DNS query
4971 */
4972 if (enqueueLongLivedQuery(target)) {
4973 /* request initiated */
4974 break;
4975 }
4976 }
4977
4978 SCLog(_sc_debug, LOG_INFO,
4979 CFSTR("%sstart DNS query for %s%s%s%s%s"),
4980 targetPrivate->log_prefix,
4981 targetPrivate->name != NULL ? "name = " : "",
4982 targetPrivate->name != NULL ? targetPrivate->name : "",
4983 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
4984 targetPrivate->serv != NULL ? "serv = " : "",
4985 targetPrivate->serv != NULL ? targetPrivate->serv : "");
4986
4987 /*
4988 * initiate an async DNS query
4989 */
4990 if (startAsyncDNSQuery(target)) {
4991 /* request initiated */
4992 break;
4993 }
4994
4995 /* if we could not initiate the request, process error */
4996 goto checkResolvedAddress;
4997 }
4998
4999 SCLog(_sc_debug, LOG_INFO,
5000 CFSTR("%scheck DNS for %s%s%s%s%s"),
5001 targetPrivate->log_prefix,
5002 targetPrivate->name != NULL ? "name = " : "",
5003 targetPrivate->name != NULL ? targetPrivate->name : "",
5004 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
5005 targetPrivate->serv != NULL ? "serv = " : "",
5006 targetPrivate->serv != NULL ? targetPrivate->serv : "");
5007
5008 /*
5009 * OK, all of the DNS name servers are available. Let's
5010 * resolve the nodename into an address.
5011 */
5012 __dns_query_start(&dnsQueryStart, &dnsQueryEnd);
5013
5014 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5015 if (targetPrivate->if_index == 0) {
5016 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5017 error = getaddrinfo(targetPrivate->name,
5018 targetPrivate->serv,
5019 &targetPrivate->hints,
5020 &res);
5021 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5022 } else {
5023 error = getaddrinfo_interface_sync(targetPrivate->name,
5024 targetPrivate->serv,
5025 &targetPrivate->hints,
5026 targetPrivate->if_name,
5027 &res);
5028 }
5029 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5030
5031 __dns_query_end(target,
5032 ((error == 0) && (res != NULL)), // if successful query
5033 dns_query_sync, // sync
5034 &dnsQueryStart, // start time
5035 &dnsQueryEnd); // end time
5036
5037 __SCNetworkReachabilitySetResolvedAddress(error, res, target);
5038
5039 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
5040
5041 checkResolvedAddress :
5042
5043 /*
5044 * We first assume that the requested host is NOT available.
5045 * Then, check each address for accessibility and return the
5046 * best status available.
5047 */
5048 my_info = NOT_REACHABLE;
5049
5050 if (isA_CFArray(addresses)) {
5051 CFIndex i;
5052 CFIndex n = CFArrayGetCount(addresses);
5053
5054 for (i = 0; i < n; i++) {
5055 ReachabilityInfo ns_info = NOT_REACHABLE;
5056 struct sockaddr *sa;
5057
5058 sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
5059
5060 ok = checkAddress(store_info,
5061 sa,
5062 targetPrivate->if_index,
5063 &ns_info,
5064 targetPrivate->log_prefix);
5065 if (!ok) {
5066 goto error; /* not today */
5067 }
5068
5069 if (rankReachability(ns_info.flags) > rankReachability(my_info.flags)) {
5070 /* return the best case result */
5071 my_info = ns_info;
5072 if (rankReachability(my_info.flags) == 2) {
5073 /* we're in luck */
5074 break;
5075 }
5076 }
5077 }
5078 } else {
5079 if ((error == EAI_NONAME)
5080 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
5081 || (error == EAI_NODATA)
5082 #endif
5083 ) {
5084 /*
5085 * the target host name could not be resolved
5086 */
5087 if (!targetPrivate->onDemandBypass) {
5088 Boolean onDemand;
5089
5090 /*
5091 * our initial DNS query failed, check again to see if there
5092 * there is an OnDemand configuration that we should be using.
5093 */
5094 onDemand = __SCNetworkReachabilityOnDemandCheck(store_info, target, TRUE, &my_info.flags);
5095 if (onDemand) {
5096 /* if OnDemand connection is needed */
5097 goto done;
5098 }
5099 }
5100
5101 if (!targetPrivate->haveDNS) {
5102 /*
5103 * No DNS servers are defined. Set flags based on
5104 * the availability of configured (but not active)
5105 * services.
5106 */
5107 ok = checkAddress(store_info,
5108 NULL,
5109 targetPrivate->if_index,
5110 &my_info,
5111 targetPrivate->log_prefix);
5112 if (!ok) {
5113 goto error; /* not today */
5114 }
5115
5116 if ((my_info.flags & kSCNetworkReachabilityFlagsReachable) &&
5117 (my_info.flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
5118 /*
5119 * Since we might pick up a set of DNS servers when this connection
5120 * is established, don't reply with a "HOST NOT FOUND" error just yet.
5121 */
5122 break;
5123 }
5124
5125 /* Host not found, not reachable! */
5126 my_info = NOT_REACHABLE;
5127 }
5128 }
5129 }
5130
5131 break;
5132 }
5133 }
5134
5135 done:
5136
5137 _reach_set(reach_info, &my_info, targetPrivate->cycle);
5138
5139 error :
5140
5141 if (addresses != NULL) CFRelease(addresses);
5142 return ok;
5143 }
5144
5145 int
5146 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target)
5147 {
5148 SCNetworkReachabilityFlags flags;
5149 int if_index = -1;
5150 Boolean ok = TRUE;
5151 ReachabilityStoreInfo store_info;
5152 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5153
5154 if (!isA_SCNetworkReachability(target)) {
5155 _SCErrorSet(kSCStatusInvalidArgument);
5156 return if_index;
5157 }
5158
5159 ReachabilityStoreInfo_init(&store_info);
5160
5161 MUTEX_LOCK(&targetPrivate->lock);
5162
5163 if (targetPrivate->scheduled) {
5164 // if being watched, return the last known (and what should be current) status
5165 flags = targetPrivate->info.flags & ~kSCNetworkReachabilityFlagsFirstResolvePending;
5166 goto done;
5167 }
5168
5169
5170 ok = __SCNetworkReachabilityGetFlags(&store_info, target, &targetPrivate->info, FALSE);
5171 flags = targetPrivate->info.flags & ~kSCNetworkReachabilityFlagsFirstResolvePending;
5172
5173 done :
5174
5175 /* Only return the if_index if the connection is reachable not for reachable connection
5176 * required etc ... */
5177 if (ok && rankReachability(flags) == 2) {
5178 if_index = targetPrivate->info.if_index;
5179 }
5180
5181 MUTEX_UNLOCK(&targetPrivate->lock);
5182 ReachabilityStoreInfo_free(&store_info);
5183 return if_index;
5184 }
5185
5186
5187 Boolean
5188 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target,
5189 SCNetworkReachabilityFlags *flags)
5190 {
5191 Boolean ok = TRUE;
5192 ReachabilityStoreInfo store_info;
5193 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5194
5195 if (!isA_SCNetworkReachability(target)) {
5196 _SCErrorSet(kSCStatusInvalidArgument);
5197 return FALSE;
5198 }
5199
5200 ReachabilityStoreInfo_init(&store_info);
5201
5202 MUTEX_LOCK(&targetPrivate->lock);
5203
5204 if (targetPrivate->scheduled) {
5205 // if being watched, return the last known (and what should be current) status
5206 *flags = targetPrivate->info.flags & ~kSCNetworkReachabilityFlagsFirstResolvePending;
5207 goto done;
5208 }
5209
5210
5211 ok = __SCNetworkReachabilityGetFlags(&store_info, target, &targetPrivate->info, FALSE);
5212 *flags = targetPrivate->info.flags & ~kSCNetworkReachabilityFlagsFirstResolvePending;
5213
5214 done :
5215
5216 MUTEX_UNLOCK(&targetPrivate->lock);
5217 ReachabilityStoreInfo_free(&store_info);
5218 return ok;
5219 }
5220
5221
5222 #pragma mark -
5223 #pragma mark Notifications
5224
5225
5226 static void
5227 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store)
5228 {
5229 CFStringRef key;
5230 CFMutableArrayRef keys;
5231 CFStringRef pattern;
5232 CFMutableArrayRef patterns;
5233
5234 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5235 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5236
5237 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
5238 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
5239 kSCDynamicStoreDomainSetup,
5240 kSCEntNetIPv4);
5241 CFArrayAppendValue(keys, key);
5242 CFRelease(key);
5243
5244 // State:/Network/Global/DNS
5245 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
5246 kSCDynamicStoreDomainState,
5247 kSCEntNetDNS);
5248 CFArrayAppendValue(keys, key);
5249 CFRelease(key);
5250
5251 // State:/Network/Global/IPv4 (default route)
5252 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
5253 kSCDynamicStoreDomainState,
5254 kSCEntNetIPv4);
5255 CFArrayAppendValue(keys, key);
5256 CFRelease(key);
5257
5258 // State:/Network/Global/OnDemand
5259 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
5260 kSCDynamicStoreDomainState,
5261 kSCEntNetOnDemand);
5262 CFArrayAppendValue(keys, key);
5263 CFRelease(key);
5264
5265 // Setup: per-service Interface info
5266 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5267 kSCDynamicStoreDomainSetup,
5268 kSCCompAnyRegex,
5269 kSCEntNetInterface);
5270 CFArrayAppendValue(patterns, pattern);
5271 CFRelease(pattern);
5272
5273 // per-service IPv4 info
5274 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5275 kSCDynamicStoreDomainSetup,
5276 kSCCompAnyRegex,
5277 kSCEntNetIPv4);
5278 CFArrayAppendValue(patterns, pattern);
5279 CFRelease(pattern);
5280 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5281 kSCDynamicStoreDomainState,
5282 kSCCompAnyRegex,
5283 kSCEntNetIPv4);
5284 CFArrayAppendValue(patterns, pattern);
5285 CFRelease(pattern);
5286
5287 // per-service IPv6 info
5288 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5289 kSCDynamicStoreDomainSetup,
5290 kSCCompAnyRegex,
5291 kSCEntNetIPv6);
5292 CFArrayAppendValue(patterns, pattern);
5293 CFRelease(pattern);
5294 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5295 kSCDynamicStoreDomainState,
5296 kSCCompAnyRegex,
5297 kSCEntNetIPv6);
5298 CFArrayAppendValue(patterns, pattern);
5299 CFRelease(pattern);
5300
5301 // per-service PPP info (for existence, kSCPropNetPPPDialOnDemand, kSCPropNetPPPStatus)
5302 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5303 kSCDynamicStoreDomainSetup,
5304 kSCCompAnyRegex,
5305 kSCEntNetPPP);
5306 CFArrayAppendValue(patterns, pattern);
5307 CFRelease(pattern);
5308 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5309 kSCDynamicStoreDomainState,
5310 kSCCompAnyRegex,
5311 kSCEntNetPPP);
5312 CFArrayAppendValue(patterns, pattern);
5313 CFRelease(pattern);
5314
5315 #if !TARGET_IPHONE_SIMULATOR
5316 // per-service VPN info (for existence, kSCPropNetVPNStatus)
5317 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5318 kSCDynamicStoreDomainSetup,
5319 kSCCompAnyRegex,
5320 kSCEntNetVPN);
5321 CFArrayAppendValue(patterns, pattern);
5322 CFRelease(pattern);
5323 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5324 kSCDynamicStoreDomainState,
5325 kSCCompAnyRegex,
5326 kSCEntNetVPN);
5327 CFArrayAppendValue(patterns, pattern);
5328 CFRelease(pattern);
5329 #endif // !TARGET_IPHONE_SIMULATOR
5330
5331 // per-service IPSec info (for existence, kSCPropNetIPSecStatus)
5332 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5333 kSCDynamicStoreDomainSetup,
5334 kSCCompAnyRegex,
5335 kSCEntNetIPSec);
5336 CFArrayAppendValue(patterns, pattern);
5337 CFRelease(pattern);
5338 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5339 kSCDynamicStoreDomainState,
5340 kSCCompAnyRegex,
5341 kSCEntNetIPSec);
5342 CFArrayAppendValue(patterns, pattern);
5343 CFRelease(pattern);
5344
5345 #if !TARGET_OS_IPHONE
5346 // State: Power Management Capabilities
5347 key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@%@"),
5348 kSCDynamicStoreDomainState,
5349 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix));
5350 CFArrayAppendValue(keys, key);
5351 CFRelease(key);
5352 #endif // TARGET_OS_IPHONE
5353
5354
5355 // SCDynamicStore key to force posting a reachability change
5356 CFArrayAppendValue(keys, SCNETWORKREACHABILITY_TRIGGER_KEY);
5357
5358 (void)SCDynamicStoreSetNotificationKeys(store, keys, patterns);
5359 CFRelease(keys);
5360 CFRelease(patterns);
5361
5362 return;
5363 }
5364
5365
5366 static dispatch_queue_t
5367 _hn_queue()
5368 {
5369 static dispatch_once_t once;
5370 static dispatch_queue_t q;
5371
5372 dispatch_once(&once, ^{
5373 q = dispatch_queue_create("SCNetworkReachabilty.changes", NULL);
5374 });
5375
5376 return q;
5377 }
5378
5379
5380 static void
5381 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store,
5382 CFArrayRef changedKeys,
5383 void *info)
5384 {
5385 #if !TARGET_OS_IPHONE
5386 Boolean cpuStatusChanged = FALSE;
5387 #endif // !TARGET_OS_IPHONE
5388 Boolean dnsConfigChanged = FALSE;
5389 CFIndex i;
5390 Boolean forcedChange = FALSE;
5391 CFStringRef key;
5392 CFIndex nChanges;
5393 CFIndex nGlobals = 0;
5394 CFIndex nTargets;
5395 Boolean networkConfigChanged = FALSE;
5396 struct timeval now;
5397 #if !TARGET_OS_IPHONE
5398 Boolean powerStatusChanged = FALSE;
5399 #endif // !TARGET_OS_IPHONE
5400 ReachabilityStoreInfo store_info;
5401 const void * targets_q[N_QUICK];
5402 const void ** targets = targets_q;
5403 __block CFSetRef watchers = NULL;
5404
5405 nChanges = CFArrayGetCount(changedKeys);
5406 if (nChanges == 0) {
5407 /* if no changes */
5408 return;
5409 }
5410
5411 /* "something" changed, start fresh */
5412 ReachabilityStoreInfo_save(NULL);
5413
5414 dispatch_sync(_hn_queue(), ^{
5415 /* grab the currently watched targets */
5416 if (hn_targets != NULL) {
5417 watchers = CFSetCreateCopy(NULL, hn_targets);
5418 }
5419 });
5420
5421 nTargets = (watchers != NULL) ? CFSetGetCount(watchers) : 0;
5422 if (nTargets == 0) {
5423 /* if no addresses being monitored */
5424 goto done;
5425 }
5426
5427 /* grab the current time */
5428 (void)gettimeofday(&now, NULL);
5429
5430 #if !TARGET_OS_IPHONE
5431 key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@%@"),
5432 kSCDynamicStoreDomainState,
5433 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix));
5434 if (CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key)) {
5435 CFNumberRef num;
5436
5437 nGlobals++;
5438
5439 num = SCDynamicStoreCopyValue(store, key);
5440 if (num != NULL) {
5441 if (isA_CFNumber(num) &&
5442 CFNumberGetValue(num, kCFNumberSInt32Type, &power_capabilities)) {
5443 static Boolean haveCPU_old = TRUE;
5444 Boolean haveCPU_new;
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 } else {
5460 power_capabilities = kIOPMSytemPowerStateCapabilitiesMask;
5461 }
5462
5463 CFRelease(num);
5464 } else {
5465 power_capabilities = kIOPMSytemPowerStateCapabilitiesMask;
5466 }
5467
5468 powerStatusChanged = TRUE;
5469 }
5470 CFRelease(key);
5471 #endif // !TARGET_OS_IPHONE
5472
5473 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
5474 kSCDynamicStoreDomainState,
5475 kSCEntNetDNS);
5476 if (CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key)) {
5477 nGlobals++;
5478 dnsConfigChanged = TRUE; /* the DNS server(s) have changed */
5479 }
5480 CFRelease(key);
5481
5482 if (CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), SCNETWORKREACHABILITY_TRIGGER_KEY)) {
5483 nGlobals++;
5484 forcedChange = TRUE; /* an SCDynamicStore driven "network" change */
5485 }
5486
5487 if (nChanges > nGlobals) {
5488 networkConfigChanged = TRUE;
5489 }
5490
5491 if (_sc_debug) {
5492 unsigned int changes = 0;
5493 static const char *change_strings[] = {
5494 // with no "power" status change
5495 "",
5496 "network ",
5497 "DNS ",
5498 "network and DNS ",
5499 #if !TARGET_OS_IPHONE
5500 // with "power" status change
5501 "power ",
5502 "network and power ",
5503 "DNS and power ",
5504 "network, DNS, and power ",
5505
5506 // with "power" status change (including CPU "on")
5507 "power* ",
5508 "network and power* ",
5509 "DNS and power* ",
5510 "network, DNS, and power* ",
5511 #endif // !TARGET_OS_IPHONE
5512 };
5513
5514 #if !TARGET_OS_IPHONE
5515 #define PWR 4
5516 if (powerStatusChanged) {
5517 changes |= PWR;
5518 if (cpuStatusChanged) {
5519 changes += PWR;
5520 }
5521 }
5522 #endif // !TARGET_OS_IPHONE
5523
5524 #define DNS 2
5525 if (dnsConfigChanged) {
5526 changes |= DNS;
5527 }
5528
5529 #define NET 1
5530 if (networkConfigChanged) {
5531 changes |= NET;
5532 }
5533
5534 SCLog(TRUE, LOG_INFO,
5535 CFSTR("process %s%sconfiguration change"),
5536 forcedChange ? "[forced] " : "",
5537 change_strings[changes]);
5538 }
5539
5540 ReachabilityStoreInfo_init(&store_info);
5541
5542 if (nTargets > (CFIndex)(sizeof(targets_q) / sizeof(CFTypeRef)))
5543 targets = CFAllocatorAllocate(NULL, nTargets * sizeof(CFTypeRef), 0);
5544 CFSetGetValues(watchers, targets);
5545 for (i = 0; i < nTargets; i++) {
5546 SCNetworkReachabilityRef target = targets[i];
5547 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5548
5549 MUTEX_LOCK(&targetPrivate->lock);
5550
5551 if (dnsConfigChanged) {
5552 targetPrivate->last_dns = now;
5553 targetPrivate->dnsRetryCount = 0;
5554 }
5555
5556 if (networkConfigChanged) {
5557 targetPrivate->last_network = now;
5558 }
5559
5560 #if !TARGET_OS_IPHONE
5561 if (powerStatusChanged) {
5562 targetPrivate->last_power = now;
5563 }
5564 #endif // !TARGET_OS_IPHONE
5565
5566 if (targetPrivate->type == reachabilityTypeName) {
5567 Boolean dnsChanged = dnsConfigChanged;
5568
5569 if (!dnsChanged) {
5570 /*
5571 * if the DNS configuration didn't change we still need to
5572 * check that the DNS servers are accessible.
5573 */
5574 SCNetworkReachabilityFlags ns_flags;
5575 Boolean ok;
5576
5577 /* check the reachability of the DNS servers */
5578 ok = ReachabilityStoreInfo_update(&store_info, &store, AF_UNSPEC);
5579 if (ok) {
5580 ok = _SC_R_checkResolverReachability(&store_info,
5581 &ns_flags,
5582 &targetPrivate->haveDNS,
5583 targetPrivate->name,
5584 targetPrivate->serv,
5585 targetPrivate->if_index,
5586 NULL,
5587 NULL,
5588 targetPrivate->log_prefix);
5589 }
5590
5591 if (!ok) {
5592 /* if we could not get DNS server info */
5593 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server reachability unknown"),
5594 targetPrivate->log_prefix);
5595 dnsChanged = TRUE;
5596 } else if (rankReachability(ns_flags) < 2) {
5597 /*
5598 * if DNS servers are not (or are no longer) reachable, set
5599 * flags based on the availability of configured (but not
5600 * active) services.
5601 */
5602 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"),
5603 targetPrivate->log_prefix);
5604 dnsChanged = TRUE;
5605 }
5606 }
5607
5608 if (dnsChanged) {
5609 if (targetPrivate->dnsMP != MACH_PORT_NULL) {
5610 /* cancel the outstanding DNS query */
5611 SCLog(_sc_debug, LOG_INFO,
5612 CFSTR("%scancel DNS query for %s%s%s%s%s"),
5613 targetPrivate->log_prefix,
5614 targetPrivate->name != NULL ? "name = " : "",
5615 targetPrivate->name != NULL ? targetPrivate->name : "",
5616 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
5617 targetPrivate->serv != NULL ? "serv = " : "",
5618 targetPrivate->serv != NULL ? targetPrivate->serv : "");
5619 dequeueAsyncDNSQuery(target, TRUE);
5620 }
5621
5622 if (targetPrivate->dnsRetry != NULL) {
5623 /* cancel the outstanding DNS retry */
5624 dequeueAsyncDNSRetry(target);
5625 }
5626
5627 /* schedule request to resolve the name again */
5628 targetPrivate->needResolve = TRUE;
5629 }
5630 }
5631
5632 if (forcedChange) {
5633 targetPrivate->cycle++;
5634 }
5635
5636 if (targetPrivate->scheduled) {
5637 __SCNetworkReachabilityPerform(target);
5638 }
5639
5640 MUTEX_UNLOCK(&targetPrivate->lock);
5641 }
5642 if (targets != targets_q) CFAllocatorDeallocate(NULL, targets);
5643
5644 ReachabilityStoreInfo_free(&store_info);
5645
5646 done :
5647
5648 if (watchers != NULL) CFRelease(watchers);
5649 return;
5650 }
5651
5652
5653 #if !TARGET_OS_IPHONE
5654
5655 static Boolean
5656 darkWakeNotify(SCNetworkReachabilityRef target)
5657 {
5658 return FALSE;
5659 }
5660
5661
5662 static Boolean
5663 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities)
5664 {
5665
5666 #define POWER_CAPABILITIES_NEED (kIOPMSystemPowerStateCapabilityCPU \
5667 | kIOPMSystemPowerStateCapabilityNetwork \
5668 | kIOPMSystemPowerStateCapabilityDisk)
5669
5670 if ((power_capabilities & POWER_CAPABILITIES_NEED) != POWER_CAPABILITIES_NEED) {
5671 /*
5672 * we're not awake (from a networking point of view) unless we
5673 * have the CPU, disk, *and* network.
5674 */
5675 return FALSE;
5676 }
5677
5678 if ((power_capabilities & kIOPMSytemPowerStateCapabilitiesMask) == POWER_CAPABILITIES_NEED) {
5679 /*
5680 * if all we have is the CPU, disk, and network than this must
5681 * be a "maintenance" wake.
5682 */
5683 return FALSE;
5684 }
5685
5686 return TRUE;
5687 }
5688
5689 #endif // !TARGET_OS_IPHONE
5690
5691
5692 static void
5693 reachPerform(void *info)
5694 {
5695 void *context_info;
5696 void (*context_release)(const void *);
5697 uint64_t cycle;
5698 Boolean defer = FALSE;
5699 Boolean forced;
5700 Boolean ok;
5701 ReachabilityInfo reach_info = NOT_REACHABLE;
5702 SCNetworkReachabilityCallBack rlsFunction;
5703 ReachabilityStoreInfo store_info;
5704 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
5705 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5706
5707 SCLog(_sc_debug, LOG_INFO, CFSTR("%schecking target reachability"),
5708 targetPrivate->log_prefix);
5709
5710
5711 MUTEX_LOCK(&targetPrivate->lock);
5712
5713 if (targetPrivate->dnsRetry != NULL) {
5714 // cancel DNS retry
5715 dequeueAsyncDNSRetry(target);
5716 }
5717
5718 if (!targetPrivate->scheduled) {
5719 // if not currently scheduled
5720 MUTEX_UNLOCK(&targetPrivate->lock);
5721 return;
5722 }
5723
5724 /* update reachability, notify if status changed */
5725 ReachabilityStoreInfo_init(&store_info);
5726 ok = __SCNetworkReachabilityGetFlags(&store_info, target, &reach_info, TRUE);
5727 ReachabilityStoreInfo_free(&store_info);
5728 if (!ok) {
5729 /* if reachability status not available */
5730 SCLog(_sc_debug, LOG_INFO, CFSTR("%flags not available"),
5731 targetPrivate->log_prefix);
5732 reach_info = NOT_REACHABLE;
5733 }
5734
5735 #if !TARGET_OS_IPHONE
5736 /*
5737 * We want to defer the notification if this is a maintenance wake *and*
5738 * the reachability flags that we would be reporting to the application
5739 * are better than those that we last reported.
5740 */
5741 if (!systemIsAwake(power_capabilities)) {
5742 /* if this is a maintenace wake */
5743 reach_info.sleeping = TRUE;
5744
5745 if (rankReachability(reach_info.flags) >= rankReachability(targetPrivate->info.flags)) {
5746 /*
5747 * don't report the change if the new reachability flags are
5748 * the same or "better"
5749 */
5750 defer = !darkWakeNotify(target);
5751 } else if (!__reach_changed(&targetPrivate->last_notify, &reach_info)) {
5752 /*
5753 * if we have already posted this change
5754 */
5755 defer = !darkWakeNotify(target);
5756 }
5757 }
5758 #endif // !TARGET_OS_IPHONE
5759
5760 cycle = targetPrivate->cycle;
5761 forced = ((cycle != 0) && (targetPrivate->info.cycle != cycle));
5762
5763 if (!forced && !__reach_changed(&targetPrivate->info, &reach_info)) {
5764 if (_sc_debug) {
5765 if (targetPrivate->info.sleeping == reach_info.sleeping) {
5766 SCLog(TRUE, LOG_INFO,
5767 CFSTR("%sflags/interface match (now 0x%08x/%hu%s)"),
5768 targetPrivate->log_prefix,
5769 reach_info.flags,
5770 reach_info.if_index,
5771 reach_info.sleeping ? ", z" : "");
5772 } else {
5773 SCLog(TRUE, LOG_INFO,
5774 CFSTR("%sflags/interface equiv (was 0x%08x/%hu%s, now 0x%08x/%hu%s)"),
5775 targetPrivate->log_prefix,
5776 targetPrivate->info.flags,
5777 targetPrivate->info.if_index,
5778 targetPrivate->info.sleeping ? ", z" : "",
5779 reach_info.flags,
5780 reach_info.if_index,
5781 reach_info.sleeping ? ", z" : "");
5782 }
5783
5784 }
5785 MUTEX_UNLOCK(&targetPrivate->lock);
5786 return;
5787 }
5788
5789 SCLog(_sc_debug, LOG_INFO,
5790 CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s%s"),
5791 targetPrivate->log_prefix,
5792 targetPrivate->info.flags,
5793 targetPrivate->info.if_index,
5794 targetPrivate->info.sleeping ? ", z" : "",
5795 reach_info.flags,
5796 reach_info.if_index,
5797 reach_info.sleeping ? ", z" : "",
5798 defer ? ", deferred" : "",
5799 forced ? ", forced" : "");
5800
5801 /* as needed, defer the notification */
5802 if (defer) {
5803 MUTEX_UNLOCK(&targetPrivate->lock);
5804 return;
5805 }
5806
5807 /* update flags / interface */
5808 _reach_set(&targetPrivate->info, &reach_info, cycle);
5809
5810 /* save last notification info */
5811 _reach_set(&targetPrivate->last_notify, &reach_info, cycle);
5812
5813 /* save last notification time */
5814 (void)gettimeofday(&targetPrivate->last_push, NULL);
5815
5816 /* callout */
5817 rlsFunction = targetPrivate->rlsFunction;
5818 if (targetPrivate->rlsContext.retain != NULL) {
5819 context_info = (void *)(*targetPrivate->rlsContext.retain)(targetPrivate->rlsContext.info);
5820 context_release = targetPrivate->rlsContext.release;
5821 } else {
5822 context_info = targetPrivate->rlsContext.info;
5823 context_release = NULL;
5824 }
5825
5826 MUTEX_UNLOCK(&targetPrivate->lock);
5827
5828 if (rlsFunction != NULL) {
5829 (*rlsFunction)(target,
5830 reach_info.flags & ~kSCNetworkReachabilityFlagsFirstResolvePending,
5831 context_info);
5832 }
5833
5834 if (context_release != NULL) {
5835 (*context_release)(context_info);
5836 }
5837
5838 return;
5839 }
5840
5841
5842 Boolean
5843 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target,
5844 SCNetworkReachabilityCallBack callout,
5845 SCNetworkReachabilityContext *context)
5846 {
5847 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5848
5849 MUTEX_LOCK(&targetPrivate->lock);
5850
5851 if (targetPrivate->rlsContext.release != NULL) {
5852 /* let go of the current context */
5853 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
5854 }
5855
5856 targetPrivate->rlsFunction = callout;
5857 targetPrivate->rlsContext.info = NULL;
5858 targetPrivate->rlsContext.retain = NULL;
5859 targetPrivate->rlsContext.release = NULL;
5860 targetPrivate->rlsContext.copyDescription = NULL;
5861 if (context) {
5862 bcopy(context, &targetPrivate->rlsContext, sizeof(SCNetworkReachabilityContext));
5863 if (context->retain != NULL) {
5864 targetPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
5865 }
5866 }
5867
5868 MUTEX_UNLOCK(&targetPrivate->lock);
5869
5870 return TRUE;
5871 }
5872
5873
5874 static CFStringRef
5875 reachRLSCopyDescription(const void *info)
5876 {
5877 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
5878
5879 return CFStringCreateWithFormat(NULL,
5880 NULL,
5881 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
5882 target);
5883 }
5884
5885
5886 static Boolean
5887 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target,
5888 CFRunLoopRef runLoop,
5889 CFStringRef runLoopMode,
5890 dispatch_queue_t queue,
5891 Boolean onDemand)
5892 {
5893 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
5894 Boolean init = FALSE;
5895 __block Boolean ok = FALSE;
5896
5897 MUTEX_LOCK(&targetPrivate->lock);
5898
5899 if ((targetPrivate->dispatchQueue != NULL) || // if we are already scheduled with a dispatch queue
5900 ((queue != NULL) && targetPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
5901 _SCErrorSet(kSCStatusInvalidArgument);
5902 goto done;
5903 }
5904
5905 #ifdef HAVE_REACHABILITY_SERVER
5906 if (!targetPrivate->serverBypass) {
5907 if (!targetPrivate->serverActive) {
5908 ok = __SCNetworkReachabilityServer_targetAdd(target);
5909 if (!ok) {
5910 targetPrivate->serverBypass = TRUE;
5911 }
5912 }
5913
5914 if (targetPrivate->serverActive) {
5915 if (targetPrivate->scheduled) {
5916 // if already scheduled
5917 goto watch;
5918 }
5919
5920 ok = __SCNetworkReachabilityServer_targetSchedule(target);
5921 if (!ok) {
5922 SCLog(TRUE, LOG_DEBUG,
5923 CFSTR("__SCNetworkReachabilityScheduleWithRunLoop _targetMonitor() failed"));
5924 _SCErrorSet(kSCStatusFailed);
5925 goto done;
5926 }
5927
5928 goto watch;
5929 }
5930 }
5931 #endif // HAVE_REACHABILITY_SERVER
5932
5933 /* schedule the SCNetworkReachability did-something-change handler */
5934
5935 dispatch_sync(_hn_queue(), ^{
5936 ok = FALSE;
5937
5938 if (!onDemand && (hn_store == NULL)) {
5939 /*
5940 * if we are not monitoring any hosts, start watching
5941 */
5942 if (!dns_configuration_watch()) {
5943 // if error
5944 _SCErrorSet(kSCStatusFailed);
5945 return;
5946 }
5947
5948 hn_store = SCDynamicStoreCreate(NULL,
5949 CFSTR("SCNetworkReachability"),
5950 __SCNetworkReachabilityHandleChanges,
5951 NULL);
5952 if (hn_store == NULL) {
5953 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreCreate() failed"));
5954 dns_configuration_unwatch();
5955 return;
5956 }
5957
5958 __SCNetworkReachabilityReachabilitySetNotifications(hn_store);
5959
5960 hn_dispatchQueue = dispatch_queue_create("SCNetworkReachabilty.changes", NULL);
5961 if (hn_dispatchQueue == NULL) {
5962 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed"));
5963 CFRelease(hn_store);
5964 hn_store = NULL;
5965 dns_configuration_unwatch();
5966 _SCErrorSet(kSCStatusFailed);
5967 return;
5968 }
5969
5970 ok = SCDynamicStoreSetDispatchQueue(hn_store, hn_dispatchQueue);
5971 if (!ok) {
5972 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
5973 dispatch_release(hn_dispatchQueue);
5974 hn_dispatchQueue = NULL;
5975 CFRelease(hn_store);
5976 hn_store = NULL;
5977 dns_configuration_unwatch();
5978 return;
5979 }
5980 hn_targets = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
5981
5982 ReachabilityStoreInfo_enable(TRUE);
5983 }
5984
5985 CFSetAddValue(hn_targets, target);
5986
5987 ok = TRUE;
5988 });
5989
5990 if (!ok) {
5991 goto done;
5992 }
5993
5994 #ifdef HAVE_REACHABILITY_SERVER
5995 watch :
5996 #endif // HAVE_REACHABILITY_SERVER
5997
5998 if (!targetPrivate->scheduled) {
5999 CFRunLoopSourceContext context = { 0 // version
6000 , (void *)target // info
6001 , CFRetain // retain
6002 , CFRelease // release
6003 , reachRLSCopyDescription // copyDescription
6004 , CFEqual // equal
6005 , CFHash // hash
6006 , NULL // schedule
6007 , NULL // cancel
6008 , reachPerform // perform
6009 };
6010
6011 if (runLoop != NULL) {
6012 targetPrivate->rls = CFRunLoopSourceCreate(NULL, 0, &context);
6013 targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
6014 }
6015
6016 if (targetPrivate->type == reachabilityTypeName) {
6017 /*
6018 * we're now scheduled so let's ensure that we
6019 * are starting with a clean slate before we
6020 * resolve the name
6021 */
6022 if (targetPrivate->resolvedAddress != NULL) {
6023 CFRelease(targetPrivate->resolvedAddress);
6024 targetPrivate->resolvedAddress = NULL;
6025 }
6026 targetPrivate->resolvedAddressError = NETDB_SUCCESS;
6027 targetPrivate->needResolve = TRUE;
6028 _reach_set(&targetPrivate->info, &NOT_REACHABLE, targetPrivate->info.cycle);
6029 targetPrivate->info.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
6030 #ifdef HAVE_REACHABILITY_SERVER
6031 _reach_set(&targetPrivate->serverInfo, &NOT_REACHABLE, targetPrivate->serverInfo.cycle);
6032 targetPrivate->serverInfo.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
6033 #endif // HAVE_REACHABILITY_SERVER
6034 }
6035
6036 targetPrivate->scheduled = TRUE;
6037
6038 init = TRUE;
6039 }
6040
6041 if (queue != NULL) {
6042 // retain dispatch queue
6043 dispatch_retain(queue);
6044 targetPrivate->dispatchQueue = queue;
6045
6046 //
6047 // We've taken a reference to the client's dispatch_queue and we
6048 // want to hold on to that reference until we've processed any/all
6049 // notifications. To facilitate this we create a group, dispatch
6050 // any notification blocks to via that group, and when the caller
6051 // has told us to stop the notifications (unschedule) we wait for
6052 // the group to empty and use the group's finalizer to release
6053 // our reference to the client's queue.
6054 //
6055
6056 // make sure that we have group to track any async requests
6057 targetPrivate->dispatchGroup = dispatch_group_create();
6058
6059 // retain the target ... and release it when the group is released
6060 CFRetain(target);
6061 dispatch_set_context(targetPrivate->dispatchGroup, (void *)target);
6062 dispatch_set_finalizer_f(targetPrivate->dispatchGroup, (dispatch_function_t)CFRelease);
6063 } else {
6064 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
6065 /*
6066 * if we do not already have host notifications scheduled with
6067 * this runLoop / runLoopMode
6068 */
6069 CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode);
6070
6071 if (targetPrivate->dnsRLS != NULL) {
6072 // if we have an active async DNS query too
6073 CFRunLoopAddSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
6074 }
6075 }
6076
6077 _SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
6078 }
6079
6080 if (init) {
6081 ReachabilityInfo reach_info = NOT_REACHABLE;
6082 ReachabilityStoreInfo store_info;
6083
6084 /*
6085 * if we have yet to schedule SC notifications for this address
6086 * - initialize current reachability status
6087 */
6088 ReachabilityStoreInfo_init(&store_info);
6089 if (__SCNetworkReachabilityGetFlags(&store_info, target, &reach_info, TRUE)) {
6090 /*
6091 * if reachability status available
6092 * - set flags
6093 * - schedule notification to report status via callback
6094 */
6095 #ifdef HAVE_REACHABILITY_SERVER
6096 reach_info.flags |= (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending);
6097 #endif // HAVE_REACHABILITY_SERVER
6098 _reach_set(&targetPrivate->info, &reach_info, targetPrivate->cycle);
6099 __SCNetworkReachabilityPerform(target);
6100 } else {
6101 /* if reachability status not available, async lookup started */
6102 _reach_set(&targetPrivate->info, &NOT_REACHABLE, targetPrivate->cycle);
6103 #ifdef HAVE_REACHABILITY_SERVER
6104 _reach_set(&targetPrivate->serverInfo, &NOT_REACHABLE, targetPrivate->cycle);
6105 #endif // HAVE_REACHABILITY_SERVER
6106 }
6107 ReachabilityStoreInfo_free(&store_info);
6108 }
6109
6110 if (targetPrivate->onDemandServer != NULL) {
6111 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, runLoop, runLoopMode, queue, TRUE);
6112 }
6113
6114 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%sscheduled"),
6115 targetPrivate->log_prefix);
6116
6117 ok = TRUE;
6118
6119 done :
6120
6121 MUTEX_UNLOCK(&targetPrivate->lock);
6122 return ok;
6123 }
6124
6125
6126 static Boolean
6127 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target,
6128 CFRunLoopRef runLoop,
6129 CFStringRef runLoopMode,
6130 Boolean onDemand)
6131 {
6132 dispatch_group_t drainGroup = NULL;
6133 dispatch_queue_t drainQueue = NULL;
6134 CFIndex n = 0;
6135 Boolean ok = FALSE;
6136 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
6137
6138 // hold a reference while we unschedule
6139 CFRetain(target);
6140
6141 MUTEX_LOCK(&targetPrivate->lock);
6142
6143 if (((runLoop == NULL) && (targetPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
6144 ((runLoop != NULL) && (targetPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are not)
6145 _SCErrorSet(kSCStatusInvalidArgument);
6146 goto done;
6147 }
6148
6149 if (!targetPrivate->scheduled) {
6150 // if not currently scheduled
6151 _SCErrorSet(kSCStatusInvalidArgument);
6152 goto done;
6153 }
6154
6155 // unschedule the target specific sources
6156 if (targetPrivate->dispatchQueue != NULL) {
6157 if (targetPrivate->onDemandServer != NULL) {
6158 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE);
6159 }
6160
6161 // save dispatchQueue, release reference when we've queue'd blocks complete, allow re-scheduling
6162 drainGroup = targetPrivate->dispatchGroup;
6163 targetPrivate->dispatchGroup = NULL;
6164 drainQueue = targetPrivate->dispatchQueue;
6165 targetPrivate->dispatchQueue = NULL;
6166 } else {
6167 if (!_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
6168 // if not currently scheduled
6169 _SCErrorSet(kSCStatusInvalidArgument);
6170 goto done;
6171 }
6172
6173 if (targetPrivate->onDemandServer != NULL) {
6174 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, runLoop, runLoopMode, TRUE);
6175 }
6176
6177 n = CFArrayGetCount(targetPrivate->rlList);
6178 if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
6179 // if target is no longer scheduled for this runLoop / runLoopMode
6180 CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode);
6181
6182 if (targetPrivate->dnsRLS != NULL) {
6183 // if we have an active async DNS query too
6184 CFRunLoopRemoveSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
6185 }
6186
6187 if (n == 0) {
6188 // if *all* notifications have been unscheduled
6189 CFRelease(targetPrivate->rlList);
6190 targetPrivate->rlList = NULL;
6191 CFRunLoopSourceInvalidate(targetPrivate->rls);
6192 CFRelease(targetPrivate->rls);
6193 targetPrivate->rls = NULL;
6194 }
6195 }
6196 }
6197
6198 if (n == 0) {
6199 #ifdef HAVE_REACHABILITY_SERVER
6200 //
6201 // Cancel our request for server monitoring
6202 //
6203 if (targetPrivate->serverActive) {
6204 ok = __SCNetworkReachabilityServer_targetUnschedule(target);
6205 if (!ok) {
6206 SCLog(TRUE, LOG_DEBUG,
6207 CFSTR("__SCNetworkReachabilityUnscheduleFromRunLoop _targetMonitor() failed"));
6208 _SCErrorSet(kSCStatusFailed);
6209 }
6210 }
6211 #endif // HAVE_REACHABILITY_SERVER
6212
6213 // if *all* notifications have been unscheduled
6214 targetPrivate->scheduled = FALSE;
6215 }
6216
6217 #ifdef HAVE_REACHABILITY_SERVER
6218 if (targetPrivate->serverActive) {
6219 goto unwatch;
6220 }
6221 #endif // HAVE_REACHABILITY_SERVER
6222
6223 if (n == 0) {
6224 if (targetPrivate->dnsMP != MACH_PORT_NULL) {
6225 // if we have an active async DNS query
6226 dequeueAsyncDNSQuery(target, TRUE);
6227 }
6228
6229 if (targetPrivate->dnsRetry != NULL) {
6230 // if we have an outstanding DNS retry
6231 dequeueAsyncDNSRetry(target);
6232 }
6233
6234 if (targetPrivate->llqActive) {
6235 // if we have a long-lived-query
6236 dequeueLongLivedQuery(target);
6237 }
6238
6239 dispatch_sync(_hn_queue(), ^{
6240 CFSetRemoveValue(hn_targets, target);
6241
6242 if (onDemand) {
6243 return;
6244 }
6245
6246 if (CFSetGetCount(hn_targets) > 0) {
6247 return;
6248 }
6249
6250 // if we are no longer monitoring any targets
6251 SCDynamicStoreSetDispatchQueue(hn_store, NULL);
6252 dispatch_release(hn_dispatchQueue);
6253 hn_dispatchQueue = NULL;
6254 CFRelease(hn_store);
6255 hn_store = NULL;
6256 CFRelease(hn_targets);
6257 hn_targets = NULL;
6258
6259 ReachabilityStoreInfo_enable(FALSE);
6260 ReachabilityStoreInfo_save(NULL);
6261
6262 /*
6263 * until we start monitoring again, ensure that
6264 * any resources associated with tracking the
6265 * DNS configuration have been released.
6266 */
6267 dns_configuration_unwatch();
6268 });
6269 }
6270
6271 #ifdef HAVE_REACHABILITY_SERVER
6272 unwatch :
6273 #endif // HAVE_REACHABILITY_SERVER
6274
6275 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%sunscheduled"),
6276 targetPrivate->log_prefix);
6277
6278 ok = TRUE;
6279
6280 done :
6281
6282 MUTEX_UNLOCK(&targetPrivate->lock);
6283
6284 if (drainGroup != NULL) {
6285 dispatch_group_notify(drainGroup, __SCNetworkReachability_concurrent_queue(), ^{
6286 // release group/queue references
6287 dispatch_release(drainQueue);
6288 dispatch_release(drainGroup); // releases our target reference
6289 });
6290 }
6291
6292 // release our reference
6293 CFRelease(target);
6294
6295 return ok;
6296 }
6297
6298 Boolean
6299 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target,
6300 CFRunLoopRef runLoop,
6301 CFStringRef runLoopMode)
6302 {
6303 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) {
6304 _SCErrorSet(kSCStatusInvalidArgument);
6305 return FALSE;
6306 }
6307
6308 return __SCNetworkReachabilityScheduleWithRunLoop(target, runLoop, runLoopMode, NULL, FALSE);
6309 }
6310
6311 Boolean
6312 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target,
6313 CFRunLoopRef runLoop,
6314 CFStringRef runLoopMode)
6315 {
6316 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) {
6317 _SCErrorSet(kSCStatusInvalidArgument);
6318 return FALSE;
6319 }
6320
6321 return __SCNetworkReachabilityUnscheduleFromRunLoop(target, runLoop, runLoopMode, FALSE);
6322 }
6323
6324 Boolean
6325 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target,
6326 dispatch_queue_t queue)
6327 {
6328 Boolean ok = FALSE;
6329
6330 if (!isA_SCNetworkReachability(target)) {
6331 _SCErrorSet(kSCStatusInvalidArgument);
6332 return FALSE;
6333 }
6334
6335 if (queue != NULL) {
6336 ok = __SCNetworkReachabilityScheduleWithRunLoop(target, NULL, NULL, queue, FALSE);
6337 } else {
6338 ok = __SCNetworkReachabilityUnscheduleFromRunLoop(target, NULL, NULL, FALSE);
6339 }
6340
6341 return ok;
6342 }