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