]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkReachability.c
configd-293.4.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkReachability.c
1 /*
2 * Copyright (c) 2003-2009 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 * March 31, 2004 Allan Nathanson <ajn@apple.com>
28 * - use [SC] DNS configuration information
29 *
30 * January 19, 2003 Allan Nathanson <ajn@apple.com>
31 * - add advanced reachability APIs
32 */
33
34 #include <Availability.h>
35 #include <TargetConditionals.h>
36 #include <sys/cdefs.h>
37 #if !TARGET_OS_IPHONE
38 #include <dispatch/dispatch.h>
39 #endif // !TARGET_OS_IPHONE
40 #include <CoreFoundation/CoreFoundation.h>
41 #include <CoreFoundation/CFRuntime.h>
42 #include <SystemConfiguration/SystemConfiguration.h>
43 #include <SystemConfiguration/SCValidation.h>
44 #include <SystemConfiguration/SCPrivate.h>
45 #include <pthread.h>
46 #include <libkern/OSAtomic.h>
47
48 #if !TARGET_OS_IPHONE
49 #include <IOKit/pwr_mgt/IOPMLibPrivate.h>
50 #endif // !TARGET_OS_IPHONE
51
52 #include <notify.h>
53 #include <dnsinfo.h>
54 #include <netinet/in.h>
55 #include <arpa/inet.h>
56 #include <netdb.h>
57 #include <netdb_async.h>
58 #include <resolv.h>
59 #include <unistd.h>
60 #include <sys/ioctl.h>
61 #include <sys/socket.h>
62 #include <net/if.h>
63 #include <net/if_dl.h>
64 #include <net/if_types.h>
65 #define KERNEL_PRIVATE
66 #include <net/route.h>
67 #undef KERNEL_PRIVATE
68
69 #ifndef s6_addr16
70 #define s6_addr16 __u6_addr.__u6_addr16
71 #endif
72
73 #include <ppp/ppp_msg.h>
74
75
76
77 #define kSCNetworkReachabilityFlagsFirstResolvePending (1<<31)
78
79
80 #define N_QUICK 32
81
82
83 typedef enum {
84 reachabilityTypeAddress,
85 reachabilityTypeAddressPair,
86 reachabilityTypeName
87 } addressType;
88
89
90 static CFStringRef __SCNetworkReachabilityCopyDescription (CFTypeRef cf);
91 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf);
92 static void rlsPerform(void *info);
93
94
95 static Boolean
96 __SCNetworkReachabilityScheduleWithRunLoop (SCNetworkReachabilityRef target,
97 CFRunLoopRef runLoop,
98 CFStringRef runLoopMode,
99 #if !TARGET_OS_IPHONE
100 dispatch_queue_t queue,
101 #else // !TARGET_OS_IPHONE
102 void *queue,
103 #endif // !TARGET_OS_IPHONE
104 Boolean onDemand);
105 static Boolean
106 __SCNetworkReachabilityUnscheduleFromRunLoop (SCNetworkReachabilityRef target,
107 CFRunLoopRef runLoop,
108 CFStringRef runLoopMode,
109 Boolean onDemand);
110
111
112 typedef struct {
113 SCNetworkReachabilityFlags flags;
114 uint16_t if_index;
115 Boolean sleeping;
116 } ReachabilityInfo;
117
118
119 typedef struct {
120
121 /* base CFType information */
122 CFRuntimeBase cfBase;
123
124 /* lock */
125 pthread_mutex_t lock;
126
127 /* address type */
128 addressType type;
129
130 /* target host name */
131 const char *name;
132 const char *serv;
133 struct addrinfo hints;
134 Boolean needResolve;
135 CFArrayRef resolvedAddress; /* CFArray[CFData] */
136 int resolvedAddressError;
137
138 /* local & remote addresses */
139 struct sockaddr *localAddress;
140 struct sockaddr *remoteAddress;
141
142 /* current reachability flags */
143 ReachabilityInfo info;
144 ReachabilityInfo last_notify;
145
146 /* run loop source, callout, context, rl scheduling info */
147 Boolean scheduled;
148 CFRunLoopSourceRef rls;
149 SCNetworkReachabilityCallBack rlsFunction;
150 SCNetworkReachabilityContext rlsContext;
151 CFMutableArrayRef rlList;
152
153 #if !TARGET_OS_IPHONE
154 dispatch_queue_t dispatchQueue; // SCNetworkReachabilitySetDispatchQueue
155 dispatch_queue_t asyncDNSQueue;
156 dispatch_source_t asyncDNSSource;
157 #endif // !TARGET_OS_IPHONE
158
159 /* [async] DNS query info */
160 Boolean haveDNS;
161 mach_port_t dnsMP;
162 CFMachPortRef dnsPort;
163 CFRunLoopSourceRef dnsRLS;
164 struct timeval dnsQueryStart;
165
166 /* on demand info */
167 Boolean onDemandBypass;
168 CFStringRef onDemandName;
169 CFStringRef onDemandRemoteAddress;
170 SCNetworkReachabilityRef onDemandServer;
171 CFStringRef onDemandServiceID;
172
173 /* logging */
174 char log_prefix[32];
175
176 } SCNetworkReachabilityPrivate, *SCNetworkReachabilityPrivateRef;
177
178
179 static CFTypeID __kSCNetworkReachabilityTypeID = _kCFRuntimeNotATypeID;
180
181
182 static const CFRuntimeClass __SCNetworkReachabilityClass = {
183 0, // version
184 "SCNetworkReachability", // className
185 NULL, // init
186 NULL, // copy
187 __SCNetworkReachabilityDeallocate, // dealloc
188 NULL, // equal
189 NULL, // hash
190 NULL, // copyFormattingDesc
191 __SCNetworkReachabilityCopyDescription // copyDebugDesc
192 };
193
194
195 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
196 static ReachabilityInfo NOT_REACHABLE = { 0, 0, FALSE };
197 static ReachabilityInfo NOT_REPORTED = { 0xFFFFFFFF, 0, FALSE };
198 static int rtm_seq = 0;
199
200
201 #if !TARGET_OS_IPHONE
202 /*
203 * Power capabilities (sleep/wake)
204 */
205 static IOPMSystemPowerStateCapabilities power_capabilities = kIOPMSytemPowerStateCapabilitiesMask;
206 #endif // !TARGET_OS_IPHONE
207
208
209 /*
210 * host "something has changed" notifications
211 */
212
213 static pthread_mutex_t hn_lock = PTHREAD_MUTEX_INITIALIZER;
214 static SCDynamicStoreRef hn_store = NULL;
215 #if !TARGET_OS_IPHONE
216 static dispatch_queue_t hn_dispatchQueue = NULL;
217 #else // !TARGET_OS_IPHONE
218 static CFRunLoopSourceRef hn_storeRLS = NULL;
219 static CFMutableArrayRef hn_rlList = NULL;
220 #endif // !TARGET_OS_IPHONE
221 static CFMutableSetRef hn_targets = NULL;
222
223
224 /*
225 * DNS configuration
226 */
227
228 typedef struct {
229 dns_config_t *config;
230 int refs;
231 } dns_configuration_t;
232
233
234 static pthread_mutex_t dns_lock = PTHREAD_MUTEX_INITIALIZER;
235 static dns_configuration_t *dns_configuration = NULL;
236 static int dns_token;
237 static Boolean dns_token_valid = FALSE;
238
239
240 static __inline__ CFTypeRef
241 isA_SCNetworkReachability(CFTypeRef obj)
242 {
243 return (isA_CFType(obj, SCNetworkReachabilityGetTypeID()));
244 }
245
246
247 static void
248 __log_query_time(SCNetworkReachabilityRef target, Boolean found, Boolean async, struct timeval *start)
249 {
250 struct timeval dnsQueryComplete;
251 struct timeval dnsQueryElapsed;
252 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
253
254 if (!_sc_debug) {
255 return;
256 }
257
258 if (start->tv_sec == 0) {
259 return;
260 }
261
262 (void) gettimeofday(&dnsQueryComplete, NULL);
263 timersub(&dnsQueryComplete, start, &dnsQueryElapsed);
264 SCLog(TRUE, LOG_INFO,
265 CFSTR("%s%ssync DNS complete%s (query time = %d.%3.3d)"),
266 targetPrivate->log_prefix,
267 async ? "a" : "",
268 found ? "" : ", host not found",
269 dnsQueryElapsed.tv_sec,
270 dnsQueryElapsed.tv_usec / 1000);
271
272 return;
273 }
274
275
276 static int
277 updatePPPStatus(SCDynamicStoreRef *storeP,
278 const struct sockaddr *sa,
279 const char *if_name,
280 SCNetworkReachabilityFlags *flags,
281 CFStringRef *ppp_server,
282 const char *log_prefix)
283 {
284 CFDictionaryRef dict = NULL;
285 CFStringRef entity;
286 CFIndex i;
287 const void * keys_q[N_QUICK];
288 const void ** keys = keys_q;
289 CFIndex n;
290 CFStringRef ppp_if;
291 int sc_status = kSCStatusReachabilityUnknown;
292 SCDynamicStoreRef store = *storeP;
293 const void * values_q[N_QUICK];
294 const void ** values = values_q;
295
296 switch (sa->sa_family) {
297 case AF_INET :
298 entity = kSCEntNetIPv4;
299 break;
300 case AF_INET6 :
301 entity = kSCEntNetIPv6;
302 break;
303 default :
304 goto done;
305 }
306
307 if (store == NULL) {
308 store = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkReachability"), NULL, NULL);
309 if (store == NULL) {
310 SCLog(TRUE, LOG_ERR, CFSTR("updatePPPStatus SCDynamicStoreCreate() failed"));
311 goto done;
312 }
313 *storeP = store;
314 }
315
316 // grab a snapshot of the PPP configuration from the dynamic store
317 {
318 CFStringRef pattern;
319 CFMutableArrayRef patterns;
320
321 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
322 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
323 kSCDynamicStoreDomainState,
324 kSCCompAnyRegex,
325 entity);
326 CFArrayAppendValue(patterns, pattern);
327 CFRelease(pattern);
328 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
329 kSCDynamicStoreDomainSetup,
330 kSCCompAnyRegex,
331 kSCEntNetPPP);
332 CFArrayAppendValue(patterns, pattern);
333 CFRelease(pattern);
334 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
335 kSCDynamicStoreDomainState,
336 kSCCompAnyRegex,
337 kSCEntNetPPP);
338 CFArrayAppendValue(patterns, pattern);
339 CFRelease(pattern);
340 dict = SCDynamicStoreCopyMultiple(store, NULL, patterns);
341 CFRelease(patterns);
342 }
343 if (dict == NULL) {
344 // if we could not access the dynamic store
345 goto done;
346 }
347
348 sc_status = kSCStatusOK;
349
350 // look for the service which matches the provided interface
351 n = CFDictionaryGetCount(dict);
352 if (n <= 0) {
353 goto done;
354 }
355
356 ppp_if = CFStringCreateWithCStringNoCopy(NULL,
357 if_name,
358 kCFStringEncodingASCII,
359 kCFAllocatorNull);
360
361 if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
362 keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
363 values = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
364 }
365 CFDictionaryGetKeysAndValues(dict, keys, values);
366
367 for (i=0; i < n; i++) {
368 CFArrayRef components;
369 CFStringRef key;
370 CFNumberRef num;
371 CFDictionaryRef p_setup;
372 CFDictionaryRef p_state;
373 int32_t ppp_demand;
374 int32_t ppp_status;
375 CFStringRef service = NULL;
376 CFStringRef s_key = (CFStringRef) keys[i];
377 CFDictionaryRef s_dict = (CFDictionaryRef)values[i];
378 CFStringRef s_if;
379
380 if (!isA_CFString(s_key) || !isA_CFDictionary(s_dict)) {
381 continue;
382 }
383
384 if (!CFStringHasSuffix(s_key, entity)) {
385 continue; // if not an IPv4 or IPv6 entity
386 }
387
388 s_if = CFDictionaryGetValue(s_dict, kSCPropInterfaceName);
389 if (!isA_CFString(s_if)) {
390 continue; // if no interface
391 }
392
393 if (!CFEqual(ppp_if, s_if)) {
394 continue; // if not this interface
395 }
396
397 // extract the service ID, get the PPP "state" entity for
398 // the "Status", and get the PPP "setup" entity for the
399 // the "DialOnDemand" flag
400 components = CFStringCreateArrayBySeparatingStrings(NULL, s_key, CFSTR("/"));
401 if (CFArrayGetCount(components) != 5) {
402 CFRelease(components);
403 break;
404 }
405 service = CFArrayGetValueAtIndex(components, 3);
406 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
407 kSCDynamicStoreDomainState,
408 service,
409 kSCEntNetPPP);
410 p_state = CFDictionaryGetValue(dict, key);
411 CFRelease(key);
412 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
413 kSCDynamicStoreDomainSetup,
414 service,
415 kSCEntNetPPP);
416 p_setup = CFDictionaryGetValue(dict, key);
417 CFRelease(key);
418 CFRelease(components);
419
420 // ensure that this is a PPP service
421 if (!isA_CFDictionary(p_state)) {
422 break;
423 }
424
425 *flags |= kSCNetworkReachabilityFlagsTransientConnection;
426
427 // get PPP server
428 if (ppp_server != NULL) {
429 *ppp_server = CFDictionaryGetValue(s_dict, CFSTR("ServerAddress"));
430 *ppp_server = isA_CFString(*ppp_server);
431 if (*ppp_server != NULL) {
432 CFRetain(*ppp_server);
433 }
434 }
435
436 // get PPP status
437 if (!CFDictionaryGetValueIfPresent(p_state,
438 kSCPropNetPPPStatus,
439 (const void **)&num) ||
440 !isA_CFNumber(num) ||
441 !CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_status)) {
442 break;
443 }
444 switch (ppp_status) {
445 case PPP_RUNNING :
446 // if we're really UP and RUNNING
447 break;
448 case PPP_ONHOLD :
449 // if we're effectively UP and RUNNING
450 break;
451 case PPP_IDLE :
452 case PPP_STATERESERVED :
453 // if we're not connected at all
454 SCLog(_sc_debug, LOG_INFO, CFSTR("%s PPP link idle, dial-on-traffic to connect"),
455 log_prefix);
456 *flags |= kSCNetworkReachabilityFlagsConnectionRequired;
457 break;
458 default :
459 // if we're in the process of [dis]connecting
460 SCLog(_sc_debug, LOG_INFO, CFSTR("%s PPP link, connection in progress"),
461 log_prefix);
462 *flags |= kSCNetworkReachabilityFlagsConnectionRequired;
463 break;
464 }
465
466 // get PPP dial-on-traffic status
467 if (isA_CFDictionary(p_setup) &&
468 CFDictionaryGetValueIfPresent(p_setup,
469 kSCPropNetPPPDialOnDemand,
470 (const void **)&num) &&
471 isA_CFNumber(num) &&
472 CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand) &&
473 (ppp_demand != 0)) {
474 *flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic;
475 if (ppp_status == PPP_IDLE) {
476 *flags |= kSCNetworkReachabilityFlagsInterventionRequired;
477 }
478 }
479
480 break;
481 }
482
483 CFRelease(ppp_if);
484 if (keys != keys_q) {
485 CFAllocatorDeallocate(NULL, keys);
486 CFAllocatorDeallocate(NULL, values);
487 }
488
489 done :
490
491 if (dict != NULL) CFRelease(dict);
492 return sc_status;
493 }
494
495
496 static int
497 updatePPPAvailable(SCDynamicStoreRef *storeP,
498 const struct sockaddr *sa,
499 SCNetworkReachabilityFlags *flags,
500 const char *log_prefix)
501 {
502 CFDictionaryRef dict = NULL;
503 CFStringRef entity;
504 CFIndex i;
505 const void * keys_q[N_QUICK];
506 const void ** keys = keys_q;
507 CFIndex n;
508 int sc_status = kSCStatusReachabilityUnknown;
509 SCDynamicStoreRef store = *storeP;
510 const void * values_q[N_QUICK];
511 const void ** values = values_q;
512
513 if (sa == NULL) {
514 entity = kSCEntNetIPv4;
515 } else {
516 switch (sa->sa_family) {
517 case AF_INET :
518 entity = kSCEntNetIPv4;
519 break;
520 case AF_INET6 :
521 entity = kSCEntNetIPv6;
522 break;
523 default :
524 goto done;
525 }
526 }
527
528 if (store == NULL) {
529 store = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkReachability"), NULL, NULL);
530 if (store == NULL) {
531 SCLog(TRUE, LOG_ERR, CFSTR("updatePPPAvailable SCDynamicStoreCreate() failed"));
532 goto done;
533 }
534 *storeP = store;
535 }
536
537 // grab a snapshot of the PPP configuration from the dynamic store
538 {
539 CFStringRef pattern;
540 CFMutableArrayRef patterns;
541
542 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
543 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
544 kSCDynamicStoreDomainSetup,
545 kSCCompAnyRegex,
546 entity);
547 CFArrayAppendValue(patterns, pattern);
548 CFRelease(pattern);
549 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
550 kSCDynamicStoreDomainSetup,
551 kSCCompAnyRegex,
552 kSCEntNetInterface);
553 CFArrayAppendValue(patterns, pattern);
554 CFRelease(pattern);
555 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
556 kSCDynamicStoreDomainSetup,
557 kSCCompAnyRegex,
558 kSCEntNetPPP);
559 CFArrayAppendValue(patterns, pattern);
560 CFRelease(pattern);
561 dict = SCDynamicStoreCopyMultiple(store, NULL, patterns);
562 CFRelease(patterns);
563 }
564 if (dict == NULL) {
565 // if we could not access the dynamic store
566 goto done;
567 }
568
569 sc_status = kSCStatusOK;
570
571 // look for an available service which will provide connectivity
572 // for the requested address family.
573 n = CFDictionaryGetCount(dict);
574 if (n <= 0) {
575 goto done;
576 }
577
578 if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
579 keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
580 values = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
581 }
582 CFDictionaryGetKeysAndValues(dict, keys, values);
583
584 for (i = 0; i < n; i++) {
585 CFArrayRef components;
586 Boolean found = FALSE;
587 CFStringRef i_key;
588 CFDictionaryRef i_dict;
589 CFStringRef p_key;
590 CFDictionaryRef p_dict;
591 CFStringRef service;
592 CFStringRef s_key = (CFStringRef) keys[i];
593 CFDictionaryRef s_dict = (CFDictionaryRef)values[i];
594
595 if (!isA_CFString(s_key) || !isA_CFDictionary(s_dict)) {
596 continue;
597 }
598
599 if (!CFStringHasSuffix(s_key, entity)) {
600 continue; // if not an IPv4 or IPv6 entity
601 }
602
603 // extract service ID
604 components = CFStringCreateArrayBySeparatingStrings(NULL, s_key, CFSTR("/"));
605 if (CFArrayGetCount(components) != 5) {
606 CFRelease(components);
607 continue;
608 }
609 service = CFArrayGetValueAtIndex(components, 3);
610
611 // check for [non-VPN] PPP entity
612 p_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
613 kSCDynamicStoreDomainSetup,
614 service,
615 kSCEntNetPPP);
616 p_dict = CFDictionaryGetValue(dict, p_key);
617 CFRelease(p_key);
618
619 i_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
620 kSCDynamicStoreDomainSetup,
621 service,
622 kSCEntNetInterface);
623 i_dict = CFDictionaryGetValue(dict, i_key);
624 CFRelease(i_key);
625
626 if (isA_CFDictionary(p_dict) &&
627 isA_CFDictionary(i_dict) &&
628 CFDictionaryContainsKey(i_dict, kSCPropNetInterfaceDeviceName)) {
629 CFNumberRef num;
630
631 // we have a PPP service for this address family
632 found = TRUE;
633
634 *flags |= kSCNetworkReachabilityFlagsReachable;
635 *flags |= kSCNetworkReachabilityFlagsTransientConnection;
636 *flags |= kSCNetworkReachabilityFlagsConnectionRequired;
637
638 // get PPP dial-on-traffic status
639 num = CFDictionaryGetValue(p_dict, kSCPropNetPPPDialOnDemand);
640 if (isA_CFNumber(num)) {
641 int32_t ppp_demand;
642
643 if (CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand)) {
644 if (ppp_demand) {
645 *flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic;
646 }
647 }
648 }
649
650 if (_sc_debug) {
651 SCLog(TRUE, LOG_INFO, CFSTR("%s status = isReachable (after connect)"),
652 log_prefix);
653 SCLog(TRUE, LOG_INFO, CFSTR("%s service = %@"),
654 log_prefix,
655 service);
656 }
657
658 }
659
660 CFRelease(components);
661
662 if (found) {
663 break;
664 }
665 }
666
667 if (keys != keys_q) {
668 CFAllocatorDeallocate(NULL, keys);
669 CFAllocatorDeallocate(NULL, values);
670 }
671
672 done :
673
674 if (dict != NULL) CFRelease(dict);
675 return sc_status;
676 }
677
678
679 static int
680 updateIPSecStatus(SCDynamicStoreRef *storeP,
681 const struct sockaddr *sa,
682 const char *if_name,
683 SCNetworkReachabilityFlags *flags,
684 CFStringRef *ipsec_server)
685 {
686 CFDictionaryRef dict = NULL;
687 CFStringRef entity;
688 CFIndex i;
689 CFStringRef ipsec_if;
690 const void * keys_q[N_QUICK];
691 const void ** keys = keys_q;
692 CFIndex n;
693 int sc_status = kSCStatusReachabilityUnknown;
694 SCDynamicStoreRef store = *storeP;
695 const void * values_q[N_QUICK];
696 const void ** values = values_q;
697
698 switch (sa->sa_family) {
699 case AF_INET :
700 entity = kSCEntNetIPv4;
701 break;
702 case AF_INET6 :
703 entity = kSCEntNetIPv6;
704 break;
705 default :
706 goto done;
707 }
708
709 if (store == NULL) {
710 store = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkReachability"), NULL, NULL);
711 if (store == NULL) {
712 SCLog(TRUE, LOG_ERR, CFSTR("updateIPSecStatus SCDynamicStoreCreate() failed"));
713 goto done;
714 }
715 *storeP = store;
716 }
717
718 // grab a snapshot of the IPSec configuration from the dynamic store
719 {
720 CFStringRef pattern;
721 CFMutableArrayRef patterns;
722
723 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
724 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
725 kSCDynamicStoreDomainState,
726 kSCCompAnyRegex,
727 entity);
728 CFArrayAppendValue(patterns, pattern);
729 CFRelease(pattern);
730 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
731 kSCDynamicStoreDomainSetup,
732 kSCCompAnyRegex,
733 kSCEntNetIPSec);
734 CFArrayAppendValue(patterns, pattern);
735 CFRelease(pattern);
736 dict = SCDynamicStoreCopyMultiple(store, NULL, patterns);
737 CFRelease(patterns);
738 }
739 if (dict == NULL) {
740 // if we could not access the dynamic store
741 goto done;
742 }
743
744 sc_status = kSCStatusOK;
745
746 // look for the service which matches the provided interface
747 n = CFDictionaryGetCount(dict);
748 if (n <= 0) {
749 goto done;
750 }
751
752 ipsec_if = CFStringCreateWithCStringNoCopy(NULL,
753 if_name,
754 kCFStringEncodingASCII,
755 kCFAllocatorNull);
756
757 if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
758 keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
759 values = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
760 }
761 CFDictionaryGetKeysAndValues(dict, keys, values);
762
763 for (i=0; i < n; i++) {
764 CFArrayRef components;
765 CFStringRef key;
766 CFDictionaryRef i_setup;
767 CFStringRef service = NULL;
768 CFStringRef s_key = (CFStringRef) keys[i];
769 CFDictionaryRef s_dict = (CFDictionaryRef)values[i];
770 CFStringRef s_if;
771
772 if (!isA_CFString(s_key) || !isA_CFDictionary(s_dict)) {
773 continue;
774 }
775
776 if (!CFStringHasSuffix(s_key, entity)) {
777 continue; // if not an IPv4 or IPv6 entity
778 }
779
780 s_if = CFDictionaryGetValue(s_dict, kSCPropInterfaceName);
781 if (!isA_CFString(s_if)) {
782 continue; // if no interface
783 }
784
785 if (!CFEqual(ipsec_if, s_if)) {
786 continue; // if not this interface
787 }
788
789 // extract the service ID and get the IPSec "setup" entity
790 // to confirm that we're looking at what we're expecting
791 components = CFStringCreateArrayBySeparatingStrings(NULL, s_key, CFSTR("/"));
792 if (CFArrayGetCount(components) != 5) {
793 CFRelease(components);
794 break;
795 }
796 service = CFArrayGetValueAtIndex(components, 3);
797 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
798 kSCDynamicStoreDomainSetup,
799 service,
800 kSCEntNetIPSec);
801 i_setup = CFDictionaryGetValue(dict, key);
802 CFRelease(key);
803 CFRelease(components);
804
805 // ensure that this is an IPSec service
806 if (!isA_CFDictionary(i_setup)) {
807 break;
808 }
809
810 *flags |= kSCNetworkReachabilityFlagsTransientConnection;
811
812 // get IPSec server
813 if (ipsec_server != NULL) {
814 *ipsec_server = CFDictionaryGetValue(s_dict, CFSTR("ServerAddress"));
815 *ipsec_server = isA_CFString(*ipsec_server);
816 if (*ipsec_server != NULL) {
817 CFRetain(*ipsec_server);
818 }
819 }
820
821 break;
822 }
823
824 CFRelease(ipsec_if);
825 if (keys != keys_q) {
826 CFAllocatorDeallocate(NULL, keys);
827 CFAllocatorDeallocate(NULL, values);
828 }
829
830 done :
831
832 if (dict != NULL) CFRelease(dict);
833 return sc_status;
834 }
835
836
837
838
839 #define ROUNDUP(a, size) \
840 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
841
842 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
843 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
844 sizeof(uint32_t)) :\
845 sizeof(uint32_t)))
846
847 static void
848 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
849 {
850 int i;
851
852 for (i = 0; i < RTAX_MAX; i++) {
853 if (addrs & (1 << i)) {
854 rti_info[i] = sa;
855 NEXT_SA(sa);
856 } else
857 rti_info[i] = NULL;
858 }
859 }
860
861
862 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
863
864
865 typedef struct {
866 char buf[BUFLEN];
867 int error;
868 struct sockaddr *rti_info[RTAX_MAX];
869 struct rt_msghdr *rtm;
870 struct sockaddr_dl *sdl;
871 } route_info, *route_info_p;
872
873
874 /*
875 * route_get()
876 * returns zero if route exists an data returned, EHOSTUNREACH
877 * if no route, or errno for any other error.
878 */
879 static int
880 route_get(const struct sockaddr *address,
881 route_info *info)
882 {
883 int n;
884 pid_t pid = getpid();
885 int rsock;
886 struct sockaddr *sa;
887 int32_t seq = OSAtomicIncrement32Barrier(&rtm_seq);
888 #ifndef RTM_GET_SILENT
889 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
890 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
891 int sosize = 48 * 1024;
892 #endif
893
894 bzero(info, sizeof(*info));
895
896 info->rtm = (struct rt_msghdr *)&info->buf;
897 info->rtm->rtm_msglen = sizeof(struct rt_msghdr);
898 info->rtm->rtm_version = RTM_VERSION;
899 #ifdef RTM_GET_SILENT
900 info->rtm->rtm_type = RTM_GET_SILENT;
901 #else
902 info->rtm->rtm_type = RTM_GET;
903 #endif
904 info->rtm->rtm_flags = RTF_STATIC|RTF_UP|RTF_HOST|RTF_GATEWAY;
905 info->rtm->rtm_addrs = RTA_DST|RTA_IFP; /* Both destination and device */
906 info->rtm->rtm_pid = pid;
907 info->rtm->rtm_seq = seq;
908
909 switch (address->sa_family) {
910 case AF_INET6: {
911 struct sockaddr_in6 *sin6;
912
913 sin6 = (struct sockaddr_in6 *)address;
914 if ((IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
915 IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) &&
916 (sin6->sin6_scope_id != 0)) {
917 sin6->sin6_addr.s6_addr16[1] = htons(sin6->sin6_scope_id);
918 sin6->sin6_scope_id = 0;
919 }
920 break;
921 }
922 }
923
924 sa = (struct sockaddr *) (info->rtm + 1);
925 bcopy(address, sa, address->sa_len);
926 n = ROUNDUP(sa->sa_len, sizeof(uint32_t));
927 info->rtm->rtm_msglen += n;
928
929 info->sdl = (struct sockaddr_dl *) ((void *)sa + n);
930 info->sdl->sdl_family = AF_LINK;
931 info->sdl->sdl_len = sizeof (struct sockaddr_dl);
932 n = ROUNDUP(info->sdl->sdl_len, sizeof(uint32_t));
933 info->rtm->rtm_msglen += n;
934
935 #ifndef RTM_GET_SILENT
936 pthread_mutex_lock(&lock);
937 #endif
938 rsock = socket(PF_ROUTE, SOCK_RAW, 0);
939 if (rsock == -1) {
940 int error = errno;
941
942 #ifndef RTM_GET_SILENT
943 pthread_mutex_unlock(&lock);
944 #endif
945 SCLog(TRUE, LOG_ERR, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error));
946 return error;
947 }
948
949 #ifndef RTM_GET_SILENT
950 if (setsockopt(rsock, SOL_SOCKET, SO_RCVBUF, &sosize, sizeof(sosize)) == -1) {
951 int error = errno;
952
953 (void)close(rsock);
954 pthread_mutex_unlock(&lock);
955 SCLog(TRUE, LOG_ERR, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error));
956 return error;
957 }
958 #endif
959
960 if (write(rsock, &info->buf, info->rtm->rtm_msglen) == -1) {
961 int error = errno;
962
963 (void)close(rsock);
964 #ifndef RTM_GET_SILENT
965 pthread_mutex_unlock(&lock);
966 #endif
967 if (error != ESRCH) {
968 SCLog(TRUE, LOG_ERR, CFSTR("write() failed: %s"), strerror(error));
969 return error;
970 }
971 return EHOSTUNREACH;
972 }
973
974 /*
975 * Type, seq, pid identify our response.
976 * Routing sockets are broadcasters on input.
977 */
978 do {
979 int n;
980
981 n = read(rsock, (void *)&info->buf, sizeof(info->buf));
982 if (n == -1) {
983 if (errno != EINTR) {
984 int error = errno;
985
986 (void)close(rsock);
987 #ifndef RTM_GET_SILENT
988 pthread_mutex_unlock(&lock);
989 #endif
990 SCLog(TRUE, LOG_ERR, CFSTR("read() failed: %s"), strerror(error));
991 return error;
992 }
993 }
994 } while ((info->rtm->rtm_type != RTM_GET) ||
995 (info->rtm->rtm_seq != seq) ||
996 (info->rtm->rtm_pid != pid));
997
998 (void)close(rsock);
999 #ifndef RTM_GET_SILENT
1000 pthread_mutex_unlock(&lock);
1001 #endif
1002
1003 get_rtaddrs(info->rtm->rtm_addrs, sa, info->rti_info);
1004
1005 #ifdef LOG_RTADDRS
1006 {
1007 int i;
1008
1009 SCLog(_sc_debug, LOG_DEBUG, CFSTR("rtm_flags = 0x%8.8x"), info->rtm->rtm_flags);
1010
1011 if ((info->rti_info[RTAX_NETMASK] != NULL) && (info->rti_info[RTAX_DST] != NULL)) {
1012 info->rti_info[RTAX_NETMASK]->sa_family = info->rti_info[RTAX_DST]->sa_family;
1013 }
1014
1015 for (i = 0; i < RTAX_MAX; i++) {
1016 if (info->rti_info[i] != NULL) {
1017 char addr[128];
1018
1019 _SC_sockaddr_to_string(info->rti_info[i], addr, sizeof(addr));
1020 SCLog(_sc_debug, LOG_DEBUG, CFSTR("%d: %s"), i, addr);
1021 }
1022 }
1023 }
1024 #endif /* LOG_RTADDRS */
1025
1026 if ((info->rti_info[RTAX_IFP] == NULL) ||
1027 (info->rti_info[RTAX_IFP]->sa_family != AF_LINK)) {
1028 /* no interface info */
1029 SCLog(TRUE, LOG_DEBUG, CFSTR("route_get() no interface info"));
1030 return EINVAL;
1031 }
1032
1033 info->sdl = (struct sockaddr_dl *) info->rti_info[RTAX_IFP];
1034 if ((info->sdl->sdl_nlen == 0) || (info->sdl->sdl_nlen > IFNAMSIZ)) {
1035 /* no interface name */
1036 return EHOSTUNREACH;
1037 }
1038
1039 return 0;
1040 }
1041
1042
1043 static Boolean
1044 checkAddress(SCDynamicStoreRef *storeP,
1045 const struct sockaddr *address,
1046 ReachabilityInfo *reach_info,
1047 const char *log_prefix)
1048 {
1049 route_info info;
1050 struct ifreq ifr;
1051 char if_name[IFNAMSIZ + 1];
1052 int isock = -1;
1053 int ret;
1054 int sc_status = kSCStatusReachabilityUnknown;
1055 CFStringRef server = NULL;
1056 char *statusMessage = NULL;
1057 struct sockaddr_in v4mapped;
1058
1059 *reach_info = NOT_REACHABLE;
1060
1061 if (address == NULL) {
1062 /* special case: check only for available paths off the system */
1063 goto checkAvailable;
1064 }
1065
1066 switch (address->sa_family) {
1067 case AF_INET :
1068 case AF_INET6 :
1069 if (_sc_debug) {
1070 char addr[128];
1071
1072 _SC_sockaddr_to_string(address, addr, sizeof(addr));
1073 SCLog(TRUE, LOG_INFO, CFSTR("%scheckAddress(%s)"),
1074 log_prefix,
1075 addr);
1076 }
1077 break;
1078 default :
1079 /*
1080 * if no code for this address family (yet)
1081 */
1082 SCLog(TRUE, LOG_INFO,
1083 CFSTR("checkAddress(): unexpected address family %d"),
1084 address->sa_family);
1085 sc_status = kSCStatusInvalidArgument;
1086 goto done;
1087 }
1088
1089 if (address->sa_family == AF_INET6) {
1090 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)address;
1091
1092 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1093 bzero(&v4mapped, sizeof(v4mapped));
1094 v4mapped.sin_len = sizeof(v4mapped);
1095 v4mapped.sin_family = AF_INET;
1096 v4mapped.sin_port = sin6->sin6_port;
1097 v4mapped.sin_addr.s_addr = sin6->sin6_addr.__u6_addr.__u6_addr32[3];
1098 address = (struct sockaddr *)&v4mapped;
1099 }
1100 }
1101
1102 ret = route_get(address, &info);
1103 switch (ret) {
1104 case 0 :
1105 break;
1106 case EHOSTUNREACH :
1107 // if no route
1108 goto checkAvailable;
1109 default :
1110 // if error
1111 sc_status = ret;
1112 goto done;
1113 }
1114
1115 /* get the interface flags */
1116
1117 isock = socket(AF_INET, SOCK_DGRAM, 0);
1118 if (isock == -1) {
1119 SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
1120 goto done;
1121 }
1122
1123 bzero(&ifr, sizeof(ifr));
1124 bcopy(info.sdl->sdl_data, ifr.ifr_name, info.sdl->sdl_nlen);
1125
1126 if (ioctl(isock, SIOCGIFFLAGS, (char *)&ifr) == -1) {
1127 SCLog(TRUE, LOG_ERR, CFSTR("ioctl() failed: %s"), strerror(errno));
1128 goto done;
1129 }
1130
1131 if (!(ifr.ifr_flags & IFF_UP)) {
1132 goto checkAvailable;
1133 }
1134
1135 statusMessage = "isReachable";
1136 reach_info->flags |= kSCNetworkReachabilityFlagsReachable;
1137
1138 if (info.rtm->rtm_flags & RTF_LOCAL) {
1139 statusMessage = "isReachable (is a local address)";
1140 reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1141 } else if (ifr.ifr_flags & IFF_LOOPBACK) {
1142 statusMessage = "isReachable (is loopback network)";
1143 reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1144 } else if ((info.rti_info[RTAX_IFA] != NULL) &&
1145 (info.rti_info[RTAX_IFA]->sa_family != AF_LINK)) {
1146 void *addr1 = (void *)address;
1147 void *addr2 = (void *)info.rti_info[RTAX_IFA];
1148 size_t len = address->sa_len;
1149
1150 if ((address->sa_family != info.rti_info[RTAX_IFA]->sa_family) &&
1151 (address->sa_len != info.rti_info[RTAX_IFA]->sa_len)) {
1152 SCLog(TRUE, LOG_NOTICE,
1153 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
1154 address->sa_family,
1155 address->sa_len,
1156 info.rti_info[RTAX_IFA]->sa_family,
1157 info.rti_info[RTAX_IFA]->sa_len);
1158 goto done;
1159 }
1160
1161 switch (address->sa_family) {
1162 case AF_INET :
1163 addr1 = &((struct sockaddr_in *)address)->sin_addr;
1164 addr2 = &((struct sockaddr_in *)info.rti_info[RTAX_IFA])->sin_addr;
1165 len = sizeof(struct in_addr);
1166
1167 /*
1168 * check if 0.0.0.0
1169 */
1170 if (((struct sockaddr_in *)address)->sin_addr.s_addr == 0) {
1171 statusMessage = "isReachable (this host)";
1172 reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1173 }
1174 break;
1175 case AF_INET6 :
1176 addr1 = &((struct sockaddr_in6 *)address)->sin6_addr;
1177 addr2 = &((struct sockaddr_in6 *)info.rti_info[RTAX_IFA])->sin6_addr;
1178 len = sizeof(struct in6_addr);
1179 break;
1180 default :
1181 break;
1182 }
1183
1184 if (bcmp(addr1, addr2, len) == 0) {
1185 statusMessage = "isReachable (is interface address)";
1186 reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1187 }
1188 }
1189
1190 if (!(info.rtm->rtm_flags & RTF_GATEWAY) &&
1191 (info.rti_info[RTAX_GATEWAY] != NULL) &&
1192 (info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) &&
1193 !(ifr.ifr_flags & IFF_POINTOPOINT)) {
1194 reach_info->flags |= kSCNetworkReachabilityFlagsIsDirect;
1195 }
1196
1197 bzero(&if_name, sizeof(if_name));
1198 bcopy(info.sdl->sdl_data,
1199 if_name,
1200 (info.sdl->sdl_nlen <= IFNAMSIZ) ? info.sdl->sdl_nlen : IFNAMSIZ);
1201
1202 reach_info->if_index = info.sdl->sdl_index;
1203
1204 if (_sc_debug) {
1205 SCLog(TRUE, LOG_INFO, CFSTR("%s status = %s"), log_prefix, statusMessage);
1206 SCLog(TRUE, LOG_INFO, CFSTR("%s device = %s (%hu)"), log_prefix, if_name, info.sdl->sdl_index);
1207 SCLog(TRUE, LOG_INFO, CFSTR("%s sdl_type = 0x%x"), log_prefix, info.sdl->sdl_type);
1208 SCLog(TRUE, LOG_INFO, CFSTR("%s ifr_flags = 0x%04hx"), log_prefix, ifr.ifr_flags);
1209 SCLog(TRUE, LOG_INFO, CFSTR("%s rtm_flags = 0x%08x"), log_prefix, info.rtm->rtm_flags);
1210 }
1211
1212 sc_status = kSCStatusOK;
1213
1214 if (ifr.ifr_flags & IFF_POINTOPOINT) {
1215 reach_info->flags |= kSCNetworkReachabilityFlagsTransientConnection;
1216 }
1217
1218 if (info.sdl->sdl_type == IFT_PPP) {
1219 /*
1220 * 1. check if PPP service
1221 * 2. check for dial-on-demand PPP link that is not yet connected
1222 * 3. get PPP server address
1223 */
1224 sc_status = updatePPPStatus(storeP, address, if_name, &reach_info->flags, &server, log_prefix);
1225 } else if (info.sdl->sdl_type == IFT_OTHER) {
1226 /*
1227 * 1. check if IPSec service
1228 * 2. get IPSec server address
1229 */
1230 sc_status = updateIPSecStatus(storeP, address, if_name, &reach_info->flags, &server);
1231 }
1232
1233
1234 goto done;
1235
1236 checkAvailable :
1237
1238
1239 sc_status = updatePPPAvailable(storeP, address, &reach_info->flags, log_prefix);
1240
1241 done :
1242
1243 if (reach_info->flags == 0) {
1244 SCLog(_sc_debug, LOG_INFO, CFSTR("%s cannot be reached"), log_prefix);
1245 }
1246
1247 if (isock != -1) (void)close(isock);
1248 if (server != NULL) CFRelease(server);
1249 if (sc_status != kSCStatusOK) {
1250 _SCErrorSet(sc_status);
1251 return FALSE;
1252 }
1253
1254 return TRUE;
1255 }
1256
1257
1258 #pragma mark -
1259 #pragma mark SCNetworkReachability APIs
1260
1261
1262 static CFStringRef
1263 __SCNetworkReachabilityCopyDescription(CFTypeRef cf)
1264 {
1265 CFAllocatorRef allocator = CFGetAllocator(cf);
1266 CFMutableStringRef result;
1267 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)cf;
1268
1269 result = CFStringCreateMutable(allocator, 0);
1270 CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkReachability %p [%p]> {"), cf, allocator);
1271 switch (targetPrivate->type) {
1272 case reachabilityTypeAddress :
1273 case reachabilityTypeAddressPair : {
1274 char buf[64];
1275
1276 if (targetPrivate->localAddress != NULL) {
1277 _SC_sockaddr_to_string(targetPrivate->localAddress, buf, sizeof(buf));
1278 CFStringAppendFormat(result, NULL, CFSTR("local address = %s"),
1279 buf);
1280 }
1281
1282 if (targetPrivate->remoteAddress != NULL) {
1283 _SC_sockaddr_to_string(targetPrivate->remoteAddress, buf, sizeof(buf));
1284 CFStringAppendFormat(result, NULL, CFSTR("%s%saddress = %s"),
1285 targetPrivate->localAddress ? ", " : "",
1286 (targetPrivate->type == reachabilityTypeAddressPair) ? "remote " : "",
1287 buf);
1288 }
1289 break;
1290 }
1291 case reachabilityTypeName : {
1292 if ((targetPrivate->name != NULL)) {
1293 CFStringAppendFormat(result, NULL, CFSTR("name = %s"), targetPrivate->name);
1294 }
1295 if ((targetPrivate->serv != NULL)) {
1296 CFStringAppendFormat(result, NULL, CFSTR("%sserv = %s"),
1297 targetPrivate->name != NULL ? ", " : "",
1298 targetPrivate->serv);
1299 }
1300 if ((targetPrivate->resolvedAddress != NULL) || (targetPrivate->resolvedAddressError != NETDB_SUCCESS)) {
1301 if (targetPrivate->resolvedAddress != NULL) {
1302 if (isA_CFArray(targetPrivate->resolvedAddress)) {
1303 CFIndex i;
1304 CFIndex n = CFArrayGetCount(targetPrivate->resolvedAddress);
1305
1306 CFStringAppendFormat(result, NULL, CFSTR(" ("));
1307 for (i = 0; i < n; i++) {
1308 CFDataRef address;
1309 char buf[64];
1310 struct sockaddr *sa;
1311
1312 address = CFArrayGetValueAtIndex(targetPrivate->resolvedAddress, i);
1313 sa = (struct sockaddr *)CFDataGetBytePtr(address);
1314 _SC_sockaddr_to_string(sa, buf, sizeof(buf));
1315 CFStringAppendFormat(result, NULL, CFSTR("%s%s"),
1316 i > 0 ? ", " : "",
1317 buf);
1318 }
1319 CFStringAppendFormat(result, NULL, CFSTR(")"));
1320 } else {
1321 CFStringAppendFormat(result, NULL, CFSTR(" (no addresses)"));
1322 }
1323 } else {
1324 CFStringAppendFormat(result, NULL, CFSTR(" (%s)"),
1325 gai_strerror(targetPrivate->resolvedAddressError));
1326 }
1327 } else if (targetPrivate->dnsPort != NULL) {
1328 CFStringAppendFormat(result, NULL, CFSTR(" (DNS query active)"));
1329 }
1330 break;
1331 }
1332 }
1333 if (targetPrivate->scheduled) {
1334 CFStringAppendFormat(result,
1335 NULL,
1336 CFSTR(", flags = 0x%08x, if_index = %hu"),
1337 targetPrivate->info.flags,
1338 targetPrivate->info.if_index);
1339 }
1340 CFStringAppendFormat(result, NULL, CFSTR("}"));
1341
1342 return result;
1343 }
1344
1345
1346 static void
1347 __SCNetworkReachabilityDeallocate(CFTypeRef cf)
1348 {
1349 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)cf;
1350
1351 /* release resources */
1352
1353 pthread_mutex_destroy(&targetPrivate->lock);
1354
1355 if (targetPrivate->name != NULL)
1356 CFAllocatorDeallocate(NULL, (void *)targetPrivate->name);
1357
1358 if (targetPrivate->serv != NULL)
1359 CFAllocatorDeallocate(NULL, (void *)targetPrivate->serv);
1360
1361 if (targetPrivate->resolvedAddress != NULL)
1362 CFRelease(targetPrivate->resolvedAddress);
1363
1364 if (targetPrivate->localAddress != NULL)
1365 CFAllocatorDeallocate(NULL, (void *)targetPrivate->localAddress);
1366
1367 if (targetPrivate->remoteAddress != NULL)
1368 CFAllocatorDeallocate(NULL, (void *)targetPrivate->remoteAddress);
1369
1370 if (targetPrivate->rlsContext.release != NULL) {
1371 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
1372 }
1373
1374 if (targetPrivate->onDemandName != NULL) {
1375 CFRelease(targetPrivate->onDemandName);
1376 }
1377
1378 if (targetPrivate->onDemandRemoteAddress != NULL) {
1379 CFRelease(targetPrivate->onDemandRemoteAddress);
1380 }
1381
1382 if (targetPrivate->onDemandServer != NULL) {
1383 CFRelease(targetPrivate->onDemandServer);
1384 }
1385
1386 if (targetPrivate->onDemandServiceID != NULL) {
1387 CFRelease(targetPrivate->onDemandServiceID);
1388 }
1389
1390 return;
1391 }
1392
1393
1394 static void
1395 __SCNetworkReachabilityInitialize(void)
1396 {
1397 __kSCNetworkReachabilityTypeID = _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass);
1398 return;
1399 }
1400
1401
1402 static void
1403 __SCNetworkReachabilityPerform(SCNetworkReachabilityRef target)
1404 {
1405 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1406
1407 #if !TARGET_OS_IPHONE
1408 if (targetPrivate->dispatchQueue != NULL) {
1409 CFRetain(target);
1410 dispatch_async(targetPrivate->dispatchQueue,
1411 ^{
1412 rlsPerform((void *)target);
1413 CFRelease(target);
1414 });
1415 } else
1416 #endif // !TARGET_OS_IPHONE
1417 if (targetPrivate->rls != NULL) {
1418 CFRunLoopSourceSignal(targetPrivate->rls);
1419 _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
1420 }
1421
1422 return;
1423 }
1424
1425 static SCNetworkReachabilityPrivateRef
1426 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator)
1427 {
1428 SCNetworkReachabilityPrivateRef targetPrivate;
1429 uint32_t size;
1430
1431 /* initialize runtime */
1432 pthread_once(&initialized, __SCNetworkReachabilityInitialize);
1433
1434 /* allocate target */
1435 size = sizeof(SCNetworkReachabilityPrivate) - sizeof(CFRuntimeBase);
1436 targetPrivate = (SCNetworkReachabilityPrivateRef)_CFRuntimeCreateInstance(allocator,
1437 __kSCNetworkReachabilityTypeID,
1438 size,
1439 NULL);
1440 if (targetPrivate == NULL) {
1441 return NULL;
1442 }
1443
1444 pthread_mutex_init(&targetPrivate->lock, NULL);
1445
1446 targetPrivate->name = NULL;
1447 targetPrivate->serv = NULL;
1448 bzero(&targetPrivate->hints, sizeof(targetPrivate->hints));
1449 targetPrivate->hints.ai_flags = AI_ADDRCONFIG;
1450 #ifdef AI_PARALLEL
1451 targetPrivate->hints.ai_flags |= AI_PARALLEL;
1452 #endif /* AI_PARALLEL */
1453
1454 targetPrivate->needResolve = FALSE;
1455 targetPrivate->resolvedAddress = NULL;
1456 targetPrivate->resolvedAddressError = NETDB_SUCCESS;
1457
1458 targetPrivate->localAddress = NULL;
1459 targetPrivate->remoteAddress = NULL;
1460
1461 targetPrivate->info = NOT_REACHABLE;
1462 targetPrivate->last_notify = NOT_REPORTED;
1463
1464 targetPrivate->scheduled = FALSE;
1465 targetPrivate->rls = NULL;
1466 targetPrivate->rlsFunction = NULL;
1467 targetPrivate->rlsContext.info = NULL;
1468 targetPrivate->rlsContext.retain = NULL;
1469 targetPrivate->rlsContext.release = NULL;
1470 targetPrivate->rlsContext.copyDescription = NULL;
1471 targetPrivate->rlList = NULL;
1472
1473 targetPrivate->haveDNS = FALSE;
1474 targetPrivate->dnsMP = MACH_PORT_NULL;
1475 targetPrivate->dnsPort = NULL;
1476 targetPrivate->dnsRLS = NULL;
1477
1478 targetPrivate->onDemandBypass = FALSE;
1479 targetPrivate->onDemandName = NULL;
1480 targetPrivate->onDemandRemoteAddress = NULL;
1481 targetPrivate->onDemandServer = NULL;
1482 targetPrivate->onDemandServiceID = NULL;
1483
1484 targetPrivate->log_prefix[0] = '\0';
1485 if (_sc_log > 0) {
1486 snprintf(targetPrivate->log_prefix,
1487 sizeof(targetPrivate->log_prefix),
1488 "[%p] ",
1489 targetPrivate);
1490 }
1491
1492 return targetPrivate;
1493 }
1494
1495
1496 SCNetworkReachabilityRef
1497 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator,
1498 const struct sockaddr *address)
1499 {
1500 SCNetworkReachabilityPrivateRef targetPrivate;
1501
1502 if ((address == NULL) ||
1503 (address->sa_len == 0) ||
1504 (address->sa_len > sizeof(struct sockaddr_storage))) {
1505 _SCErrorSet(kSCStatusInvalidArgument);
1506 return NULL;
1507 }
1508
1509 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
1510 if (targetPrivate == NULL) {
1511 return NULL;
1512 }
1513
1514 targetPrivate->type = reachabilityTypeAddress;
1515 targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, address->sa_len, 0);
1516 bcopy(address, targetPrivate->remoteAddress, address->sa_len);
1517
1518 return (SCNetworkReachabilityRef)targetPrivate;
1519 }
1520
1521
1522 SCNetworkReachabilityRef
1523 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator,
1524 const struct sockaddr *localAddress,
1525 const struct sockaddr *remoteAddress)
1526 {
1527 SCNetworkReachabilityPrivateRef targetPrivate;
1528
1529 if ((localAddress == NULL) && (remoteAddress == NULL)) {
1530 _SCErrorSet(kSCStatusInvalidArgument);
1531 return NULL;
1532 }
1533
1534 if (localAddress != NULL) {
1535 if ((localAddress->sa_len == 0) ||
1536 (localAddress->sa_len > sizeof(struct sockaddr_storage))) {
1537 _SCErrorSet(kSCStatusInvalidArgument);
1538 return NULL;
1539 }
1540 }
1541
1542 if (remoteAddress != NULL) {
1543 if ((remoteAddress->sa_len == 0) ||
1544 (remoteAddress->sa_len > sizeof(struct sockaddr_storage))) {
1545 _SCErrorSet(kSCStatusInvalidArgument);
1546 return NULL;
1547 }
1548 }
1549
1550 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
1551 if (targetPrivate == NULL) {
1552 return NULL;
1553 }
1554
1555 targetPrivate->type = reachabilityTypeAddressPair;
1556
1557 if (localAddress != NULL) {
1558 targetPrivate->localAddress = CFAllocatorAllocate(NULL, localAddress->sa_len, 0);
1559 bcopy(localAddress, targetPrivate->localAddress, localAddress->sa_len);
1560 }
1561
1562 if (remoteAddress != NULL) {
1563 targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, remoteAddress->sa_len, 0);
1564 bcopy(remoteAddress, targetPrivate->remoteAddress, remoteAddress->sa_len);
1565 }
1566
1567 return (SCNetworkReachabilityRef)targetPrivate;
1568 }
1569
1570
1571 SCNetworkReachabilityRef
1572 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator,
1573 const char *nodename)
1574 {
1575 int nodenameLen;
1576 struct sockaddr_in sin;
1577 struct sockaddr_in6 sin6;
1578 SCNetworkReachabilityPrivateRef targetPrivate;
1579
1580 if (nodename == NULL) {
1581 _SCErrorSet(kSCStatusInvalidArgument);
1582 return NULL;
1583 }
1584
1585 nodenameLen = strlen(nodename);
1586 if (nodenameLen == 0) {
1587 _SCErrorSet(kSCStatusInvalidArgument);
1588 return NULL;
1589 }
1590
1591 /* check if this "nodename" is really an IP[v6] address in disguise */
1592
1593 bzero(&sin, sizeof(sin));
1594 sin.sin_len = sizeof(sin);
1595 sin.sin_family = AF_INET;
1596 if (inet_aton(nodename, &sin.sin_addr) == 1) {
1597 /* if IPv4 address */
1598 return SCNetworkReachabilityCreateWithAddress(allocator, (struct sockaddr *)&sin);
1599 }
1600
1601 bzero(&sin6, sizeof(sin6));
1602 sin6.sin6_len = sizeof(sin6);
1603 sin6.sin6_family = AF_INET6;
1604 if (inet_pton(AF_INET6, nodename, &sin6.sin6_addr) == 1) {
1605 /* if IPv6 address */
1606 char *p;
1607
1608 p = strchr(nodename, '%');
1609 if (p != NULL) {
1610 sin6.sin6_scope_id = if_nametoindex(p + 1);
1611 }
1612
1613 return SCNetworkReachabilityCreateWithAddress(allocator, (struct sockaddr *)&sin6);
1614 }
1615
1616 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
1617 if (targetPrivate == NULL) {
1618 return NULL;
1619 }
1620
1621 targetPrivate->type = reachabilityTypeName;
1622
1623 targetPrivate->name = CFAllocatorAllocate(NULL, nodenameLen + 1, 0);
1624 strlcpy((char *)targetPrivate->name, nodename, nodenameLen + 1);
1625
1626 targetPrivate->needResolve = TRUE;
1627 targetPrivate->info.flags |= kSCNetworkReachabilityFlagsFirstResolvePending;
1628
1629 return (SCNetworkReachabilityRef)targetPrivate;
1630 }
1631
1632
1633 SCNetworkReachabilityRef
1634 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator,
1635 CFDictionaryRef options)
1636 {
1637 CFBooleanRef bypass;
1638 CFDataRef data;
1639 struct addrinfo *hints = NULL;
1640 const char *name;
1641 CFStringRef nodename;
1642 CFStringRef servname;
1643 SCNetworkReachabilityPrivateRef targetPrivate;
1644
1645 if (!isA_CFDictionary(options)) {
1646 _SCErrorSet(kSCStatusInvalidArgument);
1647 return NULL;
1648 }
1649
1650 nodename = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionNodeName);
1651 if ((nodename != NULL) &&
1652 (!isA_CFString(nodename) || (CFStringGetLength(nodename) == 0))) {
1653 _SCErrorSet(kSCStatusInvalidArgument);
1654 return NULL;
1655 }
1656 servname = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionServName);
1657 if ((servname != NULL) &&
1658 (!isA_CFString(servname) || (CFStringGetLength(servname) == 0))) {
1659 _SCErrorSet(kSCStatusInvalidArgument);
1660 return NULL;
1661 }
1662 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionHints);
1663 if (data != NULL) {
1664 if (!isA_CFData(data) || (CFDataGetLength(data) != sizeof(targetPrivate->hints))) {
1665 _SCErrorSet(kSCStatusInvalidArgument);
1666 return NULL;
1667 }
1668
1669 hints = (struct addrinfo *)CFDataGetBytePtr(data);
1670 if ((hints->ai_addrlen != 0) ||
1671 (hints->ai_addr != NULL) ||
1672 (hints->ai_canonname != NULL) ||
1673 (hints->ai_next != NULL)) {
1674 _SCErrorSet(kSCStatusInvalidArgument);
1675 return NULL;
1676 }
1677 }
1678 bypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandByPass);
1679 if ((bypass != NULL) && !isA_CFBoolean(bypass)) {
1680 _SCErrorSet(kSCStatusInvalidArgument);
1681 return NULL;
1682 }
1683 if ((nodename == NULL) && (servname == NULL)) {
1684 _SCErrorSet(kSCStatusInvalidArgument);
1685 return NULL;
1686 }
1687
1688 name = _SC_cfstring_to_cstring(nodename, NULL, 0, kCFStringEncodingUTF8);
1689 targetPrivate = (SCNetworkReachabilityPrivateRef)SCNetworkReachabilityCreateWithName(allocator, name);
1690 CFAllocatorDeallocate(NULL, (void *)name);
1691 if (targetPrivate == NULL) {
1692 return NULL;
1693 }
1694
1695 if (targetPrivate->type == reachabilityTypeName) {
1696 if (servname != NULL) {
1697 targetPrivate->serv = _SC_cfstring_to_cstring(servname, NULL, 0, kCFStringEncodingUTF8);
1698 }
1699 if (hints != NULL) {
1700 bcopy(hints, &targetPrivate->hints, sizeof(targetPrivate->hints));
1701 }
1702 }
1703
1704 if (bypass != NULL) {
1705 targetPrivate->onDemandBypass = CFBooleanGetValue(bypass);
1706 }
1707
1708 return (SCNetworkReachabilityRef)targetPrivate;
1709 }
1710
1711
1712 CFTypeID
1713 SCNetworkReachabilityGetTypeID(void)
1714 {
1715 pthread_once(&initialized, __SCNetworkReachabilityInitialize); /* initialize runtime */
1716 return __kSCNetworkReachabilityTypeID;
1717 }
1718
1719
1720 CFArrayRef /* CFArray[CFData], where each CFData is a (struct sockaddr *) */
1721 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target,
1722 int *error_num)
1723 {
1724 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1725
1726 if (!isA_SCNetworkReachability(target)) {
1727 _SCErrorSet(kSCStatusInvalidArgument);
1728 return NULL;
1729 }
1730
1731 if (targetPrivate->type != reachabilityTypeName) {
1732 _SCErrorSet(kSCStatusInvalidArgument);
1733 return NULL;
1734 }
1735
1736 if (error_num) {
1737 *error_num = targetPrivate->resolvedAddressError;
1738 }
1739
1740 if ((targetPrivate->resolvedAddress != NULL) || (targetPrivate->resolvedAddressError != NETDB_SUCCESS)) {
1741 if (isA_CFArray(targetPrivate->resolvedAddress)) {
1742 return CFRetain(targetPrivate->resolvedAddress);
1743 } else {
1744 /* if status is known but no resolved addresses to return */
1745 _SCErrorSet(kSCStatusOK);
1746 return NULL;
1747 }
1748 }
1749
1750 _SCErrorSet(kSCStatusReachabilityUnknown);
1751 return NULL;
1752 }
1753
1754
1755 static void
1756 __SCNetworkReachabilitySetResolvedAddress(int32_t status,
1757 struct addrinfo *res,
1758 SCNetworkReachabilityRef target)
1759 {
1760 struct addrinfo *resP;
1761 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1762
1763 if (targetPrivate->resolvedAddress != NULL) {
1764 CFRelease(targetPrivate->resolvedAddress);
1765 targetPrivate->resolvedAddress = NULL;
1766 }
1767
1768 if ((status == 0) && (res != NULL)) {
1769 CFMutableArrayRef addresses;
1770
1771 addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1772
1773 for (resP = res; resP; resP = resP->ai_next) {
1774 CFIndex n;
1775 CFDataRef newAddress;
1776
1777 newAddress = CFDataCreate(NULL, (void *)resP->ai_addr, resP->ai_addr->sa_len);
1778 n = CFArrayGetCount(addresses);
1779 if ((n == 0) ||
1780 !CFArrayContainsValue(addresses, CFRangeMake(0, n), newAddress)) {
1781 CFArrayAppendValue(addresses, newAddress);
1782 }
1783 CFRelease(newAddress);
1784 }
1785
1786 /* save the resolved address[es] */
1787 targetPrivate->resolvedAddress = addresses;
1788 targetPrivate->resolvedAddressError = NETDB_SUCCESS;
1789 } else {
1790 SCLog(_sc_debug, LOG_INFO, CFSTR("%sgetaddrinfo() failed: %s"),
1791 targetPrivate->log_prefix,
1792 gai_strerror(status));
1793
1794 /* save the error associated with the attempt to resolve the name */
1795 targetPrivate->resolvedAddress = CFRetain(kCFNull);
1796 targetPrivate->resolvedAddressError = status;
1797 }
1798 targetPrivate->needResolve = FALSE;
1799
1800 if (res != NULL) freeaddrinfo(res);
1801
1802 if (targetPrivate->scheduled) {
1803 __SCNetworkReachabilityPerform(target);
1804 }
1805
1806 return;
1807 }
1808
1809
1810 static void
1811 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status, struct addrinfo *res, void *context)
1812 {
1813 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
1814 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1815
1816 __log_query_time(target,
1817 ((status == 0) && (res != NULL)), // if successful query
1818 TRUE, // async
1819 &targetPrivate->dnsQueryStart); // start time
1820
1821 __SCNetworkReachabilitySetResolvedAddress(status, res, target);
1822 return;
1823 }
1824
1825
1826 /*
1827 * rankReachability()
1828 * Not reachable == 0
1829 * Connection Required == 1
1830 * Reachable == 2
1831 */
1832 static int
1833 rankReachability(SCNetworkReachabilityFlags flags)
1834 {
1835 int rank = 0;
1836
1837 if (flags & kSCNetworkReachabilityFlagsReachable) rank = 2;
1838 if (flags & kSCNetworkReachabilityFlagsConnectionRequired) rank = 1;
1839 return rank;
1840 }
1841
1842
1843 #pragma mark -
1844 #pragma mark DNS name resolution
1845
1846
1847 static CFStringRef
1848 replyMPCopyDescription(const void *info)
1849 {
1850 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
1851 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1852
1853 return CFStringCreateWithFormat(NULL,
1854 NULL,
1855 CFSTR("<getaddrinfo_async_start reply MP> {%s%s%s%s%s, target = %p}"),
1856 targetPrivate->name != NULL ? "name = " : "",
1857 targetPrivate->name != NULL ? targetPrivate->name : "",
1858 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
1859 targetPrivate->serv != NULL ? "serv = " : "",
1860 targetPrivate->serv != NULL ? targetPrivate->serv : "",
1861 target);
1862 }
1863
1864
1865 static void
1866 processAsyncDNSReply(mach_port_t mp, void *msg, SCNetworkReachabilityRef target);
1867
1868
1869 static void
1870 getaddrinfo_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info)
1871 {
1872 mach_port_t mp = CFMachPortGetPort(port);
1873 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
1874
1875 processAsyncDNSReply(mp, msg, target);
1876 return;
1877 }
1878
1879
1880 #if !TARGET_OS_IPHONE
1881 static boolean_t
1882 SCNetworkReachabilityNotifyMIGCallback(mach_msg_header_t *message, mach_msg_header_t *reply)
1883 {
1884 mach_port_t mp = message->msgh_local_port;
1885 SCNetworkReachabilityRef target = dispatch_get_context(dispatch_get_current_queue());
1886
1887 processAsyncDNSReply(mp, message, target);
1888 reply->msgh_remote_port = MACH_PORT_NULL;
1889 return false;
1890 }
1891 #endif // !TARGET_OS_IPHONE
1892
1893
1894 static Boolean
1895 enqueueAsyncDNSQuery(SCNetworkReachabilityRef target, mach_port_t mp)
1896 {
1897 CFMachPortContext context = { 0
1898 , (void *)target
1899 , CFRetain
1900 , CFRelease
1901 , replyMPCopyDescription
1902 };
1903 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1904
1905 targetPrivate->dnsMP = mp;
1906 targetPrivate->dnsPort = CFMachPortCreateWithPort(NULL,
1907 mp,
1908 getaddrinfo_async_handleCFReply,
1909 &context,
1910 NULL);
1911 #if !TARGET_OS_IPHONE
1912 if (targetPrivate->dispatchQueue != NULL) {
1913 targetPrivate->asyncDNSQueue = dispatch_queue_create("com.apple.SCNetworkReachabilty.async_DNS_query", NULL);
1914 if (targetPrivate->asyncDNSQueue == NULL) {
1915 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability dispatch_queue_create() failed"));
1916 goto fail;
1917 }
1918 CFRetain(target); // Note: will be released when the dispatch queue is released
1919 dispatch_set_context(targetPrivate->asyncDNSQueue, (void *)target);
1920 dispatch_set_finalizer_f(targetPrivate->asyncDNSQueue, (dispatch_function_t)CFRelease);
1921
1922 targetPrivate->asyncDNSSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
1923 mp,
1924 0,
1925 targetPrivate->asyncDNSQueue);
1926 if (targetPrivate->asyncDNSSource == NULL) {
1927 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkReachability dispatch_source_create() failed"));
1928 goto fail;
1929 }
1930 dispatch_source_set_event_handler(targetPrivate->asyncDNSSource, ^{
1931 dispatch_mig_server(targetPrivate->asyncDNSSource,
1932 sizeof(mach_msg_header_t),
1933 SCNetworkReachabilityNotifyMIGCallback);
1934 });
1935 dispatch_resume(targetPrivate->asyncDNSSource);
1936 } else
1937 #endif // !TARGET_OS_IPHONE
1938 if (targetPrivate->rls != NULL) {
1939 CFIndex i;
1940 CFIndex n;
1941
1942 targetPrivate->dnsRLS = CFMachPortCreateRunLoopSource(NULL, targetPrivate->dnsPort, 0);
1943
1944 n = CFArrayGetCount(targetPrivate->rlList);
1945 for (i = 0; i < n; i += 3) {
1946 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
1947 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
1948
1949 CFRunLoopAddSource(rl, targetPrivate->dnsRLS, rlMode);
1950 }
1951 }
1952
1953 return TRUE;
1954
1955 #if !TARGET_OS_IPHONE
1956 fail :
1957
1958 if (targetPrivate->asyncDNSSource != NULL) {
1959 dispatch_source_cancel(targetPrivate->asyncDNSSource);
1960 dispatch_release(targetPrivate->asyncDNSSource);
1961 targetPrivate->asyncDNSSource = NULL;
1962 }
1963 if (targetPrivate->asyncDNSQueue != NULL) {
1964 dispatch_release(targetPrivate->asyncDNSQueue);
1965 targetPrivate->asyncDNSQueue = NULL;
1966 }
1967
1968 CFMachPortInvalidate(targetPrivate->dnsPort);
1969 CFRelease(targetPrivate->dnsPort);
1970 targetPrivate->dnsPort = NULL;
1971 targetPrivate->dnsMP = MACH_PORT_NULL;
1972
1973 _SCErrorSet(kSCStatusFailed);
1974 return FALSE;
1975 #endif // !TARGET_OS_IPHONE
1976 }
1977
1978
1979 static void
1980 dequeueAsyncDNSQuery(SCNetworkReachabilityRef target)
1981 {
1982 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1983
1984 #if !TARGET_OS_IPHONE
1985 if (targetPrivate->asyncDNSSource != NULL) {
1986 dispatch_source_cancel(targetPrivate->asyncDNSSource);
1987 if (targetPrivate->asyncDNSQueue != dispatch_get_current_queue()) {
1988 // ensure the cancellation has completed
1989 pthread_mutex_unlock(&targetPrivate->lock);
1990 dispatch_sync(targetPrivate->asyncDNSQueue, ^{});
1991 pthread_mutex_lock(&targetPrivate->lock);
1992 }
1993 }
1994 if (targetPrivate->asyncDNSSource != NULL) {
1995 dispatch_release(targetPrivate->asyncDNSSource);
1996 targetPrivate->asyncDNSSource = NULL;
1997 }
1998 if (targetPrivate->asyncDNSQueue != NULL) {
1999 dispatch_release(targetPrivate->asyncDNSQueue);
2000 targetPrivate->asyncDNSQueue = NULL;
2001 }
2002 #endif // !TARGET_OS_IPHONE
2003
2004 if (targetPrivate->dnsRLS != NULL) {
2005 CFRelease(targetPrivate->dnsRLS);
2006 targetPrivate->dnsRLS = NULL;
2007 }
2008
2009 CFMachPortInvalidate(targetPrivate->dnsPort);
2010 CFRelease(targetPrivate->dnsPort);
2011 targetPrivate->dnsPort = NULL;
2012 targetPrivate->dnsMP = MACH_PORT_NULL;
2013
2014 return;
2015 }
2016
2017
2018 static void
2019 processAsyncDNSReply(mach_port_t mp, void *msg, SCNetworkReachabilityRef target)
2020 {
2021 int32_t status;
2022 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2023
2024 pthread_mutex_lock(&targetPrivate->lock);
2025
2026 if (mp != targetPrivate->dnsMP) {
2027 // we've received a callback on the async DNS port but since the
2028 // associated CFMachPort doesn't match than the request must have
2029 // already been cancelled.
2030 SCLog(TRUE, LOG_ERR, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP"));
2031 pthread_mutex_unlock(&targetPrivate->lock);
2032 return;
2033 }
2034
2035 dequeueAsyncDNSQuery(target);
2036 status = getaddrinfo_async_handle_reply(msg);
2037 if ((status == 0) &&
2038 (targetPrivate->resolvedAddress == NULL) && (targetPrivate->resolvedAddressError == NETDB_SUCCESS)) {
2039 Boolean ok;
2040
2041 // if the request is not complete and needs to be re-queued
2042 ok = enqueueAsyncDNSQuery(target, mp);
2043 if (!ok) {
2044 SCLog(TRUE, LOG_ERR, CFSTR("processAsyncDNSReply enqueueAsyncDNSQuery() failed"));
2045 }
2046 }
2047
2048 pthread_mutex_unlock(&targetPrivate->lock);
2049
2050 return;
2051 }
2052
2053
2054 static Boolean
2055 check_resolver_reachability(SCDynamicStoreRef *storeP,
2056 dns_resolver_t *resolver,
2057 SCNetworkReachabilityFlags *flags,
2058 Boolean *haveDNS,
2059 const char *log_prefix)
2060 {
2061 int i;
2062 Boolean ok = TRUE;
2063
2064 *flags = kSCNetworkReachabilityFlagsReachable;
2065 *haveDNS = FALSE;
2066
2067 for (i = 0; i < resolver->n_nameserver; i++) {
2068 struct sockaddr *address = resolver->nameserver[i];
2069 ReachabilityInfo ns_info;
2070
2071 *haveDNS = TRUE;
2072
2073 if (address->sa_family != AF_INET) {
2074 /*
2075 * we need to skip non-IPv4 DNS server
2076 * addresses (at least until [3510431] has
2077 * been resolved).
2078 */
2079 continue;
2080 }
2081
2082 ok = checkAddress(storeP, address, &ns_info, log_prefix);
2083 if (!ok) {
2084 /* not today */
2085 goto done;
2086 }
2087
2088 if (rankReachability(ns_info.flags) < rankReachability(*flags)) {
2089 /* return the worst case result */
2090 *flags = ns_info.flags;
2091 }
2092 }
2093
2094 done :
2095
2096 return ok;
2097 }
2098
2099
2100 static Boolean
2101 check_matching_resolvers(SCDynamicStoreRef *storeP,
2102 dns_config_t *dns_config,
2103 const char *fqdn,
2104 SCNetworkReachabilityFlags *flags,
2105 Boolean *haveDNS,
2106 const char *log_prefix)
2107 {
2108 int i;
2109 Boolean matched = FALSE;
2110 const char *name = fqdn;
2111
2112 while (!matched && (name != NULL)) {
2113 int len;
2114
2115 /*
2116 * check if the provided name (or sub-component)
2117 * matches one of our resolver configurations.
2118 */
2119 len = strlen(name);
2120 for (i = 0; i < dns_config->n_resolver; i++) {
2121 char *domain;
2122 dns_resolver_t *resolver;
2123
2124 resolver = dns_config->resolver[i];
2125 domain = resolver->domain;
2126 if (domain != NULL && (len == strlen(domain))) {
2127 if (strcasecmp(name, domain) == 0) {
2128 Boolean ok;
2129
2130 /*
2131 * if name matches domain
2132 */
2133 matched = TRUE;
2134 ok = check_resolver_reachability(storeP, resolver, flags, haveDNS, log_prefix);
2135 if (!ok) {
2136 /* not today */
2137 return FALSE;
2138 }
2139 }
2140 }
2141 }
2142
2143 if (!matched) {
2144 /*
2145 * we have not found a matching resolver, try
2146 * a less qualified domain
2147 */
2148 name = strchr(name, '.');
2149 if ((name != NULL) && (*name != '\0')) {
2150 name++;
2151 } else {
2152 name = NULL;
2153 }
2154 }
2155 }
2156
2157 return matched;
2158 }
2159
2160
2161 static dns_configuration_t *
2162 dns_configuration_retain()
2163 {
2164 pthread_mutex_lock(&dns_lock);
2165
2166 if ((dns_configuration != NULL) && dns_token_valid) {
2167 int check = 0;
2168 uint32_t status;
2169
2170 /*
2171 * check if the global [DNS] configuration snapshot needs
2172 * to be updated
2173 */
2174 status = notify_check(dns_token, &check);
2175 if (status != NOTIFY_STATUS_OK) {
2176 SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%lu"), status);
2177 }
2178
2179 if ((status != NOTIFY_STATUS_OK) || (check != 0)) {
2180 /*
2181 * if the snapshot needs to be refreshed
2182 */
2183 if (dns_configuration->refs == 0) {
2184 dns_configuration_free(dns_configuration->config);
2185 CFAllocatorDeallocate(NULL, dns_configuration);
2186 }
2187 dns_configuration = NULL;
2188 }
2189 }
2190
2191 if (dns_configuration == NULL) {
2192 dns_config_t *new_config;
2193
2194 new_config = dns_configuration_copy();
2195 if (new_config != NULL) {
2196 dns_configuration = CFAllocatorAllocate(NULL, sizeof(dns_configuration_t), 0);
2197 dns_configuration->config = new_config;
2198 dns_configuration->refs = 0;
2199 }
2200 }
2201
2202 if (dns_configuration != NULL) {
2203 dns_configuration->refs++;
2204 }
2205
2206 pthread_mutex_unlock(&dns_lock);
2207 return dns_configuration;
2208 }
2209
2210
2211 static void
2212 dns_configuration_release(dns_configuration_t *config)
2213 {
2214 pthread_mutex_lock(&dns_lock);
2215
2216 config->refs--;
2217 if (config->refs == 0) {
2218 if ((dns_configuration != config)) {
2219 dns_configuration_free(config->config);
2220 CFAllocatorDeallocate(NULL, config);
2221 }
2222 }
2223
2224 pthread_mutex_unlock(&dns_lock);
2225 return;
2226 }
2227
2228
2229 static Boolean
2230 dns_configuration_watch()
2231 {
2232 int dns_check = 0;
2233 const char *dns_key;
2234 Boolean ok = FALSE;
2235 uint32_t status;
2236
2237 pthread_mutex_lock(&dns_lock);
2238
2239 dns_key = dns_configuration_notify_key();
2240 if (dns_key == NULL) {
2241 SCLog(TRUE, LOG_INFO, CFSTR("dns_configuration_notify_key() failed"));
2242 goto done;
2243 }
2244
2245 status = notify_register_check(dns_key, &dns_token);
2246 if (status == NOTIFY_STATUS_OK) {
2247 dns_token_valid = TRUE;
2248 } else {
2249 SCLog(TRUE, LOG_INFO, CFSTR("notify_register_check() failed, status=%lu"), status);
2250 goto done;
2251 }
2252
2253 status = notify_check(dns_token, &dns_check);
2254 if (status != NOTIFY_STATUS_OK) {
2255 SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%lu"), status);
2256 (void)notify_cancel(dns_token);
2257 dns_token_valid = FALSE;
2258 goto done;
2259 }
2260
2261 ok = TRUE;
2262
2263 done :
2264
2265 pthread_mutex_unlock(&dns_lock);
2266 return ok;
2267 }
2268
2269
2270 static void
2271 dns_configuration_unwatch()
2272 {
2273 pthread_mutex_lock(&dns_lock);
2274
2275 (void)notify_cancel(dns_token);
2276 dns_token_valid = FALSE;
2277
2278 if ((dns_configuration != NULL) && (dns_configuration->refs == 0)) {
2279 dns_configuration_free(dns_configuration->config);
2280 CFAllocatorDeallocate(NULL, dns_configuration);
2281 dns_configuration = NULL;
2282 }
2283
2284 pthread_mutex_unlock(&dns_lock);
2285 return;
2286 }
2287
2288
2289 static Boolean
2290 _SC_R_checkResolverReachability(SCDynamicStoreRef *storeP,
2291 SCNetworkReachabilityFlags *flags,
2292 Boolean *haveDNS,
2293 const char *nodename,
2294 const char *servname,
2295 const char *log_prefix)
2296 {
2297 dns_resolver_t *default_resolver;
2298 dns_configuration_t *dns;
2299 Boolean found = FALSE;
2300 char *fqdn = (char *)nodename;
2301 int i;
2302 Boolean isFQDN = FALSE;
2303 uint32_t len;
2304 Boolean ok = TRUE;
2305 Boolean useDefault = FALSE;
2306
2307 /*
2308 * We first assume that all of the configured DNS servers
2309 * are available. Since we don't know which name server will
2310 * be consulted to resolve the specified nodename we need to
2311 * check the availability of ALL name servers. We can only
2312 * proceed if we know that our query can be answered.
2313 */
2314
2315 *flags = kSCNetworkReachabilityFlagsReachable;
2316 *haveDNS = FALSE;
2317
2318 len = (nodename != NULL) ? strlen(nodename) : 0;
2319 if (len == 0) {
2320 if ((servname == NULL) || (strlen(servname) == 0)) {
2321 // if no nodename or servname, return not reachable
2322 *flags = 0;
2323 }
2324 return ok;
2325 }
2326
2327 dns = dns_configuration_retain();
2328 if (dns == NULL) {
2329 // if error
2330 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no configuration"), log_prefix);
2331 goto done;
2332 }
2333
2334 if (dns->config->n_resolver == 0) {
2335 // if no resolver configuration
2336 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no resolvers"), log_prefix);
2337 goto done;
2338 }
2339
2340 *flags = kSCNetworkReachabilityFlagsReachable;
2341
2342 if (fqdn[len - 1] == '.') {
2343 isFQDN = TRUE;
2344
2345 // trim trailing '.''s
2346 while ((len > 0) && (fqdn[len-1] == '.')) {
2347 if (fqdn == nodename) {
2348 fqdn = strdup(nodename);
2349 }
2350 fqdn[--len] = '\0';
2351 }
2352 }
2353
2354 default_resolver = dns->config->resolver[0];
2355
2356 /*
2357 * check if the provided name matches a supplemental domain
2358 */
2359 found = check_matching_resolvers(storeP, dns->config, fqdn, flags, haveDNS, log_prefix);
2360
2361 if (!found && !isFQDN) {
2362 /*
2363 * if we did not match a supplemental domain name and if the
2364 * provided name has enough "."s then the first query will be
2365 * directed to the default resolver.
2366 */
2367 char *cp;
2368 int dots;
2369 int ndots = 1;
2370
2371 #define NDOTS_OPT "ndots="
2372 #define NDOTS_OPT_LEN (sizeof("ndots=") - 1)
2373
2374 if (default_resolver->options != NULL) {
2375 cp = strstr(default_resolver->options, NDOTS_OPT);
2376 if ((cp != NULL) &&
2377 ((cp == default_resolver->options) || isspace(cp[-1])) &&
2378 ((cp[NDOTS_OPT_LEN] != '\0') && isdigit(cp[NDOTS_OPT_LEN]))) {
2379 char *end;
2380 long val;
2381
2382 cp += NDOTS_OPT_LEN;
2383 errno = 0;
2384 val = strtol(cp, &end, 10);
2385 if ((*cp != '\0') && (cp != end) && (errno == 0) &&
2386 ((*end == '\0') || isspace(*end))) {
2387 ndots = val;
2388 }
2389 }
2390 }
2391
2392 dots = 0;
2393 for (cp = fqdn; *cp != '\0'; cp++) {
2394 if (*cp == '.') dots++;
2395 }
2396
2397 if (dots > ndots) {
2398 useDefault = TRUE;
2399 }
2400 }
2401
2402 if (!found && !isFQDN && !useDefault && (dns->config->n_resolver > 1)) {
2403 /*
2404 * FQDN not specified, try matching w/search domains
2405 */
2406 if (default_resolver->n_search > 0) {
2407 for (i = 0; !found && (i < default_resolver->n_search); i++) {
2408 int ret;
2409 char *search_fqdn = NULL;
2410
2411 ret = asprintf(&search_fqdn, "%s.%s", fqdn, default_resolver->search[i]);
2412 if (ret == -1) {
2413 continue;
2414 }
2415
2416 // try the provided name with the search domain appended
2417 found = check_matching_resolvers(storeP,
2418 dns->config,
2419 search_fqdn,
2420 flags,
2421 haveDNS,
2422 log_prefix);
2423 free(search_fqdn);
2424 }
2425 } else if (default_resolver->domain != NULL) {
2426 char *dp;
2427 int domain_parts = 0;
2428
2429 // count domain parts
2430 for (dp = default_resolver->domain; *dp != '\0'; dp++) {
2431 if (*dp == '.') {
2432 domain_parts++;
2433 }
2434 }
2435
2436 // remove trailing dots
2437 for (dp--; (dp >= default_resolver->domain) && (*dp == '.'); dp--) {
2438 *dp = '\0';
2439 domain_parts--;
2440 }
2441
2442 if (dp >= default_resolver->domain) {
2443 // dots are separators, bump # of components
2444 domain_parts++;
2445 }
2446
2447 dp = default_resolver->domain;
2448 for (i = LOCALDOMAINPARTS; !found && (i <= domain_parts); i++) {
2449 int ret;
2450 char *search_fqdn = NULL;
2451
2452 ret = asprintf(&search_fqdn, "%s.%s", fqdn, dp);
2453 if (ret == -1) {
2454 continue;
2455 }
2456
2457 // try the provided name with the [default] domain appended
2458 found = check_matching_resolvers(storeP,
2459 dns->config,
2460 search_fqdn,
2461 flags,
2462 haveDNS,
2463 log_prefix);
2464 free(search_fqdn);
2465
2466 // move to the next component of the [default] domain
2467 dp = strchr(dp, '.') + 1;
2468 }
2469 }
2470 }
2471
2472 if (!found) {
2473 /*
2474 * check the reachability of the default resolver
2475 */
2476 ok = check_resolver_reachability(storeP, default_resolver, flags, haveDNS, log_prefix);
2477 }
2478
2479 if (fqdn != nodename) free(fqdn);
2480
2481 done :
2482
2483 if (dns != NULL) {
2484 dns_configuration_release(dns);
2485 }
2486
2487 return ok;
2488 }
2489
2490
2491 Boolean
2492 _SC_checkResolverReachability(SCDynamicStoreRef *storeP,
2493 SCNetworkReachabilityFlags *flags,
2494 Boolean *haveDNS,
2495 const char *nodename,
2496 const char *servname)
2497 {
2498 return _SC_R_checkResolverReachability(storeP, flags, haveDNS, nodename, servname, "");
2499 }
2500
2501
2502 /*
2503 * _SC_checkResolverReachabilityByAddress()
2504 *
2505 * Given an IP address, determine whether a reverse DNS query can be issued
2506 * using the current network configuration.
2507 */
2508 Boolean
2509 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef *storeP,
2510 SCNetworkReachabilityFlags *flags,
2511 Boolean *haveDNS,
2512 struct sockaddr *sa)
2513 {
2514 int i;
2515 Boolean ok = FALSE;
2516 char ptr_name[128];
2517
2518 /*
2519 * Ideally, we would have an API that given a local IP
2520 * address would return the DNS server(s) that would field
2521 * a given PTR query. Fortunately, we do have an SPI which
2522 * which will provide this information given a "name" so we
2523 * take the address, convert it into the inverse query name,
2524 * and find out which servers should be consulted.
2525 */
2526
2527 switch (sa->sa_family) {
2528 case AF_INET : {
2529 union {
2530 in_addr_t s_addr;
2531 unsigned char b[4];
2532 } rev;
2533 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
2534
2535 /*
2536 * build "PTR" query name
2537 * NNN.NNN.NNN.NNN.in-addr.arpa.
2538 */
2539 rev.s_addr = sin->sin_addr.s_addr;
2540 (void) snprintf(ptr_name, sizeof(ptr_name), "%u.%u.%u.%u.in-addr.arpa.",
2541 rev.b[3],
2542 rev.b[2],
2543 rev.b[1],
2544 rev.b[0]);
2545
2546 break;
2547 }
2548
2549 case AF_INET6 : {
2550 int s = 0;
2551 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
2552 int x = sizeof(ptr_name);
2553 int n;
2554
2555 /*
2556 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
2557 * 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.
2558 */
2559 for (i = sizeof(sin6->sin6_addr) - 1; i >= 0; i--) {
2560 n = snprintf(&ptr_name[s], x, "%x.%x.",
2561 ( sin6->sin6_addr.s6_addr[i] & 0xf),
2562 ((sin6->sin6_addr.s6_addr[i] >> 4) & 0xf));
2563 if ((n == -1) || (n >= x)) {
2564 goto done;
2565 }
2566
2567 s += n;
2568 x -= n;
2569 }
2570
2571 n = snprintf(&ptr_name[s], x, "ip6.arpa.");
2572 if ((n == -1) || (n >= x)) {
2573 goto done;
2574 }
2575
2576 break;
2577 }
2578
2579 default :
2580 goto done;
2581 }
2582
2583 ok = _SC_R_checkResolverReachability(storeP, flags, haveDNS, ptr_name, NULL, "");
2584
2585 done :
2586
2587 return ok;
2588 }
2589
2590
2591 static Boolean
2592 startAsyncDNSQuery(SCNetworkReachabilityRef target) {
2593 int error;
2594 mach_port_t mp;
2595 Boolean ok;
2596 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2597
2598 (void) gettimeofday(&targetPrivate->dnsQueryStart, NULL);
2599
2600 error = getaddrinfo_async_start(&mp,
2601 targetPrivate->name,
2602 targetPrivate->serv,
2603 &targetPrivate->hints,
2604 __SCNetworkReachabilityCallbackSetResolvedAddress,
2605 (void *)target);
2606 if (error != 0) {
2607 /* save the error associated with the attempt to resolve the name */
2608 __SCNetworkReachabilityCallbackSetResolvedAddress(error, NULL, (void *)target);
2609 return FALSE;
2610 }
2611
2612 ok = enqueueAsyncDNSQuery(target, mp);
2613 return ok;
2614 }
2615
2616
2617 #pragma mark -
2618 #pragma mark OnDemand
2619
2620
2621 SCNetworkServiceRef
2622 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target,
2623 CFDictionaryRef *userOptions)
2624 {
2625 SCNetworkServiceRef service = NULL;
2626 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2627
2628 if (!isA_SCNetworkReachability(target)) {
2629 _SCErrorSet(kSCStatusInvalidArgument);
2630 return NULL;
2631 }
2632
2633 if (targetPrivate->onDemandServiceID != NULL) {
2634 service = _SCNetworkServiceCopyActive(NULL, targetPrivate->onDemandServiceID);
2635 }
2636
2637 if (userOptions != NULL) {
2638 if (targetPrivate->onDemandName != NULL) {
2639 *userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2640 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, targetPrivate->onDemandName);
2641 } else {
2642 *userOptions = NULL;
2643 }
2644 }
2645
2646 return service;
2647 }
2648
2649
2650 static void
2651 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer,
2652 SCNetworkReachabilityFlags onDemandFlags,
2653 void *info)
2654 {
2655 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
2656 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2657
2658 pthread_mutex_lock(&targetPrivate->lock);
2659
2660 if (!targetPrivate->scheduled) {
2661 // if not currently scheduled
2662 pthread_mutex_unlock(&targetPrivate->lock);
2663 return;
2664 }
2665
2666 SCLog(_sc_debug, LOG_INFO, CFSTR("%sOnDemand \"server\" status changed"),
2667 targetPrivate->log_prefix);
2668 __SCNetworkReachabilityPerform(target);
2669
2670 pthread_mutex_unlock(&targetPrivate->lock);
2671
2672 return;
2673 }
2674
2675
2676 static Boolean
2677 __SCNetworkReachabilityOnDemandCheck(SCDynamicStoreRef *storeP,
2678 SCNetworkReachabilityRef target,
2679 Boolean onDemandRetry,
2680 SCNetworkReachabilityFlags *flags)
2681 {
2682 Boolean ok;
2683 Boolean onDemand = FALSE;
2684 CFStringRef onDemandRemoteAddress = NULL;
2685 CFStringRef onDemandServiceID = NULL;
2686 SCNetworkConnectionStatus onDemandStatus;
2687 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2688
2689 // SCLog(_sc_debug, LOG_INFO,
2690 // CFSTR("%s__SCNetworkReachabilityOnDemandCheck %s"),
2691 // targetPrivate->log_prefix,
2692 // onDemandRetry ? "after" : "before");
2693
2694 if (targetPrivate->onDemandName == NULL) {
2695 targetPrivate->onDemandName = CFStringCreateWithCString(NULL, targetPrivate->name, kCFStringEncodingUTF8);
2696 }
2697
2698 /*
2699 * check if an OnDemand VPN configuration matches the name.
2700 */
2701 ok = __SCNetworkConnectionCopyOnDemandInfoWithName(storeP,
2702 targetPrivate->onDemandName,
2703 onDemandRetry,
2704 &onDemandServiceID,
2705 &onDemandStatus,
2706 &onDemandRemoteAddress);
2707 if (!_SC_CFEqual(targetPrivate->onDemandRemoteAddress, onDemandRemoteAddress) ||
2708 !_SC_CFEqual(targetPrivate->onDemandServiceID, onDemandServiceID)) {
2709 if (targetPrivate->onDemandRemoteAddress != NULL) {
2710 CFRelease(targetPrivate->onDemandRemoteAddress);
2711 targetPrivate->onDemandRemoteAddress = NULL;
2712 }
2713
2714 if (targetPrivate->onDemandServer != NULL) {
2715 #if !TARGET_OS_IPHONE
2716 if (targetPrivate->dispatchQueue != NULL) {
2717 // unschedule
2718 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE);
2719 } else
2720 #endif // !TARGET_OS_IPHONE
2721 if (targetPrivate->rls != NULL) {
2722 CFIndex i;
2723 CFIndex n;
2724
2725 // unschedule
2726 n = CFArrayGetCount(targetPrivate->rlList);
2727 for (i = 0; i < n; i += 3) {
2728 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
2729 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
2730
2731 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, rl, rlMode, TRUE);
2732 }
2733 }
2734
2735 CFRelease(targetPrivate->onDemandServer);
2736 targetPrivate->onDemandServer = NULL;
2737 }
2738
2739 if (targetPrivate->onDemandServiceID != NULL) {
2740 CFRelease(targetPrivate->onDemandServiceID);
2741 targetPrivate->onDemandServiceID = NULL;
2742 }
2743 }
2744 if (ok) {
2745 if (onDemandStatus != kSCNetworkConnectionConnected) {
2746 /*
2747 * if we have a VPN configuration matching the name *and* we need to
2748 * bring the VPN up. Combine our flags with those of the VPN server.
2749 */
2750 if (targetPrivate->onDemandServer == NULL) {
2751 CFMutableDictionaryRef options;
2752
2753 options = CFDictionaryCreateMutable(NULL,
2754 0,
2755 &kCFTypeDictionaryKeyCallBacks,
2756 &kCFTypeDictionaryValueCallBacks);
2757 CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, onDemandRemoteAddress);
2758 CFDictionarySetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandByPass, kCFBooleanTrue);
2759 targetPrivate->onDemandServer = SCNetworkReachabilityCreateWithOptions(NULL, options);
2760 CFRelease(options);
2761
2762 if (targetPrivate->scheduled) {
2763 SCNetworkReachabilityContext context = { 0, NULL, CFRetain, CFRelease, CFCopyDescription };
2764
2765 context.info = (void *)target;
2766 SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer,
2767 __SCNetworkReachabilityOnDemandCheckCallback,
2768 &context);
2769
2770 // schedule server reachability to match that of the target
2771 #if !TARGET_OS_IPHONE
2772 if (targetPrivate->dispatchQueue != NULL) {
2773 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, NULL, NULL, targetPrivate->dispatchQueue, TRUE);
2774 } else
2775 #endif // !TARGET_OS_IPHONE
2776 {
2777 CFIndex i;
2778 CFIndex n;
2779
2780 n = CFArrayGetCount(targetPrivate->rlList);
2781 for (i = 0; i < n; i += 3) {
2782 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
2783 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
2784
2785 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, rl, rlMode, NULL, TRUE);
2786 }
2787 }
2788 }
2789 }
2790
2791 ok = SCNetworkReachabilityGetFlags(targetPrivate->onDemandServer, flags);
2792 SCLog(_sc_debug, LOG_INFO, CFSTR("%s status * = 0x%08x"),
2793 targetPrivate->log_prefix,
2794 *flags);
2795 if (ok && (*flags & kSCNetworkReachabilityFlagsReachable)) {
2796 if (!(*flags & kSCNetworkReachabilityFlagsTransientConnection)) {
2797 // start clean if not already layered on a transient network
2798 *flags = 0;
2799 }
2800 *flags |= kSCNetworkReachabilityFlagsReachable;
2801 *flags |= kSCNetworkReachabilityFlagsTransientConnection;
2802 *flags |= kSCNetworkReachabilityFlagsConnectionRequired;
2803 *flags |= kSCNetworkReachabilityFlagsConnectionOnDemand;
2804
2805 if (_sc_debug) {
2806 SCLog(TRUE, LOG_INFO, CFSTR("%s service * = %@"),
2807 targetPrivate->log_prefix,
2808 onDemandServiceID);
2809 SCLog(TRUE, LOG_INFO, CFSTR("%s status = isReachable (after OnDemand connect)"),
2810 targetPrivate->log_prefix);
2811 }
2812
2813 onDemand = TRUE;
2814 }
2815 }
2816
2817 if (onDemandRemoteAddress != NULL) {
2818 if (targetPrivate->onDemandRemoteAddress == NULL) {
2819 targetPrivate->onDemandRemoteAddress = onDemandRemoteAddress;
2820 } else {
2821 CFRelease(onDemandRemoteAddress);
2822 }
2823 }
2824
2825 if (onDemandServiceID != NULL) {
2826 if (targetPrivate->onDemandServiceID == NULL) {
2827 targetPrivate->onDemandServiceID = onDemandServiceID;
2828 } else {
2829 CFRelease(onDemandServiceID);
2830 }
2831 }
2832 }
2833
2834 return onDemand;
2835 }
2836
2837
2838 #pragma mark -
2839 #pragma mark Reachability Flags
2840
2841
2842 static Boolean
2843 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef *storeP,
2844 SCNetworkReachabilityRef target,
2845 ReachabilityInfo *reach_info,
2846 Boolean async)
2847 {
2848 CFMutableArrayRef addresses = NULL;
2849 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2850 ReachabilityInfo my_info = NOT_REACHABLE;
2851 Boolean ok = TRUE;
2852
2853 *reach_info = NOT_REACHABLE;
2854
2855 if (!isA_SCNetworkReachability(target)) {
2856 _SCErrorSet(kSCStatusInvalidArgument);
2857 return FALSE;
2858 }
2859
2860 switch (targetPrivate->type) {
2861 case reachabilityTypeAddress :
2862 case reachabilityTypeAddressPair : {
2863 /*
2864 * Check "local" address
2865 */
2866 if (targetPrivate->localAddress != NULL) {
2867 /*
2868 * Check "local" address
2869 */
2870 ok = checkAddress(storeP,
2871 targetPrivate->localAddress,
2872 &my_info,
2873 targetPrivate->log_prefix);
2874 if (!ok) {
2875 goto error; /* not today */
2876 }
2877
2878 if (!(my_info.flags & kSCNetworkReachabilityFlagsIsLocalAddress)) {
2879 goto error; /* not reachable, non-"local" address */
2880 }
2881 }
2882
2883 /*
2884 * Check "remote" address
2885 */
2886 if (targetPrivate->remoteAddress != NULL) {
2887 /*
2888 * in cases where we have "local" and "remote" addresses
2889 * we need to re-initialize the to-be-returned flags.
2890 */
2891 my_info = NOT_REACHABLE;
2892
2893 /*
2894 * Check "remote" address
2895 */
2896 ok = checkAddress(storeP,
2897 targetPrivate->remoteAddress,
2898 &my_info,
2899 targetPrivate->log_prefix);
2900 if (!ok) {
2901 goto error; /* not today */
2902 }
2903 }
2904
2905 break;
2906
2907 }
2908
2909 case reachabilityTypeName : {
2910 struct timeval dnsQueryStart;
2911 int error;
2912 SCNetworkReachabilityFlags ns_flags;
2913 struct addrinfo *res;
2914
2915 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
2916 if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
2917 /* if resolved or an error had been detected */
2918 if (!async) {
2919 /* if not an async request */
2920 goto checkResolvedAddress;
2921 } else if ((targetPrivate->dnsPort == NULL) && !targetPrivate->needResolve) {
2922 /* if async request, no query active, and no query needed */
2923 goto checkResolvedAddress;
2924 }
2925 }
2926
2927 if (!targetPrivate->onDemandBypass) {
2928 Boolean onDemand;
2929
2930 /*
2931 * before we attempt our initial DNS query, check if there is
2932 * an OnDemand configuration that we should be using.
2933 */
2934 onDemand = __SCNetworkReachabilityOnDemandCheck(storeP, target, FALSE, &my_info.flags);
2935 if (onDemand) {
2936 /* if OnDemand connection is needed */
2937 goto done;
2938 }
2939 }
2940
2941 /* check the reachability of the DNS servers */
2942 ok = _SC_R_checkResolverReachability(storeP,
2943 &ns_flags,
2944 &targetPrivate->haveDNS,
2945 targetPrivate->name,
2946 targetPrivate->serv,
2947 targetPrivate->log_prefix);
2948 if (!ok) {
2949 /* if we could not get DNS server info */
2950 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server reachability unknown"),
2951 targetPrivate->log_prefix);
2952 goto error;
2953 } else if (rankReachability(ns_flags) < 2) {
2954 /*
2955 * if DNS servers are not (or are no longer) reachable, set
2956 * flags based on the availability of configured (but not
2957 * active) services.
2958 */
2959
2960 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"),
2961 targetPrivate->log_prefix);
2962
2963 ok = checkAddress(storeP,
2964 NULL,
2965 &my_info,
2966 targetPrivate->log_prefix);
2967 if (!ok) {
2968 SCLog(_sc_debug, LOG_INFO, CFSTR("%sNo available networks"),
2969 targetPrivate->log_prefix);
2970 goto error;
2971 }
2972
2973 if (async && targetPrivate->scheduled) {
2974 /*
2975 * return "host not found", set flags appropriately,
2976 * and schedule notification.
2977 */
2978 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NONAME,
2979 NULL,
2980 (void *)target);
2981 my_info.flags |= (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending);
2982
2983 SCLog(_sc_debug, LOG_INFO, CFSTR("%sno DNS servers are reachable"),
2984 targetPrivate->log_prefix);
2985 __SCNetworkReachabilityPerform(target);
2986 }
2987 break;
2988 }
2989
2990 if (async) {
2991 /* for async requests we return the last known status */
2992 my_info = targetPrivate->info;
2993
2994 if (targetPrivate->dnsPort != NULL) {
2995 /* if request already in progress */
2996 SCLog(_sc_debug, LOG_INFO,
2997 CFSTR("%swaiting for DNS reply"),
2998 targetPrivate->log_prefix);
2999 if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
3000 /* updated reachability based on the previous reply */
3001 goto checkResolvedAddress;
3002 }
3003 break;
3004 }
3005
3006 SCLog(_sc_debug, LOG_INFO,
3007 CFSTR("%sstart DNS query for %s%s%s%s%s"),
3008 targetPrivate->log_prefix,
3009 targetPrivate->name != NULL ? "name = " : "",
3010 targetPrivate->name != NULL ? targetPrivate->name : "",
3011 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
3012 targetPrivate->serv != NULL ? "serv = " : "",
3013 targetPrivate->serv != NULL ? targetPrivate->serv : "");
3014
3015 /*
3016 * initiate an async DNS query
3017 */
3018 if (!startAsyncDNSQuery(target)) {
3019 /* if we could not initiate the request, process error */
3020 goto checkResolvedAddress;
3021 }
3022
3023 /* request initiated */
3024 break;
3025 }
3026
3027 SCLog(_sc_debug, LOG_INFO,
3028 CFSTR("%scheck DNS for %s%s%s%s%s"),
3029 targetPrivate->log_prefix,
3030 targetPrivate->name != NULL ? "name = " : "",
3031 targetPrivate->name != NULL ? targetPrivate->name : "",
3032 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
3033 targetPrivate->serv != NULL ? "serv = " : "",
3034 targetPrivate->serv != NULL ? targetPrivate->serv : "");
3035
3036 /*
3037 * OK, all of the DNS name servers are available. Let's
3038 * resolve the nodename into an address.
3039 */
3040 if (_sc_debug) {
3041 (void) gettimeofday(&dnsQueryStart, NULL);
3042 }
3043
3044 error = getaddrinfo(targetPrivate->name,
3045 targetPrivate->serv,
3046 &targetPrivate->hints,
3047 &res);
3048
3049 __log_query_time(target,
3050 ((error == 0) && (res != NULL)), // if successful query
3051 FALSE, // sync
3052 &dnsQueryStart); // start time
3053
3054 __SCNetworkReachabilitySetResolvedAddress(error, res, target);
3055
3056 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
3057
3058 checkResolvedAddress :
3059
3060 /*
3061 * We first assume that the requested host is NOT available.
3062 * Then, check each address for accessibility and return the
3063 * best status available.
3064 */
3065 my_info = NOT_REACHABLE;
3066
3067 if (isA_CFArray(addresses)) {
3068 CFIndex i;
3069 CFIndex n = CFArrayGetCount(addresses);
3070
3071 for (i = 0; i < n; i++) {
3072 ReachabilityInfo ns_info = NOT_REACHABLE;
3073 struct sockaddr *sa;
3074
3075 sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
3076
3077 ok = checkAddress(storeP,
3078 sa,
3079 &ns_info,
3080 targetPrivate->log_prefix);
3081 if (!ok) {
3082 goto error; /* not today */
3083 }
3084
3085 if (rankReachability(ns_info.flags) > rankReachability(my_info.flags)) {
3086 /* return the best case result */
3087 my_info = ns_info;
3088 if (rankReachability(my_info.flags) == 2) {
3089 /* we're in luck */
3090 break;
3091 }
3092 }
3093 }
3094 } else {
3095 if ((error == EAI_NONAME)
3096 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
3097 || (error == EAI_NODATA)
3098 #endif
3099 ) {
3100 /*
3101 * the target host name could not be resolved
3102 */
3103 if (!targetPrivate->onDemandBypass) {
3104 Boolean onDemand;
3105
3106 /*
3107 * our initial DNS query failed, check again to see if there
3108 * there is an OnDemand configuration that we should be using.
3109 */
3110 onDemand = __SCNetworkReachabilityOnDemandCheck(storeP, target, TRUE, &my_info.flags);
3111 if (onDemand) {
3112 /* if OnDemand connection is needed */
3113 goto done;
3114 }
3115 }
3116
3117 if (!targetPrivate->haveDNS) {
3118 /*
3119 * No DNS servers are defined. Set flags based on
3120 * the availability of configured (but not active)
3121 * services.
3122 */
3123 ok = checkAddress(storeP,
3124 NULL,
3125 &my_info,
3126 targetPrivate->log_prefix);
3127 if (!ok) {
3128 goto error; /* not today */
3129 }
3130
3131 if ((my_info.flags & kSCNetworkReachabilityFlagsReachable) &&
3132 (my_info.flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
3133 /*
3134 * Since we might pick up a set of DNS servers when this connection
3135 * is established, don't reply with a "HOST NOT FOUND" error just yet.
3136 */
3137 break;
3138 }
3139
3140 /* Host not found, not reachable! */
3141 my_info = NOT_REACHABLE;
3142 }
3143 }
3144 }
3145
3146 break;
3147 }
3148 }
3149
3150 done:
3151
3152 *reach_info = my_info;
3153
3154 error :
3155
3156 if (addresses != NULL) CFRelease(addresses);
3157 return ok;
3158 }
3159
3160
3161 Boolean
3162 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target,
3163 SCNetworkReachabilityFlags *flags)
3164 {
3165 Boolean ok = TRUE;
3166 SCDynamicStoreRef store = NULL;
3167 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3168
3169 if (!isA_SCNetworkReachability(target)) {
3170 _SCErrorSet(kSCStatusInvalidArgument);
3171 return FALSE;
3172 }
3173
3174 pthread_mutex_lock(&targetPrivate->lock);
3175
3176 if (targetPrivate->scheduled) {
3177 // if being watched, return the last known (and what should be current) status
3178 *flags = targetPrivate->info.flags & ~kSCNetworkReachabilityFlagsFirstResolvePending;
3179 goto done;
3180 }
3181
3182
3183 ok = __SCNetworkReachabilityGetFlags(&store, target, &targetPrivate->info, FALSE);
3184 *flags = targetPrivate->info.flags & ~kSCNetworkReachabilityFlagsFirstResolvePending;
3185 if (store != NULL) CFRelease(store);
3186
3187 done :
3188
3189 pthread_mutex_unlock(&targetPrivate->lock);
3190 return ok;
3191 }
3192
3193
3194 #pragma mark -
3195 #pragma mark Notifications
3196
3197
3198 static void
3199 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store)
3200 {
3201 CFStringRef key;
3202 CFMutableArrayRef keys;
3203 CFStringRef pattern;
3204 CFMutableArrayRef patterns;
3205
3206 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3207 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3208
3209 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
3210 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3211 kSCDynamicStoreDomainSetup,
3212 kSCEntNetIPv4);
3213 CFArrayAppendValue(keys, key);
3214 CFRelease(key);
3215
3216 // State:/Network/Global/DNS
3217 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3218 kSCDynamicStoreDomainState,
3219 kSCEntNetDNS);
3220 CFArrayAppendValue(keys, key);
3221 CFRelease(key);
3222
3223 // State:/Network/Global/IPv4 (default route)
3224 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3225 kSCDynamicStoreDomainState,
3226 kSCEntNetIPv4);
3227 CFArrayAppendValue(keys, key);
3228 CFRelease(key);
3229
3230 // State:/Network/Global/OnDemand
3231 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3232 kSCDynamicStoreDomainState,
3233 kSCEntNetOnDemand);
3234 CFArrayAppendValue(keys, key);
3235 CFRelease(key);
3236
3237 // Setup: per-service IPv4 info
3238 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
3239 kSCDynamicStoreDomainSetup,
3240 kSCCompAnyRegex,
3241 kSCEntNetIPv4);
3242 CFArrayAppendValue(patterns, pattern);
3243 CFRelease(pattern);
3244
3245 // Setup: per-service Interface info
3246 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
3247 kSCDynamicStoreDomainSetup,
3248 kSCCompAnyRegex,
3249 kSCEntNetInterface);
3250 CFArrayAppendValue(patterns, pattern);
3251 CFRelease(pattern);
3252
3253 // Setup: per-service PPP info (for kSCPropNetPPPDialOnDemand)
3254 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
3255 kSCDynamicStoreDomainSetup,
3256 kSCCompAnyRegex,
3257 kSCEntNetPPP);
3258 CFArrayAppendValue(patterns, pattern);
3259 CFRelease(pattern);
3260
3261 // State: per-service PPP info (for kSCPropNetPPPStatus)
3262 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
3263 kSCDynamicStoreDomainState,
3264 kSCCompAnyRegex,
3265 kSCEntNetPPP);
3266 CFArrayAppendValue(patterns, pattern);
3267 CFRelease(pattern);
3268
3269 // Setup: per-service IPSec info
3270 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
3271 kSCDynamicStoreDomainSetup,
3272 kSCCompAnyRegex,
3273 kSCEntNetIPSec);
3274 CFArrayAppendValue(patterns, pattern);
3275 CFRelease(pattern);
3276
3277 // State: per-interface IPv4 info
3278 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
3279 kSCDynamicStoreDomainState,
3280 kSCCompAnyRegex,
3281 kSCEntNetIPv4);
3282 CFArrayAppendValue(patterns, pattern);
3283 CFRelease(pattern);
3284
3285 // State: per-interface IPv6 info
3286 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
3287 kSCDynamicStoreDomainState,
3288 kSCCompAnyRegex,
3289 kSCEntNetIPv6);
3290 CFArrayAppendValue(patterns, pattern);
3291 CFRelease(pattern);
3292
3293 #if !TARGET_OS_IPHONE
3294 // State: Power Management Capabilities
3295 key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@%@"),
3296 kSCDynamicStoreDomainState,
3297 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix));
3298 CFArrayAppendValue(keys, key);
3299 CFRelease(key);
3300 #endif // TARGET_OS_IPHONE
3301
3302
3303 (void)SCDynamicStoreSetNotificationKeys(store, keys, patterns);
3304 CFRelease(keys);
3305 CFRelease(patterns);
3306
3307 return;
3308 }
3309
3310
3311 static void
3312 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store,
3313 CFArrayRef changedKeys,
3314 void *info)
3315 {
3316 Boolean dnsConfigChanged = FALSE;
3317 CFIndex i;
3318 CFStringRef key;
3319 CFIndex nChanges = CFArrayGetCount(changedKeys);
3320 CFIndex nTargets;
3321 #if !TARGET_OS_IPHONE
3322 Boolean powerStatusChanged = FALSE;
3323 #endif // !TARGET_OS_IPHONE
3324 const void * targets_q[N_QUICK];
3325 const void ** targets = targets_q;
3326
3327 if (nChanges == 0) {
3328 /* if no changes */
3329 return;
3330 }
3331
3332 pthread_mutex_lock(&hn_lock);
3333
3334 nTargets = (hn_targets != NULL) ? CFSetGetCount(hn_targets) : 0;
3335 if (nTargets == 0) {
3336 /* if no addresses being monitored */
3337 goto done;
3338 }
3339
3340 #if !TARGET_OS_IPHONE
3341 key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@%@"),
3342 kSCDynamicStoreDomainState,
3343 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix));
3344 if (CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key)) {
3345 CFNumberRef num;
3346
3347 num = SCDynamicStoreCopyValue(store, key);
3348 if (num != NULL) {
3349 if (isA_CFNumber(num) &&
3350 CFNumberGetValue(num, kCFNumberSInt32Type, &power_capabilities)) {
3351 powerStatusChanged = TRUE;
3352 }
3353
3354 CFRelease(num);
3355 }
3356 }
3357 CFRelease(key);
3358 #endif // !TARGET_OS_IPHONE
3359
3360 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3361 kSCDynamicStoreDomainState,
3362 kSCEntNetDNS);
3363 if (CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key)) {
3364 dnsConfigChanged = TRUE; /* the DNS server(s) have changed */
3365 }
3366 CFRelease(key);
3367
3368 if (_sc_debug) {
3369 int changes = 0;
3370 const char *str;
3371
3372 #if !TARGET_OS_IPHONE
3373 if (powerStatusChanged) {
3374 changes |= 4;
3375 nChanges -= 1;
3376 }
3377 #endif // !TARGET_OS_IPHONE
3378
3379 if (dnsConfigChanged) {
3380 changes |= 2;
3381 nChanges -= 1;
3382 }
3383
3384 if (nChanges > 0) {
3385 changes |= 1;
3386 }
3387
3388 switch (changes) {
3389 case 0 : str = ""; break;
3390 case 1 : str = "network "; break;
3391 case 2 : str = "DNS "; break;
3392 case 3 : str = "network and DNS "; break;
3393 #if !TARGET_OS_IPHONE
3394 case 4 : str = "power "; break;
3395 case 5 : str = "network and power "; break;
3396 case 6 : str = "DNS and power "; break;
3397 case 7 : str = "network, DNS, and power "; break;
3398 #endif // !TARGET_OS_IPHONE
3399 default : str = "??? ";
3400 }
3401
3402 SCLog(TRUE, LOG_INFO, CFSTR("process %sconfiguration change"), str);
3403 }
3404
3405 if (nTargets > (CFIndex)(sizeof(targets_q) / sizeof(CFTypeRef)))
3406 targets = CFAllocatorAllocate(NULL, nTargets * sizeof(CFTypeRef), 0);
3407 CFSetGetValues(hn_targets, targets);
3408 for (i = 0; i < nTargets; i++) {
3409 SCNetworkReachabilityRef target = targets[i];
3410 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3411
3412 pthread_mutex_lock(&targetPrivate->lock);
3413
3414 if (targetPrivate->type == reachabilityTypeName) {
3415 Boolean dnsChanged = dnsConfigChanged;
3416
3417 if (!dnsChanged) {
3418 /*
3419 * if the DNS configuration didn't change we still need to
3420 * check that the DNS servers are accessible.
3421 */
3422 SCNetworkReachabilityFlags ns_flags;
3423 Boolean ok;
3424
3425 /* check the reachability of the DNS servers */
3426 ok = _SC_R_checkResolverReachability(&store,
3427 &ns_flags,
3428 &targetPrivate->haveDNS,
3429 targetPrivate->name,
3430 targetPrivate->serv,
3431 targetPrivate->log_prefix);
3432 if (!ok) {
3433 /* if we could not get DNS server info */
3434 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server reachability unknown"),
3435 targetPrivate->log_prefix);
3436 dnsChanged = TRUE;
3437 } else if (rankReachability(ns_flags) < 2) {
3438 /*
3439 * if DNS servers are not (or are no longer) reachable, set
3440 * flags based on the availability of configured (but not
3441 * active) services.
3442 */
3443 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"),
3444 targetPrivate->log_prefix);
3445 dnsChanged = TRUE;
3446 }
3447 }
3448
3449 if (dnsChanged) {
3450 if (targetPrivate->dnsPort != NULL) {
3451 mach_port_t mp = CFMachPortGetPort(targetPrivate->dnsPort);
3452
3453 /* cancel the outstanding DNS query */
3454 SCLog(_sc_debug, LOG_INFO,
3455 CFSTR("%scancel DNS query for %s%s%s%s%s"),
3456 targetPrivate->log_prefix,
3457 targetPrivate->name != NULL ? "name = " : "",
3458 targetPrivate->name != NULL ? targetPrivate->name : "",
3459 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
3460 targetPrivate->serv != NULL ? "serv = " : "",
3461 targetPrivate->serv != NULL ? targetPrivate->serv : "");
3462 dequeueAsyncDNSQuery(target);
3463 getaddrinfo_async_cancel(mp);
3464 }
3465
3466 /* schedule request to resolve the name again */
3467 targetPrivate->needResolve = TRUE;
3468 }
3469 }
3470
3471 __SCNetworkReachabilityPerform(target);
3472
3473 pthread_mutex_unlock(&targetPrivate->lock);
3474 }
3475 if (targets != targets_q) CFAllocatorDeallocate(NULL, targets);
3476
3477 done :
3478
3479 pthread_mutex_unlock(&hn_lock);
3480 return;
3481 }
3482
3483
3484 #if !TARGET_OS_IPHONE
3485 static __inline__ Boolean
3486 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities)
3487 {
3488
3489 #define POWER_CAPABILITIES_NEED (kIOPMSystemPowerStateCapabilityCPU \
3490 | kIOPMSystemPowerStateCapabilityNetwork \
3491 | kIOPMSystemPowerStateCapabilityDisk)
3492
3493 if ((power_capabilities & POWER_CAPABILITIES_NEED) != POWER_CAPABILITIES_NEED) {
3494 /*
3495 * we're not awake (from a networking point of view) unless we
3496 * have the CPU, disk, *and* network.
3497 */
3498 return FALSE;
3499 }
3500
3501 if ((power_capabilities & kIOPMSytemPowerStateCapabilitiesMask) == POWER_CAPABILITIES_NEED) {
3502 /*
3503 * if all we have is the CPU, disk, and network than this must
3504 * be a "maintenance" wake.
3505 */
3506 return FALSE;
3507 }
3508
3509 return TRUE;
3510 }
3511 #endif // !TARGET_OS_IPHONE
3512
3513
3514 static void
3515 rlsPerform(void *info)
3516 {
3517 void *context_info;
3518 void (*context_release)(const void *);
3519 Boolean defer = FALSE;
3520 Boolean ok;
3521 ReachabilityInfo reach_info = NOT_REACHABLE;
3522 SCNetworkReachabilityCallBack rlsFunction;
3523 SCDynamicStoreRef store = NULL;
3524 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
3525 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3526
3527 SCLog(_sc_debug, LOG_INFO, CFSTR("%schecking target reachability"),
3528 targetPrivate->log_prefix);
3529
3530
3531 pthread_mutex_lock(&targetPrivate->lock);
3532
3533 if (!targetPrivate->scheduled) {
3534 // if not currently scheduled
3535 pthread_mutex_unlock(&targetPrivate->lock);
3536 return;
3537 }
3538
3539 /* update reachability, notify if status changed */
3540 ok = __SCNetworkReachabilityGetFlags(&store, target, &reach_info, TRUE);
3541 if (store != NULL) CFRelease(store);
3542 if (!ok) {
3543 /* if reachability status not available */
3544 SCLog(_sc_debug, LOG_INFO, CFSTR("%flags not available"),
3545 targetPrivate->log_prefix);
3546 reach_info = NOT_REACHABLE;
3547 }
3548
3549 #if !TARGET_OS_IPHONE
3550 /*
3551 * We want to defer the notification if this is a maintenance wake *and*
3552 * the reachability flags that we would be reporting to the application
3553 * are better than those that we last reported.
3554 */
3555 if (!systemIsAwake(power_capabilities)) {
3556 /* if this is a maintenace wake */
3557 reach_info.sleeping = TRUE;
3558
3559 if (rankReachability(reach_info.flags) >= rankReachability(targetPrivate->info.flags)) {
3560 /*
3561 * don't report the change if the new reachability flags are
3562 * the same or "better"
3563 */
3564 defer = TRUE;
3565 } else if (bcmp(&targetPrivate->last_notify, &reach_info, sizeof(reach_info)) == 0) {
3566 /* if we have already posted this change */
3567 defer = TRUE;
3568 }
3569 }
3570 #endif // !TARGET_OS_IPHONE
3571
3572 if (bcmp(&targetPrivate->info, &reach_info, sizeof(reach_info)) == 0) {
3573 SCLog(_sc_debug, LOG_INFO,
3574 CFSTR("%sflags/interface match (now 0x%08x/%hu%s)"),
3575 targetPrivate->log_prefix,
3576 reach_info.flags,
3577 reach_info.if_index,
3578 reach_info.sleeping ? "*" : "");
3579 pthread_mutex_unlock(&targetPrivate->lock);
3580 return;
3581 }
3582
3583 SCLog(_sc_debug, LOG_INFO,
3584 CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s"),
3585 targetPrivate->log_prefix,
3586 targetPrivate->info.flags,
3587 targetPrivate->info.if_index,
3588 targetPrivate->info.sleeping ? "*" : "",
3589 reach_info.flags,
3590 reach_info.if_index,
3591 reach_info.sleeping ? "*" : "",
3592 defer ? ", deferred" : "");
3593
3594 /* update flags / interface */
3595 targetPrivate->info = reach_info;
3596
3597 /* as needed, defer the notification */
3598 if (defer) {
3599 pthread_mutex_unlock(&targetPrivate->lock);
3600 return;
3601 }
3602
3603 /* save last notification info */
3604 targetPrivate->last_notify = reach_info;
3605
3606 /* callout */
3607 rlsFunction = targetPrivate->rlsFunction;
3608 if (targetPrivate->rlsContext.retain != NULL) {
3609 context_info = (void *)(*targetPrivate->rlsContext.retain)(targetPrivate->rlsContext.info);
3610 context_release = targetPrivate->rlsContext.release;
3611 } else {
3612 context_info = targetPrivate->rlsContext.info;
3613 context_release = NULL;
3614 }
3615
3616 pthread_mutex_unlock(&targetPrivate->lock);
3617
3618 if (rlsFunction != NULL) {
3619 (*rlsFunction)(target, reach_info.flags, context_info);
3620 }
3621
3622 if (context_release != NULL) {
3623 (*context_release)(context_info);
3624 }
3625
3626 return;
3627 }
3628
3629
3630 Boolean
3631 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target,
3632 SCNetworkReachabilityCallBack callout,
3633 SCNetworkReachabilityContext *context)
3634 {
3635 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3636
3637 pthread_mutex_lock(&targetPrivate->lock);
3638
3639 if (targetPrivate->rlsContext.release != NULL) {
3640 /* let go of the current context */
3641 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
3642 }
3643
3644 targetPrivate->rlsFunction = callout;
3645 targetPrivate->rlsContext.info = NULL;
3646 targetPrivate->rlsContext.retain = NULL;
3647 targetPrivate->rlsContext.release = NULL;
3648 targetPrivate->rlsContext.copyDescription = NULL;
3649 if (context) {
3650 bcopy(context, &targetPrivate->rlsContext, sizeof(SCNetworkReachabilityContext));
3651 if (context->retain != NULL) {
3652 targetPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
3653 }
3654 }
3655
3656 pthread_mutex_unlock(&targetPrivate->lock);
3657
3658 return TRUE;
3659 }
3660
3661
3662 static CFStringRef
3663 reachRLSCopyDescription(const void *info)
3664 {
3665 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
3666
3667 return CFStringCreateWithFormat(NULL,
3668 NULL,
3669 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
3670 target);
3671 }
3672
3673
3674 static Boolean
3675 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target,
3676 CFRunLoopRef runLoop,
3677 CFStringRef runLoopMode,
3678 #if !TARGET_OS_IPHONE
3679 dispatch_queue_t queue,
3680 #else // !TARGET_OS_IPHONE
3681 void *queue,
3682 #endif // !TARGET_OS_IPHONE
3683 Boolean onDemand)
3684 {
3685 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3686 Boolean init = FALSE;
3687 Boolean ok = FALSE;
3688
3689 if (!onDemand) {
3690 pthread_mutex_lock(&hn_lock);
3691 }
3692 pthread_mutex_lock(&targetPrivate->lock);
3693
3694 #if !TARGET_OS_IPHONE
3695 if ((targetPrivate->dispatchQueue != NULL) || // if we are already scheduled with a dispatch queue
3696 ((queue != NULL) && targetPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
3697 _SCErrorSet(kSCStatusInvalidArgument);
3698 goto done;
3699 }
3700 #endif // !TARGET_OS_IPHONE
3701
3702 /* schedule the SCNetworkReachability run loop source */
3703
3704 if (!onDemand && (hn_store == NULL)) {
3705 /*
3706 * if we are not monitoring any hosts, start watching
3707 */
3708 if (!dns_configuration_watch()) {
3709 // if error
3710 _SCErrorSet(kSCStatusFailed);
3711 goto done;
3712 }
3713
3714 hn_store = SCDynamicStoreCreate(NULL,
3715 CFSTR("SCNetworkReachability"),
3716 __SCNetworkReachabilityHandleChanges,
3717 NULL);
3718 if (hn_store == NULL) {
3719 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreCreate() failed"));
3720 goto done;
3721 }
3722
3723 __SCNetworkReachabilityReachabilitySetNotifications(hn_store);
3724
3725 #if !TARGET_OS_IPHONE
3726 hn_dispatchQueue = dispatch_queue_create("com.apple.SCNetworkReachabilty.network_changes", NULL);
3727 if (hn_dispatchQueue == NULL) {
3728 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed"));
3729 _SCErrorSet(kSCStatusFailed);
3730 CFRelease(hn_store);
3731 hn_store = NULL;
3732 goto done;
3733 }
3734 CFRetain(hn_store); // Note: will be released when the dispatch queue is released
3735 dispatch_set_context(hn_dispatchQueue, (void *)hn_store);
3736 dispatch_set_finalizer_f(hn_dispatchQueue, (dispatch_function_t)CFRelease);
3737
3738 ok = SCDynamicStoreSetDispatchQueue(hn_store, hn_dispatchQueue);
3739 if (!ok) {
3740 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
3741 dispatch_release(hn_dispatchQueue);
3742 hn_dispatchQueue = NULL;
3743 CFRelease(hn_store);
3744 hn_store = NULL;
3745 goto done;
3746 }
3747 #else // !TARGET_OS_IPHONE
3748 hn_storeRLS = SCDynamicStoreCreateRunLoopSource(NULL, hn_store, 0);
3749 hn_rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3750 #endif // !TARGET_OS_IPHONE
3751 hn_targets = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
3752 }
3753
3754 if (!targetPrivate->scheduled) {
3755 CFRunLoopSourceContext context = { 0 // version
3756 , (void *)target // info
3757 , CFRetain // retain
3758 , CFRelease // release
3759 , reachRLSCopyDescription // copyDescription
3760 , CFEqual // equal
3761 , CFHash // hash
3762 , NULL // schedule
3763 , NULL // cancel
3764 , rlsPerform // perform
3765 };
3766
3767 if (runLoop != NULL) {
3768 targetPrivate->rls = CFRunLoopSourceCreate(NULL, 0, &context);
3769 targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3770 }
3771
3772 targetPrivate->scheduled = TRUE;
3773 if (targetPrivate->type == reachabilityTypeName) {
3774 targetPrivate->needResolve = TRUE;
3775 }
3776 init = TRUE;
3777 }
3778
3779 #if !TARGET_OS_IPHONE
3780 if (queue != NULL) {
3781 targetPrivate->dispatchQueue = queue;
3782 dispatch_retain(targetPrivate->dispatchQueue);
3783 } else
3784 #endif // !TARGET_OS_IPHONE
3785 {
3786 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
3787 /*
3788 * if we do not already have host notifications scheduled with
3789 * this runLoop / runLoopMode
3790 */
3791 CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode);
3792
3793 if (targetPrivate->dnsRLS != NULL) {
3794 /* if we have an active async DNS query too */
3795 CFRunLoopAddSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
3796 }
3797 }
3798
3799 _SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
3800
3801 #if TARGET_OS_IPHONE
3802 if (!onDemand) {
3803 /* schedule the global SCDynamicStore run loop source */
3804
3805 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
3806 /*
3807 * if we do not already have SC notifications scheduled with
3808 * this runLoop / runLoopMode
3809 */
3810 CFRunLoopAddSource(runLoop, hn_storeRLS, runLoopMode);
3811 }
3812
3813 _SC_schedule(target, runLoop, runLoopMode, hn_rlList);
3814 }
3815 #endif // TARGET_OS_IPHONE
3816 }
3817
3818 CFSetAddValue(hn_targets, target);
3819
3820 if (init) {
3821 ReachabilityInfo reach_info = NOT_REACHABLE;
3822 SCDynamicStoreRef store = NULL;
3823
3824 /*
3825 * if we have yet to schedule SC notifications for this address
3826 * - initialize current reachability status
3827 */
3828 if (__SCNetworkReachabilityGetFlags(&store, target, &reach_info, TRUE)) {
3829 /*
3830 * if reachability status available
3831 * - set flags
3832 * - schedule notification to report status via callback
3833 */
3834 targetPrivate->info = reach_info;
3835 __SCNetworkReachabilityPerform(target);
3836 } else {
3837 /* if reachability status not available, async lookup started */
3838 targetPrivate->info = NOT_REACHABLE;
3839 }
3840 if (store != NULL) CFRelease(store);
3841 }
3842
3843 if (targetPrivate->onDemandServer != NULL) {
3844 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, runLoop, runLoopMode, queue, TRUE);
3845 }
3846
3847 ok = TRUE;
3848
3849 done :
3850
3851 pthread_mutex_unlock(&targetPrivate->lock);
3852 if (!onDemand) {
3853 pthread_mutex_unlock(&hn_lock);
3854 }
3855 return ok;
3856 }
3857
3858
3859 static Boolean
3860 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target,
3861 CFRunLoopRef runLoop,
3862 CFStringRef runLoopMode,
3863 Boolean onDemand)
3864 {
3865 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3866 CFIndex n = 0;
3867 Boolean ok = FALSE;
3868
3869 if (!onDemand) {
3870 pthread_mutex_lock(&hn_lock);
3871 }
3872 pthread_mutex_lock(&targetPrivate->lock);
3873
3874 #if !TARGET_OS_IPHONE
3875 if (((runLoop == NULL) && (targetPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
3876 ((runLoop != NULL) && (targetPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are not)
3877 _SCErrorSet(kSCStatusInvalidArgument);
3878 goto done;
3879 }
3880 #endif // !TARGET_OS_IPHONE
3881
3882 if (!targetPrivate->scheduled) {
3883 // if not currently scheduled
3884 _SCErrorSet(kSCStatusInvalidArgument);
3885 goto done;
3886 }
3887
3888 // first, unschedule the target specific sources
3889 #if !TARGET_OS_IPHONE
3890 if (targetPrivate->dispatchQueue != NULL) {
3891 if (targetPrivate->onDemandServer != NULL) {
3892 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE);
3893 }
3894 } else
3895 #endif // !TARGET_OS_IPHONE
3896 {
3897 if (!_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
3898 // if not currently scheduled
3899 _SCErrorSet(kSCStatusInvalidArgument);
3900 goto done;
3901 }
3902
3903 if (targetPrivate->onDemandServer != NULL) {
3904 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, runLoop, runLoopMode, TRUE);
3905 }
3906
3907 n = CFArrayGetCount(targetPrivate->rlList);
3908 if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
3909 // if target is no longer scheduled for this runLoop / runLoopMode
3910 CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode);
3911
3912 if (targetPrivate->dnsRLS != NULL) {
3913 // if we have an active async DNS query too
3914 CFRunLoopRemoveSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
3915 }
3916
3917 if (n == 0) {
3918 // if *all* notifications have been unscheduled
3919 CFRelease(targetPrivate->rlList);
3920 targetPrivate->rlList = NULL;
3921 CFRunLoopSourceInvalidate(targetPrivate->rls);
3922 CFRelease(targetPrivate->rls);
3923 targetPrivate->rls = NULL;
3924 }
3925 }
3926 }
3927
3928 if (n == 0) {
3929 // if *all* notifications have been unscheduled
3930 targetPrivate->scheduled = FALSE;
3931
3932 if (!onDemand) {
3933 CFSetRemoveValue(hn_targets, target); // cleanup notification resources
3934 }
3935
3936 if (targetPrivate->dnsPort != NULL) {
3937 mach_port_t mp = CFMachPortGetPort(targetPrivate->dnsPort);
3938
3939 // if we have an active async DNS query
3940 dequeueAsyncDNSQuery(target);
3941 getaddrinfo_async_cancel(mp);
3942 }
3943 }
3944
3945 #if !TARGET_OS_IPHONE
3946 if (runLoop == NULL) {
3947 dispatch_release(targetPrivate->dispatchQueue);
3948 targetPrivate->dispatchQueue = NULL;
3949 }
3950 #endif // !TARGET_OS_IPHONE
3951
3952 // now, unschedule the global dynamic store source
3953 #if TARGET_OS_IPHONE
3954 if (!onDemand) {
3955 (void)_SC_unschedule(target, runLoop, runLoopMode, hn_rlList, FALSE);
3956
3957 n = CFArrayGetCount(hn_rlList);
3958 if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
3959 /*
3960 * if we no longer have any addresses scheduled for
3961 * this runLoop / runLoopMode
3962 */
3963 CFRunLoopRemoveSource(runLoop, hn_storeRLS, runLoopMode);
3964
3965 if (n > 0) {
3966 if (CFEqual(runLoopMode, kCFRunLoopCommonModes)) {
3967 CFArrayRef modes;
3968
3969 modes = CFRunLoopCopyAllModes(runLoop);
3970 if (modes != NULL) {
3971 CFIndex i;
3972 CFIndex n = CFArrayGetCount(modes);
3973
3974 for (i = 0; i < n; i++) {
3975 CFStringRef mode;
3976
3977 mode = CFArrayGetValueAtIndex(modes, i);
3978 if (_SC_isScheduled(NULL, runLoop, mode, hn_rlList)) {
3979 /*
3980 * removing kCFRunLoopCommonModes cleaned up more
3981 * than we wanted. Add back the modes that were
3982 * expect to be present.
3983 */
3984 CFRunLoopAddSource(runLoop, hn_storeRLS, mode);
3985 }
3986 }
3987
3988 CFRelease(modes);
3989 }
3990 } else if (_SC_isScheduled(NULL, runLoop, kCFRunLoopCommonModes, hn_rlList)) {
3991 /*
3992 * if we are still scheduling kCFRunLoopCommonModes, make sure that
3993 * none of the common modes were inadvertently removed.
3994 */
3995 CFRunLoopAddSource(runLoop, hn_storeRLS, kCFRunLoopCommonModes);
3996 }
3997 }
3998 }
3999 }
4000 #endif // TARGET_OS_IPHONE
4001
4002 n = CFSetGetCount(hn_targets);
4003 if (n == 0) {
4004 // if we are no longer monitoring any targets
4005 #if !TARGET_OS_IPHONE
4006 SCDynamicStoreSetDispatchQueue(hn_store, NULL);
4007 dispatch_release(hn_dispatchQueue);
4008 hn_dispatchQueue = NULL;
4009 #else // !TARGET_OS_IPHONE
4010 CFRunLoopSourceInvalidate(hn_storeRLS);
4011 CFRelease(hn_storeRLS);
4012 hn_storeRLS = NULL;
4013 CFRelease(hn_rlList);
4014 hn_rlList = NULL;
4015 #endif // !TARGET_OS_IPHONE
4016 CFRelease(hn_store);
4017 hn_store = NULL;
4018 CFRelease(hn_targets);
4019 hn_targets = NULL;
4020
4021 /*
4022 * until we start monitoring again, ensure that
4023 * any resources associated with tracking the
4024 * DNS configuration have been released.
4025 */
4026 dns_configuration_unwatch();
4027 }
4028
4029 ok = TRUE;
4030
4031 done :
4032
4033 pthread_mutex_unlock(&targetPrivate->lock);
4034 if (!onDemand) {
4035 pthread_mutex_unlock(&hn_lock);
4036 }
4037 return ok;
4038 }
4039
4040 Boolean
4041 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target,
4042 CFRunLoopRef runLoop,
4043 CFStringRef runLoopMode)
4044 {
4045 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) {
4046 _SCErrorSet(kSCStatusInvalidArgument);
4047 return FALSE;
4048 }
4049
4050 return __SCNetworkReachabilityScheduleWithRunLoop(target, runLoop, runLoopMode, NULL, FALSE);
4051 }
4052
4053 Boolean
4054 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target,
4055 CFRunLoopRef runLoop,
4056 CFStringRef runLoopMode)
4057 {
4058 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) {
4059 _SCErrorSet(kSCStatusInvalidArgument);
4060 return FALSE;
4061 }
4062
4063 return __SCNetworkReachabilityUnscheduleFromRunLoop(target, runLoop, runLoopMode, FALSE);
4064 }
4065
4066 #if !TARGET_OS_IPHONE
4067 Boolean
4068 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target,
4069 dispatch_queue_t queue)
4070 {
4071 Boolean ok = FALSE;
4072
4073 if (!isA_SCNetworkReachability(target)) {
4074 _SCErrorSet(kSCStatusInvalidArgument);
4075 return FALSE;
4076 }
4077
4078 if (queue != NULL) {
4079 ok = __SCNetworkReachabilityScheduleWithRunLoop(target, NULL, NULL, queue, FALSE);
4080 } else {
4081 ok = __SCNetworkReachabilityUnscheduleFromRunLoop(target, NULL, NULL, FALSE);
4082 }
4083
4084 return ok;
4085 }
4086 #endif // !TARGET_OS_IPHONE