]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkReachability.c
a0d0a5408976a732242ff18a384f89a72a781ce5
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkReachability.c
1 /*
2 * Copyright (c) 2003-2004 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 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
1354 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1355
1356 pthread_mutex_lock(&targetPrivate->lock);
1357
1358 getaddrinfo_async_handle_reply(msg);
1359
1360 if (port == targetPrivate->dnsPort) {
1361 CFRunLoopSourceInvalidate(targetPrivate->dnsRLS);
1362 CFRelease(targetPrivate->dnsRLS);
1363 targetPrivate->dnsRLS = NULL;
1364 CFRelease(targetPrivate->dnsPort);
1365 targetPrivate->dnsPort = NULL;
1366 }
1367
1368 pthread_mutex_unlock(&targetPrivate->lock);
1369
1370 return;
1371 }
1372
1373
1374 static Boolean
1375 check_resolver_reachability(SCDynamicStoreRef *storeP,
1376 dns_resolver_t *resolver,
1377 SCNetworkConnectionFlags *flags,
1378 Boolean *haveDNS)
1379 {
1380 int i;
1381 Boolean ok = TRUE;
1382
1383 *flags = kSCNetworkFlagsReachable;
1384 *haveDNS = FALSE;
1385
1386 for (i = 0; i < resolver->n_nameserver; i++) {
1387 struct sockaddr *address = resolver->nameserver[i];
1388 SCNetworkConnectionFlags ns_flags = 0;
1389
1390 *haveDNS = TRUE;
1391
1392 if (address->sa_family != AF_INET) {
1393 /*
1394 * we need to skip non-IPv4 DNS server
1395 * addresses (at least until [3510431] has
1396 * been resolved).
1397 */
1398 continue;
1399 }
1400
1401 ok = checkAddress(storeP, address, &ns_flags, NULL);
1402 if (!ok) {
1403 /* not today */
1404 goto done;
1405 }
1406
1407 if (rankReachability(ns_flags) < rankReachability(*flags)) {
1408 /* return the worst case result */
1409 *flags = ns_flags;
1410 }
1411 }
1412
1413 done :
1414
1415 return ok;
1416 }
1417
1418
1419 static Boolean
1420 check_matching_resolvers(SCDynamicStoreRef *storeP,
1421 dns_config_t *dns_config,
1422 const char *fqdn,
1423 SCNetworkConnectionFlags *flags,
1424 Boolean *haveDNS)
1425 {
1426 int i;
1427 Boolean matched = FALSE;
1428 const char *name = fqdn;
1429
1430 while (!matched && (name != NULL)) {
1431 int len;
1432
1433 /*
1434 * check if the provided name (or sub-component)
1435 * matches one of our resolver configurations.
1436 */
1437 len = strlen(name);
1438 for (i = 0; i < dns_config->n_resolver; i++) {
1439 char *domain;
1440 dns_resolver_t *resolver;
1441
1442 resolver = dns_config->resolver[i];
1443 domain = resolver->domain;
1444 if (domain != NULL && (len == strlen(domain))) {
1445 if (strcasecmp(name, domain) == 0) {
1446 Boolean ok;
1447
1448 /*
1449 * if name matches domain
1450 */
1451 matched = TRUE;
1452 ok = check_resolver_reachability(storeP, resolver, flags, haveDNS);
1453 if (!ok) {
1454 /* not today */
1455 return FALSE;
1456 }
1457 }
1458 }
1459 }
1460
1461 if (!matched) {
1462 /*
1463 * we have not found a matching resolver, try
1464 * a less qualified domain
1465 */
1466 name = strchr(name, '.');
1467 if ((name != NULL) && (*name != '\0')) {
1468 name++;
1469 } else {
1470 name = NULL;
1471 }
1472 }
1473 }
1474
1475 return matched;
1476 }
1477
1478
1479 static dns_configuration_t *
1480 dns_configuration_retain()
1481 {
1482 pthread_mutex_lock(&dns_lock);
1483
1484 if ((dns_configuration != NULL) && dns_token_valid) {
1485 int check = 0;
1486 uint32_t status;
1487
1488 /*
1489 * check if the global [DNS] configuration snapshot needs
1490 * to be updated
1491 */
1492 status = notify_check(dns_token, &check);
1493 if (status != NOTIFY_STATUS_OK) {
1494 SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%lu"), status);
1495 }
1496
1497 if ((status != NOTIFY_STATUS_OK) || (check != 0)) {
1498 /*
1499 * if the snapshot needs to be refreshed
1500 */
1501 if (dns_configuration->refs == 0) {
1502 dns_configuration_free(dns_configuration->config);
1503 CFAllocatorDeallocate(NULL, dns_configuration);
1504 }
1505 dns_configuration = NULL;
1506 }
1507 }
1508
1509 if (dns_configuration == NULL) {
1510 dns_config_t *new_config;
1511
1512 new_config = dns_configuration_copy();
1513 if (new_config != NULL) {
1514 dns_configuration = CFAllocatorAllocate(NULL, sizeof(dns_configuration_t), 0);
1515 dns_configuration->config = new_config;
1516 dns_configuration->refs = 0;
1517 }
1518 }
1519
1520 if (dns_configuration != NULL) {
1521 dns_configuration->refs++;
1522 }
1523
1524 pthread_mutex_unlock(&dns_lock);
1525 return dns_configuration;
1526 }
1527
1528
1529 static void
1530 dns_configuration_release(dns_configuration_t *config)
1531 {
1532 pthread_mutex_lock(&dns_lock);
1533
1534 config->refs--;
1535 if (config->refs == 0) {
1536 if ((dns_configuration != config)) {
1537 dns_configuration_free(config->config);
1538 CFAllocatorDeallocate(NULL, config);
1539 }
1540 }
1541
1542 pthread_mutex_unlock(&dns_lock);
1543 return;
1544 }
1545
1546
1547 static Boolean
1548 dns_configuration_watch()
1549 {
1550 int dns_check = 0;
1551 const char *dns_key;
1552 Boolean ok = FALSE;
1553 uint32_t status;
1554
1555 pthread_mutex_lock(&dns_lock);
1556
1557 dns_key = dns_configuration_notify_key();
1558 if (dns_key == NULL) {
1559 SCLog(TRUE, LOG_INFO, CFSTR("dns_configuration_notify_key() failed"));
1560 goto done;
1561 }
1562
1563 status = notify_register_check(dns_key, &dns_token);
1564 if (status == NOTIFY_STATUS_OK) {
1565 dns_token_valid = TRUE;
1566 } else {
1567 SCLog(TRUE, LOG_INFO, CFSTR("notify_register_check() failed, status=%lu"), status);
1568 goto done;
1569 }
1570
1571 status = notify_check(dns_token, &dns_check);
1572 if (status != NOTIFY_STATUS_OK) {
1573 SCLog(TRUE, LOG_INFO, CFSTR("notify_check() failed, status=%lu"), status);
1574 (void)notify_cancel(dns_token);
1575 dns_token_valid = FALSE;
1576 goto done;
1577 }
1578
1579 ok = TRUE;
1580
1581 done :
1582
1583 pthread_mutex_unlock(&dns_lock);
1584 return ok;
1585 }
1586
1587
1588 static void
1589 dns_configuration_unwatch()
1590 {
1591 pthread_mutex_lock(&dns_lock);
1592
1593 (void)notify_cancel(dns_token);
1594 dns_token_valid = FALSE;
1595
1596 if ((dns_configuration != NULL) && (dns_configuration->refs == 0)) {
1597 dns_configuration_free(dns_configuration->config);
1598 CFAllocatorDeallocate(NULL, dns_configuration);
1599 dns_configuration = NULL;
1600 }
1601
1602 pthread_mutex_unlock(&dns_lock);
1603 return;
1604 }
1605
1606
1607 Boolean
1608 _SC_checkResolverReachability(SCDynamicStoreRef *storeP,
1609 SCNetworkConnectionFlags *flags,
1610 Boolean *haveDNS,
1611 const char * nodename)
1612 {
1613 dns_resolver_t *default_resolver;
1614 dns_configuration_t *dns;
1615 Boolean found = FALSE;
1616 char *fqdn = (char *)nodename;
1617 int i;
1618 Boolean isFQDN = FALSE;
1619 uint32_t len;
1620 Boolean ok = TRUE;
1621
1622 /*
1623 * We first assume that all of the configured DNS servers
1624 * are available. Since we don't know which name server will
1625 * be consulted to resolve the specified nodename we need to
1626 * check the availability of ALL name servers. We can only
1627 * proceed if we know that our query can be answered.
1628 */
1629
1630 *flags = kSCNetworkFlagsReachable;
1631 *haveDNS = FALSE;
1632
1633 len = strlen(fqdn);
1634 if (len == 0) {
1635 // if no nodename, return not reachable
1636 *flags = 0;
1637 return ok;
1638 }
1639
1640 dns = dns_configuration_retain();
1641 if (dns == NULL) {
1642 // if error
1643 goto done;
1644 }
1645
1646 if (dns->config->n_resolver == 0) {
1647 // if no resolver configuration
1648 goto done;
1649 }
1650
1651 *flags = kSCNetworkFlagsReachable;
1652
1653 if (fqdn[len - 1] == '.') {
1654 isFQDN = TRUE;
1655
1656 // trim trailing '.''s
1657 while ((len > 0) && (fqdn[len-1] == '.')) {
1658 if (fqdn == nodename) {
1659 fqdn = strdup(nodename);
1660 }
1661 fqdn[--len] = '\0';
1662 }
1663 }
1664
1665 default_resolver = dns->config->resolver[0];
1666
1667 /*
1668 * try the provided name
1669 */
1670 found = check_matching_resolvers(storeP, dns->config, fqdn, flags, haveDNS);
1671 if (!found && !isFQDN && (dns->config->n_resolver > 1)) {
1672 /*
1673 * FQDN not specified, try w/search or default domain(s) too
1674 */
1675 if (default_resolver->n_search > 0) {
1676 for (i = 0; !found && (i < default_resolver->n_search); i++) {
1677 int ret;
1678 char *search_fqdn = NULL;
1679
1680 ret = asprintf(&search_fqdn, "%s.%s", fqdn, default_resolver->search[i]);
1681 if (ret == -1) {
1682 continue;
1683 }
1684
1685 // try the provided name with the search domain appended
1686 found = check_matching_resolvers(storeP, dns->config, search_fqdn, flags, haveDNS);
1687 free(search_fqdn);
1688 }
1689 } else if (default_resolver->domain != NULL) {
1690 char *dp;
1691 int domain_parts = 1;
1692
1693 for (dp = default_resolver->domain; *dp != '\0'; dp++) {
1694 if (*dp == '.') {
1695 domain_parts++;
1696 }
1697 }
1698
1699 dp = default_resolver->domain;
1700 for (i = LOCALDOMAINPARTS; !found && (i <= domain_parts); i++) {
1701 int ret;
1702 char *search_fqdn = NULL;
1703
1704 ret = asprintf(&search_fqdn, "%s.%s", fqdn, dp);
1705 if (ret == -1) {
1706 continue;
1707 }
1708
1709 // try the provided name with the [default] domain appended
1710 found = check_matching_resolvers(storeP, dns->config, search_fqdn, flags, haveDNS);
1711 free(search_fqdn);
1712
1713 // move to the next component of the [default] domain
1714 dp = strchr(dp, '.') + 1;
1715 }
1716 }
1717 }
1718
1719 if (!found) {
1720 /*
1721 * check the reachability of the default resolver
1722 */
1723 ok = check_resolver_reachability(storeP, default_resolver, flags, haveDNS);
1724 }
1725
1726 if (fqdn != nodename) free(fqdn);
1727
1728 done :
1729
1730 if (dns != NULL) {
1731 dns_configuration_release(dns);
1732 }
1733
1734 return ok;
1735 }
1736
1737
1738 static Boolean
1739 startAsyncDNSQuery(SCNetworkReachabilityRef target) {
1740 CFMachPortContext context = { 0, (void *)target, CFRetain, CFRelease, CFCopyDescription };
1741 int error;
1742 struct addrinfo hints;
1743 CFIndex i;
1744 CFIndex n;
1745 mach_port_t port;
1746 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1747
1748 (void) gettimeofday(&targetPrivate->dnsQueryStart, NULL);
1749
1750 bzero(&hints, sizeof(hints));
1751 hints.ai_flags = AI_ADDRCONFIG;
1752 #ifdef AI_PARALLEL
1753 hints.ai_flags |= AI_PARALLEL;
1754 #endif /* AI_PARALLEL */
1755
1756 error = getaddrinfo_async_start(&port,
1757 targetPrivate->name,
1758 NULL,
1759 &hints,
1760 __SCNetworkReachabilityCallbackSetResolvedAddress,
1761 (void *)target);
1762 if (error != 0) {
1763 /* save the error associated with the attempt to resolve the name */
1764 __SCNetworkReachabilityCallbackSetResolvedAddress(error, NULL, (void *)target);
1765 return FALSE;
1766 }
1767
1768 targetPrivate->dnsPort = CFMachPortCreateWithPort(NULL,
1769 port,
1770 getaddrinfo_async_handleCFReply,
1771 &context,
1772 NULL);
1773 targetPrivate->dnsRLS = CFMachPortCreateRunLoopSource(NULL, targetPrivate->dnsPort, 0);
1774
1775 n = CFArrayGetCount(targetPrivate->rlList);
1776 for (i = 0; i < n; i += 3) {
1777 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(targetPrivate->rlList, i+1);
1778 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(targetPrivate->rlList, i+2);
1779
1780 CFRunLoopAddSource(rl, targetPrivate->dnsRLS, rlMode);
1781 }
1782
1783 return TRUE;
1784 }
1785
1786
1787 static Boolean
1788 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef *storeP,
1789 SCNetworkReachabilityRef target,
1790 SCNetworkConnectionFlags *flags,
1791 uint16_t *if_index,
1792 Boolean async)
1793 {
1794 CFMutableArrayRef addresses = NULL;
1795 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1796 SCNetworkConnectionFlags my_flags = 0;
1797 uint16_t my_index = 0;
1798 Boolean ok = TRUE;
1799
1800 *flags = 0;
1801 if (if_index != NULL) {
1802 *if_index = 0;
1803 }
1804
1805 if (!isA_SCNetworkReachability(target)) {
1806 _SCErrorSet(kSCStatusInvalidArgument);
1807 return FALSE;
1808 }
1809
1810 switch (targetPrivate->type) {
1811 case reachabilityTypeAddress :
1812 case reachabilityTypeAddressPair : {
1813 /*
1814 * Check "local" address
1815 */
1816 if (targetPrivate->localAddress != NULL) {
1817 /*
1818 * Check "local" address
1819 */
1820 ok = checkAddress(storeP, targetPrivate->localAddress, &my_flags, &my_index);
1821 if (!ok) {
1822 goto error; /* not today */
1823 }
1824
1825 if (!(my_flags & kSCNetworkFlagsIsLocalAddress)) {
1826 goto error; /* not reachable, non-"local" address */
1827 }
1828 }
1829
1830 /*
1831 * Check "remote" address
1832 */
1833 if (targetPrivate->remoteAddress != NULL) {
1834 /*
1835 * in cases where we have "local" and "remote" addresses
1836 * we need to re-initialize the to-be-returned flags.
1837 */
1838 my_flags = 0;
1839 my_index = 0;
1840
1841 /*
1842 * Check "remote" address
1843 */
1844 ok = checkAddress(storeP, targetPrivate->remoteAddress, &my_flags, &my_index);
1845 if (!ok) {
1846 goto error; /* not today */
1847 }
1848 }
1849
1850 break;
1851
1852 }
1853
1854 case reachabilityTypeName : {
1855 struct timeval dnsQueryStart;
1856 int error;
1857 struct addrinfo hints;
1858 SCNetworkConnectionFlags ns_flags;
1859 struct addrinfo *res;
1860
1861 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
1862 if ((addresses != NULL) || (error != NETDB_SUCCESS)) {
1863 /* if resolved or an error had been detected */
1864 goto checkResolvedAddress;
1865 }
1866
1867 /* check the reachability of the DNS servers */
1868 ok = _SC_checkResolverReachability(storeP,
1869 &ns_flags,
1870 &targetPrivate->haveDNS,
1871 targetPrivate->name);
1872 if (!ok) {
1873 /* if we could not get DNS server info */
1874 goto error;
1875 }
1876
1877 if (rankReachability(ns_flags) < 2) {
1878 /*
1879 * if DNS servers are not (or are no longer) reachable, set
1880 * flags based on the availability of configured (but not
1881 * active) services.
1882 */
1883 if (!checkAddress(storeP, NULL, &my_flags, &my_index)) {
1884 goto error;
1885 }
1886
1887 if (async && (targetPrivate->rls != NULL)) {
1888 /*
1889 * return "host not found", set flags appropriately,
1890 * and schedule notification.
1891 */
1892 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NODATA,
1893 NULL,
1894 (void *)target);
1895 my_flags |= (targetPrivate->flags & kSCNetworkFlagsFirstResolvePending);
1896
1897 SCLog(_sc_debug, LOG_INFO, CFSTR("no DNS servers are reachable"));
1898 CFRunLoopSourceSignal(targetPrivate->rls);
1899 _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
1900 }
1901 break;
1902 }
1903
1904 if (async) {
1905 /* for async requests we return the last known status */
1906 my_flags = targetPrivate->flags;
1907 my_index = targetPrivate->if_index;
1908
1909 if (targetPrivate->dnsPort) {
1910 /* if request already in progress */
1911 break;
1912 }
1913
1914 SCLog(_sc_debug, LOG_INFO, CFSTR("start DNS query for \"%s\""), targetPrivate->name);
1915
1916 /*
1917 * initiate an async DNS query
1918 */
1919 if (!startAsyncDNSQuery(target)) {
1920 /* if we could not initiate the request, process error */
1921 goto checkResolvedAddress;
1922 }
1923
1924 /* if request initiated */
1925 break;
1926 }
1927
1928 SCLog(_sc_debug, LOG_INFO, CFSTR("check DNS for \"%s\""), targetPrivate->name);
1929
1930 /*
1931 * OK, all of the DNS name servers are available. Let's
1932 * resolve the nodename into an address.
1933 */
1934 if (_sc_debug) {
1935 (void) gettimeofday(&dnsQueryStart, NULL);
1936 }
1937
1938 bzero(&hints, sizeof(hints));
1939 hints.ai_flags = AI_ADDRCONFIG;
1940 #ifdef AI_PARALLEL
1941 hints.ai_flags |= AI_PARALLEL;
1942 #endif /* AI_PARALLEL */
1943
1944 error = getaddrinfo(targetPrivate->name, NULL, &hints, &res);
1945
1946 __log_query_time(((error == 0) && (res != NULL)),// if successful query
1947 FALSE, // sync
1948 &dnsQueryStart); // start time
1949
1950 __SCNetworkReachabilitySetResolvedAddress(error, res, target);
1951
1952 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error);
1953
1954 checkResolvedAddress :
1955
1956 /*
1957 * We first assume that the requested host is NOT available.
1958 * Then, check each address for accessibility and return the
1959 * best status available.
1960 */
1961 my_flags = 0;
1962 my_index = 0;
1963
1964 if (isA_CFArray(addresses)) {
1965 CFIndex i;
1966 CFIndex n = CFArrayGetCount(addresses);
1967
1968 for (i = 0; i < n; i++) {
1969 SCNetworkConnectionFlags ns_flags = 0;
1970 uint16_t ns_if_index = 0;
1971 struct sockaddr *sa;
1972
1973 sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i));
1974
1975 ok = checkAddress(storeP, sa, &ns_flags, &ns_if_index);
1976 if (!ok) {
1977 goto error; /* not today */
1978 }
1979
1980 if (rankReachability(ns_flags) > rankReachability(my_flags)) {
1981 /* return the best case result */
1982 my_flags = ns_flags;
1983 my_index = ns_if_index;
1984 if (rankReachability(my_flags) == 2) {
1985 /* we're in luck */
1986 break;
1987 }
1988 }
1989 }
1990 } else {
1991 if ((error == EAI_NODATA) && !targetPrivate->haveDNS) {
1992 /*
1993 * No DNS servers are defined. Set flags based on
1994 * the availability of configured (but not active)
1995 * services.
1996 */
1997 ok = checkAddress(storeP, NULL, &my_flags, &my_index);
1998 if (!ok) {
1999 goto error; /* not today */
2000 }
2001
2002 if ((my_flags & kSCNetworkFlagsReachable) &&
2003 (my_flags & kSCNetworkFlagsConnectionRequired)) {
2004 /*
2005 * Since we might pick up a set of DNS servers when this connection
2006 * is established, don't reply with a "HOST NOT FOUND" error just yet.
2007 */
2008 break;
2009 }
2010
2011 /* Host not found, not reachable! */
2012 my_flags = 0;
2013 my_index = 0;
2014 }
2015 }
2016
2017 break;
2018 }
2019 }
2020
2021 *flags = my_flags;
2022 if (if_index != NULL) {
2023 *if_index = my_index;
2024 }
2025
2026 error :
2027
2028 if (addresses != NULL) CFRelease(addresses);
2029 return ok;
2030 }
2031
2032
2033 Boolean
2034 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target,
2035 SCNetworkConnectionFlags *flags)
2036 {
2037 Boolean ok;
2038 SCDynamicStoreRef store = NULL;
2039 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2040
2041 if (!isA_SCNetworkReachability(target)) {
2042 _SCErrorSet(kSCStatusInvalidArgument);
2043 return FALSE;
2044 }
2045
2046 if (targetPrivate->rlList != NULL) {
2047 // if being watched, return the last known (and what should be current) status
2048 *flags = targetPrivate->flags & ~kSCNetworkFlagsFirstResolvePending;
2049 return TRUE;
2050 }
2051
2052 ok = __SCNetworkReachabilityGetFlags(&store, target, flags, NULL, FALSE);
2053 *flags &= ~kSCNetworkFlagsFirstResolvePending;
2054 if (store != NULL) CFRelease(store);
2055 return ok;
2056 }
2057
2058
2059 static void
2060 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store)
2061 {
2062 CFStringRef dns_key;
2063 CFStringRef key;
2064 CFMutableArrayRef keys;
2065 CFStringRef pattern;
2066 CFMutableArrayRef patterns;
2067
2068 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2069 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2070
2071 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
2072 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
2073 kSCDynamicStoreDomainSetup,
2074 kSCEntNetIPv4);
2075 CFArrayAppendValue(keys, key);
2076 CFRelease(key);
2077
2078 // Notify:com.apple.SystemConfiguration.dns_configuration
2079 dns_key = CFStringCreateWithCString(NULL,
2080 dns_configuration_notify_key(),
2081 kCFStringEncodingASCII);
2082 key = CFStringCreateWithFormat(NULL, NULL, CFSTR("Notify:%@"), dns_key);
2083 CFRelease(dns_key);
2084 CFArrayAppendValue(keys, key);
2085 CFRelease(key);
2086
2087 // State:/Network/Global/IPv4 (default route)
2088 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
2089 kSCDynamicStoreDomainState,
2090 kSCEntNetIPv4);
2091 CFArrayAppendValue(keys, key);
2092 CFRelease(key);
2093
2094 // Setup: per-service IPv4 info
2095 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2096 kSCDynamicStoreDomainSetup,
2097 kSCCompAnyRegex,
2098 kSCEntNetIPv4);
2099 CFArrayAppendValue(patterns, pattern);
2100 CFRelease(pattern);
2101
2102 // Setup: per-service Interface info
2103 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2104 kSCDynamicStoreDomainSetup,
2105 kSCCompAnyRegex,
2106 kSCEntNetInterface);
2107 CFArrayAppendValue(patterns, pattern);
2108 CFRelease(pattern);
2109
2110 // Setup: per-service PPP info (for kSCPropNetPPPDialOnDemand)
2111 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2112 kSCDynamicStoreDomainSetup,
2113 kSCCompAnyRegex,
2114 kSCEntNetPPP);
2115 CFArrayAppendValue(patterns, pattern);
2116 CFRelease(pattern);
2117
2118 // State: per-interface IPv4 info
2119 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
2120 kSCDynamicStoreDomainState,
2121 kSCCompAnyRegex,
2122 kSCEntNetIPv4);
2123 CFArrayAppendValue(patterns, pattern);
2124 CFRelease(pattern);
2125
2126 // State: per-interface IPv6 info
2127 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
2128 kSCDynamicStoreDomainState,
2129 kSCCompAnyRegex,
2130 kSCEntNetIPv6);
2131 CFArrayAppendValue(patterns, pattern);
2132 CFRelease(pattern);
2133
2134 (void)SCDynamicStoreSetNotificationKeys(store, keys, patterns);
2135 CFRelease(keys);
2136 CFRelease(patterns);
2137
2138 return;
2139 }
2140
2141
2142 static void
2143 __SCNetworkReachabilityReachabilityHandleChanges(SCDynamicStoreRef store,
2144 CFArrayRef changedKeys,
2145 void *info)
2146 {
2147 Boolean dnsConfigChanged = FALSE;
2148 CFStringRef dnsKey;
2149 CFIndex i;
2150 CFStringRef key;
2151 CFIndex nTargets;
2152 const void * targets_q[N_QUICK];
2153 const void ** targets = targets_q;
2154
2155 pthread_mutex_lock(&hn_lock);
2156
2157 nTargets = CFSetGetCount(hn_targets);
2158 if (nTargets == 0) {
2159 /* if no addresses being monitored */
2160 goto done;
2161 }
2162
2163 if (CFArrayGetCount(changedKeys) == 0) {
2164 /* if no changes */
2165 goto done;
2166 }
2167
2168 SCLog(_sc_debug, LOG_INFO, CFSTR("process configuration change"));
2169
2170 dnsKey = CFStringCreateWithCString(NULL,
2171 dns_configuration_notify_key(),
2172 kCFStringEncodingASCII);
2173 key = CFStringCreateWithFormat(NULL, NULL, CFSTR("Notify:%@"), dnsKey);
2174 CFRelease(dnsKey);
2175 if (CFArrayContainsValue(changedKeys,
2176 CFRangeMake(0, CFArrayGetCount(changedKeys)),
2177 key)) {
2178 dnsConfigChanged = TRUE; /* the DNS server(s) have changed */
2179
2180 }
2181 CFRelease(key);
2182
2183 SCLog(_sc_debug && dnsConfigChanged, LOG_INFO, CFSTR(" DNS configuration changed"));
2184
2185 if (nTargets > (CFIndex)(sizeof(targets_q) / sizeof(CFTypeRef)))
2186 targets = CFAllocatorAllocate(NULL, nTargets * sizeof(CFTypeRef), 0);
2187 CFSetGetValues(hn_targets, targets);
2188 for (i = 0; i < nTargets; i++) {
2189 SCNetworkReachabilityRef target = targets[i];
2190 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2191
2192 pthread_mutex_lock(&targetPrivate->lock);
2193
2194 if (targetPrivate->type == reachabilityTypeName) {
2195 Boolean dnsChanged = dnsConfigChanged;
2196
2197 if (!dnsChanged) {
2198 /*
2199 * if the DNS configuration didn't change we still need to
2200 * check that the DNS servers are accessible.
2201 */
2202 SCNetworkConnectionFlags ns_flags;
2203 Boolean ok;
2204
2205 /* check the reachability of the DNS servers */
2206 ok = _SC_checkResolverReachability(&store,
2207 &ns_flags,
2208 &targetPrivate->haveDNS,
2209 targetPrivate->name);
2210 if (!ok || (rankReachability(ns_flags) < 2)) {
2211 /* if DNS servers are not reachable */
2212 dnsChanged = TRUE;
2213 }
2214 }
2215
2216 if (dnsChanged) {
2217 if (targetPrivate->dnsPort) {
2218 /* cancel the outstanding DNS query */
2219 lu_async_call_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
2220 CFRunLoopSourceInvalidate(targetPrivate->dnsRLS);
2221 CFRelease(targetPrivate->dnsRLS);
2222 targetPrivate->dnsRLS = NULL;
2223 CFRelease(targetPrivate->dnsPort);
2224 targetPrivate->dnsPort = NULL;
2225 }
2226
2227 /* schedule request to resolve the name again */
2228 if (targetPrivate->resolvedAddress != NULL) {
2229 CFRelease(targetPrivate->resolvedAddress);
2230 targetPrivate->resolvedAddress = NULL;
2231 }
2232 targetPrivate->resolvedAddress = NULL;
2233 targetPrivate->resolvedAddressError = NETDB_SUCCESS;
2234 }
2235 }
2236
2237 CFRunLoopSourceSignal(targetPrivate->rls);
2238 _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
2239
2240 pthread_mutex_unlock(&targetPrivate->lock);
2241 }
2242 if (targets != targets_q) CFAllocatorDeallocate(NULL, targets);
2243
2244 done :
2245
2246 pthread_mutex_unlock(&hn_lock);
2247 return;
2248 }
2249
2250
2251 static void
2252 rlsPerform(void *info)
2253 {
2254 void *context_info;
2255 void (*context_release)(const void *);
2256 SCNetworkConnectionFlags flags;
2257 uint16_t if_index;
2258 Boolean ok;
2259 SCNetworkReachabilityCallBack rlsFunction;
2260 SCDynamicStoreRef store = NULL;
2261 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
2262 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2263
2264 SCLog(_sc_debug, LOG_DEBUG, CFSTR("process reachability change"));
2265
2266 pthread_mutex_lock(&targetPrivate->lock);
2267
2268 /* update reachability, notify if status changed */
2269 ok = __SCNetworkReachabilityGetFlags(&store, target, &flags, &if_index, TRUE);
2270 if (store != NULL) CFRelease(store);
2271 if (!ok) {
2272 /* if reachability status not available */
2273 flags = 0;
2274 if_index = 0;
2275 }
2276
2277 if ((targetPrivate->flags == flags) && (targetPrivate->if_index == if_index)) {
2278 /* if reachability flags and interface have not changed */
2279 pthread_mutex_unlock(&targetPrivate->lock);
2280 SCLog(_sc_debug, LOG_DEBUG,
2281 CFSTR("flags/interface match (now %8.8x/%hu)"),
2282 flags, if_index);
2283 return;
2284 } else {
2285 SCLog(_sc_debug, LOG_DEBUG,
2286 CFSTR("flags/interface have changed (was %8.8x/%hu, now %8.8x/%hu)"),
2287 targetPrivate->flags, targetPrivate->if_index,
2288 flags, if_index);
2289 }
2290
2291 /* update flags / interface */
2292 targetPrivate->flags = flags;
2293 targetPrivate->if_index = if_index;
2294
2295 /* callout */
2296 rlsFunction = targetPrivate->rlsFunction;
2297 if (targetPrivate->rlsContext.retain != NULL) {
2298 context_info = (void *)(*targetPrivate->rlsContext.retain)(targetPrivate->rlsContext.info);
2299 context_release = targetPrivate->rlsContext.release;
2300 } else {
2301 context_info = targetPrivate->rlsContext.info;
2302 context_release = NULL;
2303 }
2304
2305 pthread_mutex_unlock(&targetPrivate->lock);
2306
2307 if (rlsFunction != NULL) {
2308 (*rlsFunction)(target, flags, context_info);
2309 }
2310
2311 if (context_release != NULL) {
2312 (*context_release)(context_info);
2313 }
2314
2315 return;
2316 }
2317
2318
2319 Boolean
2320 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target,
2321 SCNetworkReachabilityCallBack callout,
2322 SCNetworkReachabilityContext *context)
2323 {
2324 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2325
2326 pthread_mutex_lock(&targetPrivate->lock);
2327
2328 if (targetPrivate->rlsContext.release != NULL) {
2329 /* let go of the current context */
2330 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
2331 }
2332
2333 targetPrivate->rlsFunction = callout;
2334 targetPrivate->rlsContext.info = NULL;
2335 targetPrivate->rlsContext.retain = NULL;
2336 targetPrivate->rlsContext.release = NULL;
2337 targetPrivate->rlsContext.copyDescription = NULL;
2338 if (context) {
2339 bcopy(context, &targetPrivate->rlsContext, sizeof(SCNetworkReachabilityContext));
2340 if (context->retain != NULL) {
2341 targetPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
2342 }
2343 }
2344
2345 pthread_mutex_unlock(&targetPrivate->lock);
2346
2347 return TRUE;
2348 }
2349
2350
2351 Boolean
2352 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target,
2353 CFRunLoopRef runLoop,
2354 CFStringRef runLoopMode)
2355 {
2356 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2357 Boolean init = FALSE;
2358 Boolean ok = FALSE;
2359
2360 if (!isA_SCNetworkReachability(target) || runLoop == NULL || runLoopMode == NULL) {
2361 _SCErrorSet(kSCStatusInvalidArgument);
2362 return FALSE;
2363 }
2364
2365 /* schedule the SCNetworkReachability run loop source */
2366
2367 pthread_mutex_lock(&hn_lock);
2368 pthread_mutex_lock(&targetPrivate->lock);
2369
2370 if (hn_store == NULL) {
2371 /*
2372 * if we are not monitoring any hosts, start watching
2373 */
2374 if (!dns_configuration_watch()) {
2375 // if error
2376 _SCErrorSet(kSCStatusFailed);
2377 goto done;
2378 }
2379
2380 hn_store = SCDynamicStoreCreate(NULL,
2381 CFSTR("SCNetworkReachability"),
2382 __SCNetworkReachabilityReachabilityHandleChanges,
2383 NULL);
2384 if (hn_store == NULL) {
2385 SCLog(_sc_verbose, LOG_INFO, CFSTR("SCDynamicStoreCreate() failed"));
2386 goto done;
2387 }
2388
2389 __SCNetworkReachabilityReachabilitySetNotifications(hn_store);
2390
2391 hn_storeRLS = SCDynamicStoreCreateRunLoopSource(NULL, hn_store, 0);
2392 hn_rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2393 hn_targets = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
2394 }
2395
2396 if (targetPrivate->rls == NULL) {
2397 CFRunLoopSourceContext context = { 0 // version
2398 , (void *)target // info
2399 , CFRetain // retain
2400 , CFRelease // release
2401 , CFCopyDescription // copyDescription
2402 , CFEqual // equal
2403 , CFHash // hash
2404 , NULL // schedule
2405 , NULL // cancel
2406 , rlsPerform // perform
2407 };
2408
2409 targetPrivate->rls = CFRunLoopSourceCreate(NULL, 0, &context);
2410 targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2411 init = TRUE;
2412 }
2413
2414 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
2415 /*
2416 * if we do not already have host notifications scheduled with
2417 * this runLoop / runLoopMode
2418 */
2419 CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode);
2420
2421 if (targetPrivate->dnsRLS != NULL) {
2422 /* if we have an active async DNS query too */
2423 CFRunLoopAddSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
2424 }
2425 }
2426
2427 _SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
2428
2429 /* schedule the SCNetworkReachability run loop source */
2430
2431 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
2432 /*
2433 * if we do not already have SC notifications scheduled with
2434 * this runLoop / runLoopMode
2435 */
2436 CFRunLoopAddSource(runLoop, hn_storeRLS, runLoopMode);
2437 }
2438
2439 _SC_schedule(target, runLoop, runLoopMode, hn_rlList);
2440 CFSetAddValue(hn_targets, target);
2441
2442 if (init) {
2443 SCNetworkConnectionFlags flags;
2444 uint16_t if_index;
2445 SCDynamicStoreRef store = NULL;
2446
2447 /*
2448 * if we have yet to schedule SC notifications for this address
2449 * - initialize current reachability status
2450 */
2451 if (__SCNetworkReachabilityGetFlags(&store, target, &flags, &if_index, TRUE)) {
2452 /*
2453 * if reachability status available
2454 * - set flags
2455 * - schedule notification to report status via callback
2456 */
2457 targetPrivate->flags = flags;
2458 targetPrivate->if_index = if_index;
2459 CFRunLoopSourceSignal(targetPrivate->rls);
2460 _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList);
2461 } else {
2462 /* if reachability status not available, async lookup started */
2463 targetPrivate->flags = 0;
2464 targetPrivate->if_index = 0;
2465 }
2466 if (store != NULL) CFRelease(store);
2467 }
2468
2469 ok = TRUE;
2470
2471 done :
2472
2473 pthread_mutex_unlock(&targetPrivate->lock);
2474 pthread_mutex_unlock(&hn_lock);
2475 return ok;
2476 }
2477
2478
2479 Boolean
2480 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target,
2481 CFRunLoopRef runLoop,
2482 CFStringRef runLoopMode)
2483 {
2484 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
2485 CFIndex n;
2486 Boolean ok = FALSE;
2487
2488 if (!isA_SCNetworkReachability(target) || runLoop == NULL || runLoopMode == NULL) {
2489 _SCErrorSet(kSCStatusInvalidArgument);
2490 goto done;
2491 }
2492
2493 pthread_mutex_lock(&hn_lock);
2494 pthread_mutex_lock(&targetPrivate->lock);
2495
2496 if (targetPrivate->rls == NULL) {
2497 /* if not currently scheduled */
2498 goto done;
2499 }
2500
2501 if (!_SC_unschedule(NULL, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
2502 /* if not currently scheduled */
2503 goto done;
2504 }
2505
2506 n = CFArrayGetCount(targetPrivate->rlList);
2507 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
2508 /*
2509 * if this host is no longer scheduled for this runLoop / runLoopMode
2510 */
2511 CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode);
2512
2513 if (targetPrivate->dnsRLS != NULL) {
2514 /* if we have an active async DNS query too */
2515 CFRunLoopRemoveSource(runLoop, targetPrivate->dnsRLS, runLoopMode);
2516 }
2517
2518 if (n == 0) {
2519 /*
2520 * if this host is no longer scheduled
2521 */
2522 CFRunLoopSourceInvalidate(targetPrivate->rls); /* cleanup SCNetworkReachability resources */
2523 CFRelease(targetPrivate->rls);
2524 targetPrivate->rls = NULL;
2525 CFRelease(targetPrivate->rlList);
2526 targetPrivate->rlList = NULL;
2527 CFSetRemoveValue(hn_targets, target); /* cleanup notification resources */
2528
2529 if (targetPrivate->dnsPort) {
2530 /* if we have an active async DNS query too */
2531 lu_async_call_cancel(CFMachPortGetPort(targetPrivate->dnsPort));
2532 CFRunLoopSourceInvalidate(targetPrivate->dnsRLS);
2533 CFRelease(targetPrivate->dnsRLS);
2534 targetPrivate->dnsRLS = NULL;
2535 CFRelease(targetPrivate->dnsPort);
2536 targetPrivate->dnsPort = NULL;
2537 }
2538 }
2539 }
2540
2541 (void)_SC_unschedule(target, runLoop, runLoopMode, hn_rlList, FALSE);
2542
2543 n = CFArrayGetCount(hn_rlList);
2544 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, hn_rlList)) {
2545 /*
2546 * if we no longer have any addresses scheduled for
2547 * this runLoop / runLoopMode
2548 */
2549 CFRunLoopRemoveSource(runLoop, hn_storeRLS, runLoopMode);
2550
2551 if (n == 0) {
2552 /*
2553 * if we are no longer monitoring any addresses
2554 */
2555 CFRelease(hn_targets);
2556 hn_targets = NULL;
2557 CFRelease(hn_rlList);
2558 hn_rlList = NULL;
2559 CFRunLoopSourceInvalidate(hn_storeRLS);
2560 CFRelease(hn_storeRLS);
2561 hn_storeRLS = NULL;
2562 CFRelease(hn_store);
2563 hn_store = NULL;
2564
2565 /*
2566 * until we start monitoring again, ensure that
2567 * any resources associated with tracking the
2568 * DNS configuration have been released.
2569 */
2570 dns_configuration_unwatch();
2571 }
2572 }
2573
2574 ok = TRUE;
2575
2576 done :
2577
2578 pthread_mutex_unlock(&targetPrivate->lock);
2579 pthread_mutex_unlock(&hn_lock);
2580 return ok;
2581 }