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