]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkReachability.c
configd-84.1.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkReachability.c
1 /*
2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25
26 /*
27 * Modification History
28 *
29 * January 19, 2003 Allan Nathanson <ajn@apple.com>
30 * - add advanced reachability APIs
31 */
32
33 #include <SystemConfiguration/SystemConfiguration.h>
34 #include <SystemConfiguration/SCValidation.h>
35 #include <SystemConfiguration/SCPrivate.h>
36
37 #include <CoreFoundation/CFRuntime.h>
38 #include <pthread.h>
39
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <arpa/nameser.h>
43 #include <netdb.h>
44 #include <netdb_async.h>
45 #include <resolv.h>
46 #include <unistd.h>
47 #include <sys/ioctl.h>
48 #include <sys/socket.h>
49 #include <net/if.h>
50 #include <net/if_dl.h>
51 #define KERNEL_PRIVATE
52 #include <net/route.h>
53 #undef KERNEL_PRIVATE
54
55 #ifndef s6_addr16
56 #define s6_addr16 __u6_addr.__u6_addr16
57 #endif
58
59 #include "ppp.h"
60
61
62 #define kSCNetworkFlagsFirstResolvePending (1<<31)
63
64
65 #define N_QUICK 32
66
67
68 typedef enum {
69 reachabilityTypeAddress,
70 reachabilityTypeAddressPair,
71 reachabilityTypeName
72 } addressType;
73
74
75 static CFStringRef __SCNetworkReachabilityCopyDescription (CFTypeRef cf);
76 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf);
77
78
79 typedef struct {
80
81 /* base CFType information */
82 CFRuntimeBase cfBase;
83
84 /* lock */
85 pthread_mutex_t lock;
86
87 /* address type */
88 addressType type;
89
90 /* target host name */
91 const char *name;
92 CFArrayRef resolvedAddress; /* CFArray[CFData] */
93 int resolvedAddressError;
94
95 /* local & remote addresses */
96 struct sockaddr *localAddress;
97 struct sockaddr *remoteAddress;
98
99 /* current reachability flags */
100 SCNetworkConnectionFlags flags;
101 uint16_t if_index;
102
103 /* run loop source, callout, context, rl scheduling info */
104 CFRunLoopSourceRef rls;
105 SCNetworkReachabilityCallBack rlsFunction;
106 SCNetworkReachabilityContext rlsContext;
107 CFMutableArrayRef rlList;
108
109 /* async DNS query info */
110 CFMachPortRef dnsPort;
111 CFRunLoopSourceRef dnsRLS;
112
113 } SCNetworkReachabilityPrivate, *SCNetworkReachabilityPrivateRef;
114
115
116 static CFTypeID __kSCNetworkReachabilityTypeID = _kCFRuntimeNotATypeID;
117
118
119 static const CFRuntimeClass __SCNetworkReachabilityClass = {
120 0, // version
121 "SCNetworkReachability", // className
122 NULL, // init
123 NULL, // copy
124 __SCNetworkReachabilityDeallocate, // dealloc
125 NULL, // equal
126 NULL, // hash
127 NULL, // copyFormattingDesc
128 __SCNetworkReachabilityCopyDescription // copyDebugDesc
129 };
130
131
132 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
133 static Boolean needDNS = TRUE;
134
135
136 /* host "something has changed" notifications */
137 static pthread_mutex_t hn_lock = PTHREAD_MUTEX_INITIALIZER;
138 static SCDynamicStoreRef hn_store = NULL;
139 static CFRunLoopSourceRef hn_storeRLS = NULL;
140 static CFMutableArrayRef hn_rlList = NULL;
141 static CFMutableSetRef hn_targets = NULL;
142
143
144 static __inline__ CFTypeRef
145 isA_SCNetworkReachability(CFTypeRef obj)
146 {
147 return (isA_CFType(obj, SCNetworkReachabilityGetTypeID()));
148 }
149
150
151 static void
152 sockaddr_to_string(const struct sockaddr *address, char *buf, size_t bufLen)
153 {
154 bzero(buf, bufLen);
155 switch (address->sa_family) {
156 case AF_INET :
157 (void)inet_ntop(((struct sockaddr_in *)address)->sin_family,
158 &((struct sockaddr_in *)address)->sin_addr,
159 buf,
160 bufLen);
161 break;
162 case AF_INET6 : {
163 (void)inet_ntop(((struct sockaddr_in6 *)address)->sin6_family,
164 &((struct sockaddr_in6 *)address)->sin6_addr,
165 buf,
166 bufLen);
167 if (((struct sockaddr_in6 *)address)->sin6_scope_id != 0) {
168 int n;
169
170 n = strlen(buf);
171 if ((n+IF_NAMESIZE+1) <= (int)bufLen) {
172 buf[n++] = '%';
173 if_indextoname(((struct sockaddr_in6 *)address)->sin6_scope_id, &buf[n]);
174 }
175 }
176 break;
177 }
178 case AF_LINK :
179 if (((struct sockaddr_dl *)address)->sdl_len < bufLen) {
180 bufLen = ((struct sockaddr_dl *)address)->sdl_len;
181 } else {
182 bufLen = bufLen - 1;
183 }
184
185 bcopy(((struct sockaddr_dl *)address)->sdl_data, buf, bufLen);
186 break;
187 default :
188 snprintf(buf, bufLen, "unexpected address family %d", address->sa_family);
189 break;
190 }
191
192 return;
193 }
194
195
196 #ifndef CHECK_IPV6_REACHABILITY
197 static char *
198 __netdb_error(int error)
199 {
200 char *msg;
201
202 switch(error) {
203 case NETDB_INTERNAL :
204 msg = strerror(errno);
205 break;
206 case HOST_NOT_FOUND :
207 msg = "Host not found.";
208 break;
209 case TRY_AGAIN :
210 msg = "Try again.";
211 break;
212 case NO_RECOVERY :
213 msg = "No recovery.";
214 break;
215 case NO_DATA :
216 msg = "No data available.";
217 break;
218 default :
219 msg = "Unknown";
220 break;
221 }
222
223 return msg;
224 }
225 #endif /* CHECK_IPV6_REACHABILITY */
226
227
228 static void
229 __signalRunLoop(CFTypeRef obj, CFRunLoopSourceRef rls, CFArrayRef rlList)
230 {
231 CFRunLoopRef rl = NULL;
232 CFRunLoopRef rl1 = NULL;
233 CFIndex i;
234 CFIndex n = CFArrayGetCount(rlList);
235
236 if (n == 0) {
237 return;
238 }
239
240 /* get first runLoop for this object */
241 for (i = 0; i < n; i += 3) {
242 if (!CFEqual(obj, CFArrayGetValueAtIndex(rlList, i))) {
243 continue;
244 }
245
246 rl1 = (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1);
247 break;
248 }
249
250 if (!rl1) {
251 /* if not scheduled */
252 return;
253 }
254
255 /* check if we have another runLoop for this object */
256 rl = rl1;
257 for (i = i+3; i < n; i += 3) {
258 CFRunLoopRef rl2;
259
260 if (!CFEqual(obj, CFArrayGetValueAtIndex(rlList, i))) {
261 continue;
262 }
263
264 rl2 = (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1);
265 if (!CFEqual(rl1, rl2)) {
266 /* we've got more than one runLoop */
267 rl = NULL;
268 break;
269 }
270 }
271
272 if (rl) {
273 /* if we only have one runLoop */
274 CFRunLoopWakeUp(rl);
275 return;
276 }
277
278 /* more than one different runLoop, so we must pick one */
279 for (i = 0; i < n; i+=3) {
280 CFStringRef rlMode;
281
282 if (!CFEqual(obj, CFArrayGetValueAtIndex(rlList, i))) {
283 continue;
284 }
285
286 rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1);
287 rlMode = CFRunLoopCopyCurrentMode(rl);
288 if (rlMode && CFRunLoopIsWaiting(rl) && CFRunLoopContainsSource(rl, rls, rlMode)) {
289 /* we've found a runLoop that's "ready" */
290 CFRelease(rlMode);
291 CFRunLoopWakeUp(rl);
292 return;
293 }
294 if (rlMode) CFRelease(rlMode);
295 }
296
297 /* didn't choose one above, so choose first */
298 CFRunLoopWakeUp(rl1);
299 return;
300 }
301
302
303 static int
304 updatePPPStatus(SCDynamicStoreRef *storeP,
305 const struct sockaddr *sa,
306 const char *if_name,
307 SCNetworkConnectionFlags *flags)
308 {
309 CFDictionaryRef dict = NULL;
310 CFStringRef entity;
311 CFIndex i;
312 const void * keys_q[N_QUICK];
313 const void ** keys = keys_q;
314 CFIndex n;
315 CFStringRef ppp_if;
316 int sc_status = kSCStatusReachabilityUnknown;
317 SCDynamicStoreRef store = (storeP) ? *storeP : NULL;
318 const void * values_q[N_QUICK];
319 const void ** values = values_q;
320
321 switch (sa->sa_family) {
322 case AF_INET :
323 entity = kSCEntNetIPv4;
324 break;
325 case AF_INET6 :
326 entity = kSCEntNetIPv6;
327 break;
328 default :
329 goto done;
330 }
331
332 if (!store) {
333 store = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkReachability"), NULL, NULL);
334 if (!store) {
335 SCLog(_sc_verbose, LOG_INFO, CFSTR("SCDynamicStoreCreate() failed"));
336 goto done;
337 }
338 }
339
340 /*
341 * grab a snapshot of the PPP configuration from the dynamic store
342 */
343 {
344 CFStringRef pattern;
345 CFMutableArrayRef patterns;
346
347 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
348 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
349 kSCDynamicStoreDomainState,
350 kSCCompAnyRegex,
351 entity);
352 CFArrayAppendValue(patterns, pattern);
353 CFRelease(pattern);
354 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
355 kSCDynamicStoreDomainSetup,
356 kSCCompAnyRegex,
357 kSCEntNetPPP);
358 CFArrayAppendValue(patterns, pattern);
359 CFRelease(pattern);
360 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
361 kSCDynamicStoreDomainState,
362 kSCCompAnyRegex,
363 kSCEntNetPPP);
364 CFArrayAppendValue(patterns, pattern);
365 CFRelease(pattern);
366 dict = SCDynamicStoreCopyMultiple(store, NULL, patterns);
367 CFRelease(patterns);
368 }
369 if (!dict) {
370 /* if we could not access the dynamic store */
371 goto done;
372 }
373
374 sc_status = kSCStatusOK;
375
376 /*
377 * look for the service which matches the provided interface
378 */
379 n = CFDictionaryGetCount(dict);
380 if (n <= 0) {
381 goto done;
382 }
383
384 ppp_if = CFStringCreateWithCStringNoCopy(NULL,
385 if_name,
386 kCFStringEncodingASCII,
387 kCFAllocatorNull);
388
389 if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
390 keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
391 values = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
392 }
393 CFDictionaryGetKeysAndValues(dict, keys, values);
394
395 for (i=0; i < n; i++) {
396 CFArrayRef components;
397 CFStringRef key;
398 CFNumberRef num;
399 CFDictionaryRef p_setup;
400 CFDictionaryRef p_state;
401 int32_t ppp_status;
402 CFStringRef service = NULL;
403 CFStringRef s_key = (CFStringRef) keys[i];
404 CFDictionaryRef s_dict = (CFDictionaryRef)values[i];
405 CFStringRef s_if;
406
407 if (!isA_CFString(s_key) || !isA_CFDictionary(s_dict)) {
408 continue;
409 }
410
411 if (!CFStringHasSuffix(s_key, entity)) {
412 continue; // if not an IPv4 or IPv6 entity
413 }
414
415 s_if = CFDictionaryGetValue(s_dict, kSCPropInterfaceName);
416 if (!isA_CFString(s_if)) {
417 continue; // if no interface
418 }
419
420 if (!CFEqual(ppp_if, s_if)) {
421 continue; // if not this interface
422 }
423
424 /*
425 * extract service ID, get PPP "state" entity (for status), and get
426 * the "setup" entity (for dial-on-traffic flag)
427 */
428 components = CFStringCreateArrayBySeparatingStrings(NULL, s_key, CFSTR("/"));
429 if (CFArrayGetCount(components) != 5) {
430 continue;
431 }
432 service = CFArrayGetValueAtIndex(components, 3);
433 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
434 kSCDynamicStoreDomainState,
435 service,
436 kSCEntNetPPP);
437 p_state = CFDictionaryGetValue(dict, key);
438 CFRelease(key);
439 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
440 kSCDynamicStoreDomainSetup,
441 service,
442 kSCEntNetPPP);
443 p_setup = CFDictionaryGetValue(dict, key);
444 CFRelease(key);
445 CFRelease(components);
446
447 // get PPP status
448 if (!isA_CFDictionary(p_state)) {
449 continue;
450 }
451 num = CFDictionaryGetValue(p_state, kSCPropNetPPPStatus);
452 if (!isA_CFNumber(num)) {
453 continue;
454 }
455
456 if (!CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_status)) {
457 continue;
458 }
459 switch (ppp_status) {
460 case PPP_RUNNING :
461 /* if we're really UP and RUNNING */
462 break;
463 case PPP_ONHOLD :
464 /* if we're effectively UP and RUNNING */
465 break;
466 case PPP_IDLE :
467 case PPP_STATERESERVED :
468 /* if we're not connected at all */
469 SCLog(_sc_debug, LOG_INFO, CFSTR(" PPP link idle, dial-on-traffic to connect"));
470 *flags |= kSCNetworkFlagsConnectionRequired;
471 break;
472 default :
473 /* if we're in the process of [dis]connecting */
474 SCLog(_sc_debug, LOG_INFO, CFSTR(" PPP link, connection in progress"));
475 *flags |= kSCNetworkFlagsConnectionRequired;
476 break;
477 }
478
479 // check PPP dial-on-traffic status
480 if (isA_CFDictionary(p_setup)) {
481 num = CFDictionaryGetValue(p_setup, kSCPropNetPPPDialOnDemand);
482 if (isA_CFNumber(num)) {
483 int32_t ppp_demand;
484
485 if (CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand)) {
486 if (ppp_demand) {
487 *flags |= kSCNetworkFlagsConnectionAutomatic;
488 if (ppp_status == PPP_IDLE) {
489 *flags |= kSCNetworkFlagsInterventionRequired;
490 }
491 }
492 }
493 }
494 }
495
496 break;
497 }
498
499 CFRelease(ppp_if);
500 if (keys != keys_q) {
501 CFAllocatorDeallocate(NULL, keys);
502 CFAllocatorDeallocate(NULL, values);
503 }
504
505 done :
506
507 if (dict) CFRelease(dict);
508 if (storeP) *storeP = store;
509 return sc_status;
510 }
511
512
513 static int
514 updatePPPAvailable(SCDynamicStoreRef *storeP,
515 const struct sockaddr *sa,
516 SCNetworkConnectionFlags *flags)
517 {
518 CFDictionaryRef dict = NULL;
519 CFStringRef entity;
520 CFIndex i;
521 const void * keys_q[N_QUICK];
522 const void ** keys = keys_q;
523 CFIndex n;
524 int sc_status = kSCStatusReachabilityUnknown;
525 SCDynamicStoreRef store = (storeP) ? *storeP : NULL;
526 const void * values_q[N_QUICK];
527 const void ** values = values_q;
528
529 switch (sa->sa_family) {
530 case AF_INET :
531 entity = kSCEntNetIPv4;
532 break;
533 case AF_INET6 :
534 entity = kSCEntNetIPv6;
535 break;
536 default :
537 goto done;
538 }
539
540 if (!store) {
541 store = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkReachability"), NULL, NULL);
542 if (!store) {
543 SCLog(_sc_verbose, LOG_INFO, CFSTR("SCDynamicStoreCreate() failed"));
544 goto done;
545 }
546 }
547
548 /*
549 * grab a snapshot of the PPP configuration from the dynamic store
550 */
551 {
552 CFStringRef pattern;
553 CFMutableArrayRef patterns;
554
555 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
556 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
557 kSCDynamicStoreDomainSetup,
558 kSCCompAnyRegex,
559 entity);
560 CFArrayAppendValue(patterns, pattern);
561 CFRelease(pattern);
562 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
563 kSCDynamicStoreDomainSetup,
564 kSCCompAnyRegex,
565 kSCEntNetPPP);
566 CFArrayAppendValue(patterns, pattern);
567 CFRelease(pattern);
568 dict = SCDynamicStoreCopyMultiple(store, NULL, patterns);
569 CFRelease(patterns);
570 }
571 if (!dict) {
572 /* if we could not access the dynamic store */
573 goto done;
574 }
575
576 sc_status = kSCStatusOK;
577
578 /*
579 * look for an available service which will provide connectivity
580 * for the requested address family.
581 */
582 n = CFDictionaryGetCount(dict);
583 if (n <= 0) {
584 goto done;
585 }
586
587 if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
588 keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
589 values = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
590 }
591 CFDictionaryGetKeysAndValues(dict, keys, values);
592
593 for (i = 0; i < n; i++) {
594 CFArrayRef components;
595 Boolean found = FALSE;
596 CFStringRef p_key;
597 CFDictionaryRef p_dict;
598 CFStringRef service;
599 CFStringRef s_key = (CFStringRef) keys[i];
600 CFDictionaryRef s_dict = (CFDictionaryRef)values[i];
601
602 if (!isA_CFString(s_key) || !isA_CFDictionary(s_dict)) {
603 continue;
604 }
605
606 if (!CFStringHasSuffix(s_key, entity)) {
607 continue; // if not an IPv4 or IPv6 entity
608 }
609
610 // extract service ID
611 components = CFStringCreateArrayBySeparatingStrings(NULL, s_key, CFSTR("/"));
612 if (CFArrayGetCount(components) != 5) {
613 continue;
614 }
615 service = CFArrayGetValueAtIndex(components, 3);
616
617 // check for PPP entity
618 p_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
619 kSCDynamicStoreDomainSetup,
620 service,
621 kSCEntNetPPP);
622 p_dict = CFDictionaryGetValue(dict, p_key);
623 CFRelease(p_key);
624
625 if (isA_CFDictionary(p_dict)) {
626 CFNumberRef num;
627
628 /*
629 * we have a PPP service for this address family
630 */
631 found = TRUE;
632
633 *flags |= kSCNetworkFlagsReachable;
634 *flags |= kSCNetworkFlagsTransientConnection;
635 *flags |= kSCNetworkFlagsConnectionRequired;
636
637 /*
638 * get PPP dial-on-traffic status
639 */
640 num = CFDictionaryGetValue(p_dict, kSCPropNetPPPDialOnDemand);
641 if (isA_CFNumber(num)) {
642 int32_t ppp_demand;
643
644 if (CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand)) {
645 if (ppp_demand) {
646 *flags |= kSCNetworkFlagsConnectionAutomatic;
647 }
648 }
649 }
650
651 if (_sc_debug) {
652 SCLog(TRUE, LOG_INFO, CFSTR(" status = isReachable (after connect)"));
653 SCLog(TRUE, LOG_INFO, CFSTR(" service = %@"), service);
654 }
655
656 }
657
658 CFRelease(components);
659
660 if (found) {
661 break;
662 }
663 }
664
665 if (keys != keys_q) {
666 CFAllocatorDeallocate(NULL, keys);
667 CFAllocatorDeallocate(NULL, values);
668 }
669
670 done :
671
672 if (dict) CFRelease(dict);
673 if (storeP) *storeP = store;
674 return sc_status;
675 }
676
677
678 #define ROUNDUP(a, size) \
679 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
680
681 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
682 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
683 sizeof(u_long)) :\
684 sizeof(u_long)))
685
686 static void
687 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
688 {
689 int i;
690
691 for (i = 0; i < RTAX_MAX; i++) {
692 if (addrs & (1 << i)) {
693 rti_info[i] = sa;
694 NEXT_SA(sa);
695 } else
696 rti_info[i] = NULL;
697 }
698 }
699
700
701 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
702
703 static Boolean
704 checkAddress(SCDynamicStoreRef *storeP,
705 const struct sockaddr *address,
706 SCNetworkConnectionFlags *flags,
707 uint16_t *if_index)
708 {
709 char buf[BUFLEN];
710 struct ifreq ifr;
711 char if_name[IFNAMSIZ+1];
712 int isock;
713 int n;
714 pid_t pid = getpid();
715 int rsock;
716 struct sockaddr *rti_info[RTAX_MAX];
717 struct rt_msghdr *rtm;
718 struct sockaddr *sa;
719 int sc_status = kSCStatusReachabilityUnknown;
720 struct sockaddr_dl *sdl;
721 int seq = (int)pthread_self();
722 SCDynamicStoreRef store = (storeP) ? *storeP : NULL;
723 char *statusMessage = NULL;
724 #ifndef RTM_GET_SILENT
725 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
726 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
727 int sosize = 48 * 1024;
728 #endif
729
730 if (!address || !flags) {
731 sc_status = kSCStatusInvalidArgument;
732 goto done;
733 }
734
735 switch (address->sa_family) {
736 case AF_INET :
737 case AF_INET6 :
738 if (_sc_debug) {
739 sockaddr_to_string(address, buf, sizeof(buf));
740 SCLog(TRUE, LOG_INFO, CFSTR("checkAddress(%s)"), buf);
741 }
742 break;
743 default :
744 /*
745 * if no code for this address family (yet)
746 */
747 SCLog(_sc_verbose, LOG_ERR,
748 CFSTR("checkAddress(): unexpected address family %d"),
749 address->sa_family);
750 sc_status = kSCStatusInvalidArgument;
751 goto done;
752 }
753
754 *flags = 0;
755 if (if_index) {
756 *if_index = 0;
757 }
758
759 if ((address->sa_family == AF_INET) && (((struct sockaddr_in *)address)->sin_addr.s_addr == 0)) {
760 /* special case: check for available paths off the system */
761 goto checkAvailable;
762 }
763
764 bzero(&buf, sizeof(buf));
765
766 rtm = (struct rt_msghdr *)&buf;
767 rtm->rtm_msglen = sizeof(struct rt_msghdr);
768 rtm->rtm_version = RTM_VERSION;
769 #ifdef RTM_GET_SILENT
770 rtm->rtm_type = RTM_GET_SILENT;
771 #else
772 rtm->rtm_type = RTM_GET;
773 #endif
774 rtm->rtm_flags = RTF_STATIC|RTF_UP|RTF_HOST|RTF_GATEWAY;
775 rtm->rtm_addrs = RTA_DST|RTA_IFP; /* Both destination and device */
776 rtm->rtm_pid = pid;
777 rtm->rtm_seq = seq;
778
779 switch (address->sa_family) {
780 case AF_INET6: {
781 struct sockaddr_in6 *sin6;
782
783 sin6 = (struct sockaddr_in6 *)address;
784 if ((IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
785 IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) &&
786 (sin6->sin6_scope_id != 0)) {
787 sin6->sin6_addr.s6_addr16[1] = htons(sin6->sin6_scope_id);
788 sin6->sin6_scope_id = 0;
789 }
790 break;
791 }
792 }
793
794 sa = (struct sockaddr *) (rtm + 1);
795 bcopy(address, sa, address->sa_len);
796 n = ROUNDUP(sa->sa_len, sizeof(u_long));
797 rtm->rtm_msglen += n;
798
799 sdl = (struct sockaddr_dl *) ((void *)sa + n);
800 sdl->sdl_family = AF_LINK;
801 sdl->sdl_len = sizeof (struct sockaddr_dl);
802 n = ROUNDUP(sdl->sdl_len, sizeof(u_long));
803 rtm->rtm_msglen += n;
804
805 #ifndef RTM_GET_SILENT
806 pthread_mutex_lock(&lock);
807 #endif
808 rsock = socket(PF_ROUTE, SOCK_RAW, 0);
809 if (rsock == -1) {
810 #ifndef RTM_GET_SILENT
811 pthread_mutex_unlock(&lock);
812 #endif
813 SCLog(TRUE, LOG_ERR, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(errno));
814 sc_status = kSCStatusFailed;
815 goto done;
816 }
817
818 #ifndef RTM_GET_SILENT
819 if (setsockopt(rsock, SOL_SOCKET, SO_RCVBUF, &sosize, sizeof(sosize)) == -1) {
820 (void)close(rsock);
821 pthread_mutex_unlock(&lock);
822 SCLog(TRUE, LOG_ERR, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(errno));
823 sc_status = kSCStatusFailed;
824 goto done;
825 }
826 #endif
827
828 if (write(rsock, &buf, rtm->rtm_msglen) == -1) {
829 int err = errno;
830
831 (void)close(rsock);
832 #ifndef RTM_GET_SILENT
833 pthread_mutex_unlock(&lock);
834 #endif
835 if (err != ESRCH) {
836 SCLog(TRUE, LOG_ERR, CFSTR("write() failed: %s"), strerror(err));
837 goto done;
838 }
839 goto checkAvailable;
840 }
841
842 /*
843 * Type, seq, pid identify our response.
844 * Routing sockets are broadcasters on input.
845 */
846 do {
847 int n;
848
849 n = read(rsock, (void *)&buf, sizeof(buf));
850 if (n == -1) {
851 int err = errno;
852
853 if (err != EINTR) {
854 (void)close(rsock);
855 SCLog(TRUE, LOG_ERR, CFSTR("read() failed: %s"), strerror(err));
856 #ifndef RTM_GET_SILENT
857 pthread_mutex_unlock(&lock);
858 #endif
859 goto done;
860 }
861 }
862 } while (rtm->rtm_type != RTM_GET ||
863 rtm->rtm_seq != seq ||
864 rtm->rtm_pid != pid);
865
866 (void)close(rsock);
867 #ifndef RTM_GET_SILENT
868 pthread_mutex_unlock(&lock);
869 #endif
870
871 get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
872
873 #ifdef DEBUG
874 {
875 int i;
876 char buf[200];
877
878 SCLog(_sc_debug, LOG_DEBUG, CFSTR("rtm_flags = 0x%8.8x"), rtm->rtm_flags);
879
880 for (i = 0; i < RTAX_MAX; i++) {
881 if (rti_info[i] != NULL) {
882 sockaddr_to_string(rti_info[i], buf, sizeof(buf));
883 SCLog(_sc_debug, LOG_DEBUG, CFSTR("%d: %s"), i, buf);
884 }
885 }
886 }
887 #endif /* DEBUG */
888
889 if ((rti_info[RTAX_IFP] == NULL) ||
890 (rti_info[RTAX_IFP]->sa_family != AF_LINK)) {
891 /* no interface info */
892 goto done; // ???
893 }
894
895 sdl = (struct sockaddr_dl *) rti_info[RTAX_IFP];
896 if ((sdl->sdl_nlen == 0) || (sdl->sdl_nlen > IFNAMSIZ)) {
897 /* no interface name */
898 goto done; // ???
899 }
900
901 /* get the interface flags */
902
903 bzero(&ifr, sizeof(ifr));
904 bcopy(sdl->sdl_data, ifr.ifr_name, sdl->sdl_nlen);
905
906 isock = socket(AF_INET, SOCK_DGRAM, 0);
907 if (isock < 0) {
908 SCLog(TRUE, LOG_NOTICE, CFSTR("socket() failed: %s"), strerror(errno));
909 goto done;
910 }
911
912 if (ioctl(isock, SIOCGIFFLAGS, (char *)&ifr) < 0) {
913 SCLog(TRUE, LOG_NOTICE, CFSTR("ioctl() failed: %s"), strerror(errno));
914 (void)close(isock);
915 goto done;
916 }
917 (void)close(isock);
918
919 if (!(ifr.ifr_flags & IFF_UP)) {
920 goto checkAvailable;
921 }
922
923 statusMessage = "isReachable";
924 *flags |= kSCNetworkFlagsReachable;
925
926 if (rtm->rtm_flags & RTF_LOCAL) {
927 statusMessage = "isReachable (is a local address)";
928 *flags |= kSCNetworkFlagsIsLocalAddress;
929 } else if (ifr.ifr_flags & IFF_LOOPBACK) {
930 statusMessage = "isReachable (is loopback network)";
931 *flags |= kSCNetworkFlagsIsLocalAddress;
932 } else if (rti_info[RTAX_IFA]) {
933 void *addr1 = (void *)address;
934 void *addr2 = (void *)rti_info[RTAX_IFA];
935 size_t len = address->sa_len;
936
937 if ((address->sa_family != rti_info[RTAX_IFA]->sa_family) &&
938 (address->sa_len != rti_info[RTAX_IFA]->sa_len)) {
939 SCLog(TRUE, LOG_NOTICE,
940 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
941 address->sa_family,
942 address->sa_len,
943 rti_info[RTAX_IFA]->sa_family,
944 rti_info[RTAX_IFA]->sa_len);
945 goto done;
946 }
947
948 switch (address->sa_family) {
949 case AF_INET :
950 addr1 = &((struct sockaddr_in *)address)->sin_addr;
951 addr2 = &((struct sockaddr_in *)rti_info[RTAX_IFA])->sin_addr;
952 len = sizeof(struct in_addr);
953 break;
954 case AF_INET6 :
955 addr1 = &((struct sockaddr_in6 *)address)->sin6_addr;
956 addr2 = &((struct sockaddr_in6 *)rti_info[RTAX_IFA])->sin6_addr;
957 len = sizeof(struct in6_addr);
958 break;
959 default :
960 break;
961 }
962
963 if (memcmp(addr1, addr2, len) == 0) {
964 statusMessage = "isReachable (is interface address)";
965 *flags |= kSCNetworkFlagsIsLocalAddress;
966 }
967 }
968
969 if (rti_info[RTAX_GATEWAY] && (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK)) {
970 *flags |= kSCNetworkFlagsIsDirect;
971 }
972
973 bzero(&if_name, sizeof(if_name));
974 bcopy(sdl->sdl_data,
975 if_name,
976 (sdl->sdl_nlen <= IFNAMSIZ) ? sdl->sdl_nlen : IFNAMSIZ);
977
978 if (if_index) {
979 *if_index = sdl->sdl_index;
980 }
981
982 if (_sc_debug) {
983 SCLog(TRUE, LOG_INFO, CFSTR(" status = %s"), statusMessage);
984 SCLog(TRUE, LOG_INFO, CFSTR(" device = %s (%hu)"), if_name, sdl->sdl_index);
985 SCLog(TRUE, LOG_INFO, CFSTR(" ifr_flags = 0x%04hx"), ifr.ifr_flags);
986 SCLog(TRUE, LOG_INFO, CFSTR(" rtm_flags = 0x%08x"), rtm->rtm_flags);
987 }
988
989 sc_status = kSCStatusOK;
990
991 if (ifr.ifr_flags & IFF_POINTOPOINT) {
992 /*
993 * We have an interface which "claims" to be a valid path
994 * off of the system.
995 */
996 *flags |= kSCNetworkFlagsTransientConnection;
997
998 /*
999 * Check if this is a dial-on-demand PPP link that isn't
1000 * connected yet.
1001 */
1002 sc_status = updatePPPStatus(&store, address, if_name, flags);
1003 }
1004
1005 goto done;
1006
1007 checkAvailable :
1008
1009 sc_status = updatePPPAvailable(&store, address, flags);
1010
1011 done :
1012
1013 if (*flags == 0) {
1014 SCLog(_sc_debug, LOG_INFO, CFSTR(" cannot be reached"));
1015 }
1016
1017 if (storeP) *storeP = store;
1018 if (sc_status != kSCStatusOK) {
1019 _SCErrorSet(sc_status);
1020 return FALSE;
1021 }
1022
1023 return TRUE;
1024 }
1025
1026
1027 static Boolean
1028 checkAddressZero(SCDynamicStoreRef *storeP,
1029 SCNetworkConnectionFlags *flags,
1030 uint16_t *if_index)
1031 {
1032 Boolean ok;
1033 struct sockaddr_in sin;
1034
1035 bzero(&sin, sizeof(sin));
1036 sin.sin_len = sizeof(sin);
1037 sin.sin_family = AF_INET;
1038 sin.sin_addr.s_addr = 0;
1039
1040 ok = checkAddress(storeP, (struct sockaddr *)&sin, flags, if_index);
1041
1042 return ok;
1043 }
1044
1045
1046 static Boolean
1047 isAddressZero(struct sockaddr *sa, SCNetworkConnectionFlags *flags)
1048 {
1049 /*
1050 * Check if 0.0.0.0
1051 */
1052 if (sa->sa_family == AF_INET) {
1053 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
1054
1055 if (sin->sin_addr.s_addr == 0) {
1056 SCLog(_sc_debug, LOG_INFO, CFSTR("isAddressZero(0.0.0.0)"));
1057 SCLog(_sc_debug, LOG_INFO, CFSTR(" status = isReachable (this host)"));
1058 *flags |= kSCNetworkFlagsReachable;
1059 *flags |= kSCNetworkFlagsIsLocalAddress;
1060 return TRUE;
1061 }
1062 }
1063
1064 return FALSE;
1065 }
1066
1067
1068 static CFStringRef
1069 __SCNetworkReachabilityCopyDescription(CFTypeRef cf)
1070 {
1071 CFAllocatorRef allocator = CFGetAllocator(cf);
1072 CFMutableStringRef result;
1073 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)cf;
1074
1075 result = CFStringCreateMutable(allocator, 0);
1076 CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkReachability %p [%p]> { "), cf, allocator);
1077 switch (targetPrivate->type) {
1078 case reachabilityTypeAddress :
1079 case reachabilityTypeAddressPair : {
1080 char buf[64];
1081
1082 if (targetPrivate->localAddress) {
1083 sockaddr_to_string(targetPrivate->localAddress, buf, sizeof(buf));
1084 CFStringAppendFormat(result, NULL, CFSTR("local address=%s"),
1085 buf);
1086 }
1087
1088 if (targetPrivate->remoteAddress) {
1089 sockaddr_to_string(targetPrivate->remoteAddress, buf, sizeof(buf));
1090 CFStringAppendFormat(result, NULL, CFSTR("%s%saddress=%s"),
1091 targetPrivate->localAddress ? ", " : "",
1092 (targetPrivate->type == reachabilityTypeAddressPair) ? "remote " : "",
1093 buf);
1094 }
1095 break;
1096 }
1097 case reachabilityTypeName : {
1098 CFStringAppendFormat(result, NULL, CFSTR("name=%s"), targetPrivate->name);
1099 if (targetPrivate->resolvedAddress || (targetPrivate->resolvedAddressError != NETDB_SUCCESS)) {
1100 if (targetPrivate->resolvedAddress) {
1101 if (isA_CFArray(targetPrivate->resolvedAddress)) {
1102 CFIndex i;
1103 CFIndex n = CFArrayGetCount(targetPrivate->resolvedAddress);
1104
1105 CFStringAppendFormat(result, NULL, CFSTR(" ("));
1106 for (i = 0; i < n; i++) {
1107 CFDataRef address;
1108 char buf[64];
1109 struct sockaddr *sa;
1110
1111 address = CFArrayGetValueAtIndex(targetPrivate->resolvedAddress, i);
1112 sa = (struct sockaddr *)CFDataGetBytePtr(address);
1113 sockaddr_to_string(sa, buf, sizeof(buf));
1114 CFStringAppendFormat(result, NULL, CFSTR("%s%s"),
1115 i > 0 ? ", " : "",
1116 buf);
1117 }
1118 CFStringAppendFormat(result, NULL, CFSTR(")"));
1119 } else {
1120 CFStringAppendFormat(result, NULL, CFSTR(" (no addresses)"));
1121 }
1122 } else {
1123 #ifdef CHECK_IPV6_REACHABILITY
1124 CFStringAppendFormat(result, NULL, CFSTR(" (%s)"),
1125 gai_strerror(targetPrivate->resolvedAddressError));
1126 #else /* CHECK_IPV6_REACHABILITY */
1127 CFStringAppendFormat(result, NULL, CFSTR(" (%s)"),
1128 __netdb_error(targetPrivate->resolvedAddressError));
1129 #endif /* CHECK_IPV6_REACHABILITY */
1130 }
1131 } else if (targetPrivate->dnsPort) {
1132 CFStringAppendFormat(result, NULL, CFSTR(" (DNS query active)"));
1133 }
1134 break;
1135 }
1136 }
1137 if (targetPrivate->rls) {
1138 CFStringAppendFormat(result,
1139 NULL,
1140 CFSTR(", flags=%8.8x, if_index=%hu"),
1141 targetPrivate->flags,
1142 targetPrivate->if_index);
1143 }
1144 CFStringAppendFormat(result, NULL, CFSTR(" }"));
1145
1146 return result;
1147 }
1148
1149
1150 static void
1151 __SCNetworkReachabilityDeallocate(CFTypeRef cf)
1152 {
1153 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)cf;
1154
1155 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCNetworkReachabilityDeallocate:"));
1156
1157 /* release resources */
1158
1159 pthread_mutex_destroy(&targetPrivate->lock);
1160
1161 if (targetPrivate->name)
1162 CFAllocatorDeallocate(NULL, (void *)targetPrivate->name);
1163
1164 if (targetPrivate->resolvedAddress)
1165 CFRelease(targetPrivate->resolvedAddress);
1166
1167 if (targetPrivate->localAddress)
1168 CFAllocatorDeallocate(NULL, (void *)targetPrivate->localAddress);
1169
1170 if (targetPrivate->remoteAddress)
1171 CFAllocatorDeallocate(NULL, (void *)targetPrivate->remoteAddress);
1172
1173 if (targetPrivate->rlsContext.release) {
1174 targetPrivate->rlsContext.release(targetPrivate->rlsContext.info);
1175 }
1176
1177 return;
1178 }
1179
1180
1181 static void
1182 __SCNetworkReachabilityInitialize(void)
1183 {
1184 __kSCNetworkReachabilityTypeID = _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass);
1185 return;
1186 }
1187
1188
1189 static SCNetworkReachabilityPrivateRef
1190 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator)
1191 {
1192 SCNetworkReachabilityPrivateRef targetPrivate;
1193 uint32_t size;
1194
1195 /* initialize runtime */
1196 pthread_once(&initialized, __SCNetworkReachabilityInitialize);
1197
1198 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCNetworkReachabilityCreatePrivate:"));
1199
1200 /* allocate target */
1201 size = sizeof(SCNetworkReachabilityPrivate) - sizeof(CFRuntimeBase);
1202 targetPrivate = (SCNetworkReachabilityPrivateRef)_CFRuntimeCreateInstance(allocator,
1203 __kSCNetworkReachabilityTypeID,
1204 size,
1205 NULL);
1206 if (!targetPrivate) {
1207 return NULL;
1208 }
1209
1210 pthread_mutex_init(&targetPrivate->lock, NULL);
1211
1212 targetPrivate->name = NULL;
1213
1214 targetPrivate->resolvedAddress = NULL;
1215 targetPrivate->resolvedAddressError = NETDB_SUCCESS;
1216
1217 targetPrivate->localAddress = NULL;
1218 targetPrivate->remoteAddress = NULL;
1219
1220 targetPrivate->flags = 0;
1221 targetPrivate->if_index = 0;
1222
1223 targetPrivate->rls = NULL;
1224 targetPrivate->rlsFunction = NULL;
1225 targetPrivate->rlsContext.info = NULL;
1226 targetPrivate->rlsContext.retain = NULL;
1227 targetPrivate->rlsContext.release = NULL;
1228 targetPrivate->rlsContext.copyDescription = NULL;
1229 targetPrivate->rlList = NULL;
1230
1231 targetPrivate->dnsPort = NULL;
1232 targetPrivate->dnsRLS = NULL;
1233
1234 return targetPrivate;
1235 }
1236
1237
1238 SCNetworkReachabilityRef
1239 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator,
1240 const struct sockaddr *address)
1241 {
1242 SCNetworkReachabilityPrivateRef targetPrivate;
1243
1244 if (!address ||
1245 (address->sa_len == 0) ||
1246 (address->sa_len > sizeof(struct sockaddr_storage))) {
1247 _SCErrorSet(kSCStatusInvalidArgument);
1248 return NULL;
1249 }
1250
1251 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
1252 if (!targetPrivate) {
1253 return NULL;
1254 }
1255
1256 targetPrivate->type = reachabilityTypeAddress;
1257 targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, address->sa_len, 0);
1258 bcopy(address, targetPrivate->remoteAddress, address->sa_len);
1259
1260 return (SCNetworkReachabilityRef)targetPrivate;
1261 }
1262
1263
1264 SCNetworkReachabilityRef
1265 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator,
1266 const struct sockaddr *localAddress,
1267 const struct sockaddr *remoteAddress)
1268 {
1269 SCNetworkReachabilityPrivateRef targetPrivate;
1270
1271 if ((localAddress == NULL) && (remoteAddress == NULL)) {
1272 _SCErrorSet(kSCStatusInvalidArgument);
1273 return NULL;
1274 }
1275
1276 if (localAddress) {
1277 if ((localAddress->sa_len == 0) ||
1278 (localAddress->sa_len > sizeof(struct sockaddr_storage))) {
1279 _SCErrorSet(kSCStatusInvalidArgument);
1280 return NULL;
1281 }
1282 }
1283
1284 if (remoteAddress) {
1285 if ((remoteAddress->sa_len == 0) ||
1286 (remoteAddress->sa_len > sizeof(struct sockaddr_storage))) {
1287 _SCErrorSet(kSCStatusInvalidArgument);
1288 return NULL;
1289 }
1290 }
1291
1292 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
1293 if (!targetPrivate) {
1294 return NULL;
1295 }
1296
1297 targetPrivate->type = reachabilityTypeAddressPair;
1298
1299 if (localAddress) {
1300 targetPrivate->localAddress = CFAllocatorAllocate(NULL, localAddress->sa_len, 0);
1301 bcopy(localAddress, targetPrivate->localAddress, localAddress->sa_len);
1302 }
1303
1304 if (remoteAddress) {
1305 targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, remoteAddress->sa_len, 0);
1306 bcopy(remoteAddress, targetPrivate->remoteAddress, remoteAddress->sa_len);
1307 }
1308
1309 return (SCNetworkReachabilityRef)targetPrivate;
1310 }
1311
1312
1313 SCNetworkReachabilityRef
1314 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator,
1315 const char *nodename)
1316 {
1317 SCNetworkReachabilityPrivateRef targetPrivate;
1318
1319 if (!nodename) {
1320 _SCErrorSet(kSCStatusInvalidArgument);
1321 return NULL;
1322 }
1323
1324 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
1325 if (!targetPrivate) {
1326 return NULL;
1327 }
1328
1329 targetPrivate->type = reachabilityTypeName;
1330
1331 targetPrivate->flags |= kSCNetworkFlagsFirstResolvePending;
1332
1333 targetPrivate->name = CFAllocatorAllocate(NULL, strlen(nodename) + 1, 0);
1334 strcpy((char *)targetPrivate->name, nodename);
1335
1336 return (SCNetworkReachabilityRef)targetPrivate;
1337 }
1338
1339
1340 CFTypeID
1341 SCNetworkReachabilityGetTypeID(void)
1342 {
1343 pthread_once(&initialized, __SCNetworkReachabilityInitialize); /* initialize runtime */
1344 return __kSCNetworkReachabilityTypeID;
1345 }
1346
1347
1348 CFArrayRef
1349 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target,
1350 int *error_num)
1351 {
1352 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1353
1354 if (!isA_SCNetworkReachability(target)) {
1355 _SCErrorSet(kSCStatusInvalidArgument);
1356 return NULL;
1357 }
1358
1359 if (targetPrivate->type != reachabilityTypeName) {
1360 _SCErrorSet(kSCStatusInvalidArgument);
1361 return NULL;
1362 }
1363
1364 if (error_num) {
1365 *error_num = targetPrivate->resolvedAddressError;
1366 }
1367
1368 if (targetPrivate->resolvedAddress || (targetPrivate->resolvedAddressError != NETDB_SUCCESS)) {
1369 if (targetPrivate->resolvedAddress) {
1370 return CFRetain(targetPrivate->resolvedAddress);
1371 } else {
1372 /* if status is known but no resolved addresses to return */
1373 _SCErrorSet(kSCStatusOK);
1374 return NULL;
1375 }
1376 }
1377
1378 _SCErrorSet(kSCStatusReachabilityUnknown);
1379 return NULL;
1380 }
1381
1382
1383 static void
1384 __SCNetworkReachabilitySetResolvedAddress(SCNetworkReachabilityRef target,
1385 CFArrayRef addresses,
1386 int error_num)
1387 {
1388 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1389
1390 if (targetPrivate->resolvedAddress) {
1391 CFRelease(targetPrivate->resolvedAddress);
1392 }
1393 targetPrivate->resolvedAddress = addresses ? CFRetain(addresses) : NULL;
1394 targetPrivate->resolvedAddressError = error_num;
1395 return;
1396 }
1397
1398
1399 #ifdef CHECK_IPV6_REACHABILITY
1400 static void
1401 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status, struct addrinfo *res, void *context)
1402 {
1403 Boolean ok;
1404 struct addrinfo *resP;
1405 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
1406 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1407
1408 ok = (status == 0) && (res != NULL);
1409
1410 SCLog(_sc_debug, LOG_DEBUG,
1411 CFSTR("process async DNS complete%s"),
1412 ok ? "" : ", host not found");
1413
1414 if (ok) {
1415 CFMutableArrayRef addresses;
1416
1417 addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1418
1419 for (resP = res; resP; resP = resP->ai_next) {
1420 CFDataRef newAddress;
1421
1422 newAddress = CFDataCreate(NULL, (void *)resP->ai_addr, resP->ai_addr->sa_len);
1423 CFArrayAppendValue(addresses, newAddress);
1424 CFRelease(newAddress);
1425 }
1426
1427 /* save the resolved address[es] */
1428 __SCNetworkReachabilitySetResolvedAddress(target, addresses, NETDB_SUCCESS);
1429 CFRelease(addresses);
1430 } else {
1431 SCLog(_sc_debug, LOG_INFO, CFSTR("getaddrinfo() failed: %s"), gai_strerror(status));
1432
1433 /* save the error associated with the attempt to resolve the name */
1434 __SCNetworkReachabilitySetResolvedAddress(target, (CFArrayRef)kCFNull, status);
1435 }
1436
1437 if (res) freeaddrinfo(res);
1438
1439 if (targetPrivate->rls) {
1440 SCLog(_sc_debug, LOG_INFO, CFSTR("DNS request completed"));
1441 CFRunLoopSourceSignal(targetPrivate->rls);
1442 __signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
1443 }
1444
1445 return;
1446 }
1447 #else /* CHECK_IPV6_REACHABILITY */
1448 static void
1449 __SCNetworkReachabilityCallbackSetResolvedAddress(struct hostent *h, int error, void *context)
1450 {
1451 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context;
1452 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1453
1454 SCLog(_sc_debug, LOG_DEBUG,
1455 CFSTR("process async DNS complete%s"),
1456 (h == NULL) ? ", host not found" : "");
1457
1458 if (h && h->h_length) {
1459 CFMutableArrayRef addresses;
1460 union {
1461 struct sockaddr sa;
1462 struct sockaddr_in sin;
1463 struct sockaddr_in6 sin6;
1464 struct sockaddr_storage ss;
1465 } addr;
1466 char **ha = h->h_addr_list;
1467
1468 addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1469
1470 bzero(&addr, sizeof(addr));
1471
1472 while (*ha) {
1473 CFDataRef newAddress;
1474
1475 switch (h->h_length) {
1476 case sizeof(struct in_addr) :
1477 addr.sin.sin_family = AF_INET;
1478 addr.sin.sin_len = sizeof(struct sockaddr_in);
1479 bcopy(*ha, &addr.sin.sin_addr, h->h_length);
1480 break;
1481 case sizeof(struct in6_addr) :
1482 addr.sin6.sin6_family = AF_INET6;
1483 addr.sin6.sin6_len = sizeof(struct sockaddr_in6);
1484 bcopy(*ha, &addr.sin6.sin6_addr, h->h_length);
1485 break;
1486 }
1487
1488 newAddress = CFDataCreate(NULL, (void *)&addr, addr.sa.sa_len);
1489 CFArrayAppendValue(addresses, newAddress);
1490 CFRelease(newAddress);
1491
1492 ha++;
1493 }
1494
1495 /* save the resolved address[es] */
1496 __SCNetworkReachabilitySetResolvedAddress(target, addresses, NETDB_SUCCESS);
1497 CFRelease(addresses);
1498 } else {
1499 SCLog(_sc_debug, LOG_INFO, CFSTR("getipnodebyname() failed: %s"), __netdb_error(error));
1500
1501 /* save the error associated with the attempt to resolve the name */
1502 __SCNetworkReachabilitySetResolvedAddress(target, (CFArrayRef)kCFNull, error);
1503 }
1504
1505 if (h) freehostent(h);
1506
1507 if (targetPrivate->rls) {
1508 SCLog(_sc_debug, LOG_INFO, CFSTR("DNS request completed"));
1509 CFRunLoopSourceSignal(targetPrivate->rls);
1510 __signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
1511 }
1512
1513 return;
1514 }
1515 #endif /* CHECK_IPV6_REACHABILITY */
1516
1517
1518 /*
1519 * rankReachability()
1520 * Not reachable == 0
1521 * Connection Required == 1
1522 * Reachable == 2
1523 */
1524 static int
1525 rankReachability(SCNetworkConnectionFlags flags)
1526 {
1527 int rank = 0;
1528
1529 if (flags & kSCNetworkFlagsReachable) rank = 2;
1530 if (flags & kSCNetworkFlagsConnectionRequired) rank = 1;
1531 return rank;
1532 }
1533
1534
1535 #ifdef CHECK_IPV6_REACHABILITY
1536 static void
1537 getaddrinfo_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info)
1538 {
1539 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
1540 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1541
1542 pthread_mutex_lock(&targetPrivate->lock);
1543
1544 getaddrinfo_async_handle_reply(msg);
1545
1546 if (port == targetPrivate->dnsPort) {
1547 CFRelease(targetPrivate->dnsRLS);
1548 targetPrivate->dnsRLS = NULL;
1549 CFRelease(targetPrivate->dnsPort);
1550 targetPrivate->dnsPort = NULL;
1551 }
1552
1553 pthread_mutex_unlock(&targetPrivate->lock);
1554
1555 return;
1556 }
1557 #else /* CHECK_IPV6_REACHABILITY */
1558 static void
1559 getipnodebyname_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info)
1560 {
1561 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
1562 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1563
1564 pthread_mutex_lock(&targetPrivate->lock);
1565
1566 getipnodebyname_async_handleReply(msg);
1567
1568 if (port == targetPrivate->dnsPort) {
1569 CFRelease(targetPrivate->dnsRLS);
1570 targetPrivate->dnsRLS = NULL;
1571 CFRelease(targetPrivate->dnsPort);
1572 targetPrivate->dnsPort = NULL;
1573 }
1574
1575 pthread_mutex_unlock(&targetPrivate->lock);
1576
1577 return;
1578 }
1579 #endif /* CHECK_IPV6_REACHABILITY */
1580
1581
1582 static Boolean
1583 checkResolverReachability(SCDynamicStoreRef *storeP,
1584 SCNetworkConnectionFlags *flags,
1585 Boolean *haveDNS)
1586 {
1587 int i;
1588 Boolean ok = TRUE;
1589
1590 /*
1591 * We first assume that all of the configured DNS servers
1592 * are available. Since we don't know which name server will
1593 * be consulted to resolve the specified nodename we need to
1594 * check the availability of ALL name servers. We can only
1595 * proceed if we know that our query can be answered.
1596 */
1597
1598 *flags = kSCNetworkFlagsReachable;
1599 *haveDNS = FALSE;
1600
1601 if (needDNS) {
1602 if (hn_store) {
1603 /* if we are actively watching at least one host */
1604 needDNS = FALSE;
1605 }
1606 res_init();
1607 }
1608
1609 for (i = 0; i < _res.nscount; i++) {
1610 SCNetworkConnectionFlags ns_flags = 0;
1611
1612 if (_res.nsaddr_list[i].sin_addr.s_addr == 0) {
1613 continue;
1614 }
1615
1616 *haveDNS = TRUE;
1617
1618 if (_res.nsaddr_list[i].sin_len == 0) {
1619 _res.nsaddr_list[i].sin_len = sizeof(_res.nsaddr_list[i]);
1620 }
1621
1622 ok = checkAddress(storeP, (struct sockaddr *)&_res.nsaddr_list[i], &ns_flags, NULL);
1623 if (!ok) {
1624 /* not today */
1625 break;
1626 }
1627 if (rankReachability(ns_flags) < rankReachability(*flags)) {
1628 /* return the worst case result */
1629 *flags = ns_flags;
1630 }
1631 }
1632
1633 if (!*haveDNS) {
1634 /* if no DNS server addresses */
1635 *flags = 0;
1636 }
1637
1638 return ok;
1639 }
1640
1641
1642 static Boolean
1643 startAsyncDNSQuery(SCNetworkReachabilityRef target) {
1644 CFMachPortContext context = { 0, (void *)target, CFRetain, CFRelease, CFCopyDescription };
1645 int error;
1646 #ifdef CHECK_IPV6_REACHABILITY
1647 struct addrinfo hints;
1648 #endif /* CHECK_IPV6_REACHABILITY */
1649 CFIndex i;
1650 CFIndex n;
1651 mach_port_t port;
1652 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1653
1654 #ifdef CHECK_IPV6_REACHABILITY
1655 bzero(&hints, sizeof(hints));
1656 hints.ai_flags = AI_ADDRCONFIG;
1657
1658 error = getaddrinfo_async_start(&port,
1659 targetPrivate->name,
1660 NULL,
1661 &hints,
1662 __SCNetworkReachabilityCallbackSetResolvedAddress,
1663 (void *)target);
1664 if (error != 0) {
1665 /* save the error associated with the attempt to resolve the name */
1666 __SCNetworkReachabilitySetResolvedAddress(target, (CFArrayRef)kCFNull, error);
1667 return FALSE;
1668 }
1669
1670 targetPrivate->dnsPort = CFMachPortCreateWithPort(NULL,
1671 port,
1672 getaddrinfo_async_handleCFReply,
1673 &context,
1674 NULL);
1675 #else /* CHECK_IPV6_REACHABILITY */
1676 port = getipnodebyname_async_start(targetPrivate->name,
1677 AF_INET,
1678 0,
1679 &error,
1680 __SCNetworkReachabilityCallbackSetResolvedAddress,
1681 (void *)target);
1682 if (port == MACH_PORT_NULL) {
1683 /* save the error associated with the attempt to resolve the name */
1684 __SCNetworkReachabilitySetResolvedAddress(target, (CFArrayRef)kCFNull, error);
1685 return FALSE;
1686 }
1687
1688 targetPrivate->dnsPort = CFMachPortCreateWithPort(NULL,
1689 port,
1690 getipnodebyname_async_handleCFReply,
1691 &context,
1692 NULL);
1693 #endif /* CHECK_IPV6_REACHABILITY */
1694
1695 targetPrivate->dnsRLS = CFMachPortCreateRunLoopSource(NULL, targetPrivate->dnsPort, 0);
1696
1697 n = CFArrayGetCount(targetPrivate->rlList);
1698 for (i = 0; i < n; i += 3) {
1699 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
1700 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
1701
1702 CFRunLoopAddSource(rl, targetPrivate->dnsRLS, rlMode);
1703 }
1704
1705 return TRUE;
1706 }
1707
1708
1709 static Boolean
1710 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef *storeP,
1711 SCNetworkReachabilityRef target,
1712 SCNetworkConnectionFlags *flags,
1713 uint16_t *if_index,
1714 Boolean async)
1715 {
1716 CFMutableArrayRef addresses = NULL;
1717 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1718 SCNetworkConnectionFlags my_flags = 0;
1719 uint16_t my_index = 0;
1720 Boolean ok = TRUE;
1721
1722 *flags = 0;
1723 if (if_index) {
1724 *if_index = 0;
1725 }
1726
1727 if (!isA_SCNetworkReachability(target)) {
1728 _SCErrorSet(kSCStatusInvalidArgument);
1729 return FALSE;
1730 }
1731
1732 switch (targetPrivate->type) {
1733 case reachabilityTypeAddress :
1734 case reachabilityTypeAddressPair : {
1735 /*
1736 * Check "local" address
1737 */
1738 if (targetPrivate->localAddress) {
1739 /*
1740 * Check if 0.0.0.0
1741 */
1742 if (isAddressZero(targetPrivate->localAddress, &my_flags)) {
1743 goto checkRemote;
1744 }
1745
1746 /*
1747 * Check "local" address
1748 */
1749 ok = checkAddress(storeP, targetPrivate->localAddress, &my_flags, &my_index);
1750 if (!ok) {
1751 goto error; /* not today */
1752 }
1753
1754 if (!(my_flags & kSCNetworkFlagsIsLocalAddress)) {
1755 goto error; /* not reachable, non-"local" address */
1756 }
1757 }
1758
1759 checkRemote :
1760
1761 /*
1762 * Check "remote" address
1763 */
1764 if (targetPrivate->remoteAddress) {
1765 /*
1766 * in cases where we have "local" and "remote" addresses
1767 * we need to re-initialize the to-be-returned flags.
1768 */
1769 my_flags = 0;
1770 my_index = 0;
1771
1772 /*
1773 * Check if 0.0.0.0
1774 */
1775 if (isAddressZero(targetPrivate->remoteAddress, &my_flags)) {
1776 break;
1777 }
1778
1779 /*
1780 * Check "remote" address
1781 */
1782 ok = checkAddress(storeP, targetPrivate->remoteAddress, &my_flags, &my_index);
1783 if (!ok) {
1784 goto error; /* not today */
1785 }
1786 }
1787
1788 break;
1789
1790 }
1791
1792 case reachabilityTypeName : {
1793 int error;
1794 #ifndef CHECK_IPV6_REACHABILITY
1795 struct hostent *h;
1796 #endif /* CHECK_IPV6_REACHABILITY */
1797 Boolean haveDNS = FALSE;
1798 #ifdef CHECK_IPV6_REACHABILITY
1799 struct addrinfo hints;
1800 #endif /* CHECK_IPV6_REACHABILITY */
1801 SCNetworkConnectionFlags ns_flags;
1802 #ifdef CHECK_IPV6_REACHABILITY
1803 struct addrinfo *res;
1804 #endif /* CHECK_IPV6_REACHABILITY */
1805
1806 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
1807 if (addresses || (error != NETDB_SUCCESS)) {
1808 /* if resolved or an error had been detected */
1809 goto checkResolvedAddress;
1810 }
1811
1812 /* check the reachability of the DNS servers */
1813 ok = checkResolverReachability(storeP, &ns_flags, &haveDNS);\
1814 if (!ok) {
1815 /* if we could not get DNS server info */
1816 goto error;
1817 }
1818
1819 if (rankReachability(ns_flags) < 2) {
1820 /*
1821 * if DNS servers are not (or are no longer) reachable, set
1822 * flags based on the availability of configured (but not
1823 * active) services.
1824 */
1825 if (!checkAddressZero(storeP, &my_flags, &my_index)) {
1826 goto error;
1827 }
1828
1829 if (async && targetPrivate->rls) {
1830 /*
1831 * return HOST_NOT_FOUND, set flags appropriately,
1832 * and schedule notification.
1833 */
1834 #ifdef CHECK_IPV6_REACHABILITY
1835 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NODATA,
1836 NULL,
1837 (void *)target);
1838 #else /* CHECK_IPV6_REACHABILITY */
1839 __SCNetworkReachabilityCallbackSetResolvedAddress(NULL,
1840 HOST_NOT_FOUND,
1841 (void *)target);
1842 #endif /* CHECK_IPV6_REACHABILITY */
1843
1844 my_flags |= (targetPrivate->flags & kSCNetworkFlagsFirstResolvePending);
1845
1846 SCLog(_sc_debug, LOG_INFO, CFSTR("no DNS servers are reachable"));
1847 CFRunLoopSourceSignal(targetPrivate->rls);
1848 __signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
1849 }
1850 break;
1851 }
1852
1853 if (async) {
1854 /* for async requests we return the last known status */
1855 my_flags = targetPrivate->flags;
1856 my_index = targetPrivate->if_index;
1857
1858 if (targetPrivate->dnsPort) {
1859 /* if request already in progress */
1860 break;
1861 }
1862
1863 SCLog(_sc_debug, LOG_INFO, CFSTR("start DNS query for \"%s\""), targetPrivate->name);
1864
1865 /*
1866 * initiate an async DNS query
1867 */
1868 if (!startAsyncDNSQuery(target)) {
1869 /* if we could not initiate the request, process error */
1870 goto checkResolvedAddress;
1871 }
1872
1873 /* if request initiated */
1874 break;
1875 }
1876
1877 SCLog(_sc_debug, LOG_INFO, CFSTR("check DNS for \"%s\""), targetPrivate->name);
1878
1879 /*
1880 * OK, all of the DNS name servers are available. Let's
1881 * resolve the nodename into an address.
1882 */
1883 #ifdef CHECK_IPV6_REACHABILITY
1884 bzero(&hints, sizeof(hints));
1885 hints.ai_flags = AI_ADDRCONFIG;
1886
1887 error = getaddrinfo(targetPrivate->name, NULL, &hints, &res);
1888 __SCNetworkReachabilityCallbackSetResolvedAddress(error, res, (void *)target);
1889 #else /* CHECK_IPV6_REACHABILITY */
1890 h = getipnodebyname(targetPrivate->name, AF_INET, 0, &error);
1891 __SCNetworkReachabilityCallbackSetResolvedAddress(h, error, (void *)target);
1892 #endif /* CHECK_IPV6_REACHABILITY */
1893
1894 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
1895
1896 checkResolvedAddress :
1897
1898 /*
1899 * We first assume that the requested host is NOT available.
1900 * Then, check each address for accessibility and return the
1901 * best status available.
1902 */
1903 my_flags = 0;
1904 my_index = 0;
1905
1906 if (isA_CFArray(addresses)) {
1907 CFIndex i;
1908 CFIndex n = CFArrayGetCount(addresses);
1909
1910 for (i = 0; i < n; i++) {
1911 SCNetworkConnectionFlags ns_flags = 0;
1912 uint16_t ns_if_index = 0;
1913 struct sockaddr *sa;
1914
1915 sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
1916 ok = checkAddress(storeP, sa, &ns_flags, &ns_if_index);
1917 if (!ok) {
1918 goto error; /* not today */
1919 }
1920
1921 if (rankReachability(ns_flags) > rankReachability(my_flags)) {
1922 /* return the best case result */
1923 my_flags = ns_flags;
1924 my_index = ns_if_index;
1925 if (rankReachability(my_flags) == 2) {
1926 /* we're in luck */
1927 break;
1928 }
1929 }
1930 }
1931 } else {
1932 if ((error == HOST_NOT_FOUND) && !haveDNS) {
1933 /*
1934 * No DNS servers are defined. Set flags based on
1935 * the availability of configured (but not active)
1936 * services.
1937 */
1938 ok = checkAddressZero(storeP, &my_flags, &my_index);
1939 if (!ok) {
1940 goto error; /* not today */
1941 }
1942
1943 if ((my_flags & kSCNetworkFlagsReachable) &&
1944 (my_flags & kSCNetworkFlagsConnectionRequired)) {
1945 /*
1946 * Since we might pick up a set of DNS servers when this connection
1947 * is established, don't reply with a "HOST NOT FOUND" error just yet.
1948 */
1949 break;
1950 }
1951
1952 /* Host not found, not reachable! */
1953 my_flags = 0;
1954 my_index = 0;
1955 }
1956 }
1957
1958 break;
1959 }
1960 }
1961
1962 *flags = my_flags;
1963 if (if_index) {
1964 *if_index = my_index;
1965 }
1966
1967 error :
1968
1969 if (addresses) CFRelease(addresses);
1970 return ok;
1971 }
1972
1973
1974 Boolean
1975 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target,
1976 SCNetworkConnectionFlags *flags)
1977 {
1978 Boolean ok;
1979 SCDynamicStoreRef store = NULL;
1980 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1981
1982 if (!isA_SCNetworkReachability(target)) {
1983 _SCErrorSet(kSCStatusInvalidArgument);
1984 return FALSE;
1985 }
1986
1987 if (targetPrivate->rlList) {
1988 /* if being watched, return current (OK, last known) status */
1989 *flags = targetPrivate->flags & ~kSCNetworkFlagsFirstResolvePending;
1990 return TRUE;
1991 }
1992
1993 ok = __SCNetworkReachabilityGetFlags(&store, target, flags, NULL, FALSE);
1994 *flags &= ~kSCNetworkFlagsFirstResolvePending;
1995 if (store) CFRelease(store);
1996 return ok;
1997 }
1998
1999
2000 static void
2001 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store)
2002 {
2003 CFStringRef key;
2004 CFMutableArrayRef keys;
2005 CFStringRef pattern;
2006 CFMutableArrayRef patterns;
2007
2008 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2009 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2010
2011 /*
2012 * Setup:/Network/Global/IPv4 (for the ServiceOrder)
2013 */
2014 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
2015 kSCDynamicStoreDomainSetup,
2016 kSCEntNetIPv4);
2017 CFArrayAppendValue(keys, key);
2018 CFRelease(key);
2019
2020 /*
2021 * State:/Network/Global/DNS
2022 */
2023 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
2024 kSCDynamicStoreDomainState,
2025 kSCEntNetDNS);
2026 CFArrayAppendValue(keys, key);
2027 CFRelease(key);
2028
2029 /*
2030 * State:/Network/Global/IPv4
2031 */
2032 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
2033 kSCDynamicStoreDomainState,
2034 kSCEntNetIPv4);
2035 CFArrayAppendValue(keys, key);
2036 CFRelease(key);
2037
2038 /* Setup: per-service IPv4 info */
2039 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2040 kSCDynamicStoreDomainSetup,
2041 kSCCompAnyRegex,
2042 kSCEntNetIPv4);
2043 CFArrayAppendValue(patterns, pattern);
2044 CFRelease(pattern);
2045
2046 /* Setup: per-service Interface info */
2047 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2048 kSCDynamicStoreDomainSetup,
2049 kSCCompAnyRegex,
2050 kSCEntNetInterface);
2051 CFArrayAppendValue(patterns, pattern);
2052 CFRelease(pattern);
2053
2054 /* Setup: per-service PPP info */
2055 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2056 kSCDynamicStoreDomainSetup,
2057 kSCCompAnyRegex,
2058 kSCEntNetPPP);
2059 CFArrayAppendValue(patterns, pattern);
2060 CFRelease(pattern);
2061
2062 /* State: per-service IPv4 info */
2063 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2064 kSCDynamicStoreDomainState,
2065 kSCCompAnyRegex,
2066 kSCEntNetIPv4);
2067 CFArrayAppendValue(patterns, pattern);
2068 CFRelease(pattern);
2069
2070 /* State: per-interface IPv4 info */
2071 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
2072 kSCDynamicStoreDomainState,
2073 kSCCompAnyRegex,
2074 kSCEntNetIPv4);
2075 CFArrayAppendValue(patterns, pattern);
2076 CFRelease(pattern);
2077
2078 /* State: per-interface IPv6 info */
2079 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
2080 kSCDynamicStoreDomainState,
2081 kSCCompAnyRegex,
2082 kSCEntNetIPv6);
2083 CFArrayAppendValue(patterns, pattern);
2084 CFRelease(pattern);
2085
2086 (void)SCDynamicStoreSetNotificationKeys(store, keys, patterns);
2087 CFRelease(keys);
2088 CFRelease(patterns);
2089
2090 return;
2091 }
2092
2093
2094 static void
2095 __SCNetworkReachabilityReachabilityHandleChanges(SCDynamicStoreRef store,
2096 CFArrayRef changedKeys,
2097 void *info)
2098 {
2099 Boolean dnsChanged = FALSE;
2100 CFIndex i;
2101 CFStringRef key;
2102 CFIndex nTargets;
2103 const void * targets_q[N_QUICK];
2104 const void ** targets = targets_q;
2105
2106 pthread_mutex_lock(&hn_lock);
2107
2108 nTargets = CFSetGetCount(hn_targets);
2109 if (nTargets == 0) {
2110 /* if no addresses being monitored */
2111 goto done;
2112 }
2113
2114 if (CFArrayGetCount(changedKeys) == 0) {
2115 /* if no changes */
2116 goto done;
2117 }
2118
2119 SCLog(_sc_debug, LOG_INFO, CFSTR("process configuration change"));
2120
2121 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
2122 kSCDynamicStoreDomainState,
2123 kSCEntNetDNS);
2124 if (CFArrayContainsValue(changedKeys,
2125 CFRangeMake(0, CFArrayGetCount(changedKeys)),
2126 key)) {
2127 dnsChanged = TRUE; /* the DNS server(s) have changed */
2128 needDNS = TRUE; /* ... and we need to res_init() on the next query */
2129 }
2130 CFRelease(key);
2131
2132 if (!dnsChanged) {
2133 /*
2134 * if the DNS configuration didn't change we still need to
2135 * check that the DNS servers are accessible.
2136 */
2137 Boolean haveDNS = FALSE;
2138 SCNetworkConnectionFlags ns_flags;
2139 Boolean ok;
2140
2141 /* check the reachability of the DNS servers */
2142 ok = checkResolverReachability(&store, &ns_flags, &haveDNS);\
2143 if (!ok || (rankReachability(ns_flags) < 2)) {
2144 /* if DNS servers are not reachable */
2145 dnsChanged = TRUE;
2146 }
2147 }
2148
2149 SCLog(_sc_debug && dnsChanged, LOG_INFO, CFSTR(" DNS changed"));
2150
2151 if (nTargets > (CFIndex)(sizeof(targets_q) / sizeof(CFTypeRef)))
2152 targets = CFAllocatorAllocate(NULL, nTargets * sizeof(CFTypeRef), 0);
2153 CFSetGetValues(hn_targets, targets);
2154 for (i = 0; i < nTargets; i++) {
2155 SCNetworkReachabilityRef target = targets[i];
2156 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2157
2158 pthread_mutex_lock(&targetPrivate->lock);
2159
2160 if (dnsChanged) {
2161 if (targetPrivate->dnsPort) {
2162 /* cancel the outstanding DNS query */
2163 #ifdef CHECK_IPV6_REACHABILITY
2164 lu_async_call_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
2165 #else /* CHECK_IPV6_REACHABILITY */
2166 getipnodebyname_async_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
2167 #endif /* CHECK_IPV6_REACHABILITY */
2168 CFRelease(targetPrivate->dnsRLS);
2169 targetPrivate->dnsRLS = NULL;
2170 CFRelease(targetPrivate->dnsPort);
2171 targetPrivate->dnsPort = NULL;
2172 }
2173
2174 /* schedule request to resolve the name again */
2175 __SCNetworkReachabilitySetResolvedAddress(target, NULL, NETDB_SUCCESS);
2176 }
2177
2178 CFRunLoopSourceSignal(targetPrivate->rls);
2179 __signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
2180
2181 pthread_mutex_unlock(&targetPrivate->lock);
2182 }
2183 if (targets != targets_q) CFAllocatorDeallocate(NULL, targets);
2184
2185 done :
2186
2187 pthread_mutex_unlock(&hn_lock);
2188 return;
2189 }
2190
2191
2192 static Boolean
2193 __isScheduled(CFTypeRef obj, CFRunLoopRef runLoop, CFStringRef runLoopMode, CFMutableArrayRef rlList)
2194 {
2195 CFIndex i;
2196 CFIndex n = CFArrayGetCount(rlList);
2197
2198 for (i = 0; i < n; i += 3) {
2199 if (obj && !CFEqual(obj, CFArrayGetValueAtIndex(rlList, i))) {
2200 continue;
2201 }
2202 if (runLoop && !CFEqual(runLoop, CFArrayGetValueAtIndex(rlList, i+1))) {
2203 continue;
2204 }
2205 if (runLoopMode && !CFEqual(runLoopMode, CFArrayGetValueAtIndex(rlList, i+2))) {
2206 continue;
2207 }
2208 return TRUE;
2209 }
2210
2211 return FALSE;
2212 }
2213
2214
2215 static void
2216 __schedule(CFTypeRef obj, CFRunLoopRef runLoop, CFStringRef runLoopMode, CFMutableArrayRef rlList)
2217 {
2218 CFArrayAppendValue(rlList, obj);
2219 CFArrayAppendValue(rlList, runLoop);
2220 CFArrayAppendValue(rlList, runLoopMode);
2221
2222 return;
2223 }
2224
2225
2226 static Boolean
2227 __unschedule(CFTypeRef obj, CFRunLoopRef runLoop, CFStringRef runLoopMode, CFMutableArrayRef rlList, Boolean all)
2228 {
2229 CFIndex i = 0;
2230 Boolean found = FALSE;
2231 CFIndex n = CFArrayGetCount(rlList);
2232
2233 while (i < n) {
2234 if (obj && !CFEqual(obj, CFArrayGetValueAtIndex(rlList, i))) {
2235 i += 3;
2236 continue;
2237 }
2238 if (runLoop && !CFEqual(runLoop, CFArrayGetValueAtIndex(rlList, i+1))) {
2239 i += 3;
2240 continue;
2241 }
2242 if (runLoopMode && !CFEqual(runLoopMode, CFArrayGetValueAtIndex(rlList, i+2))) {
2243 i += 3;
2244 continue;
2245 }
2246
2247 found = TRUE;
2248
2249 CFArrayRemoveValueAtIndex(rlList, i + 2);
2250 CFArrayRemoveValueAtIndex(rlList, i + 1);
2251 CFArrayRemoveValueAtIndex(rlList, i);
2252
2253 if (!all) {
2254 return found;
2255 }
2256
2257 n -= 3;
2258 }
2259
2260 return found;
2261 }
2262
2263
2264 static void
2265 rlsPerform(void *info)
2266 {
2267 void *context_info;
2268 void (*context_release)(const void *);
2269 SCNetworkConnectionFlags flags;
2270 uint16_t if_index;
2271 Boolean ok;
2272 SCNetworkReachabilityCallBack rlsFunction;
2273 SCDynamicStoreRef store = NULL;
2274 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
2275 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2276
2277 SCLog(_sc_debug, LOG_DEBUG, CFSTR("process reachability change"));
2278
2279 pthread_mutex_lock(&targetPrivate->lock);
2280
2281 /* update reachability, notify if status changed */
2282 ok = __SCNetworkReachabilityGetFlags(&store, target, &flags, &if_index, TRUE);
2283 if (store) CFRelease(store);
2284 if (!ok) {
2285 /* if reachability status not available */
2286 flags = 0;
2287 if_index = 0;
2288 }
2289
2290 if ((targetPrivate->flags == flags) && (targetPrivate->if_index == if_index)) {
2291 /* if reachability flags and interface have not changed */
2292 pthread_mutex_unlock(&targetPrivate->lock);
2293 SCLog(_sc_debug, LOG_DEBUG, CFSTR("flags/interface match"));
2294 return;
2295 }
2296
2297 /* update flags / interface */
2298 targetPrivate->flags = flags;
2299 targetPrivate->if_index = if_index;
2300
2301 /* callout */
2302 rlsFunction = targetPrivate->rlsFunction;
2303 if (NULL != targetPrivate->rlsContext.retain) {
2304 context_info = (void *)targetPrivate->rlsContext.retain(targetPrivate->rlsContext.info);
2305 context_release = targetPrivate->rlsContext.release;
2306 } else {
2307 context_info = targetPrivate->rlsContext.info;
2308 context_release = NULL;
2309 }
2310
2311 pthread_mutex_unlock(&targetPrivate->lock);
2312
2313 if (rlsFunction) {
2314 SCLog(_sc_debug, LOG_DEBUG, CFSTR("flags/interface have changed"));
2315 (*rlsFunction)(target, flags, context_info);
2316 }
2317
2318 if (context_release) {
2319 context_release(context_info);
2320 }
2321
2322 return;
2323 }
2324
2325
2326 Boolean
2327 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target,
2328 SCNetworkReachabilityCallBack callout,
2329 SCNetworkReachabilityContext *context)
2330 {
2331 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2332
2333 pthread_mutex_lock(&targetPrivate->lock);
2334
2335 if (targetPrivate->rlsContext.release) {
2336 /* let go of the current context */
2337 targetPrivate->rlsContext.release(targetPrivate->rlsContext.info);
2338 }
2339
2340 targetPrivate->rlsFunction = callout;
2341 targetPrivate->rlsContext.info = NULL;
2342 targetPrivate->rlsContext.retain = NULL;
2343 targetPrivate->rlsContext.release = NULL;
2344 targetPrivate->rlsContext.copyDescription = NULL;
2345 if (context) {
2346 bcopy(context, &targetPrivate->rlsContext, sizeof(SCNetworkReachabilityContext));
2347 if (context->retain) {
2348 targetPrivate->rlsContext.info = (void *)context->retain(context->info);
2349 }
2350 }
2351
2352 pthread_mutex_unlock(&targetPrivate->lock);
2353
2354 return TRUE;
2355 }
2356
2357
2358 Boolean
2359 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target,
2360 CFRunLoopRef runLoop,
2361 CFStringRef runLoopMode)
2362 {
2363 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2364 Boolean init = FALSE;
2365 Boolean ok = FALSE;
2366
2367 if (!isA_SCNetworkReachability(target) || runLoop == NULL || runLoopMode == NULL) {
2368 _SCErrorSet(kSCStatusInvalidArgument);
2369 return FALSE;
2370 }
2371
2372 /* schedule the SCNetworkReachability run loop source */
2373
2374 pthread_mutex_lock(&hn_lock);
2375 pthread_mutex_lock(&targetPrivate->lock);
2376
2377 if (!hn_store) {
2378 /*
2379 * if we are not monitoring any hosts
2380 */
2381 hn_store = SCDynamicStoreCreate(NULL,
2382 CFSTR("SCNetworkReachability"),
2383 __SCNetworkReachabilityReachabilityHandleChanges,
2384 NULL);
2385 if (!hn_store) {
2386 SCLog(_sc_verbose, LOG_INFO, CFSTR("SCDynamicStoreCreate() failed"));
2387 goto done;
2388 }
2389
2390 __SCNetworkReachabilityReachabilitySetNotifications(hn_store);
2391
2392 hn_storeRLS = SCDynamicStoreCreateRunLoopSource(NULL, hn_store, 0);
2393 hn_rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2394 hn_targets = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
2395 }
2396
2397 if (!targetPrivate->rls) {
2398 CFRunLoopSourceContext context = { 0 // version
2399 , (void *)target // info
2400 , CFRetain // retain
2401 , CFRelease // release
2402 , CFCopyDescription // copyDescription
2403 , CFEqual // equal
2404 , CFHash // hash
2405 , NULL // schedule
2406 , NULL // cancel
2407 , rlsPerform // perform
2408 };
2409
2410 targetPrivate->rls = CFRunLoopSourceCreate(NULL, 0, &context);
2411 targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2412 init = TRUE;
2413 }
2414
2415 if (!__isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
2416 /*
2417 * if we do not already have host notifications scheduled with
2418 * this runLoop / runLoopMode
2419 */
2420 CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode);
2421
2422 if (targetPrivate->dnsRLS) {
2423 /* if we have an active async DNS query too */
2424 CFRunLoopAddSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
2425 }
2426 }
2427
2428 __schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
2429
2430 /* schedule the SCNetworkReachability run loop source */
2431
2432 if (!__isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
2433 /*
2434 * if we do not already have SC notifications scheduled with
2435 * this runLoop / runLoopMode
2436 */
2437 CFRunLoopAddSource(runLoop, hn_storeRLS, runLoopMode);
2438 }
2439
2440 __schedule(target, runLoop, runLoopMode, hn_rlList);
2441 CFSetAddValue(hn_targets, target);
2442
2443 if (init) {
2444 SCNetworkConnectionFlags flags;
2445 uint16_t if_index;
2446 SCDynamicStoreRef store = NULL;
2447
2448 /*
2449 * if we have yet to schedule SC notifications for this address
2450 * - initialize current reachability status
2451 */
2452 if (__SCNetworkReachabilityGetFlags(&store, target, &flags, &if_index, TRUE)) {
2453 /*
2454 * if reachability status available
2455 * - set flags
2456 * - schedule notification to report status via callback
2457 */
2458 targetPrivate->flags = flags;
2459 targetPrivate->if_index = if_index;
2460 CFRunLoopSourceSignal(targetPrivate->rls);
2461 __signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
2462 } else {
2463 /* if reachability status not available, async lookup started */
2464 targetPrivate->flags = 0;
2465 targetPrivate->if_index = 0;
2466 }
2467 if (store) CFRelease(store);
2468 }
2469
2470 ok = TRUE;
2471
2472 done :
2473
2474 pthread_mutex_unlock(&targetPrivate->lock);
2475 pthread_mutex_unlock(&hn_lock);
2476 return ok;
2477 }
2478
2479
2480 Boolean
2481 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target,
2482 CFRunLoopRef runLoop,
2483 CFStringRef runLoopMode)
2484 {
2485 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2486 CFIndex n;
2487 Boolean ok = FALSE;
2488
2489 if (!isA_SCNetworkReachability(target) || runLoop == NULL || runLoopMode == NULL) {
2490 _SCErrorSet(kSCStatusInvalidArgument);
2491 goto done;
2492 }
2493
2494 pthread_mutex_lock(&hn_lock);
2495 pthread_mutex_lock(&targetPrivate->lock);
2496
2497 if (!targetPrivate->rls) {
2498 /* if not currently scheduled */
2499 goto done;
2500 }
2501
2502 if (!__unschedule(NULL, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
2503 /* if not currently scheduled */
2504 goto done;
2505 }
2506
2507 n = CFArrayGetCount(targetPrivate->rlList);
2508 if (n == 0 || !__isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
2509 /*
2510 * if this host is no longer scheduled for this runLoop / runLoopMode
2511 */
2512 CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode);
2513
2514 if (targetPrivate->dnsRLS) {
2515 /* if we have an active async DNS query too */
2516 CFRunLoopRemoveSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
2517 }
2518
2519 if (n == 0) {
2520 /*
2521 * if this host is no longer scheduled
2522 */
2523 CFRelease(targetPrivate->rls); /* cleanup SCNetworkReachability resources */
2524 targetPrivate->rls = NULL;
2525 CFRelease(targetPrivate->rlList);
2526 targetPrivate->rlList = NULL;
2527 CFSetRemoveValue(hn_targets, target); /* cleanup notification resources */
2528
2529 if (targetPrivate->dnsPort) {
2530 /* if we have an active async DNS query too */
2531 #ifdef CHECK_IPV6_REACHABILITY
2532 lu_async_call_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
2533 #else /* CHECK_IPV6_REACHABILITY */
2534 getipnodebyname_async_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
2535 #endif /* CHECK_IPV6_REACHABILITY */
2536 CFRelease(targetPrivate->dnsRLS);
2537 targetPrivate->dnsRLS = NULL;
2538 CFRelease(targetPrivate->dnsPort);
2539 targetPrivate->dnsPort = NULL;
2540 }
2541 }
2542 }
2543
2544 (void)__unschedule(target, runLoop, runLoopMode, hn_rlList, FALSE);
2545
2546 n = CFArrayGetCount(hn_rlList);
2547 if (n == 0 || !__isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
2548 /*
2549 * if we no longer have any addresses scheduled for
2550 * this runLoop / runLoopMode
2551 */
2552 CFRunLoopRemoveSource(runLoop, hn_storeRLS, runLoopMode);
2553
2554 if (n == 0) {
2555 /*
2556 * if we are no longer monitoring any addresses
2557 */
2558 CFRelease(hn_targets);
2559 CFRelease(hn_rlList);
2560 CFRelease(hn_storeRLS);
2561 CFRelease(hn_store);
2562 hn_store = NULL;
2563
2564 /*
2565 * until we start monitoring again, ensure that
2566 * all subsequent reachability-by-name checks
2567 * call res_init()
2568 */
2569 needDNS = TRUE;
2570 }
2571 }
2572
2573 ok = TRUE;
2574
2575 done :
2576
2577 pthread_mutex_unlock(&targetPrivate->lock);
2578 pthread_mutex_unlock(&hn_lock);
2579 return ok;
2580 }