]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkReachability.c
configd-801.1.1.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkReachability.c
1 /*
2 * Copyright (c) 2003-2015 Apple 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 * April 12, 2011 Allan Nathanson <ajn@apple.com>
28 * - add SCNetworkReachability "server"
29 *
30 * March 31, 2004 Allan Nathanson <ajn@apple.com>
31 * - use [SC] DNS configuration information
32 *
33 * January 19, 2003 Allan Nathanson <ajn@apple.com>
34 * - add advanced reachability APIs
35 */
36
37 #include <Availability.h>
38 #include <TargetConditionals.h>
39 #include <sys/cdefs.h>
40 #include <dispatch/dispatch.h>
41 #include <dispatch/private.h>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include <CoreFoundation/CFRuntime.h>
44 #include <SystemConfiguration/SystemConfiguration.h>
45 #include <SystemConfiguration/SCValidation.h>
46 #include <SystemConfiguration/SCPrivate.h>
47 #include <pthread.h>
48 #include <libkern/OSAtomic.h>
49
50 #include <notify.h>
51 #include <dnsinfo.h>
52 #include <netinet/in.h>
53 #include <arpa/inet.h>
54 #include <netdb.h>
55 #include <resolv.h>
56 #include <unistd.h>
57 #include <sys/ioctl.h>
58 #include <sys/socket.h>
59 #include <net/if.h>
60 #include <net/if_dl.h>
61 #include <net/if_types.h>
62 #include <net/network_agent.h>
63
64 #include "SCNetworkReachabilityInternal.h"
65
66 #include <network_information.h>
67
68
69
70
71
72 #include <network/private.h>
73
74 #define DEBUG_REACHABILITY_TYPE_NAME "create w/name"
75 #define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options"
76
77 #define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address"
78 #define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options"
79
80 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair"
81 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options"
82
83 #define DEBUG_REACHABILITY_TYPE_PTR "create w/ptr"
84 #define DEBUG_REACHABILITY_TYPE_PTR_OPTIONS " + options"
85
86 static pthread_mutexattr_t lock_attr;
87
88 #define MUTEX_INIT(m) { \
89 int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \
90 assert(_lock_); \
91 }
92
93 #define MUTEX_LOCK(m) { \
94 int _lock_ = (pthread_mutex_lock(m) == 0); \
95 assert(_lock_); \
96 }
97
98 #define MUTEX_UNLOCK(m) { \
99 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
100 assert(_unlock_); \
101 }
102
103 #define MUTEX_ASSERT_HELD(m) { \
104 int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \
105 assert(_locked_); \
106 }
107
108
109 #define N_QUICK 64
110
111 #define REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN "NetworkExtension"
112 #define REACHABILITY_AGENT_DATA_KEY "data"
113
114
115 static CFStringRef __SCNetworkReachabilityCopyDescription (CFTypeRef cf);
116 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf);
117
118 static SCNetworkReachabilityFlags
119 __SCNetworkReachabilityGetFlagsFromPath(nw_path_t path,
120 ReachabilityAddressType type,
121 nw_resolver_status_t resolverStatus,
122 nw_array_t resolvedEndpoints,
123 Boolean resolvedEndpointUseFlags,
124 SCNetworkReachabilityFlags resolvedEndpointFlags);
125 static Boolean __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate,
126 dispatch_queue_t queue);
127
128 static CFTypeID __kSCNetworkReachabilityTypeID = _kCFRuntimeNotATypeID;
129
130
131 static const CFRuntimeClass __SCNetworkReachabilityClass = {
132 0, // version
133 "SCNetworkReachability", // className
134 NULL, // init
135 NULL, // copy
136 __SCNetworkReachabilityDeallocate, // dealloc
137 NULL, // equal
138 NULL, // hash
139 NULL, // copyFormattingDesc
140 __SCNetworkReachabilityCopyDescription // copyDebugDesc
141 };
142
143
144 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
145
146 static dispatch_queue_t
147 _callback_queue()
148 {
149 static dispatch_once_t once;
150 static dispatch_queue_t q;
151
152 dispatch_once(&once, ^{
153 q = dispatch_queue_create("SCNetworkReachability.callback", NULL);
154 });
155
156 return q;
157 }
158
159 #pragma mark -
160 #pragma mark SCNetworkReachability APIs
161
162
163 static __inline__ CFTypeRef
164 isA_SCNetworkReachability(CFTypeRef obj)
165 {
166 return (isA_CFType(obj, SCNetworkReachabilityGetTypeID()));
167 }
168
169 CFStringRef
170 _SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target)
171 {
172 CFAllocatorRef allocator = CFGetAllocator(target);
173 CFMutableStringRef str;
174 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
175
176 str = CFStringCreateMutable(allocator, 0);
177 switch (targetPrivate->type) {
178 case reachabilityTypeAddress :
179 case reachabilityTypeAddressPair : {
180 char buf[64];
181 if (targetPrivate->localAddressEndpoint != NULL) {
182 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate->localAddressEndpoint), buf, sizeof(buf));
183 CFStringAppendFormat(str, NULL, CFSTR("local address = %s"),
184 buf);
185 }
186 if (targetPrivate->remoteAddressEndpoint != NULL) {
187 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate->remoteAddressEndpoint), buf, sizeof(buf));
188 CFStringAppendFormat(str, NULL, CFSTR("%s%saddress = %s"),
189 targetPrivate->localAddressEndpoint ? ", " : "",
190 (targetPrivate->type == reachabilityTypeAddressPair) ? "remote " : "",
191 buf);
192 } else {
193 CFStringAppendFormat(str, NULL, CFSTR("default path"));
194 }
195 break;
196 }
197 case reachabilityTypeName : {
198 CFStringAppendFormat(str, NULL, CFSTR("name = %s"), nw_endpoint_get_hostname(targetPrivate->hostnameEndpoint));
199 break;
200 }
201 case reachabilityTypePTR : {
202 char buf[64];
203 if (targetPrivate->remoteAddressEndpoint != NULL) {
204 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate->remoteAddressEndpoint), buf, sizeof(buf));
205 CFStringAppendFormat(str, NULL, CFSTR("ptr = %s"),
206 buf);
207 }
208 break;
209 }
210 }
211
212 return str;
213 }
214
215
216 CFStringRef
217 _SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target)
218 {
219 CFAllocatorRef allocator = CFGetAllocator(target);
220 CFStringRef str;
221 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
222
223 str = CFStringCreateWithFormat(allocator,
224 NULL,
225 CFSTR("flags = 0x%08x, if_index = %u"),
226 __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, targetPrivate->type, targetPrivate->lastResolverStatus, targetPrivate->lastResolvedEndpoints, targetPrivate->lastResolvedEndpointHasFlags, targetPrivate->lastResolvedEndpointFlags),
227 targetPrivate->lastResolvedEndpointHasFlags ? targetPrivate->lastResolvedEndpointInterfaceIndex : nw_path_get_interface_index(targetPrivate->lastPath));
228 return str;
229 }
230
231
232 static CFStringRef
233 __SCNetworkReachabilityCopyDescription(CFTypeRef cf)
234 {
235 CFAllocatorRef allocator = CFGetAllocator(cf);
236 CFMutableStringRef result;
237 CFStringRef str;
238 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf;
239 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
240
241 MUTEX_LOCK(&targetPrivate->lock);
242
243 result = CFStringCreateMutable(allocator, 0);
244 CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkReachability %p [%p]> {"), cf, allocator);
245
246 // add target description
247 str = _SCNetworkReachabilityCopyTargetDescription(target);
248 CFStringAppend(result, str);
249 CFRelease(str);
250
251 // add additional "name" info
252 if (isReachabilityTypeName(targetPrivate->type)) {
253 if (targetPrivate->resolver && targetPrivate->lastResolverStatus == nw_resolver_status_invalid) {
254 CFStringAppendFormat(result, NULL, CFSTR(" (DNS query active)"));
255 } else if (targetPrivate->lastResolverStatus != nw_resolver_status_invalid) {
256 CFStringAppendFormat(result, NULL, CFSTR(" (%s"), (targetPrivate->lastResolverStatus == nw_resolver_status_complete) ? "complete" : "in progress");
257 if (nw_array_get_count(targetPrivate->lastResolvedEndpoints) > 0) {
258 nw_array_apply(targetPrivate->lastResolvedEndpoints, ^bool(size_t index, nw_object_t object) {
259 nw_endpoint_t endpoint = (nw_endpoint_t)object;
260 if (nw_endpoint_get_type(endpoint) == nw_endpoint_type_address) {
261 char buf[64];
262 const struct sockaddr *sa = nw_endpoint_get_address(endpoint);
263 _SC_sockaddr_to_string(sa, buf, sizeof(buf));
264 CFStringAppendFormat(result, NULL, CFSTR(", %s"), buf);
265 } else if (nw_endpoint_get_type(endpoint) == nw_endpoint_type_host) {
266 CFStringAppendFormat(result, NULL, CFSTR(", %s"), nw_endpoint_get_hostname(endpoint));
267 }
268 return TRUE;
269 });
270 } else {
271 CFStringAppendFormat(result, NULL, CFSTR(", no addresses"));
272 }
273 CFStringAppendFormat(result, NULL, CFSTR(")"));
274 }
275 }
276
277 if (targetPrivate->resolverBypass) {
278 CFStringAppendFormat(result, NULL, CFSTR(", !resolve"));
279 }
280
281 // add flags
282 if (targetPrivate->scheduled) {
283 str = _SCNetworkReachabilityCopyTargetFlags(target);
284 CFStringAppendFormat(result, NULL, CFSTR(", %@"), str);
285 CFRelease(str);
286 }
287
288 CFStringAppendFormat(result, NULL, CFSTR("}"));
289
290 MUTEX_UNLOCK(&targetPrivate->lock);
291
292 return result;
293 }
294
295
296 static void
297 __SCNetworkReachabilityDeallocate(CFTypeRef cf)
298 {
299 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf;
300 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
301
302 if (_sc_debug && (_sc_log > 0)) {
303 SC_log(LOG_INFO, "%srelease", targetPrivate->log_prefix);
304 }
305
306 /* release resources */
307 MUTEX_LOCK(&targetPrivate->lock);
308 targetPrivate->scheduled = FALSE;
309
310 if (targetPrivate->hostnameEndpoint) {
311 network_release(targetPrivate->hostnameEndpoint);
312 targetPrivate->hostnameEndpoint = NULL;
313 }
314 if (targetPrivate->localAddressEndpoint) {
315 network_release(targetPrivate->localAddressEndpoint);
316 targetPrivate->localAddressEndpoint = NULL;
317 }
318 if (targetPrivate->remoteAddressEndpoint) {
319 network_release(targetPrivate->remoteAddressEndpoint);
320 targetPrivate->remoteAddressEndpoint = NULL;
321 }
322 if (targetPrivate->parameters) {
323 network_release(targetPrivate->parameters);
324 targetPrivate->parameters = NULL;
325 }
326 if (targetPrivate->lastPath) {
327 network_release(targetPrivate->lastPath);
328 targetPrivate->lastPath = NULL;
329 }
330 if (targetPrivate->lastPathParameters) {
331 network_release(targetPrivate->lastPathParameters);
332 targetPrivate->lastPathParameters = NULL;
333 }
334 if (targetPrivate->lastResolvedEndpoints) {
335 network_release(targetPrivate->lastResolvedEndpoints);
336 targetPrivate->lastResolvedEndpoints = NULL;
337 }
338
339 if (targetPrivate->rlsContext.release != NULL) {
340 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
341 }
342
343 MUTEX_UNLOCK(&targetPrivate->lock);
344
345 pthread_mutex_destroy(&targetPrivate->lock);
346
347 return;
348 }
349
350
351 static void
352 __SCNetworkReachabilityInitialize(void)
353 {
354 __kSCNetworkReachabilityTypeID = _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass);
355
356 // provide a way to enable SCNetworkReachability logging without
357 // having to set _sc_debug=1.
358 if ((getenv("REACH_LOGGING") != NULL) ||
359 (CFPreferencesGetAppBooleanValue(CFSTR("com.apple.SCNetworkReachability.debug"),
360 kCFPreferencesCurrentApplication,
361 NULL))) {
362 _sc_debug = TRUE;
363 }
364
365 pthread_mutexattr_init(&lock_attr);
366 pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_ERRORCHECK);
367
368 return;
369 }
370
371 static SCNetworkReachabilityPrivateRef
372 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator)
373 {
374 SCNetworkReachabilityPrivateRef targetPrivate;
375 uint32_t size;
376
377 /* initialize runtime */
378 pthread_once(&initialized, __SCNetworkReachabilityInitialize);
379
380 /* allocate target */
381 size = sizeof(SCNetworkReachabilityPrivate) - sizeof(CFRuntimeBase);
382 targetPrivate = (SCNetworkReachabilityPrivateRef)_CFRuntimeCreateInstance(allocator,
383 __kSCNetworkReachabilityTypeID,
384 size,
385 NULL);
386 if (targetPrivate == NULL) {
387 return NULL;
388 }
389
390 bzero((void *)targetPrivate + sizeof(CFRuntimeBase), size);
391
392 MUTEX_INIT(&targetPrivate->lock);
393
394 targetPrivate->pathEvaluator = NULL;
395 targetPrivate->resolver = NULL;
396
397 targetPrivate->log_prefix[0] = '\0';
398 if (_sc_log > 0) {
399 snprintf(targetPrivate->log_prefix,
400 sizeof(targetPrivate->log_prefix),
401 "[%p] ",
402 targetPrivate);
403 }
404
405 return targetPrivate;
406 }
407
408
409 static const struct sockaddr *
410 is_valid_address(const struct sockaddr *address)
411 {
412 const struct sockaddr *valid = NULL;
413 static Boolean warned = FALSE;
414
415 if ((address != NULL) &&
416 (address->sa_len <= sizeof(struct sockaddr_storage))) {
417 switch (address->sa_family) {
418 case AF_INET :
419 if (address->sa_len >= sizeof(struct sockaddr_in)) {
420 valid = address;
421 } else {
422 if (!warned) {
423 SC_log(LOG_NOTICE, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
424 address->sa_len,
425 sizeof(struct sockaddr_in));
426 warned = TRUE;
427 }
428 }
429 break;
430 case AF_INET6 :
431 if (address->sa_len >= sizeof(struct sockaddr_in6)) {
432 valid = address;
433 } else if (!warned) {
434 SC_log(LOG_NOTICE, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
435 address->sa_len,
436 sizeof(struct sockaddr_in6));
437 warned = TRUE;
438 }
439 break;
440 default :
441 if (!warned) {
442 SC_log(LOG_NOTICE, "SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d",
443 address->sa_family);
444 warned = TRUE;
445 }
446 }
447 }
448
449 return valid;
450 }
451
452 static bool
453 __SCNetworkReachabilityAddressIsEmpty(const struct sockaddr *address)
454 {
455 if (address == NULL) {
456 return TRUE;
457 }
458
459 if (address->sa_family == AF_INET) {
460 return (((struct sockaddr_in *)(void *)address)->sin_addr.s_addr == 0);
461 } else if (address->sa_family == AF_INET6) {
462 return IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)(void *)address)->sin6_addr);
463 } else {
464 return FALSE;
465 }
466 }
467
468 SCNetworkReachabilityRef
469 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator,
470 const struct sockaddr *address)
471 {
472 SCNetworkReachabilityPrivateRef targetPrivate;
473
474 address = is_valid_address(address);
475 if (address == NULL) {
476 _SCErrorSet(kSCStatusInvalidArgument);
477 return NULL;
478 }
479
480 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
481 if (targetPrivate == NULL) {
482 return NULL;
483 }
484
485 targetPrivate->type = reachabilityTypeAddress;
486
487 if (!__SCNetworkReachabilityAddressIsEmpty(address)) {
488 targetPrivate->remoteAddressEndpoint = nw_endpoint_create_address(address);
489 }
490
491 if (_sc_debug && (_sc_log > 0)) {
492 SC_log(LOG_INFO, "%s%s %@",
493 targetPrivate->log_prefix,
494 DEBUG_REACHABILITY_TYPE_ADDRESS,
495 targetPrivate);
496 }
497
498 return (SCNetworkReachabilityRef)targetPrivate;
499 }
500
501
502 static Boolean
503 is_same_address(const struct sockaddr *a, const struct sockaddr *b)
504 {
505 const void *a_addr;
506 const void *b_addr;
507 size_t len;
508
509 if ((a == NULL) ||
510 (b == NULL) ||
511 (a->sa_family != b->sa_family) ||
512 (a->sa_len != b->sa_len )) {
513 return FALSE;
514 }
515
516 switch (a->sa_family) {
517 case AF_INET : {
518 struct sockaddr_in *a_sin = (struct sockaddr_in *)(void *)a;
519 struct sockaddr_in *b_sin = (struct sockaddr_in *)(void *)b;
520
521 /* ALIGN: assuming a (and b) are aligned, then cast ok. */
522 a_addr = &a_sin->sin_addr;
523 b_addr = &b_sin->sin_addr;
524 len = sizeof(struct in_addr);
525 break;
526 }
527
528 case AF_INET6 : {
529 struct sockaddr_in6 *a_sin6 = (struct sockaddr_in6 *)(void *)a;
530 struct sockaddr_in6 *b_sin6 = (struct sockaddr_in6 *)(void *)b;
531
532 if (a_sin6->sin6_scope_id != b_sin6->sin6_scope_id) {
533 return FALSE;
534 }
535
536 a_addr = &a_sin6->sin6_addr;
537 b_addr = &b_sin6->sin6_addr;
538 len = sizeof(struct in6_addr);
539 break;
540 }
541
542 default :
543 a_addr = a;
544 b_addr = b;
545 len = a->sa_len;
546 break;
547 }
548
549 return (bcmp(a_addr, b_addr, len) == 0);
550 }
551
552
553 SCNetworkReachabilityRef
554 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator,
555 const struct sockaddr *localAddress,
556 const struct sockaddr *remoteAddress)
557 {
558 SCNetworkReachabilityPrivateRef targetPrivate;
559
560 if ((localAddress == NULL) && (remoteAddress == NULL)) {
561 _SCErrorSet(kSCStatusInvalidArgument);
562 return NULL;
563 }
564
565 if (localAddress != NULL) {
566 localAddress = is_valid_address(localAddress);
567 if (localAddress == NULL) {
568 _SCErrorSet(kSCStatusInvalidArgument);
569 return NULL;
570 }
571 }
572
573 if (remoteAddress != NULL) {
574 remoteAddress = is_valid_address(remoteAddress);
575 if (remoteAddress == NULL) {
576 _SCErrorSet(kSCStatusInvalidArgument);
577 return NULL;
578 }
579 }
580
581 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
582 if (targetPrivate == NULL) {
583 return NULL;
584 }
585
586 targetPrivate->type = reachabilityTypeAddressPair;
587
588 if (localAddress != NULL) {
589 targetPrivate->localAddressEndpoint = nw_endpoint_create_address(localAddress);
590 }
591
592 if (remoteAddress != NULL) {
593 if (is_same_address(localAddress, remoteAddress)) {
594 targetPrivate->remoteAddressEndpoint = network_retain(targetPrivate->localAddressEndpoint);
595 } else {
596 targetPrivate->remoteAddressEndpoint = nw_endpoint_create_address(remoteAddress);
597 }
598 }
599
600 targetPrivate->parameters = nw_parameters_create();
601 nw_parameters_set_local_address(targetPrivate->parameters, targetPrivate->localAddressEndpoint);
602
603 if (_sc_debug && (_sc_log > 0)) {
604 SC_log(LOG_INFO, "%s%s %@",
605 targetPrivate->log_prefix,
606 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR,
607 targetPrivate);
608 }
609
610 return (SCNetworkReachabilityRef)targetPrivate;
611 }
612
613
614 SCNetworkReachabilityRef
615 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator,
616 const char *nodename)
617 {
618 union {
619 struct sockaddr sa;
620 struct sockaddr_in sin;
621 struct sockaddr_in6 sin6;
622 } addr;
623 size_t nodenameLen;
624 SCNetworkReachabilityPrivateRef targetPrivate;
625
626 if (nodename == NULL) {
627 _SCErrorSet(kSCStatusInvalidArgument);
628 return NULL;
629 }
630
631 nodenameLen = strlen(nodename);
632 if (nodenameLen == 0) {
633 _SCErrorSet(kSCStatusInvalidArgument);
634 return NULL;
635 }
636
637 if (_SC_string_to_sockaddr(nodename, AF_UNSPEC, (void *)&addr, sizeof(addr)) != NULL) {
638 /* if this "nodename" is really an IP[v6] address in disguise */
639 return SCNetworkReachabilityCreateWithAddress(allocator, &addr.sa);
640 }
641
642 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
643 if (targetPrivate == NULL) {
644 return NULL;
645 }
646
647 targetPrivate->type = reachabilityTypeName;
648
649 targetPrivate->hostnameEndpoint = nw_endpoint_create_host(nodename, "0");
650
651 if (_sc_debug && (_sc_log > 0)) {
652 SC_log(LOG_INFO, "%s%s %@",
653 targetPrivate->log_prefix,
654 DEBUG_REACHABILITY_TYPE_NAME,
655 targetPrivate);
656 }
657
658 return (SCNetworkReachabilityRef)targetPrivate;
659 }
660
661
662 static SCNetworkReachabilityRef
663 __SCNetworkReachabilityCreateWithPTR(CFAllocatorRef allocator,
664 const struct sockaddr *ptrAddress)
665 {
666 SCNetworkReachabilityPrivateRef targetPrivate;
667
668 ptrAddress = is_valid_address(ptrAddress);
669 if (ptrAddress == NULL) {
670 _SCErrorSet(kSCStatusInvalidArgument);
671 return NULL;
672 }
673
674 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator);
675 if (targetPrivate == NULL) {
676 return NULL;
677 }
678
679 targetPrivate->type = reachabilityTypePTR;
680
681 targetPrivate->remoteAddressEndpoint = nw_endpoint_create_address(ptrAddress);
682
683 targetPrivate->parameters = nw_parameters_create();
684 nw_parameters_set_resolve_ptr(targetPrivate->parameters, TRUE);
685
686 if (_sc_debug && (_sc_log > 0)) {
687 SC_log(LOG_INFO, "%s%s %@",
688 targetPrivate->log_prefix,
689 DEBUG_REACHABILITY_TYPE_PTR,
690 targetPrivate);
691 }
692
693 return (SCNetworkReachabilityRef)targetPrivate;
694 }
695
696 SCNetworkReachabilityRef
697 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator,
698 CFDictionaryRef options)
699 {
700 const struct sockaddr *addr_l = NULL;
701 const struct sockaddr *addr_p = NULL;
702 const struct sockaddr *addr_r = NULL;
703 CFDataRef data;
704 CFStringRef interface = NULL;
705 CFStringRef nodename;
706 CFBooleanRef resolverBypass;
707 SCNetworkReachabilityRef target;
708 SCNetworkReachabilityPrivateRef targetPrivate;
709 unsigned int if_index = 0;
710 char if_name[IFNAMSIZ];
711
712 if (!isA_CFDictionary(options)) {
713 _SCErrorSet(kSCStatusInvalidArgument);
714 return NULL;
715 }
716
717 nodename = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionNodeName);
718 if ((nodename != NULL) &&
719 (!isA_CFString(nodename) || (CFStringGetLength(nodename) == 0))) {
720 _SCErrorSet(kSCStatusInvalidArgument);
721 return NULL;
722 }
723 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionLocalAddress);
724 if (data != NULL) {
725 if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
726 _SCErrorSet(kSCStatusInvalidArgument);
727 return NULL;
728 }
729 addr_l = (const struct sockaddr *)CFDataGetBytePtr(data);
730 }
731 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionPTRAddress);
732 if (data != NULL) {
733 if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
734 _SCErrorSet(kSCStatusInvalidArgument);
735 return NULL;
736 }
737 addr_p = (const struct sockaddr *)CFDataGetBytePtr(data);
738 }
739 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionRemoteAddress);
740 if (data != NULL) {
741 if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) {
742 _SCErrorSet(kSCStatusInvalidArgument);
743 return NULL;
744 }
745 addr_r = (const struct sockaddr *)CFDataGetBytePtr(data);
746 }
747 interface = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionInterface);
748 if ((interface != NULL) &&
749 (!isA_CFString(interface) || (CFStringGetLength(interface) == 0))) {
750 _SCErrorSet(kSCStatusInvalidArgument);
751 return NULL;
752 }
753 resolverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionResolverBypass);
754 if ((resolverBypass != NULL) && !isA_CFBoolean(resolverBypass)) {
755 _SCErrorSet(kSCStatusInvalidArgument);
756 return NULL;
757 }
758
759 if (nodename != NULL) {
760 const char *name;
761
762 if ((addr_l != NULL) || (addr_r != NULL) || (addr_p != NULL)) {
763 // can't have both a nodename and an address
764 _SCErrorSet(kSCStatusInvalidArgument);
765 return NULL;
766 }
767
768 name = _SC_cfstring_to_cstring(nodename, NULL, 0, kCFStringEncodingUTF8);
769 target = SCNetworkReachabilityCreateWithName(allocator, name);
770 CFAllocatorDeallocate(NULL, (void *)name);
771 } else if (addr_p != NULL) {
772 if ((addr_l != NULL) || // can't have PTR and target address
773 (addr_r != NULL)) {
774 _SCErrorSet(kSCStatusInvalidArgument);
775 return NULL;
776 }
777
778 target = __SCNetworkReachabilityCreateWithPTR(NULL, addr_p);
779 } else {
780 if ((addr_l != NULL) && (addr_r != NULL)) {
781 target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, addr_r);
782 } else if (addr_r != NULL) {
783 target = SCNetworkReachabilityCreateWithAddress(NULL, addr_r);
784 } else if (addr_l != NULL) {
785 target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, NULL);
786 } else {
787 _SCErrorSet(kSCStatusInvalidArgument);
788 return NULL;
789 }
790 }
791 if (target == NULL) {
792 return NULL;
793 }
794
795 targetPrivate = (SCNetworkReachabilityPrivateRef)target;
796
797 if (interface != NULL) {
798 if ((_SC_cfstring_to_cstring(interface,
799 if_name,
800 sizeof(if_name),
801 kCFStringEncodingASCII) == NULL) ||
802 ((if_index = if_nametoindex(if_name)) == 0)) {
803 CFRelease(targetPrivate);
804 _SCErrorSet(kSCStatusInvalidArgument);
805 return NULL;
806 }
807 }
808
809 if (targetPrivate->parameters == NULL) {
810 targetPrivate->parameters = nw_parameters_create();
811 }
812
813 if (if_index != 0) {
814 nw_interface_t interfaceObject = nw_interface_create_with_index(if_index);
815 nw_parameters_require_interface(targetPrivate->parameters, interfaceObject);
816 network_release(interfaceObject);
817 }
818
819 if (resolverBypass != NULL) {
820 targetPrivate->resolverBypass = CFBooleanGetValue(resolverBypass);
821 }
822
823 if (_sc_debug && (_sc_log > 0)) {
824 const char *opt = "???";
825
826 switch (targetPrivate->type) {
827 case reachabilityTypeAddress :
828 opt = DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS;
829 break;
830 case reachabilityTypeAddressPair :
831 opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS;
832 break;
833 case reachabilityTypeName :
834 opt = DEBUG_REACHABILITY_TYPE_NAME_OPTIONS;
835 break;
836 case reachabilityTypePTR :
837 opt = DEBUG_REACHABILITY_TYPE_PTR_OPTIONS;
838 break;
839 }
840
841 SC_log(LOG_INFO, "%s%s %@",
842 targetPrivate->log_prefix,
843 opt,
844 targetPrivate);
845 }
846
847 return (SCNetworkReachabilityRef)targetPrivate;
848 }
849
850
851 CFTypeID
852 SCNetworkReachabilityGetTypeID(void)
853 {
854 pthread_once(&initialized, __SCNetworkReachabilityInitialize); /* initialize runtime */
855 return __kSCNetworkReachabilityTypeID;
856 }
857
858
859 CFArrayRef /* CFArray[CFData], where each CFData is a (struct sockaddr *) */
860 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target,
861 int *error_num)
862 {
863 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
864
865 if (!isA_SCNetworkReachability(target)) {
866 _SCErrorSet(kSCStatusInvalidArgument);
867 return NULL;
868 }
869
870 if (!isReachabilityTypeName(targetPrivate->type)) {
871 _SCErrorSet(kSCStatusInvalidArgument);
872 return NULL;
873 }
874
875 if (error_num) {
876 *error_num = 0;
877 }
878
879 MUTEX_LOCK(&targetPrivate->lock);
880
881 if (nw_array_get_count(targetPrivate->lastResolvedEndpoints) > 0) {
882 CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
883 if (array != NULL) {
884 nw_array_apply(targetPrivate->lastResolvedEndpoints, ^bool(size_t index, nw_object_t object) {
885 if (nw_endpoint_get_type((nw_endpoint_t)object) == nw_endpoint_type_address) {
886 const struct sockaddr *address = nw_endpoint_get_address((nw_endpoint_t)object);
887 if (address != NULL) {
888 CFDataRef addressData = CFDataCreate(kCFAllocatorDefault, (const uint8_t *)address, address->sa_len);
889 if (addressData != NULL) {
890 CFArrayAppendValue(array, addressData);
891 CFRelease(addressData);
892 }
893 }
894 } else if (nw_endpoint_get_type((nw_endpoint_t)object) == nw_endpoint_type_host) {
895 CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, nw_endpoint_get_hostname((nw_endpoint_t)object), kCFStringEncodingASCII);
896 if (string != NULL) {
897 if (CFStringHasPrefix(string, CFSTR(".")) || CFStringHasSuffix(string, CFSTR("."))) {
898 CFMutableStringRef mutableString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, string);
899 if (mutableString != NULL) {
900 CFRelease(string);
901 string = mutableString;
902 CFStringTrim(mutableString, CFSTR("."));
903 }
904 }
905 CFArrayAppendValue(array, string);
906 CFRelease(string);
907 }
908 }
909 return TRUE;
910 });
911 MUTEX_UNLOCK(&targetPrivate->lock);
912 return array;
913 }
914 }
915
916 MUTEX_UNLOCK(&targetPrivate->lock);
917
918 _SCErrorSet(kSCStatusOK);
919 return NULL;
920 }
921
922 /*
923 * rankReachability()
924 * Not reachable == 0
925 * Connection Required == 1
926 * Reachable == 2
927 */
928 static int
929 rankReachability(SCNetworkReachabilityFlags flags)
930 {
931 int rank = 0;
932
933 if (flags & kSCNetworkReachabilityFlagsReachable) rank = 2;
934 if (flags & kSCNetworkReachabilityFlagsConnectionRequired) rank = 1;
935 return rank;
936 }
937
938 #pragma mark -
939 #pragma mark Reachability Flags
940
941 static void
942 __SCNetworkReachabilityGetAgentVPNFlags(xpc_object_t dictionary, Boolean *vpn, Boolean *onDemand)
943 {
944 const struct netagent *agent = NULL;
945 size_t length = 0;
946
947 if (dictionary == NULL || vpn == NULL || onDemand == NULL) {
948 return;
949 }
950
951 *vpn = FALSE;
952 *onDemand = FALSE;
953
954 agent = xpc_dictionary_get_data(dictionary, REACHABILITY_AGENT_DATA_KEY, &length);
955 if (agent == NULL || length < sizeof(struct netagent) || length != (sizeof(struct netagent) + agent->netagent_data_size)) {
956 return;
957 }
958
959 if (strncmp(REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN, agent->netagent_domain, NETAGENT_DOMAINSIZE) == 0) {
960 *vpn = TRUE;
961 if ((agent->netagent_flags & NETAGENT_FLAG_VOLUNTARY) &&
962 !(agent->netagent_flags & NETAGENT_FLAG_ACTIVE)) {
963 *onDemand = TRUE;
964 }
965 }
966 }
967
968 static SCNetworkReachabilityFlags
969 __SCNetworkReachabilityGetFlagsFromPath(nw_path_t path,
970 ReachabilityAddressType type,
971 nw_resolver_status_t resolverStatus,
972 nw_array_t resolvedEndpoints,
973 Boolean resolvedEndpointUseFlags,
974 SCNetworkReachabilityFlags resolvedEndpointFlags)
975 {
976 __block SCNetworkReachabilityFlags flags = kSCNetworkReachabilityFlagsReachable;
977 if (path != NULL) {
978 nw_path_status_t status = nw_path_get_status(path);
979 if (status == nw_path_status_satisfied) {
980 __block bool checkDNSFlags = TRUE;
981 flags = kSCNetworkReachabilityFlagsReachable;
982 #if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
983 if (nw_path_uses_interface_type(path, nw_interface_type_cellular)) {
984 flags |= (kSCNetworkReachabilityFlagsTransientConnection | kSCNetworkReachabilityFlagsIsWWAN);
985 }
986 #endif
987 xpc_object_t agent_dictionary = nw_path_copy_netagent_dictionary(path);
988 if (agent_dictionary != NULL) {
989 if (xpc_dictionary_get_count(agent_dictionary) > 0) {
990 xpc_dictionary_apply(agent_dictionary, ^bool(__unused const char *key, xpc_object_t value) {
991 Boolean vpn = FALSE;
992 Boolean onDemand = FALSE;
993 __SCNetworkReachabilityGetAgentVPNFlags(value, &vpn, &onDemand);
994 if (vpn) {
995 // VPN flows are transient
996 flags |= kSCNetworkReachabilityFlagsTransientConnection;
997 }
998 if (onDemand &&
999 type == reachabilityTypeName &&
1000 resolverStatus == nw_resolver_status_complete &&
1001 nw_array_get_count(resolvedEndpoints) == 0) {
1002 // On Demand by hostname, when no address has been resolved
1003 flags |= (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsConnectionOnDemand);
1004 checkDNSFlags = FALSE;
1005 }
1006 return TRUE;
1007 });
1008
1009 }
1010 xpc_release(agent_dictionary);
1011 }
1012 if (isReachabilityTypeName(type)) {
1013 if (checkDNSFlags) {
1014 if (resolverStatus == nw_resolver_status_complete &&
1015 nw_array_get_count(resolvedEndpoints) == 0) {
1016 // DNS didn't resolve, as a final answer for now. Not reachable!
1017 flags = 0;
1018 } else if (resolvedEndpointUseFlags) {
1019 flags = resolvedEndpointFlags;
1020 }
1021 }
1022 } else {
1023 if (nw_path_is_direct(path)) {
1024 flags |= kSCNetworkReachabilityFlagsIsDirect;
1025 }
1026 if (nw_path_is_local(path)) {
1027 flags |= kSCNetworkReachabilityFlagsIsLocalAddress;
1028 }
1029 }
1030 } else if (status == nw_path_status_unsatisfied) {
1031 flags = 0;
1032 } else if (status == nw_path_status_satisfiable) {
1033 flags = (kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection);
1034 uuid_t vpn_uuid;
1035 if (nw_path_get_vpn_config_id(path, &vpn_uuid)) {
1036 flags |= kSCNetworkReachabilityFlagsConnectionOnDemand;
1037 }
1038 #if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
1039 else if (nw_path_uses_interface_type(path, nw_interface_type_cellular)) {
1040 flags |= (kSCNetworkReachabilityFlagsIsWWAN);
1041 }
1042 #endif
1043 }
1044 }
1045 return (flags & kSCNetworkReachabilityFlagsMask);
1046 }
1047
1048 static nw_endpoint_t
1049 __SCNetworkReachabilityGetPrimaryEndpoint(SCNetworkReachabilityPrivateRef targetPrivate)
1050 {
1051 if (targetPrivate->type == reachabilityTypeName) {
1052 return targetPrivate->hostnameEndpoint;
1053 } else if (targetPrivate->type == reachabilityTypeAddress ||
1054 targetPrivate->type == reachabilityTypeAddressPair ||
1055 targetPrivate->type == reachabilityTypePTR) {
1056 return targetPrivate->remoteAddressEndpoint;
1057 }
1058 return NULL;
1059 }
1060
1061 int
1062 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target)
1063 {
1064 int if_index = -1;
1065 Boolean ok = TRUE;
1066 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1067 SCNetworkReachabilityFlags flags = 0;
1068
1069 if (!isA_SCNetworkReachability(target)) {
1070 _SCErrorSet(kSCStatusInvalidArgument);
1071 return if_index;
1072 }
1073
1074 MUTEX_LOCK(&targetPrivate->lock);
1075
1076 flags = __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, targetPrivate->type, nw_resolver_status_invalid, NULL, targetPrivate->lastResolvedEndpointHasFlags, targetPrivate->lastResolvedEndpointFlags);
1077
1078 /* Only return the if_index if the connection is reachable not for reachable connection
1079 * required etc ... */
1080 if (ok && rankReachability(flags) == 2) {
1081 if (targetPrivate->lastResolvedEndpointHasFlags) {
1082 if_index = targetPrivate->lastResolvedEndpointInterfaceIndex;
1083 } else {
1084 if_index = nw_path_get_interface_index(targetPrivate->lastPath);
1085 }
1086 }
1087
1088 MUTEX_UNLOCK(&targetPrivate->lock);
1089 return if_index;
1090 }
1091
1092 Boolean
1093 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target,
1094 SCNetworkReachabilityFlags *flags)
1095 {
1096 Boolean ok = TRUE;
1097 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1098
1099 if (!isA_SCNetworkReachability(target)) {
1100 _SCErrorSet(kSCStatusInvalidArgument);
1101 return FALSE;
1102 }
1103
1104 MUTEX_LOCK(&targetPrivate->lock);
1105
1106 if (targetPrivate->scheduled) {
1107 // if being watched, return the last known (and what should be current) status
1108 *flags = __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, targetPrivate->type, targetPrivate->lastResolverStatus, targetPrivate->lastResolvedEndpoints, targetPrivate->lastResolvedEndpointHasFlags, targetPrivate->lastResolvedEndpointFlags);
1109 // because we have synchronously captured the current status, we no longer
1110 // need our by-name required callback
1111 targetPrivate->sentFirstUpdate = TRUE;
1112 goto done;
1113 }
1114
1115
1116 // Not being watched, so run a one-shot path evaluator
1117 // We don't care about DNS resolution in this case, since we only need to have the DNS resolution to support clients watching reachability to get updates
1118 nw_path_evaluator_t pathEvaluator = nw_path_create_evaluator_for_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate), targetPrivate->parameters);
1119 nw_path_t path = nw_path_evaluator_copy_path(pathEvaluator);
1120 *flags = __SCNetworkReachabilityGetFlagsFromPath(path, 0, nw_resolver_status_invalid, NULL, FALSE, 0);
1121 network_release(path);
1122 network_release(pathEvaluator);
1123
1124 done :
1125
1126 MUTEX_UNLOCK(&targetPrivate->lock);
1127 return ok;
1128 }
1129
1130
1131 #pragma mark -
1132 #pragma mark Notifications
1133
1134 static void
1135 reachPerformAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate)
1136 {
1137 os_activity_t activity_id;
1138 void *context_info;
1139 void (*context_release)(const void *);
1140 SCNetworkReachabilityCallBack rlsFunction;
1141 SCNetworkReachabilityFlags flags = 0;
1142
1143 activity_id = os_activity_start("processing SCNetworkReachability notification",
1144 OS_ACTIVITY_FLAG_DEFAULT);
1145
1146 if (!targetPrivate->scheduled) {
1147 // if no longer scheduled
1148 SC_log(LOG_INFO, "%sskipping SCNetworkReachability callback, no longer scheduled",
1149 targetPrivate->log_prefix);
1150 MUTEX_UNLOCK(&targetPrivate->lock);
1151 goto done;
1152 }
1153
1154 // callout
1155 rlsFunction = targetPrivate->rlsFunction;
1156 if (targetPrivate->rlsContext.retain != NULL) {
1157 context_info = (void *)(*targetPrivate->rlsContext.retain)(targetPrivate->rlsContext.info);
1158 context_release = targetPrivate->rlsContext.release;
1159 } else {
1160 context_info = targetPrivate->rlsContext.info;
1161 context_release = NULL;
1162 }
1163
1164 flags = __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, targetPrivate->type, targetPrivate->lastResolverStatus, targetPrivate->lastResolvedEndpoints, targetPrivate->lastResolvedEndpointHasFlags, targetPrivate->lastResolvedEndpointFlags);
1165
1166 MUTEX_UNLOCK(&targetPrivate->lock);
1167
1168 if (rlsFunction != NULL) {
1169 (*rlsFunction)((SCNetworkReachabilityRef)targetPrivate,
1170 flags,
1171 context_info);
1172 }
1173
1174 if (context_release != NULL) {
1175 (*context_release)(context_info);
1176 }
1177
1178 done :
1179
1180 os_activity_end(activity_id);
1181
1182 return;
1183 }
1184
1185 static void
1186 reachPerform(void *info)
1187 {
1188 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)info;
1189
1190 MUTEX_LOCK(&targetPrivate->lock);
1191 reachPerformAndUnlock(targetPrivate);
1192
1193 }
1194
1195 static void
1196 reachUpdateAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate)
1197 {
1198 targetPrivate->sentFirstUpdate = TRUE;
1199 if (targetPrivate->rls != NULL) {
1200 if (targetPrivate->rlList != NULL) {
1201 CFRunLoopSourceSignal(targetPrivate->rls);
1202 _SC_signalRunLoop(targetPrivate, targetPrivate->rls, targetPrivate->rlList);
1203 }
1204 MUTEX_UNLOCK(&targetPrivate->lock);
1205 } else {
1206 reachPerformAndUnlock(targetPrivate);
1207 }
1208 }
1209
1210 Boolean
1211 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target,
1212 SCNetworkReachabilityCallBack callout,
1213 SCNetworkReachabilityContext *context)
1214 {
1215 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1216
1217 MUTEX_LOCK(&targetPrivate->lock);
1218
1219 if (targetPrivate->rlsContext.release != NULL) {
1220 /* let go of the current context */
1221 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info);
1222 }
1223
1224 targetPrivate->rlsFunction = callout;
1225 targetPrivate->rlsContext.info = NULL;
1226 targetPrivate->rlsContext.retain = NULL;
1227 targetPrivate->rlsContext.release = NULL;
1228 targetPrivate->rlsContext.copyDescription = NULL;
1229 if (context) {
1230 bcopy(context, &targetPrivate->rlsContext, sizeof(SCNetworkReachabilityContext));
1231 if (context->retain != NULL) {
1232 targetPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
1233 }
1234 }
1235
1236 MUTEX_UNLOCK(&targetPrivate->lock);
1237
1238 return TRUE;
1239 }
1240
1241
1242 static CFStringRef
1243 reachRLSCopyDescription(const void *info)
1244 {
1245 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
1246
1247 return CFStringCreateWithFormat(NULL,
1248 NULL,
1249 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
1250 target);
1251 }
1252
1253 Boolean
1254 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target,
1255 CFRunLoopRef runLoop,
1256 CFStringRef runLoopMode)
1257 {
1258 Boolean success = FALSE;
1259 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1260 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) {
1261 _SCErrorSet(kSCStatusInvalidArgument);
1262 return FALSE;
1263 }
1264
1265 MUTEX_LOCK(&targetPrivate->lock);
1266
1267 if (targetPrivate->scheduled) {
1268 if (targetPrivate->rls != NULL && targetPrivate->rlList != NULL) {
1269 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
1270 /*
1271 * if we do not already have host notifications scheduled with
1272 * this runLoop / runLoopMode
1273 */
1274 CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode);
1275 }
1276
1277 _SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
1278
1279 MUTEX_UNLOCK(&targetPrivate->lock);
1280 return TRUE;
1281 } else {
1282 MUTEX_UNLOCK(&targetPrivate->lock);
1283 _SCErrorSet(kSCStatusInvalidArgument);
1284 return FALSE;
1285 }
1286 }
1287
1288 CFRunLoopSourceContext context = {
1289 0 // version
1290 , (void *)target // info
1291 , CFRetain // retain
1292 , CFRelease // release
1293 , reachRLSCopyDescription // copyDescription
1294 , CFEqual // equal
1295 , CFHash // hash
1296 , NULL // schedule
1297 , NULL // cancel
1298 , reachPerform // perform
1299 };
1300
1301 targetPrivate->rls = CFRunLoopSourceCreate(NULL, 0, &context);
1302 targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1303
1304 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
1305 /*
1306 * if we do not already have host notifications scheduled with
1307 * this runLoop / runLoopMode
1308 */
1309 CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode);
1310 }
1311
1312 _SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
1313
1314 success = __SCNetworkReachabilitySetDispatchQueue(targetPrivate, _callback_queue());
1315 if (!success) {
1316 if (_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
1317 CFIndex n = CFArrayGetCount(targetPrivate->rlList);
1318 if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
1319 // if target is no longer scheduled for this runLoop / runLoopMode
1320 CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode);
1321 if (n == 0) {
1322 // if *all* notifications have been unscheduled
1323 CFRelease(targetPrivate->rlList);
1324 targetPrivate->rlList = NULL;
1325 CFRunLoopSourceInvalidate(targetPrivate->rls);
1326 CFRelease(targetPrivate->rls);
1327 targetPrivate->rls = NULL;
1328 }
1329 }
1330 }
1331 }
1332
1333 MUTEX_UNLOCK(&targetPrivate->lock);
1334 return success;
1335 }
1336
1337 Boolean
1338 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target,
1339 CFRunLoopRef runLoop,
1340 CFStringRef runLoopMode)
1341 {
1342 Boolean success = FALSE;
1343 Boolean unscheduleDispatchQueue = FALSE;
1344 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1345 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) {
1346 _SCErrorSet(kSCStatusInvalidArgument);
1347 return FALSE;
1348 }
1349
1350 MUTEX_LOCK(&targetPrivate->lock);
1351
1352 if (targetPrivate->rlList == NULL || targetPrivate->rls == NULL || !targetPrivate->scheduled) {
1353 MUTEX_UNLOCK(&targetPrivate->lock);
1354 _SCErrorSet(kSCStatusInvalidArgument);
1355 return FALSE;
1356 }
1357
1358 if (_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) {
1359 CFIndex n = CFArrayGetCount(targetPrivate->rlList);
1360 if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) {
1361 // if target is no longer scheduled for this runLoop / runLoopMode
1362 CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode);
1363 if (n == 0) {
1364 // if *all* notifications have been unscheduled
1365 unscheduleDispatchQueue = TRUE;
1366 CFRelease(targetPrivate->rlList);
1367 targetPrivate->rlList = NULL;
1368 CFRunLoopSourceInvalidate(targetPrivate->rls);
1369 CFRelease(targetPrivate->rls);
1370 targetPrivate->rls = NULL;
1371 }
1372 }
1373 }
1374
1375 if (unscheduleDispatchQueue) {
1376 success = __SCNetworkReachabilitySetDispatchQueue(targetPrivate, NULL);
1377 } else {
1378 success = TRUE;
1379 }
1380 MUTEX_UNLOCK(&targetPrivate->lock);
1381 return success;
1382 }
1383
1384 static __inline__ void
1385 __SCNetworkReachabilityCopyPathStatus(SCNetworkReachabilityPrivateRef targetPrivate, SCNetworkReachabilityFlags *flags, uint *ifIndex, size_t *endpointCount)
1386 {
1387 if (flags) {
1388 *flags = __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, targetPrivate->type, targetPrivate->lastResolverStatus, targetPrivate->lastResolvedEndpoints, targetPrivate->lastResolvedEndpointHasFlags, targetPrivate->lastResolvedEndpointFlags);
1389 }
1390 if (ifIndex) {
1391 *ifIndex = nw_path_get_interface_index(targetPrivate->lastPath);
1392 }
1393 if (endpointCount) {
1394 *endpointCount = nw_array_get_count(targetPrivate->lastResolvedEndpoints);
1395 }
1396 return;
1397 }
1398
1399 static __inline__ Boolean
1400 __SCNetworkReachabilityShouldUpdateClient(SCNetworkReachabilityPrivateRef targetPrivate, SCNetworkReachabilityFlags oldFlags, uint oldIFIndex, size_t oldEndpointCount)
1401 {
1402 SCNetworkReachabilityFlags newFlags = 0;
1403 uint newIFIndex = 0;
1404 size_t newEndpointCount = 0;
1405 __SCNetworkReachabilityCopyPathStatus(targetPrivate, &newFlags, &newIFIndex, &newEndpointCount);
1406 return (!targetPrivate->sentFirstUpdate ||
1407 oldFlags != newFlags ||
1408 oldIFIndex != newIFIndex ||
1409 oldEndpointCount != newEndpointCount);
1410 }
1411
1412 static void
1413 __SCNetworkReachabilityRestartResolver(SCNetworkReachabilityPrivateRef targetPrivate)
1414 {
1415 if (targetPrivate &&
1416 !targetPrivate->resolverBypass &&
1417 isReachabilityTypeName(targetPrivate->type)) {
1418 targetPrivate = (SCNetworkReachabilityPrivateRef)CFRetain(targetPrivate);
1419 nw_resolver_cancel(targetPrivate->resolver);
1420 nw_resolver_t resolver = nw_resolver_create_with_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate), targetPrivate->lastPathParameters ? targetPrivate->lastPathParameters : targetPrivate->parameters);
1421 targetPrivate->resolver = resolver;
1422 nw_resolver_set_cancel_handler(resolver, ^(void) {
1423 MUTEX_LOCK(&targetPrivate->lock);
1424 if (resolver == targetPrivate->resolver) {
1425 targetPrivate->resolver = NULL;
1426 }
1427 network_release(resolver);
1428 MUTEX_UNLOCK(&targetPrivate->lock);
1429 CFRelease(targetPrivate);
1430 });
1431 if (!nw_resolver_set_update_handler(resolver, targetPrivate->dispatchQueue, ^(nw_resolver_status_t status, nw_array_t resolved_endpoints) {
1432 MUTEX_LOCK(&targetPrivate->lock);
1433 if (targetPrivate->scheduled) {
1434 SCNetworkReachabilityFlags oldFlags = 0;
1435 uint oldIFIndex = 0;
1436 size_t oldEndpointCount = 0;
1437 __SCNetworkReachabilityCopyPathStatus(targetPrivate, &oldFlags, &oldIFIndex, &oldEndpointCount);
1438
1439 targetPrivate->lastResolverStatus = status;
1440 network_release(targetPrivate->lastResolvedEndpoints);
1441 targetPrivate->lastResolvedEndpoints = network_retain(resolved_endpoints);
1442
1443 // Run path evaluation on the resolved endpoints
1444 __block Boolean hasFlags = FALSE;
1445 targetPrivate->lastResolvedEndpointHasFlags = FALSE;
1446 targetPrivate->lastResolvedEndpointFlags = 0;
1447 targetPrivate->lastResolvedEndpointInterfaceIndex = 0;
1448 nw_array_apply(targetPrivate->lastResolvedEndpoints, ^bool(size_t index, nw_object_t object) {
1449 SCNetworkReachabilityFlags flags = 0;
1450 uint interfaceIndex = 0;
1451 nw_endpoint_t resolvedEndpoint = (nw_endpoint_t)object;
1452 nw_path_evaluator_t pathEvaluator = nw_path_create_evaluator_for_endpoint(resolvedEndpoint, targetPrivate->lastPathParameters ? targetPrivate->lastPathParameters : targetPrivate->parameters);
1453 nw_path_t path = nw_path_evaluator_copy_path(pathEvaluator);
1454 if (path != NULL) {
1455 flags = __SCNetworkReachabilityGetFlagsFromPath(path, 0, nw_resolver_status_invalid, NULL, FALSE, 0);
1456 hasFlags = TRUE;
1457 }
1458 interfaceIndex = nw_path_get_interface_index(path);
1459 network_release(path);
1460 network_release(pathEvaluator);
1461
1462 if (rankReachability(flags) > rankReachability(targetPrivate->lastResolvedEndpointFlags)) {
1463 // Return the best case result
1464 targetPrivate->lastResolvedEndpointFlags = flags;
1465 targetPrivate->lastResolvedEndpointInterfaceIndex = interfaceIndex;
1466 if (rankReachability(flags) == 2) {
1467 // Can't get any better than REACHABLE
1468 return FALSE;
1469 }
1470 }
1471 return TRUE;
1472 });
1473 targetPrivate->lastResolvedEndpointHasFlags = hasFlags;
1474
1475 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate, oldFlags, oldIFIndex, oldEndpointCount)) {
1476 reachUpdateAndUnlock(targetPrivate);
1477 } else {
1478 MUTEX_UNLOCK(&targetPrivate->lock);
1479 }
1480 } else {
1481 MUTEX_UNLOCK(&targetPrivate->lock);
1482 }
1483 })) {
1484 network_release(resolver);
1485 targetPrivate->resolver = NULL;
1486 CFRelease(targetPrivate);
1487 }
1488 }
1489 }
1490
1491 static Boolean
1492 __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate,
1493 dispatch_queue_t queue)
1494 {
1495 Boolean ok = FALSE;
1496
1497 if (queue != NULL) {
1498 if ((targetPrivate->dispatchQueue != NULL) || // if we are already scheduled with a dispatch queue
1499 ((queue != NULL) && targetPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
1500 _SCErrorSet(kSCStatusInvalidArgument);
1501 goto done;
1502 }
1503
1504 // retain dispatch queue
1505 dispatch_retain(queue);
1506 nw_path_evaluator_cancel(targetPrivate->pathEvaluator);
1507 nw_path_evaluator_t pathEvaluator = nw_path_create_evaluator_for_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate), targetPrivate->parameters);
1508 targetPrivate->pathEvaluator = pathEvaluator;
1509 targetPrivate->dispatchQueue = queue;
1510 targetPrivate->scheduled = TRUE;
1511 if (isReachabilityTypeName(targetPrivate->type)) {
1512 // we must have at least one callback for by-name queries
1513 targetPrivate->sentFirstUpdate = FALSE;
1514 } else {
1515 targetPrivate->sentFirstUpdate = TRUE;
1516 }
1517
1518 network_release(targetPrivate->lastPath);
1519 targetPrivate->lastPath = nw_path_evaluator_copy_path(pathEvaluator);
1520
1521 network_release(targetPrivate->lastPathParameters);
1522 targetPrivate->lastPathParameters = nw_path_copy_derived_parameters(targetPrivate->lastPath);
1523
1524 targetPrivate->lastResolverStatus = nw_resolver_status_invalid;
1525 network_release(targetPrivate->lastResolvedEndpoints);
1526 targetPrivate->lastResolvedEndpoints = NULL;
1527 __SCNetworkReachabilityRestartResolver(targetPrivate);
1528
1529 targetPrivate = (SCNetworkReachabilityPrivateRef)CFRetain(targetPrivate);
1530 nw_path_evaluator_set_cancel_handler(pathEvaluator, ^(void) {
1531 MUTEX_LOCK(&targetPrivate->lock);
1532 if (pathEvaluator == targetPrivate->pathEvaluator) {
1533 targetPrivate->pathEvaluator = NULL;
1534 }
1535 network_release(pathEvaluator);
1536 MUTEX_UNLOCK(&targetPrivate->lock);
1537 CFRelease(targetPrivate);
1538 });
1539
1540 if (!nw_path_evaluator_set_update_handler(pathEvaluator, targetPrivate->dispatchQueue, ^(nw_path_t path) {
1541 MUTEX_LOCK(&targetPrivate->lock);
1542 if (targetPrivate->scheduled) {
1543 SCNetworkReachabilityFlags oldFlags = 0;
1544 uint oldIFIndex = 0;
1545 size_t oldEndpointCount = 0;
1546 __SCNetworkReachabilityCopyPathStatus(targetPrivate, &oldFlags, &oldIFIndex, &oldEndpointCount);
1547
1548 network_release(targetPrivate->lastPath);
1549 targetPrivate->lastPath = network_retain(path);
1550 if (targetPrivate->lastResolverStatus == nw_resolver_status_complete) {
1551 targetPrivate->lastResolverStatus = nw_resolver_status_invalid;
1552 __SCNetworkReachabilityRestartResolver(targetPrivate);
1553 }
1554
1555 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate, oldFlags, oldIFIndex, oldEndpointCount)) {
1556 reachUpdateAndUnlock(targetPrivate);
1557 } else {
1558 MUTEX_UNLOCK(&targetPrivate->lock);
1559 }
1560 } else {
1561 MUTEX_UNLOCK(&targetPrivate->lock);
1562 }
1563 })) {
1564 targetPrivate->pathEvaluator = NULL;
1565 network_release(pathEvaluator);
1566 CFRelease(targetPrivate);
1567 }
1568 } else {
1569 if (targetPrivate->dispatchQueue == NULL) { // if we should be scheduled on a dispatch queue (but are not)
1570 _SCErrorSet(kSCStatusInvalidArgument);
1571 goto done;
1572 }
1573
1574 if (!targetPrivate->scheduled) {
1575 // if not currently scheduled
1576 _SCErrorSet(kSCStatusInvalidArgument);
1577 goto done;
1578 }
1579
1580
1581 targetPrivate->scheduled = FALSE;
1582 targetPrivate->sentFirstUpdate = FALSE;
1583 nw_path_evaluator_cancel(targetPrivate->pathEvaluator);
1584 targetPrivate->pathEvaluator = NULL;
1585 network_release(targetPrivate->lastPath);
1586 targetPrivate->lastPath = NULL;
1587 network_release(targetPrivate->lastPathParameters);
1588 targetPrivate->lastPathParameters = NULL;
1589 network_release(targetPrivate->lastResolvedEndpoints);
1590 targetPrivate->lastResolvedEndpoints = NULL;
1591 nw_resolver_cancel(targetPrivate->resolver);
1592 targetPrivate->resolver = NULL;
1593 if (targetPrivate->dispatchQueue != NULL) {
1594 dispatch_release(targetPrivate->dispatchQueue);
1595 targetPrivate->dispatchQueue = NULL;
1596 }
1597 }
1598 ok = TRUE;
1599 done:
1600 return ok;
1601 }
1602
1603 Boolean
1604 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target,
1605 dispatch_queue_t queue)
1606 {
1607 if (!isA_SCNetworkReachability(target)) {
1608 _SCErrorSet(kSCStatusInvalidArgument);
1609 return FALSE;
1610 }
1611
1612 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
1613 MUTEX_LOCK(&targetPrivate->lock);
1614 Boolean success = __SCNetworkReachabilitySetDispatchQueue(targetPrivate, queue);
1615 MUTEX_UNLOCK(&targetPrivate->lock);
1616 return success;
1617 }
1618
1619 /*
1620 * _SC_checkResolverReachabilityByAddress()
1621 *
1622 * Given an IP address, determine whether a reverse DNS query can be issued
1623 * using the current network configuration.
1624 */
1625 Boolean
1626 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef *storeP,
1627 SCNetworkReachabilityFlags *flags,
1628 Boolean *haveDNS,
1629 struct sockaddr *sa)
1630 {
1631 nw_path_evaluator_t evaluator = nw_path_create_default_evaluator();
1632 nw_path_t path = nw_path_evaluator_copy_path(evaluator);
1633 if (nw_path_get_status(path) == nw_path_status_unsatisfied_network) {
1634 if (flags) {
1635 *flags = 0;
1636 }
1637 if (haveDNS) {
1638 *haveDNS = FALSE;
1639 }
1640 } else {
1641 if (flags) {
1642 *flags = kSCNetworkReachabilityFlagsReachable;
1643 }
1644 if (haveDNS) {
1645 *haveDNS = TRUE;
1646 }
1647 }
1648 network_release(evaluator);
1649 network_release(path);
1650
1651 return TRUE;
1652 }
1653