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