]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkReachability.c
038c61be677aac40505e441b935404b97cc4b1c7
[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 = 0;
1700
1701 // count domain parts
1702 for (dp = default_resolver->domain; *dp != '\0'; dp++) {
1703 if (*dp == '.') {
1704 domain_parts++;
1705 }
1706 }
1707
1708 // remove trailing dots
1709 for (dp--; (dp >= default_resolver->domain) && (*dp == '.'); dp--) {
1710 *dp = '\0';
1711 domain_parts--;
1712 }
1713
1714 if (dp >= default_resolver->domain) {
1715 // dots are separators, bump # of components
1716 domain_parts++;
1717 }
1718
1719 dp = default_resolver->domain;
1720 for (i = LOCALDOMAINPARTS; !found && (i <= domain_parts); i++) {
1721 int ret;
1722 char *search_fqdn = NULL;
1723
1724 ret = asprintf(&search_fqdn, "%s.%s", fqdn, dp);
1725 if (ret == -1) {
1726 continue;
1727 }
1728
1729 // try the provided name with the [default] domain appended
1730 found = check_matching_resolvers(storeP, dns->config, search_fqdn, flags, haveDNS);
1731 free(search_fqdn);
1732
1733 // move to the next component of the [default] domain
1734 dp = strchr(dp, '.') + 1;
1735 }
1736 }
1737 }
1738
1739 if (!found) {
1740 /*
1741 * check the reachability of the default resolver
1742 */
1743 ok = check_resolver_reachability(storeP, default_resolver, flags, haveDNS);
1744 }
1745
1746 if (fqdn != nodename) free(fqdn);
1747
1748 done :
1749
1750 if (dns != NULL) {
1751 dns_configuration_release(dns);
1752 }
1753
1754 return ok;
1755 }
1756
1757
1758 static Boolean
1759 startAsyncDNSQuery(SCNetworkReachabilityRef target) {
1760 CFMachPortContext context = { 0, (void *)target, CFRetain, CFRelease, CFCopyDescription };
1761 int error;
1762 struct addrinfo hints;
1763 CFIndex i;
1764 CFIndex n;
1765 mach_port_t port;
1766 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1767
1768 (void) gettimeofday(&targetPrivate->dnsQueryStart, NULL);
1769
1770 bzero(&hints, sizeof(hints));
1771 hints.ai_flags = AI_ADDRCONFIG;
1772 #ifdef AI_PARALLEL
1773 hints.ai_flags |= AI_PARALLEL;
1774 #endif /* AI_PARALLEL */
1775
1776 error = getaddrinfo_async_start(&port,
1777 targetPrivate->name,
1778 NULL,
1779 &hints,
1780 __SCNetworkReachabilityCallbackSetResolvedAddress,
1781 (void *)target);
1782 if (error != 0) {
1783 /* save the error associated with the attempt to resolve the name */
1784 __SCNetworkReachabilityCallbackSetResolvedAddress(error, NULL, (void *)target);
1785 return FALSE;
1786 }
1787
1788 targetPrivate->dnsPort = CFMachPortCreateWithPort(NULL,
1789 port,
1790 getaddrinfo_async_handleCFReply,
1791 &context,
1792 NULL);
1793 targetPrivate->dnsRLS = CFMachPortCreateRunLoopSource(NULL, targetPrivate->dnsPort, 0);
1794
1795 n = CFArrayGetCount(targetPrivate->rlList);
1796 for (i = 0; i < n; i += 3) {
1797 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
1798 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
1799
1800 CFRunLoopAddSource(rl, targetPrivate->dnsRLS, rlMode);
1801 }
1802
1803 return TRUE;
1804 }
1805
1806
1807 static Boolean
1808 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef *storeP,
1809 SCNetworkReachabilityRef target,
1810 SCNetworkConnectionFlags *flags,
1811 uint16_t *if_index,
1812 Boolean async)
1813 {
1814 CFMutableArrayRef addresses = NULL;
1815 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1816 SCNetworkConnectionFlags my_flags = 0;
1817 uint16_t my_index = 0;
1818 Boolean ok = TRUE;
1819
1820 *flags = 0;
1821 if (if_index != NULL) {
1822 *if_index = 0;
1823 }
1824
1825 if (!isA_SCNetworkReachability(target)) {
1826 _SCErrorSet(kSCStatusInvalidArgument);
1827 return FALSE;
1828 }
1829
1830 switch (targetPrivate->type) {
1831 case reachabilityTypeAddress :
1832 case reachabilityTypeAddressPair : {
1833 /*
1834 * Check "local" address
1835 */
1836 if (targetPrivate->localAddress != NULL) {
1837 /*
1838 * Check "local" address
1839 */
1840 ok = checkAddress(storeP, targetPrivate->localAddress, &my_flags, &my_index);
1841 if (!ok) {
1842 goto error; /* not today */
1843 }
1844
1845 if (!(my_flags & kSCNetworkFlagsIsLocalAddress)) {
1846 goto error; /* not reachable, non-"local" address */
1847 }
1848 }
1849
1850 /*
1851 * Check "remote" address
1852 */
1853 if (targetPrivate->remoteAddress != NULL) {
1854 /*
1855 * in cases where we have "local" and "remote" addresses
1856 * we need to re-initialize the to-be-returned flags.
1857 */
1858 my_flags = 0;
1859 my_index = 0;
1860
1861 /*
1862 * Check "remote" address
1863 */
1864 ok = checkAddress(storeP, targetPrivate->remoteAddress, &my_flags, &my_index);
1865 if (!ok) {
1866 goto error; /* not today */
1867 }
1868 }
1869
1870 break;
1871
1872 }
1873
1874 case reachabilityTypeName : {
1875 struct timeval dnsQueryStart;
1876 int error;
1877 struct addrinfo hints;
1878 SCNetworkConnectionFlags ns_flags;
1879 struct addrinfo *res;
1880
1881 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
1882 if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
1883 /* if resolved or an error had been detected */
1884 goto checkResolvedAddress;
1885 }
1886
1887 /* check the reachability of the DNS servers */
1888 ok = _SC_checkResolverReachability(storeP,
1889 &ns_flags,
1890 &targetPrivate->haveDNS,
1891 targetPrivate->name);
1892 if (!ok) {
1893 /* if we could not get DNS server info */
1894 goto error;
1895 }
1896
1897 if (rankReachability(ns_flags) < 2) {
1898 /*
1899 * if DNS servers are not (or are no longer) reachable, set
1900 * flags based on the availability of configured (but not
1901 * active) services.
1902 */
1903 if (!checkAddress(storeP, NULL, &my_flags, &my_index)) {
1904 goto error;
1905 }
1906
1907 if (async && (targetPrivate->rls != NULL)) {
1908 /*
1909 * return "host not found", set flags appropriately,
1910 * and schedule notification.
1911 */
1912 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NODATA,
1913 NULL,
1914 (void *)target);
1915 my_flags |= (targetPrivate->flags & kSCNetworkFlagsFirstResolvePending);
1916
1917 SCLog(_sc_debug, LOG_INFO, CFSTR("no DNS servers are reachable"));
1918 CFRunLoopSourceSignal(targetPrivate->rls);
1919 _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
1920 }
1921 break;
1922 }
1923
1924 if (async) {
1925 /* for async requests we return the last known status */
1926 my_flags = targetPrivate->flags;
1927 my_index = targetPrivate->if_index;
1928
1929 if (targetPrivate->dnsPort) {
1930 /* if request already in progress */
1931 break;
1932 }
1933
1934 SCLog(_sc_debug, LOG_INFO, CFSTR("start DNS query for \"%s\""), targetPrivate->name);
1935
1936 /*
1937 * initiate an async DNS query
1938 */
1939 if (!startAsyncDNSQuery(target)) {
1940 /* if we could not initiate the request, process error */
1941 goto checkResolvedAddress;
1942 }
1943
1944 /* if request initiated */
1945 break;
1946 }
1947
1948 SCLog(_sc_debug, LOG_INFO, CFSTR("check DNS for \"%s\""), targetPrivate->name);
1949
1950 /*
1951 * OK, all of the DNS name servers are available. Let's
1952 * resolve the nodename into an address.
1953 */
1954 if (_sc_debug) {
1955 (void) gettimeofday(&dnsQueryStart, NULL);
1956 }
1957
1958 bzero(&hints, sizeof(hints));
1959 hints.ai_flags = AI_ADDRCONFIG;
1960 #ifdef AI_PARALLEL
1961 hints.ai_flags |= AI_PARALLEL;
1962 #endif /* AI_PARALLEL */
1963
1964 error = getaddrinfo(targetPrivate->name, NULL, &hints, &res);
1965
1966 __log_query_time(((error == 0) && (res != NULL)),// if successful query
1967 FALSE, // sync
1968 &dnsQueryStart); // start time
1969
1970 __SCNetworkReachabilitySetResolvedAddress(error, res, target);
1971
1972 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
1973
1974 checkResolvedAddress :
1975
1976 /*
1977 * We first assume that the requested host is NOT available.
1978 * Then, check each address for accessibility and return the
1979 * best status available.
1980 */
1981 my_flags = 0;
1982 my_index = 0;
1983
1984 if (isA_CFArray(addresses)) {
1985 CFIndex i;
1986 CFIndex n = CFArrayGetCount(addresses);
1987
1988 for (i = 0; i < n; i++) {
1989 SCNetworkConnectionFlags ns_flags = 0;
1990 uint16_t ns_if_index = 0;
1991 struct sockaddr *sa;
1992
1993 sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
1994
1995 ok = checkAddress(storeP, sa, &ns_flags, &ns_if_index);
1996 if (!ok) {
1997 goto error; /* not today */
1998 }
1999
2000 if (rankReachability(ns_flags) > rankReachability(my_flags)) {
2001 /* return the best case result */
2002 my_flags = ns_flags;
2003 my_index = ns_if_index;
2004 if (rankReachability(my_flags) == 2) {
2005 /* we're in luck */
2006 break;
2007 }
2008 }
2009 }
2010 } else {
2011 if ((error == EAI_NODATA) && !targetPrivate->haveDNS) {
2012 /*
2013 * No DNS servers are defined. Set flags based on
2014 * the availability of configured (but not active)
2015 * services.
2016 */
2017 ok = checkAddress(storeP, NULL, &my_flags, &my_index);
2018 if (!ok) {
2019 goto error; /* not today */
2020 }
2021
2022 if ((my_flags & kSCNetworkFlagsReachable) &&
2023 (my_flags & kSCNetworkFlagsConnectionRequired)) {
2024 /*
2025 * Since we might pick up a set of DNS servers when this connection
2026 * is established, don't reply with a "HOST NOT FOUND" error just yet.
2027 */
2028 break;
2029 }
2030
2031 /* Host not found, not reachable! */
2032 my_flags = 0;
2033 my_index = 0;
2034 }
2035 }
2036
2037 break;
2038 }
2039 }
2040
2041 *flags = my_flags;
2042 if (if_index != NULL) {
2043 *if_index = my_index;
2044 }
2045
2046 error :
2047
2048 if (addresses != NULL) CFRelease(addresses);
2049 return ok;
2050 }
2051
2052
2053 Boolean
2054 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target,
2055 SCNetworkConnectionFlags *flags)
2056 {
2057 Boolean ok;
2058 SCDynamicStoreRef store = NULL;
2059 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2060
2061 if (!isA_SCNetworkReachability(target)) {
2062 _SCErrorSet(kSCStatusInvalidArgument);
2063 return FALSE;
2064 }
2065
2066 if (targetPrivate->rlList != NULL) {
2067 // if being watched, return the last known (and what should be current) status
2068 *flags = targetPrivate->flags & ~kSCNetworkFlagsFirstResolvePending;
2069 return TRUE;
2070 }
2071
2072 ok = __SCNetworkReachabilityGetFlags(&store, target, flags, NULL, FALSE);
2073 *flags &= ~kSCNetworkFlagsFirstResolvePending;
2074 if (store != NULL) CFRelease(store);
2075 return ok;
2076 }
2077
2078
2079 static void
2080 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store)
2081 {
2082 CFStringRef dns_key;
2083 CFStringRef key;
2084 CFMutableArrayRef keys;
2085 CFStringRef pattern;
2086 CFMutableArrayRef patterns;
2087
2088 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2089 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2090
2091 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
2092 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
2093 kSCDynamicStoreDomainSetup,
2094 kSCEntNetIPv4);
2095 CFArrayAppendValue(keys, key);
2096 CFRelease(key);
2097
2098 dns_key = CFStringCreateWithCString(NULL,
2099 dns_configuration_notify_key(),
2100 kCFStringEncodingASCII);
2101 key = CFStringCreateWithFormat(NULL, NULL, CFSTR("Notify:%@"), dns_key);
2102 CFRelease(dns_key);
2103 CFArrayAppendValue(keys, key);
2104 CFRelease(key);
2105
2106 // State:/Network/Global/IPv4 (default route)
2107 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
2108 kSCDynamicStoreDomainState,
2109 kSCEntNetIPv4);
2110 CFArrayAppendValue(keys, key);
2111 CFRelease(key);
2112
2113 // Setup: per-service IPv4 info
2114 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2115 kSCDynamicStoreDomainSetup,
2116 kSCCompAnyRegex,
2117 kSCEntNetIPv4);
2118 CFArrayAppendValue(patterns, pattern);
2119 CFRelease(pattern);
2120
2121 // Setup: per-service Interface info
2122 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2123 kSCDynamicStoreDomainSetup,
2124 kSCCompAnyRegex,
2125 kSCEntNetInterface);
2126 CFArrayAppendValue(patterns, pattern);
2127 CFRelease(pattern);
2128
2129 // Setup: per-service PPP info (for kSCPropNetPPPDialOnDemand)
2130 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2131 kSCDynamicStoreDomainSetup,
2132 kSCCompAnyRegex,
2133 kSCEntNetPPP);
2134 CFArrayAppendValue(patterns, pattern);
2135 CFRelease(pattern);
2136
2137 // State: per-interface IPv4 info
2138 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
2139 kSCDynamicStoreDomainState,
2140 kSCCompAnyRegex,
2141 kSCEntNetIPv4);
2142 CFArrayAppendValue(patterns, pattern);
2143 CFRelease(pattern);
2144
2145 // State: per-interface IPv6 info
2146 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
2147 kSCDynamicStoreDomainState,
2148 kSCCompAnyRegex,
2149 kSCEntNetIPv6);
2150 CFArrayAppendValue(patterns, pattern);
2151 CFRelease(pattern);
2152
2153 (void)SCDynamicStoreSetNotificationKeys(store, keys, patterns);
2154 CFRelease(keys);
2155 CFRelease(patterns);
2156
2157 return;
2158 }
2159
2160
2161 static void
2162 __SCNetworkReachabilityReachabilityHandleChanges(SCDynamicStoreRef store,
2163 CFArrayRef changedKeys,
2164 void *info)
2165 {
2166 Boolean dnsConfigChanged = FALSE;
2167 CFStringRef dnsKey;
2168 CFIndex i;
2169 CFStringRef key;
2170 CFIndex nTargets;
2171 const void * targets_q[N_QUICK];
2172 const void ** targets = targets_q;
2173
2174 pthread_mutex_lock(&hn_lock);
2175
2176 nTargets = CFSetGetCount(hn_targets);
2177 if (nTargets == 0) {
2178 /* if no addresses being monitored */
2179 goto done;
2180 }
2181
2182 if (CFArrayGetCount(changedKeys) == 0) {
2183 /* if no changes */
2184 goto done;
2185 }
2186
2187 SCLog(_sc_debug, LOG_INFO, CFSTR("process configuration change"));
2188
2189 dnsKey = CFStringCreateWithCString(NULL,
2190 dns_configuration_notify_key(),
2191 kCFStringEncodingASCII);
2192 key = CFStringCreateWithFormat(NULL, NULL, CFSTR("Notify:%@"), dnsKey);
2193 CFRelease(dnsKey);
2194 if (CFArrayContainsValue(changedKeys,
2195 CFRangeMake(0, CFArrayGetCount(changedKeys)),
2196 key)) {
2197 dnsConfigChanged = TRUE; /* the DNS server(s) have changed */
2198
2199 }
2200 CFRelease(key);
2201
2202 SCLog(_sc_debug && dnsConfigChanged, LOG_INFO, CFSTR(" DNS configuration changed"));
2203
2204 if (nTargets > (CFIndex)(sizeof(targets_q) / sizeof(CFTypeRef)))
2205 targets = CFAllocatorAllocate(NULL, nTargets * sizeof(CFTypeRef), 0);
2206 CFSetGetValues(hn_targets, targets);
2207 for (i = 0; i < nTargets; i++) {
2208 SCNetworkReachabilityRef target = targets[i];
2209 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2210
2211 pthread_mutex_lock(&targetPrivate->lock);
2212
2213 if (targetPrivate->type == reachabilityTypeName) {
2214 Boolean dnsChanged = dnsConfigChanged;
2215
2216 if (!dnsChanged) {
2217 /*
2218 * if the DNS configuration didn't change we still need to
2219 * check that the DNS servers are accessible.
2220 */
2221 SCNetworkConnectionFlags ns_flags;
2222 Boolean ok;
2223
2224 /* check the reachability of the DNS servers */
2225 ok = _SC_checkResolverReachability(&store,
2226 &ns_flags,
2227 &targetPrivate->haveDNS,
2228 targetPrivate->name);
2229 if (!ok || (rankReachability(ns_flags) < 2)) {
2230 /* if DNS servers are not reachable */
2231 dnsChanged = TRUE;
2232 }
2233 }
2234
2235 if (dnsChanged) {
2236 if (targetPrivate->dnsPort) {
2237 /* cancel the outstanding DNS query */
2238 lu_async_call_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
2239 CFRunLoopSourceInvalidate(targetPrivate->dnsRLS);
2240 CFRelease(targetPrivate->dnsRLS);
2241 targetPrivate->dnsRLS = NULL;
2242 CFRelease(targetPrivate->dnsPort);
2243 targetPrivate->dnsPort = NULL;
2244 }
2245
2246 /* schedule request to resolve the name again */
2247 if (targetPrivate->resolvedAddress != NULL) {
2248 CFRelease(targetPrivate->resolvedAddress);
2249 targetPrivate->resolvedAddress = NULL;
2250 }
2251 targetPrivate->resolvedAddress = NULL;
2252 targetPrivate->resolvedAddressError = NETDB_SUCCESS;
2253 }
2254 }
2255
2256 CFRunLoopSourceSignal(targetPrivate->rls);
2257 _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
2258
2259 pthread_mutex_unlock(&targetPrivate->lock);
2260 }
2261 if (targets != targets_q) CFAllocatorDeallocate(NULL, targets);
2262
2263 done :
2264
2265 pthread_mutex_unlock(&hn_lock);
2266 return;
2267 }
2268
2269
2270 static void
2271 rlsPerform(void *info)
2272 {
2273 void *context_info;
2274 void (*context_release)(const void *);
2275 SCNetworkConnectionFlags flags;
2276 uint16_t if_index;
2277 Boolean ok;
2278 SCNetworkReachabilityCallBack rlsFunction;
2279 SCDynamicStoreRef store = NULL;
2280 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
2281 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2282
2283 SCLog(_sc_debug, LOG_DEBUG, CFSTR("process reachability change"));
2284
2285 pthread_mutex_lock(&targetPrivate->lock);
2286
2287 /* update reachability, notify if status changed */
2288 ok = __SCNetworkReachabilityGetFlags(&store, target, &flags, &if_index, TRUE);
2289 if (store != NULL) CFRelease(store);
2290 if (!ok) {
2291 /* if reachability status not available */
2292 flags = 0;
2293 if_index = 0;
2294 }
2295
2296 if ((targetPrivate->flags == flags) && (targetPrivate->if_index == if_index)) {
2297 /* if reachability flags and interface have not changed */
2298 pthread_mutex_unlock(&targetPrivate->lock);
2299 SCLog(_sc_debug, LOG_DEBUG,
2300 CFSTR("flags/interface match (now %8.8x/%hu)"),
2301 flags, if_index);
2302 return;
2303 } else {
2304 SCLog(_sc_debug, LOG_DEBUG,
2305 CFSTR("flags/interface have changed (was %8.8x/%hu, now %8.8x/%hu)"),
2306 targetPrivate->flags, targetPrivate->if_index,
2307 flags, if_index);
2308 }
2309
2310 /* update flags / interface */
2311 targetPrivate->flags = flags;
2312 targetPrivate->if_index = if_index;
2313
2314 /* callout */
2315 rlsFunction = targetPrivate->rlsFunction;
2316 if (targetPrivate->rlsContext.retain != NULL) {
2317 context_info = (void *)(*targetPrivate->rlsContext.retain)(targetPrivate->rlsContext.info);
2318 context_release = targetPrivate->rlsContext.release;
2319 } else {
2320 context_info = targetPrivate->rlsContext.info;
2321 context_release = NULL;
2322 }
2323
2324 pthread_mutex_unlock(&targetPrivate->lock);
2325
2326 if (rlsFunction != NULL) {
2327 (*rlsFunction)(target, flags, context_info);
2328 }
2329
2330 if (context_release != NULL) {
2331 (*context_release)(context_info);
2332 }
2333
2334 return;
2335 }
2336
2337
2338 Boolean
2339 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target,
2340 SCNetworkReachabilityCallBack callout,
2341 SCNetworkReachabilityContext *context)
2342 {
2343 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2344
2345 pthread_mutex_lock(&targetPrivate->lock);
2346
2347 if (targetPrivate->rlsContext.release != NULL) {
2348 /* let go of the current context */
2349 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
2350 }
2351
2352 targetPrivate->rlsFunction = callout;
2353 targetPrivate->rlsContext.info = NULL;
2354 targetPrivate->rlsContext.retain = NULL;
2355 targetPrivate->rlsContext.release = NULL;
2356 targetPrivate->rlsContext.copyDescription = NULL;
2357 if (context) {
2358 bcopy(context, &targetPrivate->rlsContext, sizeof(SCNetworkReachabilityContext));
2359 if (context->retain != NULL) {
2360 targetPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
2361 }
2362 }
2363
2364 pthread_mutex_unlock(&targetPrivate->lock);
2365
2366 return TRUE;
2367 }
2368
2369
2370 Boolean
2371 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target,
2372 CFRunLoopRef runLoop,
2373 CFStringRef runLoopMode)
2374 {
2375 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2376 Boolean init = FALSE;
2377 Boolean ok = FALSE;
2378
2379 if (!isA_SCNetworkReachability(target) || runLoop == NULL || runLoopMode == NULL) {
2380 _SCErrorSet(kSCStatusInvalidArgument);
2381 return FALSE;
2382 }
2383
2384 /* schedule the SCNetworkReachability run loop source */
2385
2386 pthread_mutex_lock(&hn_lock);
2387 pthread_mutex_lock(&targetPrivate->lock);
2388
2389 if (hn_store == NULL) {
2390 /*
2391 * if we are not monitoring any hosts, start watching
2392 */
2393 if (!dns_configuration_watch()) {
2394 // if error
2395 _SCErrorSet(kSCStatusFailed);
2396 goto done;
2397 }
2398
2399 hn_store = SCDynamicStoreCreate(NULL,
2400 CFSTR("SCNetworkReachability"),
2401 __SCNetworkReachabilityReachabilityHandleChanges,
2402 NULL);
2403 if (hn_store == NULL) {
2404 SCLog(_sc_verbose, LOG_INFO, CFSTR("SCDynamicStoreCreate() failed"));
2405 goto done;
2406 }
2407
2408 __SCNetworkReachabilityReachabilitySetNotifications(hn_store);
2409
2410 hn_storeRLS = SCDynamicStoreCreateRunLoopSource(NULL, hn_store, 0);
2411 hn_rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2412 hn_targets = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
2413 }
2414
2415 if (targetPrivate->rls == NULL) {
2416 CFRunLoopSourceContext context = { 0 // version
2417 , (void *)target // info
2418 , CFRetain // retain
2419 , CFRelease // release
2420 , CFCopyDescription // copyDescription
2421 , CFEqual // equal
2422 , CFHash // hash
2423 , NULL // schedule
2424 , NULL // cancel
2425 , rlsPerform // perform
2426 };
2427
2428 targetPrivate->rls = CFRunLoopSourceCreate(NULL, 0, &context);
2429 targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2430 init = TRUE;
2431 }
2432
2433 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
2434 /*
2435 * if we do not already have host notifications scheduled with
2436 * this runLoop / runLoopMode
2437 */
2438 CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode);
2439
2440 if (targetPrivate->dnsRLS != NULL) {
2441 /* if we have an active async DNS query too */
2442 CFRunLoopAddSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
2443 }
2444 }
2445
2446 _SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
2447
2448 /* schedule the SCNetworkReachability run loop source */
2449
2450 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
2451 /*
2452 * if we do not already have SC notifications scheduled with
2453 * this runLoop / runLoopMode
2454 */
2455 CFRunLoopAddSource(runLoop, hn_storeRLS, runLoopMode);
2456 }
2457
2458 _SC_schedule(target, runLoop, runLoopMode, hn_rlList);
2459 CFSetAddValue(hn_targets, target);
2460
2461 if (init) {
2462 SCNetworkConnectionFlags flags;
2463 uint16_t if_index;
2464 SCDynamicStoreRef store = NULL;
2465
2466 /*
2467 * if we have yet to schedule SC notifications for this address
2468 * - initialize current reachability status
2469 */
2470 if (__SCNetworkReachabilityGetFlags(&store, target, &flags, &if_index, TRUE)) {
2471 /*
2472 * if reachability status available
2473 * - set flags
2474 * - schedule notification to report status via callback
2475 */
2476 targetPrivate->flags = flags;
2477 targetPrivate->if_index = if_index;
2478 CFRunLoopSourceSignal(targetPrivate->rls);
2479 _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
2480 } else {
2481 /* if reachability status not available, async lookup started */
2482 targetPrivate->flags = 0;
2483 targetPrivate->if_index = 0;
2484 }
2485 if (store != NULL) CFRelease(store);
2486 }
2487
2488 ok = TRUE;
2489
2490 done :
2491
2492 pthread_mutex_unlock(&targetPrivate->lock);
2493 pthread_mutex_unlock(&hn_lock);
2494 return ok;
2495 }
2496
2497
2498 Boolean
2499 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target,
2500 CFRunLoopRef runLoop,
2501 CFStringRef runLoopMode)
2502 {
2503 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2504 CFIndex n;
2505 Boolean ok = FALSE;
2506
2507 if (!isA_SCNetworkReachability(target) || runLoop == NULL || runLoopMode == NULL) {
2508 _SCErrorSet(kSCStatusInvalidArgument);
2509 goto done;
2510 }
2511
2512 pthread_mutex_lock(&hn_lock);
2513 pthread_mutex_lock(&targetPrivate->lock);
2514
2515 if (targetPrivate->rls == NULL) {
2516 /* if not currently scheduled */
2517 goto done;
2518 }
2519
2520 if (!_SC_unschedule(NULL, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
2521 /* if not currently scheduled */
2522 goto done;
2523 }
2524
2525 n = CFArrayGetCount(targetPrivate->rlList);
2526 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
2527 /*
2528 * if this host is no longer scheduled for this runLoop / runLoopMode
2529 */
2530 CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode);
2531
2532 if (targetPrivate->dnsRLS != NULL) {
2533 /* if we have an active async DNS query too */
2534 CFRunLoopRemoveSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
2535 }
2536
2537 if (n == 0) {
2538 /*
2539 * if this host is no longer scheduled
2540 */
2541 CFRunLoopSourceInvalidate(targetPrivate->rls); /* cleanup SCNetworkReachability resources */
2542 CFRelease(targetPrivate->rls);
2543 targetPrivate->rls = NULL;
2544 CFRelease(targetPrivate->rlList);
2545 targetPrivate->rlList = NULL;
2546 CFSetRemoveValue(hn_targets, target); /* cleanup notification resources */
2547
2548 if (targetPrivate->dnsPort) {
2549 /* if we have an active async DNS query too */
2550 lu_async_call_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
2551 CFRunLoopSourceInvalidate(targetPrivate->dnsRLS);
2552 CFRelease(targetPrivate->dnsRLS);
2553 targetPrivate->dnsRLS = NULL;
2554 CFRelease(targetPrivate->dnsPort);
2555 targetPrivate->dnsPort = NULL;
2556 }
2557 }
2558 }
2559
2560 (void)_SC_unschedule(target, runLoop, runLoopMode, hn_rlList, FALSE);
2561
2562 n = CFArrayGetCount(hn_rlList);
2563 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
2564 /*
2565 * if we no longer have any addresses scheduled for
2566 * this runLoop / runLoopMode
2567 */
2568 CFRunLoopRemoveSource(runLoop, hn_storeRLS, runLoopMode);
2569
2570 if (n == 0) {
2571 /*
2572 * if we are no longer monitoring any addresses
2573 */
2574 CFRelease(hn_targets);
2575 hn_targets = NULL;
2576 CFRelease(hn_rlList);
2577 hn_rlList = NULL;
2578 CFRunLoopSourceInvalidate(hn_storeRLS);
2579 CFRelease(hn_storeRLS);
2580 hn_storeRLS = NULL;
2581 CFRelease(hn_store);
2582 hn_store = NULL;
2583
2584 /*
2585 * until we start monitoring again, ensure that
2586 * any resources associated with tracking the
2587 * DNS configuration have been released.
2588 */
2589 dns_configuration_unwatch();
2590 }
2591 }
2592
2593 ok = TRUE;
2594
2595 done :
2596
2597 pthread_mutex_unlock(&targetPrivate->lock);
2598 pthread_mutex_unlock(&hn_lock);
2599 return ok;
2600 }