]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkReachability.c
configd-293.5.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 if (targetPrivate->dnsPort != NULL) {
2010 CFMachPortInvalidate(targetPrivate->dnsPort);
2011 CFRelease(targetPrivate->dnsPort);
2012 targetPrivate->dnsPort = NULL;
2013 targetPrivate->dnsMP = MACH_PORT_NULL;
2014 }
2015
2016 return;
2017 }
2018
2019
2020 static void
2021 processAsyncDNSReply(mach_port_t mp, void *msg, SCNetworkReachabilityRef target)
2022 {
2023 int32_t status;
2024 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2025
2026 pthread_mutex_lock(&targetPrivate->lock);
2027
2028 if (mp != targetPrivate->dnsMP) {
2029 // we've received a callback on the async DNS port but since the
2030 // associated CFMachPort doesn't match than the request must have
2031 // already been cancelled.
2032 SCLog(TRUE, LOG_ERR, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP"));
2033 pthread_mutex_unlock(&targetPrivate->lock);
2034 return;
2035 }
2036
2037 dequeueAsyncDNSQuery(target);
2038 status = getaddrinfo_async_handle_reply(msg);
2039 if ((status == 0) &&
2040 (targetPrivate->resolvedAddress == NULL) && (targetPrivate->resolvedAddressError == NETDB_SUCCESS)) {
2041 Boolean ok;
2042
2043 // if the request is not complete and needs to be re-queued
2044 ok = enqueueAsyncDNSQuery(target, mp);
2045 if (!ok) {
2046 SCLog(TRUE, LOG_ERR, CFSTR("processAsyncDNSReply enqueueAsyncDNSQuery() failed"));
2047 }
2048 }
2049
2050 pthread_mutex_unlock(&targetPrivate->lock);
2051
2052 return;
2053 }
2054
2055
2056 static Boolean
2057 check_resolver_reachability(SCDynamicStoreRef *storeP,
2058 dns_resolver_t *resolver,
2059 SCNetworkReachabilityFlags *flags,
2060 Boolean *haveDNS,
2061 const char *log_prefix)
2062 {
2063 int i;
2064 Boolean ok = TRUE;
2065
2066 *flags = kSCNetworkReachabilityFlagsReachable;
2067 *haveDNS = FALSE;
2068
2069 for (i = 0; i < resolver->n_nameserver; i++) {
2070 struct sockaddr *address = resolver->nameserver[i];
2071 ReachabilityInfo ns_info;
2072
2073 *haveDNS = TRUE;
2074
2075 if (address->sa_family != AF_INET) {
2076 /*
2077 * we need to skip non-IPv4 DNS server
2078 * addresses (at least until [3510431] has
2079 * been resolved).
2080 */
2081 continue;
2082 }
2083
2084 ok = checkAddress(storeP, address, &ns_info, log_prefix);
2085 if (!ok) {
2086 /* not today */
2087 goto done;
2088 }
2089
2090 if (rankReachability(ns_info.flags) < rankReachability(*flags)) {
2091 /* return the worst case result */
2092 *flags = ns_info.flags;
2093 }
2094 }
2095
2096 done :
2097
2098 return ok;
2099 }
2100
2101
2102 static Boolean
2103 check_matching_resolvers(SCDynamicStoreRef *storeP,
2104 dns_config_t *dns_config,
2105 const char *fqdn,
2106 SCNetworkReachabilityFlags *flags,
2107 Boolean *haveDNS,
2108 const char *log_prefix)
2109 {
2110 int i;
2111 Boolean matched = FALSE;
2112 const char *name = fqdn;
2113
2114 while (!matched && (name != NULL)) {
2115 int len;
2116
2117 /*
2118 * check if the provided name (or sub-component)
2119 * matches one of our resolver configurations.
2120 */
2121 len = strlen(name);
2122 for (i = 0; i < dns_config->n_resolver; i++) {
2123 char *domain;
2124 dns_resolver_t *resolver;
2125
2126 resolver = dns_config->resolver[i];
2127 domain = resolver->domain;
2128 if (domain != NULL && (len == strlen(domain))) {
2129 if (strcasecmp(name, domain) == 0) {
2130 Boolean ok;
2131
2132 /*
2133 * if name matches domain
2134 */
2135 matched = TRUE;
2136 ok = check_resolver_reachability(storeP, resolver, flags, haveDNS, log_prefix);
2137 if (!ok) {
2138 /* not today */
2139 return FALSE;
2140 }
2141 }
2142 }
2143 }
2144
2145 if (!matched) {
2146 /*
2147 * we have not found a matching resolver, try
2148 * a less qualified domain
2149 */
2150 name = strchr(name, '.');
2151 if ((name != NULL) && (*name != '\0')) {
2152 name++;
2153 } else {
2154 name = NULL;
2155 }
2156 }
2157 }
2158
2159 return matched;
2160 }
2161
2162
2163 static dns_configuration_t *
2164 dns_configuration_retain()
2165 {
2166 pthread_mutex_lock(&dns_lock);
2167
2168 if ((dns_configuration != NULL) && dns_token_valid) {
2169 int check = 0;
2170 uint32_t status;
2171
2172 /*
2173 * check if the global [DNS] configuration snapshot needs
2174 * to be updated
2175 */
2176 status = notify_check(dns_token, &check);
2177 if (status != NOTIFY_STATUS_OK) {
2178 SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%lu"), status);
2179 }
2180
2181 if ((status != NOTIFY_STATUS_OK) || (check != 0)) {
2182 /*
2183 * if the snapshot needs to be refreshed
2184 */
2185 if (dns_configuration->refs == 0) {
2186 dns_configuration_free(dns_configuration->config);
2187 CFAllocatorDeallocate(NULL, dns_configuration);
2188 }
2189 dns_configuration = NULL;
2190 }
2191 }
2192
2193 if (dns_configuration == NULL) {
2194 dns_config_t *new_config;
2195
2196 new_config = dns_configuration_copy();
2197 if (new_config != NULL) {
2198 dns_configuration = CFAllocatorAllocate(NULL, sizeof(dns_configuration_t), 0);
2199 dns_configuration->config = new_config;
2200 dns_configuration->refs = 0;
2201 }
2202 }
2203
2204 if (dns_configuration != NULL) {
2205 dns_configuration->refs++;
2206 }
2207
2208 pthread_mutex_unlock(&dns_lock);
2209 return dns_configuration;
2210 }
2211
2212
2213 static void
2214 dns_configuration_release(dns_configuration_t *config)
2215 {
2216 pthread_mutex_lock(&dns_lock);
2217
2218 config->refs--;
2219 if (config->refs == 0) {
2220 if ((dns_configuration != config)) {
2221 dns_configuration_free(config->config);
2222 CFAllocatorDeallocate(NULL, config);
2223 }
2224 }
2225
2226 pthread_mutex_unlock(&dns_lock);
2227 return;
2228 }
2229
2230
2231 static Boolean
2232 dns_configuration_watch()
2233 {
2234 int dns_check = 0;
2235 const char *dns_key;
2236 Boolean ok = FALSE;
2237 uint32_t status;
2238
2239 pthread_mutex_lock(&dns_lock);
2240
2241 dns_key = dns_configuration_notify_key();
2242 if (dns_key == NULL) {
2243 SCLog(TRUE, LOG_INFO, CFSTR("dns_configuration_notify_key() failed"));
2244 goto done;
2245 }
2246
2247 status = notify_register_check(dns_key, &dns_token);
2248 if (status == NOTIFY_STATUS_OK) {
2249 dns_token_valid = TRUE;
2250 } else {
2251 SCLog(TRUE, LOG_INFO, CFSTR("notify_register_check() failed, status=%lu"), status);
2252 goto done;
2253 }
2254
2255 status = notify_check(dns_token, &dns_check);
2256 if (status != NOTIFY_STATUS_OK) {
2257 SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%lu"), status);
2258 (void)notify_cancel(dns_token);
2259 dns_token_valid = FALSE;
2260 goto done;
2261 }
2262
2263 ok = TRUE;
2264
2265 done :
2266
2267 pthread_mutex_unlock(&dns_lock);
2268 return ok;
2269 }
2270
2271
2272 static void
2273 dns_configuration_unwatch()
2274 {
2275 pthread_mutex_lock(&dns_lock);
2276
2277 (void)notify_cancel(dns_token);
2278 dns_token_valid = FALSE;
2279
2280 if ((dns_configuration != NULL) && (dns_configuration->refs == 0)) {
2281 dns_configuration_free(dns_configuration->config);
2282 CFAllocatorDeallocate(NULL, dns_configuration);
2283 dns_configuration = NULL;
2284 }
2285
2286 pthread_mutex_unlock(&dns_lock);
2287 return;
2288 }
2289
2290
2291 static Boolean
2292 _SC_R_checkResolverReachability(SCDynamicStoreRef *storeP,
2293 SCNetworkReachabilityFlags *flags,
2294 Boolean *haveDNS,
2295 const char *nodename,
2296 const char *servname,
2297 const char *log_prefix)
2298 {
2299 dns_resolver_t *default_resolver;
2300 dns_configuration_t *dns;
2301 Boolean found = FALSE;
2302 char *fqdn = (char *)nodename;
2303 int i;
2304 Boolean isFQDN = FALSE;
2305 uint32_t len;
2306 Boolean ok = TRUE;
2307 Boolean useDefault = FALSE;
2308
2309 /*
2310 * We first assume that all of the configured DNS servers
2311 * are available. Since we don't know which name server will
2312 * be consulted to resolve the specified nodename we need to
2313 * check the availability of ALL name servers. We can only
2314 * proceed if we know that our query can be answered.
2315 */
2316
2317 *flags = kSCNetworkReachabilityFlagsReachable;
2318 *haveDNS = FALSE;
2319
2320 len = (nodename != NULL) ? strlen(nodename) : 0;
2321 if (len == 0) {
2322 if ((servname == NULL) || (strlen(servname) == 0)) {
2323 // if no nodename or servname, return not reachable
2324 *flags = 0;
2325 }
2326 return ok;
2327 }
2328
2329 dns = dns_configuration_retain();
2330 if (dns == NULL) {
2331 // if error
2332 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no configuration"), log_prefix);
2333 goto done;
2334 }
2335
2336 if (dns->config->n_resolver == 0) {
2337 // if no resolver configuration
2338 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no resolvers"), log_prefix);
2339 goto done;
2340 }
2341
2342 *flags = kSCNetworkReachabilityFlagsReachable;
2343
2344 if (fqdn[len - 1] == '.') {
2345 isFQDN = TRUE;
2346
2347 // trim trailing '.''s
2348 while ((len > 0) && (fqdn[len-1] == '.')) {
2349 if (fqdn == nodename) {
2350 fqdn = strdup(nodename);
2351 }
2352 fqdn[--len] = '\0';
2353 }
2354 }
2355
2356 default_resolver = dns->config->resolver[0];
2357
2358 /*
2359 * check if the provided name matches a supplemental domain
2360 */
2361 found = check_matching_resolvers(storeP, dns->config, fqdn, flags, haveDNS, log_prefix);
2362
2363 if (!found && !isFQDN) {
2364 /*
2365 * if we did not match a supplemental domain name and if the
2366 * provided name has enough "."s then the first query will be
2367 * directed to the default resolver.
2368 */
2369 char *cp;
2370 int dots;
2371 int ndots = 1;
2372
2373 #define NDOTS_OPT "ndots="
2374 #define NDOTS_OPT_LEN (sizeof("ndots=") - 1)
2375
2376 if (default_resolver->options != NULL) {
2377 cp = strstr(default_resolver->options, NDOTS_OPT);
2378 if ((cp != NULL) &&
2379 ((cp == default_resolver->options) || isspace(cp[-1])) &&
2380 ((cp[NDOTS_OPT_LEN] != '\0') && isdigit(cp[NDOTS_OPT_LEN]))) {
2381 char *end;
2382 long val;
2383
2384 cp += NDOTS_OPT_LEN;
2385 errno = 0;
2386 val = strtol(cp, &end, 10);
2387 if ((*cp != '\0') && (cp != end) && (errno == 0) &&
2388 ((*end == '\0') || isspace(*end))) {
2389 ndots = val;
2390 }
2391 }
2392 }
2393
2394 dots = 0;
2395 for (cp = fqdn; *cp != '\0'; cp++) {
2396 if (*cp == '.') dots++;
2397 }
2398
2399 if (dots > ndots) {
2400 useDefault = TRUE;
2401 }
2402 }
2403
2404 if (!found && !isFQDN && !useDefault && (dns->config->n_resolver > 1)) {
2405 /*
2406 * FQDN not specified, try matching w/search domains
2407 */
2408 if (default_resolver->n_search > 0) {
2409 for (i = 0; !found && (i < default_resolver->n_search); i++) {
2410 int ret;
2411 char *search_fqdn = NULL;
2412
2413 ret = asprintf(&search_fqdn, "%s.%s", fqdn, default_resolver->search[i]);
2414 if (ret == -1) {
2415 continue;
2416 }
2417
2418 // try the provided name with the search domain appended
2419 found = check_matching_resolvers(storeP,
2420 dns->config,
2421 search_fqdn,
2422 flags,
2423 haveDNS,
2424 log_prefix);
2425 free(search_fqdn);
2426 }
2427 } else if (default_resolver->domain != NULL) {
2428 char *dp;
2429 int domain_parts = 0;
2430
2431 // count domain parts
2432 for (dp = default_resolver->domain; *dp != '\0'; dp++) {
2433 if (*dp == '.') {
2434 domain_parts++;
2435 }
2436 }
2437
2438 // remove trailing dots
2439 for (dp--; (dp >= default_resolver->domain) && (*dp == '.'); dp--) {
2440 *dp = '\0';
2441 domain_parts--;
2442 }
2443
2444 if (dp >= default_resolver->domain) {
2445 // dots are separators, bump # of components
2446 domain_parts++;
2447 }
2448
2449 dp = default_resolver->domain;
2450 for (i = LOCALDOMAINPARTS; !found && (i <= domain_parts); i++) {
2451 int ret;
2452 char *search_fqdn = NULL;
2453
2454 ret = asprintf(&search_fqdn, "%s.%s", fqdn, dp);
2455 if (ret == -1) {
2456 continue;
2457 }
2458
2459 // try the provided name with the [default] domain appended
2460 found = check_matching_resolvers(storeP,
2461 dns->config,
2462 search_fqdn,
2463 flags,
2464 haveDNS,
2465 log_prefix);
2466 free(search_fqdn);
2467
2468 // move to the next component of the [default] domain
2469 dp = strchr(dp, '.') + 1;
2470 }
2471 }
2472 }
2473
2474 if (!found) {
2475 /*
2476 * check the reachability of the default resolver
2477 */
2478 ok = check_resolver_reachability(storeP, default_resolver, flags, haveDNS, log_prefix);
2479 }
2480
2481 if (fqdn != nodename) free(fqdn);
2482
2483 done :
2484
2485 if (dns != NULL) {
2486 dns_configuration_release(dns);
2487 }
2488
2489 return ok;
2490 }
2491
2492
2493 Boolean
2494 _SC_checkResolverReachability(SCDynamicStoreRef *storeP,
2495 SCNetworkReachabilityFlags *flags,
2496 Boolean *haveDNS,
2497 const char *nodename,
2498 const char *servname)
2499 {
2500 return _SC_R_checkResolverReachability(storeP, flags, haveDNS, nodename, servname, "");
2501 }
2502
2503
2504 /*
2505 * _SC_checkResolverReachabilityByAddress()
2506 *
2507 * Given an IP address, determine whether a reverse DNS query can be issued
2508 * using the current network configuration.
2509 */
2510 Boolean
2511 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef *storeP,
2512 SCNetworkReachabilityFlags *flags,
2513 Boolean *haveDNS,
2514 struct sockaddr *sa)
2515 {
2516 int i;
2517 Boolean ok = FALSE;
2518 char ptr_name[128];
2519
2520 /*
2521 * Ideally, we would have an API that given a local IP
2522 * address would return the DNS server(s) that would field
2523 * a given PTR query. Fortunately, we do have an SPI which
2524 * which will provide this information given a "name" so we
2525 * take the address, convert it into the inverse query name,
2526 * and find out which servers should be consulted.
2527 */
2528
2529 switch (sa->sa_family) {
2530 case AF_INET : {
2531 union {
2532 in_addr_t s_addr;
2533 unsigned char b[4];
2534 } rev;
2535 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
2536
2537 /*
2538 * build "PTR" query name
2539 * NNN.NNN.NNN.NNN.in-addr.arpa.
2540 */
2541 rev.s_addr = sin->sin_addr.s_addr;
2542 (void) snprintf(ptr_name, sizeof(ptr_name), "%u.%u.%u.%u.in-addr.arpa.",
2543 rev.b[3],
2544 rev.b[2],
2545 rev.b[1],
2546 rev.b[0]);
2547
2548 break;
2549 }
2550
2551 case AF_INET6 : {
2552 int s = 0;
2553 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
2554 int x = sizeof(ptr_name);
2555 int n;
2556
2557 /*
2558 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
2559 * 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.
2560 */
2561 for (i = sizeof(sin6->sin6_addr) - 1; i >= 0; i--) {
2562 n = snprintf(&ptr_name[s], x, "%x.%x.",
2563 ( sin6->sin6_addr.s6_addr[i] & 0xf),
2564 ((sin6->sin6_addr.s6_addr[i] >> 4) & 0xf));
2565 if ((n == -1) || (n >= x)) {
2566 goto done;
2567 }
2568
2569 s += n;
2570 x -= n;
2571 }
2572
2573 n = snprintf(&ptr_name[s], x, "ip6.arpa.");
2574 if ((n == -1) || (n >= x)) {
2575 goto done;
2576 }
2577
2578 break;
2579 }
2580
2581 default :
2582 goto done;
2583 }
2584
2585 ok = _SC_R_checkResolverReachability(storeP, flags, haveDNS, ptr_name, NULL, "");
2586
2587 done :
2588
2589 return ok;
2590 }
2591
2592
2593 static Boolean
2594 startAsyncDNSQuery(SCNetworkReachabilityRef target) {
2595 int error;
2596 mach_port_t mp;
2597 Boolean ok;
2598 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2599
2600 (void) gettimeofday(&targetPrivate->dnsQueryStart, NULL);
2601
2602 error = getaddrinfo_async_start(&mp,
2603 targetPrivate->name,
2604 targetPrivate->serv,
2605 &targetPrivate->hints,
2606 __SCNetworkReachabilityCallbackSetResolvedAddress,
2607 (void *)target);
2608 if (error != 0) {
2609 /* save the error associated with the attempt to resolve the name */
2610 __SCNetworkReachabilityCallbackSetResolvedAddress(error, NULL, (void *)target);
2611 return FALSE;
2612 }
2613
2614 ok = enqueueAsyncDNSQuery(target, mp);
2615 return ok;
2616 }
2617
2618
2619 #pragma mark -
2620 #pragma mark OnDemand
2621
2622
2623 SCNetworkServiceRef
2624 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target,
2625 CFDictionaryRef *userOptions)
2626 {
2627 SCNetworkServiceRef service = NULL;
2628 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2629
2630 if (!isA_SCNetworkReachability(target)) {
2631 _SCErrorSet(kSCStatusInvalidArgument);
2632 return NULL;
2633 }
2634
2635 if (targetPrivate->onDemandServiceID != NULL) {
2636 service = _SCNetworkServiceCopyActive(NULL, targetPrivate->onDemandServiceID);
2637 }
2638
2639 if (userOptions != NULL) {
2640 if (targetPrivate->onDemandName != NULL) {
2641 *userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2642 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, targetPrivate->onDemandName);
2643 } else {
2644 *userOptions = NULL;
2645 }
2646 }
2647
2648 return service;
2649 }
2650
2651
2652 static void
2653 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer,
2654 SCNetworkReachabilityFlags onDemandFlags,
2655 void *info)
2656 {
2657 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
2658 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2659
2660 pthread_mutex_lock(&targetPrivate->lock);
2661
2662 if (!targetPrivate->scheduled) {
2663 // if not currently scheduled
2664 pthread_mutex_unlock(&targetPrivate->lock);
2665 return;
2666 }
2667
2668 SCLog(_sc_debug, LOG_INFO, CFSTR("%sOnDemand \"server\" status changed"),
2669 targetPrivate->log_prefix);
2670 __SCNetworkReachabilityPerform(target);
2671
2672 pthread_mutex_unlock(&targetPrivate->lock);
2673
2674 return;
2675 }
2676
2677
2678 static Boolean
2679 __SCNetworkReachabilityOnDemandCheck(SCDynamicStoreRef *storeP,
2680 SCNetworkReachabilityRef target,
2681 Boolean onDemandRetry,
2682 SCNetworkReachabilityFlags *flags)
2683 {
2684 Boolean ok;
2685 Boolean onDemand = FALSE;
2686 CFStringRef onDemandRemoteAddress = NULL;
2687 CFStringRef onDemandServiceID = NULL;
2688 SCNetworkConnectionStatus onDemandStatus;
2689 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2690
2691 // SCLog(_sc_debug, LOG_INFO,
2692 // CFSTR("%s__SCNetworkReachabilityOnDemandCheck %s"),
2693 // targetPrivate->log_prefix,
2694 // onDemandRetry ? "after" : "before");
2695
2696 if (targetPrivate->onDemandName == NULL) {
2697 targetPrivate->onDemandName = CFStringCreateWithCString(NULL, targetPrivate->name, kCFStringEncodingUTF8);
2698 }
2699
2700 /*
2701 * check if an OnDemand VPN configuration matches the name.
2702 */
2703 ok = __SCNetworkConnectionCopyOnDemandInfoWithName(storeP,
2704 targetPrivate->onDemandName,
2705 onDemandRetry,
2706 &onDemandServiceID,
2707 &onDemandStatus,
2708 &onDemandRemoteAddress);
2709 if (!_SC_CFEqual(targetPrivate->onDemandRemoteAddress, onDemandRemoteAddress) ||
2710 !_SC_CFEqual(targetPrivate->onDemandServiceID, onDemandServiceID)) {
2711 if (targetPrivate->onDemandRemoteAddress != NULL) {
2712 CFRelease(targetPrivate->onDemandRemoteAddress);
2713 targetPrivate->onDemandRemoteAddress = NULL;
2714 }
2715
2716 if (targetPrivate->onDemandServer != NULL) {
2717 #if !TARGET_OS_IPHONE
2718 if (targetPrivate->dispatchQueue != NULL) {
2719 // unschedule
2720 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE);
2721 } else
2722 #endif // !TARGET_OS_IPHONE
2723 if (targetPrivate->rls != NULL) {
2724 CFIndex i;
2725 CFIndex n;
2726
2727 // unschedule
2728 n = CFArrayGetCount(targetPrivate->rlList);
2729 for (i = 0; i < n; i += 3) {
2730 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
2731 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
2732
2733 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, rl, rlMode, TRUE);
2734 }
2735 }
2736
2737 CFRelease(targetPrivate->onDemandServer);
2738 targetPrivate->onDemandServer = NULL;
2739 }
2740
2741 if (targetPrivate->onDemandServiceID != NULL) {
2742 CFRelease(targetPrivate->onDemandServiceID);
2743 targetPrivate->onDemandServiceID = NULL;
2744 }
2745 }
2746 if (ok) {
2747 if (onDemandStatus != kSCNetworkConnectionConnected) {
2748 /*
2749 * if we have a VPN configuration matching the name *and* we need to
2750 * bring the VPN up. Combine our flags with those of the VPN server.
2751 */
2752 if (targetPrivate->onDemandServer == NULL) {
2753 CFMutableDictionaryRef options;
2754
2755 options = CFDictionaryCreateMutable(NULL,
2756 0,
2757 &kCFTypeDictionaryKeyCallBacks,
2758 &kCFTypeDictionaryValueCallBacks);
2759 CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, onDemandRemoteAddress);
2760 CFDictionarySetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandByPass, kCFBooleanTrue);
2761 targetPrivate->onDemandServer = SCNetworkReachabilityCreateWithOptions(NULL, options);
2762 CFRelease(options);
2763
2764 if (targetPrivate->scheduled) {
2765 SCNetworkReachabilityContext context = { 0, NULL, CFRetain, CFRelease, CFCopyDescription };
2766
2767 context.info = (void *)target;
2768 SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer,
2769 __SCNetworkReachabilityOnDemandCheckCallback,
2770 &context);
2771
2772 // schedule server reachability to match that of the target
2773 #if !TARGET_OS_IPHONE
2774 if (targetPrivate->dispatchQueue != NULL) {
2775 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, NULL, NULL, targetPrivate->dispatchQueue, TRUE);
2776 } else
2777 #endif // !TARGET_OS_IPHONE
2778 {
2779 CFIndex i;
2780 CFIndex n;
2781
2782 n = CFArrayGetCount(targetPrivate->rlList);
2783 for (i = 0; i < n; i += 3) {
2784 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
2785 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
2786
2787 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, rl, rlMode, NULL, TRUE);
2788 }
2789 }
2790 }
2791 }
2792
2793 ok = SCNetworkReachabilityGetFlags(targetPrivate->onDemandServer, flags);
2794 SCLog(_sc_debug, LOG_INFO, CFSTR("%s status * = 0x%08x"),
2795 targetPrivate->log_prefix,
2796 *flags);
2797 if (ok && (*flags & kSCNetworkReachabilityFlagsReachable)) {
2798 if (!(*flags & kSCNetworkReachabilityFlagsTransientConnection)) {
2799 // start clean if not already layered on a transient network
2800 *flags = 0;
2801 }
2802 *flags |= kSCNetworkReachabilityFlagsReachable;
2803 *flags |= kSCNetworkReachabilityFlagsTransientConnection;
2804 *flags |= kSCNetworkReachabilityFlagsConnectionRequired;
2805 *flags |= kSCNetworkReachabilityFlagsConnectionOnDemand;
2806
2807 if (_sc_debug) {
2808 SCLog(TRUE, LOG_INFO, CFSTR("%s service * = %@"),
2809 targetPrivate->log_prefix,
2810 onDemandServiceID);
2811 SCLog(TRUE, LOG_INFO, CFSTR("%s status = isReachable (after OnDemand connect)"),
2812 targetPrivate->log_prefix);
2813 }
2814
2815 onDemand = TRUE;
2816 }
2817 }
2818
2819 if (onDemandRemoteAddress != NULL) {
2820 if (targetPrivate->onDemandRemoteAddress == NULL) {
2821 targetPrivate->onDemandRemoteAddress = onDemandRemoteAddress;
2822 } else {
2823 CFRelease(onDemandRemoteAddress);
2824 }
2825 }
2826
2827 if (onDemandServiceID != NULL) {
2828 if (targetPrivate->onDemandServiceID == NULL) {
2829 targetPrivate->onDemandServiceID = onDemandServiceID;
2830 } else {
2831 CFRelease(onDemandServiceID);
2832 }
2833 }
2834 }
2835
2836 return onDemand;
2837 }
2838
2839
2840 #pragma mark -
2841 #pragma mark Reachability Flags
2842
2843
2844 static Boolean
2845 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef *storeP,
2846 SCNetworkReachabilityRef target,
2847 ReachabilityInfo *reach_info,
2848 Boolean async)
2849 {
2850 CFMutableArrayRef addresses = NULL;
2851 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2852 ReachabilityInfo my_info = NOT_REACHABLE;
2853 Boolean ok = TRUE;
2854
2855 *reach_info = NOT_REACHABLE;
2856
2857 if (!isA_SCNetworkReachability(target)) {
2858 _SCErrorSet(kSCStatusInvalidArgument);
2859 return FALSE;
2860 }
2861
2862 switch (targetPrivate->type) {
2863 case reachabilityTypeAddress :
2864 case reachabilityTypeAddressPair : {
2865 /*
2866 * Check "local" address
2867 */
2868 if (targetPrivate->localAddress != NULL) {
2869 /*
2870 * Check "local" address
2871 */
2872 ok = checkAddress(storeP,
2873 targetPrivate->localAddress,
2874 &my_info,
2875 targetPrivate->log_prefix);
2876 if (!ok) {
2877 goto error; /* not today */
2878 }
2879
2880 if (!(my_info.flags & kSCNetworkReachabilityFlagsIsLocalAddress)) {
2881 goto error; /* not reachable, non-"local" address */
2882 }
2883 }
2884
2885 /*
2886 * Check "remote" address
2887 */
2888 if (targetPrivate->remoteAddress != NULL) {
2889 /*
2890 * in cases where we have "local" and "remote" addresses
2891 * we need to re-initialize the to-be-returned flags.
2892 */
2893 my_info = NOT_REACHABLE;
2894
2895 /*
2896 * Check "remote" address
2897 */
2898 ok = checkAddress(storeP,
2899 targetPrivate->remoteAddress,
2900 &my_info,
2901 targetPrivate->log_prefix);
2902 if (!ok) {
2903 goto error; /* not today */
2904 }
2905 }
2906
2907 break;
2908
2909 }
2910
2911 case reachabilityTypeName : {
2912 struct timeval dnsQueryStart;
2913 int error;
2914 SCNetworkReachabilityFlags ns_flags;
2915 struct addrinfo *res;
2916
2917 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
2918 if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
2919 /* if resolved or an error had been detected */
2920 if (!async) {
2921 /* if not an async request */
2922 goto checkResolvedAddress;
2923 } else if ((targetPrivate->dnsPort == NULL) && !targetPrivate->needResolve) {
2924 /* if async request, no query active, and no query needed */
2925 goto checkResolvedAddress;
2926 }
2927 }
2928
2929 if (!targetPrivate->onDemandBypass) {
2930 Boolean onDemand;
2931
2932 /*
2933 * before we attempt our initial DNS query, check if there is
2934 * an OnDemand configuration that we should be using.
2935 */
2936 onDemand = __SCNetworkReachabilityOnDemandCheck(storeP, target, FALSE, &my_info.flags);
2937 if (onDemand) {
2938 /* if OnDemand connection is needed */
2939 goto done;
2940 }
2941 }
2942
2943 /* check the reachability of the DNS servers */
2944 ok = _SC_R_checkResolverReachability(storeP,
2945 &ns_flags,
2946 &targetPrivate->haveDNS,
2947 targetPrivate->name,
2948 targetPrivate->serv,
2949 targetPrivate->log_prefix);
2950 if (!ok) {
2951 /* if we could not get DNS server info */
2952 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server reachability unknown"),
2953 targetPrivate->log_prefix);
2954 goto error;
2955 } else if (rankReachability(ns_flags) < 2) {
2956 /*
2957 * if DNS servers are not (or are no longer) reachable, set
2958 * flags based on the availability of configured (but not
2959 * active) services.
2960 */
2961
2962 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"),
2963 targetPrivate->log_prefix);
2964
2965 ok = checkAddress(storeP,
2966 NULL,
2967 &my_info,
2968 targetPrivate->log_prefix);
2969 if (!ok) {
2970 SCLog(_sc_debug, LOG_INFO, CFSTR("%sNo available networks"),
2971 targetPrivate->log_prefix);
2972 goto error;
2973 }
2974
2975 if (async && targetPrivate->scheduled) {
2976 /*
2977 * return "host not found", set flags appropriately,
2978 * and schedule notification.
2979 */
2980 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NONAME,
2981 NULL,
2982 (void *)target);
2983 my_info.flags |= (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending);
2984
2985 SCLog(_sc_debug, LOG_INFO, CFSTR("%sno DNS servers are reachable"),
2986 targetPrivate->log_prefix);
2987 __SCNetworkReachabilityPerform(target);
2988 }
2989 break;
2990 }
2991
2992 if (async) {
2993 /* for async requests we return the last known status */
2994 my_info = targetPrivate->info;
2995
2996 if (targetPrivate->dnsPort != NULL) {
2997 /* if request already in progress */
2998 SCLog(_sc_debug, LOG_INFO,
2999 CFSTR("%swaiting for DNS reply"),
3000 targetPrivate->log_prefix);
3001 if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
3002 /* updated reachability based on the previous reply */
3003 goto checkResolvedAddress;
3004 }
3005 break;
3006 }
3007
3008 SCLog(_sc_debug, LOG_INFO,
3009 CFSTR("%sstart DNS query for %s%s%s%s%s"),
3010 targetPrivate->log_prefix,
3011 targetPrivate->name != NULL ? "name = " : "",
3012 targetPrivate->name != NULL ? targetPrivate->name : "",
3013 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
3014 targetPrivate->serv != NULL ? "serv = " : "",
3015 targetPrivate->serv != NULL ? targetPrivate->serv : "");
3016
3017 /*
3018 * initiate an async DNS query
3019 */
3020 if (!startAsyncDNSQuery(target)) {
3021 /* if we could not initiate the request, process error */
3022 goto checkResolvedAddress;
3023 }
3024
3025 /* request initiated */
3026 break;
3027 }
3028
3029 SCLog(_sc_debug, LOG_INFO,
3030 CFSTR("%scheck DNS for %s%s%s%s%s"),
3031 targetPrivate->log_prefix,
3032 targetPrivate->name != NULL ? "name = " : "",
3033 targetPrivate->name != NULL ? targetPrivate->name : "",
3034 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
3035 targetPrivate->serv != NULL ? "serv = " : "",
3036 targetPrivate->serv != NULL ? targetPrivate->serv : "");
3037
3038 /*
3039 * OK, all of the DNS name servers are available. Let's
3040 * resolve the nodename into an address.
3041 */
3042 if (_sc_debug) {
3043 (void) gettimeofday(&dnsQueryStart, NULL);
3044 }
3045
3046 error = getaddrinfo(targetPrivate->name,
3047 targetPrivate->serv,
3048 &targetPrivate->hints,
3049 &res);
3050
3051 __log_query_time(target,
3052 ((error == 0) && (res != NULL)), // if successful query
3053 FALSE, // sync
3054 &dnsQueryStart); // start time
3055
3056 __SCNetworkReachabilitySetResolvedAddress(error, res, target);
3057
3058 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
3059
3060 checkResolvedAddress :
3061
3062 /*
3063 * We first assume that the requested host is NOT available.
3064 * Then, check each address for accessibility and return the
3065 * best status available.
3066 */
3067 my_info = NOT_REACHABLE;
3068
3069 if (isA_CFArray(addresses)) {
3070 CFIndex i;
3071 CFIndex n = CFArrayGetCount(addresses);
3072
3073 for (i = 0; i < n; i++) {
3074 ReachabilityInfo ns_info = NOT_REACHABLE;
3075 struct sockaddr *sa;
3076
3077 sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
3078
3079 ok = checkAddress(storeP,
3080 sa,
3081 &ns_info,
3082 targetPrivate->log_prefix);
3083 if (!ok) {
3084 goto error; /* not today */
3085 }
3086
3087 if (rankReachability(ns_info.flags) > rankReachability(my_info.flags)) {
3088 /* return the best case result */
3089 my_info = ns_info;
3090 if (rankReachability(my_info.flags) == 2) {
3091 /* we're in luck */
3092 break;
3093 }
3094 }
3095 }
3096 } else {
3097 if ((error == EAI_NONAME)
3098 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
3099 || (error == EAI_NODATA)
3100 #endif
3101 ) {
3102 /*
3103 * the target host name could not be resolved
3104 */
3105 if (!targetPrivate->onDemandBypass) {
3106 Boolean onDemand;
3107
3108 /*
3109 * our initial DNS query failed, check again to see if there
3110 * there is an OnDemand configuration that we should be using.
3111 */
3112 onDemand = __SCNetworkReachabilityOnDemandCheck(storeP, target, TRUE, &my_info.flags);
3113 if (onDemand) {
3114 /* if OnDemand connection is needed */
3115 goto done;
3116 }
3117 }
3118
3119 if (!targetPrivate->haveDNS) {
3120 /*
3121 * No DNS servers are defined. Set flags based on
3122 * the availability of configured (but not active)
3123 * services.
3124 */
3125 ok = checkAddress(storeP,
3126 NULL,
3127 &my_info,
3128 targetPrivate->log_prefix);
3129 if (!ok) {
3130 goto error; /* not today */
3131 }
3132
3133 if ((my_info.flags & kSCNetworkReachabilityFlagsReachable) &&
3134 (my_info.flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
3135 /*
3136 * Since we might pick up a set of DNS servers when this connection
3137 * is established, don't reply with a "HOST NOT FOUND" error just yet.
3138 */
3139 break;
3140 }
3141
3142 /* Host not found, not reachable! */
3143 my_info = NOT_REACHABLE;
3144 }
3145 }
3146 }
3147
3148 break;
3149 }
3150 }
3151
3152 done:
3153
3154 *reach_info = my_info;
3155
3156 error :
3157
3158 if (addresses != NULL) CFRelease(addresses);
3159 return ok;
3160 }
3161
3162
3163 Boolean
3164 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target,
3165 SCNetworkReachabilityFlags *flags)
3166 {
3167 Boolean ok = TRUE;
3168 SCDynamicStoreRef store = NULL;
3169 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3170
3171 if (!isA_SCNetworkReachability(target)) {
3172 _SCErrorSet(kSCStatusInvalidArgument);
3173 return FALSE;
3174 }
3175
3176 pthread_mutex_lock(&targetPrivate->lock);
3177
3178 if (targetPrivate->scheduled) {
3179 // if being watched, return the last known (and what should be current) status
3180 *flags = targetPrivate->info.flags & ~kSCNetworkReachabilityFlagsFirstResolvePending;
3181 goto done;
3182 }
3183
3184
3185 ok = __SCNetworkReachabilityGetFlags(&store, target, &targetPrivate->info, FALSE);
3186 *flags = targetPrivate->info.flags & ~kSCNetworkReachabilityFlagsFirstResolvePending;
3187 if (store != NULL) CFRelease(store);
3188
3189 done :
3190
3191 pthread_mutex_unlock(&targetPrivate->lock);
3192 return ok;
3193 }
3194
3195
3196 #pragma mark -
3197 #pragma mark Notifications
3198
3199
3200 static void
3201 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store)
3202 {
3203 CFStringRef key;
3204 CFMutableArrayRef keys;
3205 CFStringRef pattern;
3206 CFMutableArrayRef patterns;
3207
3208 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3209 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3210
3211 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
3212 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3213 kSCDynamicStoreDomainSetup,
3214 kSCEntNetIPv4);
3215 CFArrayAppendValue(keys, key);
3216 CFRelease(key);
3217
3218 // State:/Network/Global/DNS
3219 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3220 kSCDynamicStoreDomainState,
3221 kSCEntNetDNS);
3222 CFArrayAppendValue(keys, key);
3223 CFRelease(key);
3224
3225 // State:/Network/Global/IPv4 (default route)
3226 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3227 kSCDynamicStoreDomainState,
3228 kSCEntNetIPv4);
3229 CFArrayAppendValue(keys, key);
3230 CFRelease(key);
3231
3232 // State:/Network/Global/OnDemand
3233 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3234 kSCDynamicStoreDomainState,
3235 kSCEntNetOnDemand);
3236 CFArrayAppendValue(keys, key);
3237 CFRelease(key);
3238
3239 // Setup: per-service IPv4 info
3240 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
3241 kSCDynamicStoreDomainSetup,
3242 kSCCompAnyRegex,
3243 kSCEntNetIPv4);
3244 CFArrayAppendValue(patterns, pattern);
3245 CFRelease(pattern);
3246
3247 // Setup: per-service Interface info
3248 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
3249 kSCDynamicStoreDomainSetup,
3250 kSCCompAnyRegex,
3251 kSCEntNetInterface);
3252 CFArrayAppendValue(patterns, pattern);
3253 CFRelease(pattern);
3254
3255 // Setup: per-service PPP info (for kSCPropNetPPPDialOnDemand)
3256 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
3257 kSCDynamicStoreDomainSetup,
3258 kSCCompAnyRegex,
3259 kSCEntNetPPP);
3260 CFArrayAppendValue(patterns, pattern);
3261 CFRelease(pattern);
3262
3263 // State: per-service PPP info (for kSCPropNetPPPStatus)
3264 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
3265 kSCDynamicStoreDomainState,
3266 kSCCompAnyRegex,
3267 kSCEntNetPPP);
3268 CFArrayAppendValue(patterns, pattern);
3269 CFRelease(pattern);
3270
3271 // Setup: per-service IPSec info
3272 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
3273 kSCDynamicStoreDomainSetup,
3274 kSCCompAnyRegex,
3275 kSCEntNetIPSec);
3276 CFArrayAppendValue(patterns, pattern);
3277 CFRelease(pattern);
3278
3279 // State: per-interface IPv4 info
3280 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
3281 kSCDynamicStoreDomainState,
3282 kSCCompAnyRegex,
3283 kSCEntNetIPv4);
3284 CFArrayAppendValue(patterns, pattern);
3285 CFRelease(pattern);
3286
3287 // State: per-interface IPv6 info
3288 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
3289 kSCDynamicStoreDomainState,
3290 kSCCompAnyRegex,
3291 kSCEntNetIPv6);
3292 CFArrayAppendValue(patterns, pattern);
3293 CFRelease(pattern);
3294
3295 #if !TARGET_OS_IPHONE
3296 // State: Power Management Capabilities
3297 key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@%@"),
3298 kSCDynamicStoreDomainState,
3299 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix));
3300 CFArrayAppendValue(keys, key);
3301 CFRelease(key);
3302 #endif // TARGET_OS_IPHONE
3303
3304
3305 (void)SCDynamicStoreSetNotificationKeys(store, keys, patterns);
3306 CFRelease(keys);
3307 CFRelease(patterns);
3308
3309 return;
3310 }
3311
3312
3313 static void
3314 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store,
3315 CFArrayRef changedKeys,
3316 void *info)
3317 {
3318 Boolean dnsConfigChanged = FALSE;
3319 CFIndex i;
3320 CFStringRef key;
3321 CFIndex nChanges = CFArrayGetCount(changedKeys);
3322 CFIndex nTargets;
3323 #if !TARGET_OS_IPHONE
3324 Boolean powerStatusChanged = FALSE;
3325 #endif // !TARGET_OS_IPHONE
3326 const void * targets_q[N_QUICK];
3327 const void ** targets = targets_q;
3328
3329 if (nChanges == 0) {
3330 /* if no changes */
3331 return;
3332 }
3333
3334 pthread_mutex_lock(&hn_lock);
3335
3336 nTargets = (hn_targets != NULL) ? CFSetGetCount(hn_targets) : 0;
3337 if (nTargets == 0) {
3338 /* if no addresses being monitored */
3339 goto done;
3340 }
3341
3342 #if !TARGET_OS_IPHONE
3343 key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@%@"),
3344 kSCDynamicStoreDomainState,
3345 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix));
3346 if (CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key)) {
3347 CFNumberRef num;
3348
3349 num = SCDynamicStoreCopyValue(store, key);
3350 if (num != NULL) {
3351 if (isA_CFNumber(num) &&
3352 CFNumberGetValue(num, kCFNumberSInt32Type, &power_capabilities)) {
3353 powerStatusChanged = TRUE;
3354 }
3355
3356 CFRelease(num);
3357 }
3358 }
3359 CFRelease(key);
3360 #endif // !TARGET_OS_IPHONE
3361
3362 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3363 kSCDynamicStoreDomainState,
3364 kSCEntNetDNS);
3365 if (CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key)) {
3366 dnsConfigChanged = TRUE; /* the DNS server(s) have changed */
3367 }
3368 CFRelease(key);
3369
3370 if (_sc_debug) {
3371 int changes = 0;
3372 const char *str;
3373
3374 #if !TARGET_OS_IPHONE
3375 if (powerStatusChanged) {
3376 changes |= 4;
3377 nChanges -= 1;
3378 }
3379 #endif // !TARGET_OS_IPHONE
3380
3381 if (dnsConfigChanged) {
3382 changes |= 2;
3383 nChanges -= 1;
3384 }
3385
3386 if (nChanges > 0) {
3387 changes |= 1;
3388 }
3389
3390 switch (changes) {
3391 case 0 : str = ""; break;
3392 case 1 : str = "network "; break;
3393 case 2 : str = "DNS "; break;
3394 case 3 : str = "network and DNS "; break;
3395 #if !TARGET_OS_IPHONE
3396 case 4 : str = "power "; break;
3397 case 5 : str = "network and power "; break;
3398 case 6 : str = "DNS and power "; break;
3399 case 7 : str = "network, DNS, and power "; break;
3400 #endif // !TARGET_OS_IPHONE
3401 default : str = "??? ";
3402 }
3403
3404 SCLog(TRUE, LOG_INFO, CFSTR("process %sconfiguration change"), str);
3405 }
3406
3407 if (nTargets > (CFIndex)(sizeof(targets_q) / sizeof(CFTypeRef)))
3408 targets = CFAllocatorAllocate(NULL, nTargets * sizeof(CFTypeRef), 0);
3409 CFSetGetValues(hn_targets, targets);
3410 for (i = 0; i < nTargets; i++) {
3411 SCNetworkReachabilityRef target = targets[i];
3412 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3413
3414 pthread_mutex_lock(&targetPrivate->lock);
3415
3416 if (targetPrivate->type == reachabilityTypeName) {
3417 Boolean dnsChanged = dnsConfigChanged;
3418
3419 if (!dnsChanged) {
3420 /*
3421 * if the DNS configuration didn't change we still need to
3422 * check that the DNS servers are accessible.
3423 */
3424 SCNetworkReachabilityFlags ns_flags;
3425 Boolean ok;
3426
3427 /* check the reachability of the DNS servers */
3428 ok = _SC_R_checkResolverReachability(&store,
3429 &ns_flags,
3430 &targetPrivate->haveDNS,
3431 targetPrivate->name,
3432 targetPrivate->serv,
3433 targetPrivate->log_prefix);
3434 if (!ok) {
3435 /* if we could not get DNS server info */
3436 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server reachability unknown"),
3437 targetPrivate->log_prefix);
3438 dnsChanged = TRUE;
3439 } else if (rankReachability(ns_flags) < 2) {
3440 /*
3441 * if DNS servers are not (or are no longer) reachable, set
3442 * flags based on the availability of configured (but not
3443 * active) services.
3444 */
3445 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"),
3446 targetPrivate->log_prefix);
3447 dnsChanged = TRUE;
3448 }
3449 }
3450
3451 if (dnsChanged) {
3452 if (targetPrivate->dnsPort != NULL) {
3453 mach_port_t mp = CFMachPortGetPort(targetPrivate->dnsPort);
3454
3455 /* cancel the outstanding DNS query */
3456 SCLog(_sc_debug, LOG_INFO,
3457 CFSTR("%scancel DNS query for %s%s%s%s%s"),
3458 targetPrivate->log_prefix,
3459 targetPrivate->name != NULL ? "name = " : "",
3460 targetPrivate->name != NULL ? targetPrivate->name : "",
3461 targetPrivate->name != NULL && targetPrivate->serv != NULL ? ", " : "",
3462 targetPrivate->serv != NULL ? "serv = " : "",
3463 targetPrivate->serv != NULL ? targetPrivate->serv : "");
3464 dequeueAsyncDNSQuery(target);
3465 getaddrinfo_async_cancel(mp);
3466 }
3467
3468 /* schedule request to resolve the name again */
3469 targetPrivate->needResolve = TRUE;
3470 }
3471 }
3472
3473 __SCNetworkReachabilityPerform(target);
3474
3475 pthread_mutex_unlock(&targetPrivate->lock);
3476 }
3477 if (targets != targets_q) CFAllocatorDeallocate(NULL, targets);
3478
3479 done :
3480
3481 pthread_mutex_unlock(&hn_lock);
3482 return;
3483 }
3484
3485
3486 #if !TARGET_OS_IPHONE
3487 static __inline__ Boolean
3488 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities)
3489 {
3490
3491 #define POWER_CAPABILITIES_NEED (kIOPMSystemPowerStateCapabilityCPU \
3492 | kIOPMSystemPowerStateCapabilityNetwork \
3493 | kIOPMSystemPowerStateCapabilityDisk)
3494
3495 if ((power_capabilities & POWER_CAPABILITIES_NEED) != POWER_CAPABILITIES_NEED) {
3496 /*
3497 * we're not awake (from a networking point of view) unless we
3498 * have the CPU, disk, *and* network.
3499 */
3500 return FALSE;
3501 }
3502
3503 if ((power_capabilities & kIOPMSytemPowerStateCapabilitiesMask) == POWER_CAPABILITIES_NEED) {
3504 /*
3505 * if all we have is the CPU, disk, and network than this must
3506 * be a "maintenance" wake.
3507 */
3508 return FALSE;
3509 }
3510
3511 return TRUE;
3512 }
3513 #endif // !TARGET_OS_IPHONE
3514
3515
3516 static void
3517 rlsPerform(void *info)
3518 {
3519 void *context_info;
3520 void (*context_release)(const void *);
3521 Boolean defer = FALSE;
3522 Boolean ok;
3523 ReachabilityInfo reach_info = NOT_REACHABLE;
3524 SCNetworkReachabilityCallBack rlsFunction;
3525 SCDynamicStoreRef store = NULL;
3526 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
3527 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3528
3529 SCLog(_sc_debug, LOG_INFO, CFSTR("%schecking target reachability"),
3530 targetPrivate->log_prefix);
3531
3532
3533 pthread_mutex_lock(&targetPrivate->lock);
3534
3535 if (!targetPrivate->scheduled) {
3536 // if not currently scheduled
3537 pthread_mutex_unlock(&targetPrivate->lock);
3538 return;
3539 }
3540
3541 /* update reachability, notify if status changed */
3542 ok = __SCNetworkReachabilityGetFlags(&store, target, &reach_info, TRUE);
3543 if (store != NULL) CFRelease(store);
3544 if (!ok) {
3545 /* if reachability status not available */
3546 SCLog(_sc_debug, LOG_INFO, CFSTR("%flags not available"),
3547 targetPrivate->log_prefix);
3548 reach_info = NOT_REACHABLE;
3549 }
3550
3551 #if !TARGET_OS_IPHONE
3552 /*
3553 * We want to defer the notification if this is a maintenance wake *and*
3554 * the reachability flags that we would be reporting to the application
3555 * are better than those that we last reported.
3556 */
3557 if (!systemIsAwake(power_capabilities)) {
3558 /* if this is a maintenace wake */
3559 reach_info.sleeping = TRUE;
3560
3561 if (rankReachability(reach_info.flags) >= rankReachability(targetPrivate->info.flags)) {
3562 /*
3563 * don't report the change if the new reachability flags are
3564 * the same or "better"
3565 */
3566 defer = TRUE;
3567 } else if (bcmp(&targetPrivate->last_notify, &reach_info, sizeof(reach_info)) == 0) {
3568 /* if we have already posted this change */
3569 defer = TRUE;
3570 }
3571 }
3572 #endif // !TARGET_OS_IPHONE
3573
3574 if (bcmp(&targetPrivate->info, &reach_info, sizeof(reach_info)) == 0) {
3575 SCLog(_sc_debug, LOG_INFO,
3576 CFSTR("%sflags/interface match (now 0x%08x/%hu%s)"),
3577 targetPrivate->log_prefix,
3578 reach_info.flags,
3579 reach_info.if_index,
3580 reach_info.sleeping ? "*" : "");
3581 pthread_mutex_unlock(&targetPrivate->lock);
3582 return;
3583 }
3584
3585 SCLog(_sc_debug, LOG_INFO,
3586 CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s"),
3587 targetPrivate->log_prefix,
3588 targetPrivate->info.flags,
3589 targetPrivate->info.if_index,
3590 targetPrivate->info.sleeping ? "*" : "",
3591 reach_info.flags,
3592 reach_info.if_index,
3593 reach_info.sleeping ? "*" : "",
3594 defer ? ", deferred" : "");
3595
3596 /* update flags / interface */
3597 targetPrivate->info = reach_info;
3598
3599 /* as needed, defer the notification */
3600 if (defer) {
3601 pthread_mutex_unlock(&targetPrivate->lock);
3602 return;
3603 }
3604
3605 /* save last notification info */
3606 targetPrivate->last_notify = reach_info;
3607
3608 /* callout */
3609 rlsFunction = targetPrivate->rlsFunction;
3610 if (targetPrivate->rlsContext.retain != NULL) {
3611 context_info = (void *)(*targetPrivate->rlsContext.retain)(targetPrivate->rlsContext.info);
3612 context_release = targetPrivate->rlsContext.release;
3613 } else {
3614 context_info = targetPrivate->rlsContext.info;
3615 context_release = NULL;
3616 }
3617
3618 pthread_mutex_unlock(&targetPrivate->lock);
3619
3620 if (rlsFunction != NULL) {
3621 (*rlsFunction)(target, reach_info.flags, context_info);
3622 }
3623
3624 if (context_release != NULL) {
3625 (*context_release)(context_info);
3626 }
3627
3628 return;
3629 }
3630
3631
3632 Boolean
3633 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target,
3634 SCNetworkReachabilityCallBack callout,
3635 SCNetworkReachabilityContext *context)
3636 {
3637 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3638
3639 pthread_mutex_lock(&targetPrivate->lock);
3640
3641 if (targetPrivate->rlsContext.release != NULL) {
3642 /* let go of the current context */
3643 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
3644 }
3645
3646 targetPrivate->rlsFunction = callout;
3647 targetPrivate->rlsContext.info = NULL;
3648 targetPrivate->rlsContext.retain = NULL;
3649 targetPrivate->rlsContext.release = NULL;
3650 targetPrivate->rlsContext.copyDescription = NULL;
3651 if (context) {
3652 bcopy(context, &targetPrivate->rlsContext, sizeof(SCNetworkReachabilityContext));
3653 if (context->retain != NULL) {
3654 targetPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
3655 }
3656 }
3657
3658 pthread_mutex_unlock(&targetPrivate->lock);
3659
3660 return TRUE;
3661 }
3662
3663
3664 static CFStringRef
3665 reachRLSCopyDescription(const void *info)
3666 {
3667 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
3668
3669 return CFStringCreateWithFormat(NULL,
3670 NULL,
3671 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
3672 target);
3673 }
3674
3675
3676 static Boolean
3677 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target,
3678 CFRunLoopRef runLoop,
3679 CFStringRef runLoopMode,
3680 #if !TARGET_OS_IPHONE
3681 dispatch_queue_t queue,
3682 #else // !TARGET_OS_IPHONE
3683 void *queue,
3684 #endif // !TARGET_OS_IPHONE
3685 Boolean onDemand)
3686 {
3687 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3688 Boolean init = FALSE;
3689 Boolean ok = FALSE;
3690
3691 if (!onDemand) {
3692 pthread_mutex_lock(&hn_lock);
3693 }
3694 pthread_mutex_lock(&targetPrivate->lock);
3695
3696 #if !TARGET_OS_IPHONE
3697 if ((targetPrivate->dispatchQueue != NULL) || // if we are already scheduled with a dispatch queue
3698 ((queue != NULL) && targetPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
3699 _SCErrorSet(kSCStatusInvalidArgument);
3700 goto done;
3701 }
3702 #endif // !TARGET_OS_IPHONE
3703
3704 /* schedule the SCNetworkReachability run loop source */
3705
3706 if (!onDemand && (hn_store == NULL)) {
3707 /*
3708 * if we are not monitoring any hosts, start watching
3709 */
3710 if (!dns_configuration_watch()) {
3711 // if error
3712 _SCErrorSet(kSCStatusFailed);
3713 goto done;
3714 }
3715
3716 hn_store = SCDynamicStoreCreate(NULL,
3717 CFSTR("SCNetworkReachability"),
3718 __SCNetworkReachabilityHandleChanges,
3719 NULL);
3720 if (hn_store == NULL) {
3721 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreCreate() failed"));
3722 goto done;
3723 }
3724
3725 __SCNetworkReachabilityReachabilitySetNotifications(hn_store);
3726
3727 #if !TARGET_OS_IPHONE
3728 hn_dispatchQueue = dispatch_queue_create("com.apple.SCNetworkReachabilty.network_changes", NULL);
3729 if (hn_dispatchQueue == NULL) {
3730 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed"));
3731 _SCErrorSet(kSCStatusFailed);
3732 CFRelease(hn_store);
3733 hn_store = NULL;
3734 goto done;
3735 }
3736 CFRetain(hn_store); // Note: will be released when the dispatch queue is released
3737 dispatch_set_context(hn_dispatchQueue, (void *)hn_store);
3738 dispatch_set_finalizer_f(hn_dispatchQueue, (dispatch_function_t)CFRelease);
3739
3740 ok = SCDynamicStoreSetDispatchQueue(hn_store, hn_dispatchQueue);
3741 if (!ok) {
3742 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
3743 dispatch_release(hn_dispatchQueue);
3744 hn_dispatchQueue = NULL;
3745 CFRelease(hn_store);
3746 hn_store = NULL;
3747 goto done;
3748 }
3749 #else // !TARGET_OS_IPHONE
3750 hn_storeRLS = SCDynamicStoreCreateRunLoopSource(NULL, hn_store, 0);
3751 hn_rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3752 #endif // !TARGET_OS_IPHONE
3753 hn_targets = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
3754 }
3755
3756 if (!targetPrivate->scheduled) {
3757 CFRunLoopSourceContext context = { 0 // version
3758 , (void *)target // info
3759 , CFRetain // retain
3760 , CFRelease // release
3761 , reachRLSCopyDescription // copyDescription
3762 , CFEqual // equal
3763 , CFHash // hash
3764 , NULL // schedule
3765 , NULL // cancel
3766 , rlsPerform // perform
3767 };
3768
3769 if (runLoop != NULL) {
3770 targetPrivate->rls = CFRunLoopSourceCreate(NULL, 0, &context);
3771 targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3772 }
3773
3774 targetPrivate->scheduled = TRUE;
3775 if (targetPrivate->type == reachabilityTypeName) {
3776 targetPrivate->needResolve = TRUE;
3777 }
3778 init = TRUE;
3779 }
3780
3781 #if !TARGET_OS_IPHONE
3782 if (queue != NULL) {
3783 targetPrivate->dispatchQueue = queue;
3784 dispatch_retain(targetPrivate->dispatchQueue);
3785 } else
3786 #endif // !TARGET_OS_IPHONE
3787 {
3788 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
3789 /*
3790 * if we do not already have host notifications scheduled with
3791 * this runLoop / runLoopMode
3792 */
3793 CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode);
3794
3795 if (targetPrivate->dnsRLS != NULL) {
3796 /* if we have an active async DNS query too */
3797 CFRunLoopAddSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
3798 }
3799 }
3800
3801 _SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
3802
3803 #if TARGET_OS_IPHONE
3804 if (!onDemand) {
3805 /* schedule the global SCDynamicStore run loop source */
3806
3807 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
3808 /*
3809 * if we do not already have SC notifications scheduled with
3810 * this runLoop / runLoopMode
3811 */
3812 CFRunLoopAddSource(runLoop, hn_storeRLS, runLoopMode);
3813 }
3814
3815 _SC_schedule(target, runLoop, runLoopMode, hn_rlList);
3816 }
3817 #endif // TARGET_OS_IPHONE
3818 }
3819
3820 CFSetAddValue(hn_targets, target);
3821
3822 if (init) {
3823 ReachabilityInfo reach_info = NOT_REACHABLE;
3824 SCDynamicStoreRef store = NULL;
3825
3826 /*
3827 * if we have yet to schedule SC notifications for this address
3828 * - initialize current reachability status
3829 */
3830 if (__SCNetworkReachabilityGetFlags(&store, target, &reach_info, TRUE)) {
3831 /*
3832 * if reachability status available
3833 * - set flags
3834 * - schedule notification to report status via callback
3835 */
3836 targetPrivate->info = reach_info;
3837 __SCNetworkReachabilityPerform(target);
3838 } else {
3839 /* if reachability status not available, async lookup started */
3840 targetPrivate->info = NOT_REACHABLE;
3841 }
3842 if (store != NULL) CFRelease(store);
3843 }
3844
3845 if (targetPrivate->onDemandServer != NULL) {
3846 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, runLoop, runLoopMode, queue, TRUE);
3847 }
3848
3849 ok = TRUE;
3850
3851 done :
3852
3853 pthread_mutex_unlock(&targetPrivate->lock);
3854 if (!onDemand) {
3855 pthread_mutex_unlock(&hn_lock);
3856 }
3857 return ok;
3858 }
3859
3860
3861 static Boolean
3862 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target,
3863 CFRunLoopRef runLoop,
3864 CFStringRef runLoopMode,
3865 Boolean onDemand)
3866 {
3867 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
3868 CFIndex n = 0;
3869 Boolean ok = FALSE;
3870
3871 if (!onDemand) {
3872 pthread_mutex_lock(&hn_lock);
3873 }
3874 pthread_mutex_lock(&targetPrivate->lock);
3875
3876 #if !TARGET_OS_IPHONE
3877 if (((runLoop == NULL) && (targetPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
3878 ((runLoop != NULL) && (targetPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are not)
3879 _SCErrorSet(kSCStatusInvalidArgument);
3880 goto done;
3881 }
3882 #endif // !TARGET_OS_IPHONE
3883
3884 if (!targetPrivate->scheduled) {
3885 // if not currently scheduled
3886 _SCErrorSet(kSCStatusInvalidArgument);
3887 goto done;
3888 }
3889
3890 // first, unschedule the target specific sources
3891 #if !TARGET_OS_IPHONE
3892 if (targetPrivate->dispatchQueue != NULL) {
3893 if (targetPrivate->onDemandServer != NULL) {
3894 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE);
3895 }
3896 } else
3897 #endif // !TARGET_OS_IPHONE
3898 {
3899 if (!_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
3900 // if not currently scheduled
3901 _SCErrorSet(kSCStatusInvalidArgument);
3902 goto done;
3903 }
3904
3905 if (targetPrivate->onDemandServer != NULL) {
3906 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, runLoop, runLoopMode, TRUE);
3907 }
3908
3909 n = CFArrayGetCount(targetPrivate->rlList);
3910 if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
3911 // if target is no longer scheduled for this runLoop / runLoopMode
3912 CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode);
3913
3914 if (targetPrivate->dnsRLS != NULL) {
3915 // if we have an active async DNS query too
3916 CFRunLoopRemoveSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
3917 }
3918
3919 if (n == 0) {
3920 // if *all* notifications have been unscheduled
3921 CFRelease(targetPrivate->rlList);
3922 targetPrivate->rlList = NULL;
3923 CFRunLoopSourceInvalidate(targetPrivate->rls);
3924 CFRelease(targetPrivate->rls);
3925 targetPrivate->rls = NULL;
3926 }
3927 }
3928 }
3929
3930 if (n == 0) {
3931 // if *all* notifications have been unscheduled
3932 targetPrivate->scheduled = FALSE;
3933
3934 if (!onDemand) {
3935 CFSetRemoveValue(hn_targets, target); // cleanup notification resources
3936 }
3937
3938 if (targetPrivate->dnsPort != NULL) {
3939 mach_port_t mp = CFMachPortGetPort(targetPrivate->dnsPort);
3940
3941 // if we have an active async DNS query
3942 dequeueAsyncDNSQuery(target);
3943 getaddrinfo_async_cancel(mp);
3944 }
3945 }
3946
3947 #if !TARGET_OS_IPHONE
3948 if (runLoop == NULL) {
3949 dispatch_release(targetPrivate->dispatchQueue);
3950 targetPrivate->dispatchQueue = NULL;
3951 }
3952 #endif // !TARGET_OS_IPHONE
3953
3954 // now, unschedule the global dynamic store source
3955 #if TARGET_OS_IPHONE
3956 if (!onDemand) {
3957 (void)_SC_unschedule(target, runLoop, runLoopMode, hn_rlList, FALSE);
3958
3959 n = CFArrayGetCount(hn_rlList);
3960 if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
3961 /*
3962 * if we no longer have any addresses scheduled for
3963 * this runLoop / runLoopMode
3964 */
3965 CFRunLoopRemoveSource(runLoop, hn_storeRLS, runLoopMode);
3966
3967 if (n > 0) {
3968 if (CFEqual(runLoopMode, kCFRunLoopCommonModes)) {
3969 CFArrayRef modes;
3970
3971 modes = CFRunLoopCopyAllModes(runLoop);
3972 if (modes != NULL) {
3973 CFIndex i;
3974 CFIndex n = CFArrayGetCount(modes);
3975
3976 for (i = 0; i < n; i++) {
3977 CFStringRef mode;
3978
3979 mode = CFArrayGetValueAtIndex(modes, i);
3980 if (_SC_isScheduled(NULL, runLoop, mode, hn_rlList)) {
3981 /*
3982 * removing kCFRunLoopCommonModes cleaned up more
3983 * than we wanted. Add back the modes that were
3984 * expect to be present.
3985 */
3986 CFRunLoopAddSource(runLoop, hn_storeRLS, mode);
3987 }
3988 }
3989
3990 CFRelease(modes);
3991 }
3992 } else if (_SC_isScheduled(NULL, runLoop, kCFRunLoopCommonModes, hn_rlList)) {
3993 /*
3994 * if we are still scheduling kCFRunLoopCommonModes, make sure that
3995 * none of the common modes were inadvertently removed.
3996 */
3997 CFRunLoopAddSource(runLoop, hn_storeRLS, kCFRunLoopCommonModes);
3998 }
3999 }
4000 }
4001 }
4002 #endif // TARGET_OS_IPHONE
4003
4004 n = CFSetGetCount(hn_targets);
4005 if (n == 0) {
4006 // if we are no longer monitoring any targets
4007 #if !TARGET_OS_IPHONE
4008 SCDynamicStoreSetDispatchQueue(hn_store, NULL);
4009 dispatch_release(hn_dispatchQueue);
4010 hn_dispatchQueue = NULL;
4011 #else // !TARGET_OS_IPHONE
4012 CFRunLoopSourceInvalidate(hn_storeRLS);
4013 CFRelease(hn_storeRLS);
4014 hn_storeRLS = NULL;
4015 CFRelease(hn_rlList);
4016 hn_rlList = NULL;
4017 #endif // !TARGET_OS_IPHONE
4018 CFRelease(hn_store);
4019 hn_store = NULL;
4020 CFRelease(hn_targets);
4021 hn_targets = NULL;
4022
4023 /*
4024 * until we start monitoring again, ensure that
4025 * any resources associated with tracking the
4026 * DNS configuration have been released.
4027 */
4028 dns_configuration_unwatch();
4029 }
4030
4031 ok = TRUE;
4032
4033 done :
4034
4035 pthread_mutex_unlock(&targetPrivate->lock);
4036 if (!onDemand) {
4037 pthread_mutex_unlock(&hn_lock);
4038 }
4039 return ok;
4040 }
4041
4042 Boolean
4043 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target,
4044 CFRunLoopRef runLoop,
4045 CFStringRef runLoopMode)
4046 {
4047 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) {
4048 _SCErrorSet(kSCStatusInvalidArgument);
4049 return FALSE;
4050 }
4051
4052 return __SCNetworkReachabilityScheduleWithRunLoop(target, runLoop, runLoopMode, NULL, FALSE);
4053 }
4054
4055 Boolean
4056 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target,
4057 CFRunLoopRef runLoop,
4058 CFStringRef runLoopMode)
4059 {
4060 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) {
4061 _SCErrorSet(kSCStatusInvalidArgument);
4062 return FALSE;
4063 }
4064
4065 return __SCNetworkReachabilityUnscheduleFromRunLoop(target, runLoop, runLoopMode, FALSE);
4066 }
4067
4068 #if !TARGET_OS_IPHONE
4069 Boolean
4070 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target,
4071 dispatch_queue_t queue)
4072 {
4073 Boolean ok = FALSE;
4074
4075 if (!isA_SCNetworkReachability(target)) {
4076 _SCErrorSet(kSCStatusInvalidArgument);
4077 return FALSE;
4078 }
4079
4080 if (queue != NULL) {
4081 ok = __SCNetworkReachabilityScheduleWithRunLoop(target, NULL, NULL, queue, FALSE);
4082 } else {
4083 ok = __SCNetworkReachabilityUnscheduleFromRunLoop(target, NULL, NULL, FALSE);
4084 }
4085
4086 return ok;
4087 }
4088 #endif // !TARGET_OS_IPHONE