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