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