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