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