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