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