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