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