X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/4c5e92e2493bdfbbce40e998f3b607c72c47af2c..77a550b625b42f93d03018146bc3f0751ff2eed8:/scutil.tproj/tests.c diff --git a/scutil.tproj/tests.c b/scutil.tproj/tests.c index 74a4049..a243962 100644 --- a/scutil.tproj/tests.c +++ b/scutil.tproj/tests.c @@ -1,15 +1,15 @@ /* - * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000, 2001, 2003-2015 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -17,7 +17,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ */ @@ -37,140 +37,1123 @@ */ #include "scutil.h" +#include "prefs.h" #include "tests.h" +#include +#include +#include #include #include #include #include +#define my_log(__level, fmt, ...) SCPrint(TRUE, stdout, CFSTR(fmt "\n"), ## __VA_ARGS__) -void -do_checkReachability(int argc, char **argv) +#include +#include "dnsinfo_internal.h" +#include +#include "network_information_priv.h" +#include "SCNetworkReachabilityInternal.h" +#include + + +static Boolean resolver_bypass; + + +static CF_RETURNS_RETAINED CFMutableDictionaryRef +_setupReachabilityOptions(int argc, char **argv, const char *interface) { - SCNetworkConnectionFlags flags = 0; - SCNetworkReachabilityRef target = NULL; + int i; + CFMutableDictionaryRef options; + + options = CFDictionaryCreateMutable(NULL, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); - if (argc == 1) { - struct sockaddr_in sin; - struct sockaddr_in6 sin6; + for (i = 0; i < argc; i++) { + if (strcasecmp(argv[i], "interface") == 0) { + if (++i >= argc) { + SCPrint(TRUE, stderr, CFSTR("No interface\n")); + CFRelease(options); + exit(1); + } + + interface = argv[i]; + continue; + } - bzero(&sin, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - bzero(&sin6, sizeof(sin6)); - sin6.sin6_len = sizeof(sin6); - sin6.sin6_family = AF_INET6; + if (strcasecmp(argv[i], "server") == 0) { + CFDictionarySetValue(options, + kSCNetworkReachabilityOptionServerBypass, + kCFBooleanFalse); + continue; + } else if (strcasecmp(argv[i], "no-server") == 0) { + CFDictionarySetValue(options, + kSCNetworkReachabilityOptionServerBypass, + kCFBooleanTrue); + continue; + } + + if (strcasecmp(argv[i], "no-connection-on-demand") == 0) { + CFDictionarySetValue(options, + kSCNetworkReachabilityOptionConnectionOnDemandBypass, + kCFBooleanTrue); + continue; + } + + if (strcasecmp(argv[i], "no-resolve") == 0) { + CFDictionarySetValue(options, + kSCNetworkReachabilityOptionResolverBypass, + kCFBooleanTrue); + resolver_bypass = TRUE; + continue; + } + + if (strcasecmp(argv[i], "ptr") == 0) { + CFDictionarySetValue(options, + kSCNetworkReachabilityOptionPTRAddress, + kCFBooleanTrue); + continue; + } + + if (strlen(argv[i]) == 0) { + continue; + } + + SCPrint(TRUE, stderr, CFSTR("Unrecognized option: %s\n"), argv[i]); + CFRelease(options); + exit(1); + } - if (inet_aton(argv[0], &sin.sin_addr) == 1) { - target = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&sin); - } else if (inet_pton(AF_INET6, argv[0], &sin6.sin6_addr) == 1) { - char *p; + if (interface != NULL) { + CFStringRef str; + + if (if_nametoindex(interface) == 0) { + SCPrint(TRUE, stderr, CFSTR("No interface: %s\n"), interface); + exit(1); + } + + str = CFStringCreateWithCString(NULL, interface, kCFStringEncodingASCII); + CFDictionarySetValue(options, kSCNetworkReachabilityOptionInterface, str); + CFRelease(str); + } + + if (CFDictionaryGetCount(options) == 0) { + CFRelease(options); + options = NULL; + } + + return options; +} + + +static SCNetworkReachabilityRef +_setupReachability(int argc, char **argv, SCNetworkReachabilityContext *context) +{ + const char *ip_address = argv[0]; + char *ip_addressN = NULL; + const char *interface; + CFMutableDictionaryRef options = NULL; + const char *remote_address = NULL; + char *remote_addressN= NULL; + const char *remote_interface; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + SCNetworkReachabilityRef target = NULL; + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + + interface = strchr(ip_address, '%'); + if (interface != NULL) { + ip_addressN = strdup(ip_address); + ip_addressN[interface - ip_address] = '\0'; + ip_address = ip_addressN; + interface++; + } + + if ((argc > 1) && (strlen(argv[1]) > 0)) { + remote_address = argv[1]; + + remote_interface = strchr(remote_address, '%'); + if (remote_interface != NULL) { + remote_addressN = strdup(remote_address); + remote_addressN[remote_interface - remote_address] = '\0'; + remote_address = remote_addressN; + remote_interface++; + } + } + + if (inet_aton(ip_address, &sin.sin_addr) == 1) { + struct sockaddr_in r_sin; + + if (argc > 1) { + bzero(&r_sin, sizeof(r_sin)); + r_sin.sin_len = sizeof(r_sin); + r_sin.sin_family = AF_INET; + } - p = strchr(argv[0], '%'); - if (p != NULL) { - sin6.sin6_scope_id = if_nametoindex(p+1); + if ((argc == 1) + || (remote_address == NULL) + || (inet_aton(remote_address, &r_sin.sin_addr) == 0)) { + if (argc > 2) { + options = _setupReachabilityOptions(argc - 2, argv + 2, interface); } + if (options == NULL) { + target = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&sin); + if (context != NULL) { + context->info = "by address"; + } + } else { + CFDataRef data; - target = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&sin6); + data = CFDataCreate(NULL, (const UInt8 *)&sin, sizeof(sin)); + CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data); + CFRelease(data); + + if (context != NULL) { + if (CFDictionaryContainsKey(options, + kSCNetworkReachabilityOptionInterface)) { + if (CFDictionaryGetCount(options) == 2) { + context->info = "by address w/scope"; + } else { + context->info = "by address w/scope and options"; + } + } else { + context->info = "by address w/options"; + } + } + } + } else { + if (remote_interface != NULL) { + if ((interface != NULL) && (strcmp(interface, remote_interface) != 0)) { + SCPrint(TRUE, stderr, + CFSTR("Interface mismatch \"%s\" != \"%s\"\n"), + interface, + remote_interface); + exit(1); + } + + interface = remote_interface; + } + + options = _setupReachabilityOptions(argc - 2, argv + 2, interface); + if (options == NULL) { + target = SCNetworkReachabilityCreateWithAddressPair(NULL, + (struct sockaddr *)&sin, + (struct sockaddr *)&r_sin); + if (context != NULL) { + context->info = "by address pair"; + } + } else { + CFDataRef data; + + data = CFDataCreate(NULL, (const UInt8 *)&sin, sizeof(sin)); + CFDictionarySetValue(options, kSCNetworkReachabilityOptionLocalAddress, data); + CFRelease(data); + data = CFDataCreate(NULL, (const UInt8 *)&r_sin, sizeof(r_sin)); + CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data); + CFRelease(data); + + if (context != NULL) { + if (CFDictionaryContainsKey(options, + kSCNetworkReachabilityOptionInterface)) { + if (CFDictionaryGetCount(options) == 3) { + context->info = "by address pair w/scope"; + } else { + context->info = "by address pair w/scope and options"; + } + } else { + context->info = "by address pair w/options"; + } + } + } + } + } else if (inet_pton(AF_INET6, ip_address, &sin6.sin6_addr) == 1) { + struct sockaddr_in6 r_sin6; + + if (interface != NULL) { + sin6.sin6_scope_id = if_nametoindex(interface); + } + + if (argc > 1) { + bzero(&r_sin6, sizeof(r_sin6)); + r_sin6.sin6_len = sizeof(r_sin6); + r_sin6.sin6_family = AF_INET6; + } + + if ((argc == 1) + || (remote_address == NULL) + || (inet_pton(AF_INET6, remote_address, &r_sin6.sin6_addr) == 0)) { + if (argc > 2) { + options = _setupReachabilityOptions(argc - 2, argv + 2, NULL); + } + if (options == NULL) { + target = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&sin6); + if (context != NULL) { + context->info = "by (v6) address"; + } + } else { + CFDataRef data; + + data = CFDataCreate(NULL, (const UInt8 *)&sin6, sizeof(sin6)); + CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data); + CFRelease(data); + + if (context != NULL) { + context->info = "by (v6) address w/options"; + } + } } else { + if (remote_interface != NULL) { + r_sin6.sin6_scope_id = if_nametoindex(remote_interface); + + if ((interface != NULL) && (strcmp(interface, remote_interface) != 0)) { + SCPrint(TRUE, stderr, + CFSTR("Interface mismatch \"%s\" != \"%s\"\n"), + interface, + remote_interface); + exit(1); + } + } + + options = _setupReachabilityOptions(argc - 2, argv + 2, NULL); + if (options == NULL) { + target = SCNetworkReachabilityCreateWithAddressPair(NULL, + (struct sockaddr *)&sin6, + (struct sockaddr *)&r_sin6); + if (context != NULL) { + context->info = "by (v6) address pair"; + } + } else { + CFDataRef data; + + data = CFDataCreate(NULL, (const UInt8 *)&sin6, sizeof(sin6)); + CFDictionarySetValue(options, kSCNetworkReachabilityOptionLocalAddress, data); + CFRelease(data); + data = CFDataCreate(NULL, (const UInt8 *)&r_sin6, sizeof(r_sin6)); + CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data); + CFRelease(data); + + if (context != NULL) { + context->info = "by (v6) address pair w/options"; + } + } + } + } else { + if (argc == 1) { target = SCNetworkReachabilityCreateWithName(NULL, argv[0]); + if (context != NULL) { + context->info = "by name"; + } + } else { + options = _setupReachabilityOptions(argc - 1, argv + 1, NULL); + if (options == NULL) { + target = SCNetworkReachabilityCreateWithName(NULL, argv[0]); + if (context != NULL) { + context->info = "by name"; + } + } else { + CFStringRef str; + + str = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8); + CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, str); + CFRelease(str); + + if (context != NULL) { + context->info = "by name w/options"; + } + } } - } else /* if (argc == 2) */ { - struct sockaddr_in l_sin; - struct sockaddr_in r_sin; + } - bzero(&l_sin, sizeof(l_sin)); - l_sin.sin_len = sizeof(l_sin); - l_sin.sin_family = AF_INET; - if (inet_aton(argv[0], &l_sin.sin_addr) == 0) { - SCPrint(TRUE, stderr, CFSTR("Could not interpret address \"%s\"\n"), argv[0]); - exit(1); + if (ip_addressN != NULL) { + free(ip_addressN); + } + + if (remote_addressN != NULL) { + free(remote_addressN); + } + + if ((target == NULL) && (options != NULL)) { + if (CFDictionaryContainsKey(options, kSCNetworkReachabilityOptionPTRAddress)) { + CFDataRef address; + + address = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionRemoteAddress); + if (address == NULL) { + SCPrint(TRUE, stderr, CFSTR("No address\n")); + exit(1); + } + CFDictionarySetValue(options, kSCNetworkReachabilityOptionPTRAddress, address); + CFDictionaryRemoveValue(options, kSCNetworkReachabilityOptionRemoteAddress); + + if (context != NULL) { + CFIndex n = CFDictionaryGetCount(options); + + if (n == 1) { + context->info = "by PTR"; + } else if (CFDictionaryContainsKey(options, + kSCNetworkReachabilityOptionInterface)) { + if (n == 2) { + context->info = "by PTR w/scope"; + } else { + context->info = "by PTR w/scope and options"; + } + } else { + context->info = "by PTR w/options"; + } + } } - bzero(&r_sin, sizeof(r_sin)); - r_sin.sin_len = sizeof(r_sin); - r_sin.sin_family = AF_INET; - if (inet_aton(argv[1], &r_sin.sin_addr) == 0) { - SCPrint(TRUE, stderr, CFSTR("Could not interpret address \"%s\"\n"), argv[1]); - exit(1); + target = SCNetworkReachabilityCreateWithOptions(NULL, options); + CFRelease(options); + } + + return target; +} + + +static void +_printReachability(SCNetworkReachabilityRef target) +{ + SCNetworkReachabilityFlags flags; + Boolean ok; + CFStringRef str; + + ok = SCNetworkReachabilityGetFlags(target, &flags); + if (!ok) { + SCPrint(TRUE, stderr, CFSTR(" could not determine reachability, %s\n"), SCErrorString(SCError())); + return; + } + + str = __SCNetworkReachabilityCopyFlags(flags, CFSTR("flags = "), _sc_debug); + SCPrint(TRUE, stdout, CFSTR("%@\n"), str); + CFRelease(str); + + if (resolver_bypass && _sc_debug) { + int if_index; + + if_index = SCNetworkReachabilityGetInterfaceIndex(target); + SCPrint(TRUE, stdout, CFSTR("interface index = %d\n"), if_index); + } + + return; +} + + +__private_extern__ +void +do_checkReachability(int argc, char **argv) +{ + SCNetworkReachabilityRef target; + + target = _setupReachability(argc, argv, NULL); + if (target == NULL) { + SCPrint(TRUE, stderr, CFSTR(" Could not determine status: %s\n"), SCErrorString(SCError())); + exit(1); + } + + _printReachability(target); + CFRelease(target); + exit(0); +} + + +static void +_printNWIFlags(nwi_ifstate_flags flags) +{ + flags &= NWI_IFSTATE_FLAGS_MASK; + if (flags == 0) { + return; + } + + SCPrint(TRUE, stdout, CFSTR(" (")); + if (flags & NWI_IFSTATE_FLAGS_HAS_IPV4) { + SCPrint(TRUE, stdout, CFSTR("IPv4")); + flags &= ~NWI_IFSTATE_FLAGS_HAS_IPV4; + SCPrint(flags != 0, stdout, CFSTR(",")); + } + if (flags & NWI_IFSTATE_FLAGS_HAS_IPV6) { + SCPrint(TRUE, stdout, CFSTR("IPv6")); + flags &= ~NWI_IFSTATE_FLAGS_HAS_IPV6; + SCPrint(flags != 0, stdout, CFSTR(",")); + } + if (flags & NWI_IFSTATE_FLAGS_HAS_DNS) { + SCPrint(TRUE, stdout, CFSTR("DNS")); + flags &= ~NWI_IFSTATE_FLAGS_HAS_DNS; + SCPrint(flags != 0, stdout, CFSTR(",")); + } + if (flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) { + SCPrint(TRUE, stdout, CFSTR("NOT-IN-LIST")); + flags &= ~NWI_IFSTATE_FLAGS_NOT_IN_LIST; + SCPrint(flags != 0, stdout, CFSTR(",")); + } + if (flags & NWI_IFSTATE_FLAGS_HAS_SIGNATURE) { + SCPrint(TRUE, stdout, CFSTR("SIGNATURE")); + flags &= ~NWI_IFSTATE_FLAGS_HAS_SIGNATURE; + SCPrint(flags != 0, stdout, CFSTR(",")); + } + if (flags & NWI_IFSTATE_FLAGS_NOT_IN_IFLIST) { + SCPrint(TRUE, stdout, CFSTR("NOT-IN-IFLIST")); + flags &= ~NWI_IFSTATE_FLAGS_NOT_IN_IFLIST; + SCPrint(flags != 0, stdout, CFSTR(",")); + } + if (flags != 0) { + SCPrint(TRUE, stdout, CFSTR("%p"), (void *)flags); + } + SCPrint(TRUE, stdout, CFSTR(")")); + + return; +} + + +static void +_printNWIInfo(nwi_ifstate_t ifstate) +{ + nwi_ifstate_flags ifstate_flags; + SCNetworkReachabilityFlags reach_flags = nwi_ifstate_get_reachability_flags(ifstate); + const uint8_t *signature; + int signature_length; + CFStringRef str; + const struct sockaddr *vpn_addr = nwi_ifstate_get_vpn_server(ifstate); + + ifstate_flags = nwi_ifstate_get_flags(ifstate); + if (_sc_debug) { + ifstate_flags |= ifstate->flags; + } + + SCPrint(TRUE, stdout, + CFSTR(" %7s : flags %p"), + nwi_ifstate_get_ifname(ifstate), + (void *)ifstate_flags); + _printNWIFlags(ifstate_flags); + + str = __SCNetworkReachabilityCopyFlags(reach_flags, CFSTR(" reach "), TRUE); + SCPrint(TRUE, stdout, CFSTR("\n%@"), str); + CFRelease(str); + + if (vpn_addr != NULL) { + char vpn_ntopbuf[INET6_ADDRSTRLEN]; + + _SC_sockaddr_to_string(vpn_addr, vpn_ntopbuf, sizeof(vpn_ntopbuf)); + SCPrint(TRUE, stdout, CFSTR("\n VPN server: %s"), vpn_ntopbuf); + } + + signature = nwi_ifstate_get_signature(ifstate, AF_UNSPEC, &signature_length); + if (signature != NULL) { + CFDataRef digest = NULL; + + digest = CFDataCreate(NULL, signature, CC_SHA1_DIGEST_LENGTH); + SCPrint(TRUE, stdout, CFSTR("\n Signature Hash: %@"), digest); + CFRelease(digest); + } else { + SCPrint(TRUE, stdout, CFSTR("\n Signature Hash: ")); + } + + SCPrint(TRUE, stdout, CFSTR("\n generation %llu\n"), + nwi_ifstate_get_generation(ifstate)); + + return; +} + + +static void +_printNWIReachInfo(nwi_state_t state, int af) +{ + uint32_t reach_flags; + CFStringRef str; + + reach_flags = nwi_state_get_reachability_flags(state, af); + + str = __SCNetworkReachabilityCopyFlags(reach_flags, CFSTR(" REACH : flags "), TRUE); + SCPrint(TRUE, stdout, CFSTR("\n%@\n"), str); + CFRelease(str); + + return; +} + + +static void +do_printNWI(int argc, char **argv, nwi_state_t state) +{ + unsigned int count; + nwi_ifstate_t ifstate; + + if (state == NULL) { + SCPrint(TRUE, stdout, CFSTR("No network information\n")); + return; + } + + if (argc > 0) { + ifstate = nwi_state_get_ifstate(state, argv[0]); + if (ifstate != NULL) { + nwi_ifstate_t alias; + + _printNWIInfo(ifstate); + + alias = nwi_ifstate_get_alias(ifstate, + ifstate->af == AF_INET ? AF_INET6 : AF_INET); + if (alias != NULL) { + SCPrint(TRUE, stdout, CFSTR("\n")); + _printNWIInfo(alias); + } + } else { + SCPrint(TRUE, stdout, CFSTR("No network information (for %s)\n"), argv[0]); + } + return; + } + + SCPrint(TRUE, stdout, CFSTR("Network information (generation %llu)"), + nwi_state_get_generation(state)); + + SCPrint(TRUE, stdout, CFSTR("\nIPv4 network interface information\n")); + + ifstate = nwi_state_get_first_ifstate(state, AF_INET); + if (ifstate == NULL) { + SCPrint(TRUE, stdout, CFSTR(" No IPv4 states found\n")); + } else { + while (ifstate != NULL) { + _printNWIInfo(ifstate); + ifstate = nwi_ifstate_get_next(ifstate, AF_INET); } + } + _printNWIReachInfo(state, AF_INET); - target = SCNetworkReachabilityCreateWithAddressPair(NULL, - (struct sockaddr *)&l_sin, - (struct sockaddr *)&r_sin); + SCPrint(TRUE, stdout, CFSTR("\nIPv6 network interface information\n")); + + ifstate = nwi_state_get_first_ifstate(state, AF_INET6); + if (ifstate == NULL) { + SCPrint(TRUE, stdout, CFSTR(" No IPv6 states found\n")); + } else { + while (ifstate != NULL) { + _printNWIInfo(ifstate); + ifstate = nwi_ifstate_get_next(ifstate, AF_INET6); + } } + _printNWIReachInfo(state, AF_INET6); - if (!target) { + count = nwi_state_get_interface_names(state, NULL, 0); + if (count > 0) { + const char * names[count]; + + count = nwi_state_get_interface_names(state, names, count); + if (count > 0) { + int i; + + printf("\nNetwork interfaces:"); + for (i = 0; i < count; i++) { + printf(" %s", names[i]); + } + printf("\n"); + } + } + return; +} + + +__private_extern__ +void +do_showNWI(int argc, char **argv) +{ + nwi_state_t state; + + state = nwi_state_copy(); + do_printNWI(argc, argv, state); + if (state != NULL) { + nwi_state_release(state); + } else { + exit(1); + } + + exit(0); +} + + +__private_extern__ +void +do_watchNWI(int argc, char **argv) +{ + nwi_state_t state; + int status; + int token; + + state = nwi_state_copy(); + do_printNWI(argc, argv, state); + if (state != NULL) { + nwi_state_release(state); + } + + status = notify_register_dispatch(nwi_state_get_notify_key(), + &token, + dispatch_get_main_queue(), + ^(int token){ + nwi_state_t state; + struct tm tm_now; + struct timeval tv_now; + + (void)gettimeofday(&tv_now, NULL); + (void)localtime_r(&tv_now.tv_sec, &tm_now); + SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"), + tm_now.tm_hour, + tm_now.tm_min, + tm_now.tm_sec, + tv_now.tv_usec / 1000); + + state = nwi_state_copy(); + do_printNWI(argc, argv, state); + if (state != NULL) { + nwi_state_release(state); + } + }); + if (status != NOTIFY_STATUS_OK) { + SC_log(LOG_INFO, "notify_register_dispatch() failed for nwi changes, status=%u", status); + exit(1); + } + + CFRunLoopRun(); + exit(0); +} + + +static void +callout(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) +{ + static int n = 3; + struct tm tm_now; + struct timeval tv_now; + + (void)gettimeofday(&tv_now, NULL); + (void)localtime_r(&tv_now.tv_sec, &tm_now); + + SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"), + tm_now.tm_hour, + tm_now.tm_min, + tm_now.tm_sec, + tv_now.tv_usec / 1000); + SCPrint(TRUE, stdout, CFSTR("%2d: callback w/flags=0x%08x (info=\"%s\")\n"), n++, flags, (char *)info); + SCPrint(TRUE, stdout, CFSTR(" %@\n"), target); + _printReachability(target); + SCPrint(TRUE, stdout, CFSTR("\n")); + return; +} + + +__private_extern__ +void +do_watchReachability(int argc, char **argv) +{ + SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL }; + SCNetworkReachabilityRef target; + SCNetworkReachabilityRef target_async; + + target = _setupReachability(argc, argv, NULL); + if (target == NULL) { SCPrint(TRUE, stderr, CFSTR(" Could not determine status: %s\n"), SCErrorString(SCError())); exit(1); } - if (!SCNetworkReachabilityGetFlags(target, &flags)) { + target_async = _setupReachability(argc, argv, &context); + if (target_async == NULL) { SCPrint(TRUE, stderr, CFSTR(" Could not determine status: %s\n"), SCErrorString(SCError())); exit(1); } - SCPrint(_sc_debug, stdout, CFSTR("flags = 0x%x"), flags); - if (flags != 0) { - SCPrint(_sc_debug, stdout, CFSTR(" (")); - if (flags & kSCNetworkFlagsReachable) { - SCPrint(TRUE, stdout, CFSTR("Reachable")); - flags &= ~kSCNetworkFlagsReachable; - SCPrint(flags != 0, stdout, CFSTR(",")); - } - if (flags & kSCNetworkFlagsTransientConnection) { - SCPrint(TRUE, stdout, CFSTR("Transient Connection")); - flags &= ~kSCNetworkFlagsTransientConnection; - SCPrint(flags != 0, stdout, CFSTR(",")); - } - if (flags & kSCNetworkFlagsConnectionRequired) { - SCPrint(TRUE, stdout, CFSTR("Connection Required")); - flags &= ~kSCNetworkFlagsConnectionRequired; - SCPrint(flags != 0, stdout, CFSTR(",")); - } - if (flags & kSCNetworkFlagsConnectionAutomatic) { - SCPrint(TRUE, stdout, CFSTR("Connection Automatic")); - flags &= ~kSCNetworkFlagsConnectionAutomatic; - SCPrint(flags != 0, stdout, CFSTR(",")); - } - if (flags & kSCNetworkFlagsInterventionRequired) { - SCPrint(TRUE, stdout, CFSTR("Intervention Required")); - flags &= ~kSCNetworkFlagsInterventionRequired; - SCPrint(flags != 0, stdout, CFSTR(",")); - } - if (flags & kSCNetworkFlagsIsLocalAddress) { - SCPrint(TRUE, stdout, CFSTR("Local Address")); - flags &= ~kSCNetworkFlagsIsLocalAddress; - SCPrint(flags != 0, stdout, CFSTR(",")); - } - if (flags & kSCNetworkFlagsIsDirect) { - SCPrint(TRUE, stdout, CFSTR("Directly Reachable Address")); - flags &= ~kSCNetworkFlagsIsDirect; - SCPrint(flags != 0, stdout, CFSTR(",")); - } - SCPrint(_sc_debug, stdout, CFSTR(")")); + // Normally, we don't want to make any calls to SCNetworkReachabilityGetFlags() + // until after the "target" has been scheduled on a run loop. Otherwise, we'll + // end up making a synchronous DNS request and that's not what we want. + // + // To test the case were an application first calls SCNetworkReachabilityGetFlags() + // we provide the "CHECK_REACHABILITY_BEFORE_SCHEDULING" environment variable. + if (getenv("CHECK_REACHABILITY_BEFORE_SCHEDULING") != NULL) { + CFRelease(target_async); + target_async = CFRetain(target); + } + + // Direct check of reachability + SCPrint(TRUE, stdout, CFSTR(" 0: direct\n")); + SCPrint(TRUE, stdout, CFSTR(" %@\n"), target); + _printReachability(target); + CFRelease(target); + SCPrint(TRUE, stdout, CFSTR("\n")); + + // schedule the target + SCPrint(TRUE, stdout, CFSTR(" 1: start\n")); + SCPrint(TRUE, stdout, CFSTR(" %@\n"), target_async); +// _printReachability(target_async); + SCPrint(TRUE, stdout, CFSTR("\n")); + + if (!SCNetworkReachabilitySetCallback(target_async, callout, &context)) { + printf("SCNetworkReachabilitySetCallback() failed: %s\n", SCErrorString(SCError())); + exit(1); + } + + if (doDispatch) { + if (!SCNetworkReachabilitySetDispatchQueue(target_async, dispatch_get_main_queue())) { + printf("SCNetworkReachabilitySetDispatchQueue() failed: %s\n", SCErrorString(SCError())); + exit(1); + } } else { - SCPrint(_sc_debug, stdout, CFSTR(" (")); - SCPrint(TRUE, stdout, CFSTR("Not Reachable")); - SCPrint(_sc_debug, stdout, CFSTR(")")); + if (!SCNetworkReachabilityScheduleWithRunLoop(target_async, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { + printf("SCNetworkReachabilityScheduleWithRunLoop() failed: %s\n", SCErrorString(SCError())); + exit(1); + } } + + // Note: now that we are scheduled on a run loop we can call SCNetworkReachabilityGetFlags() + // to get the current status. For "names", a DNS lookup has already been initiated. + SCPrint(TRUE, stdout, CFSTR(" 2: on %s\n"), doDispatch ? "dispatch queue" : "runloop"); + SCPrint(TRUE, stdout, CFSTR(" %@\n"), target_async); + _printReachability(target_async); SCPrint(TRUE, stdout, CFSTR("\n")); + + CFRunLoopRun(); + exit(0); +} + + +static void +do_printDNSConfiguration(int argc, char **argv, dns_config_t *dns_config) +{ + int _sc_log_save; + + if (dns_config == NULL) { + SCPrint(TRUE, stdout, CFSTR("No DNS configuration available\n")); + return; + } + + _sc_log_save = _sc_log; + _sc_log = FALSE; + _dns_configuration_log(dns_config, _sc_debug); + _sc_log = _sc_log_save; + + if (_sc_debug) { + SCPrint(TRUE, stdout, CFSTR("\ngeneration = %llu\n"), dns_config->generation); + } + + return; +} + + +__private_extern__ +void +do_showDNSConfiguration(int argc, char **argv) +{ + dns_config_t *dns_config; + + dns_config = dns_configuration_copy(); + do_printDNSConfiguration(argc, argv, dns_config); + if (dns_config != NULL) { + dns_configuration_free(dns_config); + } else { + exit(1); + } + exit(0); } +__private_extern__ +void +do_watchDNSConfiguration(int argc, char **argv) +{ + dns_config_t *dns_config; + int status; + int token; + + dns_config = dns_configuration_copy(); + do_printDNSConfiguration(argc, argv, dns_config); + if (dns_config != NULL) { + dns_configuration_free(dns_config); + } + + status = notify_register_dispatch(dns_configuration_notify_key(), + &token, + dispatch_get_main_queue(), + ^(int token){ + dns_config_t *dns_config; + struct tm tm_now; + struct timeval tv_now; + + (void)gettimeofday(&tv_now, NULL); + (void)localtime_r(&tv_now.tv_sec, &tm_now); + SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"), + tm_now.tm_hour, + tm_now.tm_min, + tm_now.tm_sec, + tv_now.tv_usec / 1000); + + dns_config = dns_configuration_copy(); + do_printDNSConfiguration(argc, argv, dns_config); + if (dns_config != NULL) { + dns_configuration_free(dns_config); + } + }); + if (status != NOTIFY_STATUS_OK) { + SC_log(LOG_INFO, "notify_register_dispatch() failed for DNS configuration changes, status=%u", status); + exit(1); + } + + CFRunLoopRun(); + exit(0); +} + + +static void +showProxy(CFDictionaryRef proxy) +{ + CFMutableDictionaryRef cleaned = NULL; + + if (!_sc_debug) { + cleaned = CFDictionaryCreateMutableCopy(NULL, 0, proxy); + CFDictionaryRemoveValue(cleaned, kSCPropNetProxiesScoped); + CFDictionaryRemoveValue(cleaned, kSCPropNetProxiesServices); + CFDictionaryRemoveValue(cleaned, kSCPropNetProxiesSupplemental); + proxy = cleaned; + } + + SCPrint(TRUE, stdout, CFSTR("%@\n"), proxy); + if (cleaned != NULL) CFRelease(cleaned); + return; +} + + +__private_extern__ +void +do_showProxyConfiguration(int argc, char **argv) +{ + CFMutableDictionaryRef options = NULL; + CFDictionaryRef proxies; + + if (getenv("BYPASS_GLOBAL_PROXY") != NULL) { + options = CFDictionaryCreateMutable(NULL, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(options, kSCProxiesNoGlobal, kCFBooleanTrue); + } + + proxies = SCDynamicStoreCopyProxiesWithOptions(NULL, options); + + if (options != NULL) { + CFRelease(options); + } + + if (proxies != NULL) { + CFStringRef interface = NULL; + CFStringRef server = NULL; + + while (argc > 0) { + if (strcasecmp(argv[0], "interface") == 0) { + argv++; + argc--; + + if (argc < 1) { + SCPrint(TRUE, stderr, CFSTR("No interface\n")); + exit(1); + } + + if (if_nametoindex(argv[0]) == 0) { + SCPrint(TRUE, stderr, CFSTR("No interface: %s\n"), argv[0]); + exit(1); + } + + interface = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8); + argv++; + argc--; + } else { + if (server != NULL) { + CFRelease(server); + } + + server = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8); + argv++; + argc--; + } + } + + if ((server != NULL) || (interface != NULL)) { + CFArrayRef matching; + + matching = SCNetworkProxiesCopyMatching(proxies, server, interface); + if (matching != NULL) { + CFIndex i; + CFIndex n; + + if (server != NULL) { + if (interface != NULL) { + SCPrint(TRUE, stdout, + CFSTR("server = %@, interface = %@\n"), + server, + interface); + } else { + SCPrint(TRUE, stdout, + CFSTR("server = %@\n"), + server); + } + } else { + SCPrint(TRUE, stdout, + CFSTR("interface = %@\n"), + interface); + } + + n = CFArrayGetCount(matching); + for (i = 0; i < n; i++) { + CFDictionaryRef proxy; + + proxy = CFArrayGetValueAtIndex(matching, i); + SCPrint(TRUE, stdout, CFSTR("\nproxy #%ld\n"), i + 1); + showProxy(proxy); + } + + CFRelease(matching); + } else { + SCPrint(TRUE, stdout, CFSTR("No matching proxy configurations\n")); + } + } else { + showProxy(proxies); + } + + if (interface != NULL) CFRelease(interface); + if (server != NULL) CFRelease(server); + CFRelease(proxies); + } else { + SCPrint(TRUE, stdout, CFSTR("No proxy configuration available\n")); + } + + exit(0); +} + + +__private_extern__ void do_snapshot(int argc, char **argv) { if (!SCDynamicStoreSnapshot(store)) { SCPrint(TRUE, stdout, CFSTR(" %s\n"), SCErrorString(SCError())); } + return; } +__private_extern__ +void +do_renew(char *if_name) +{ + CFArrayRef services; + Boolean ok = FALSE; + + if ((if_name == NULL) || (strlen(if_name) == 0)) { + SCPrint(TRUE, stderr, CFSTR("No interface name\n")); + exit(1); + } + + if (getenv("ATTEMPT_DHCP_RENEW_WITH_SCDYNAMICSTORE") != NULL) { + CFArrayRef interfaces; + + interfaces = SCNetworkInterfaceCopyAll(); + if (interfaces != NULL) { + CFIndex i; + CFStringRef match_name; + CFIndex n; + + match_name = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingASCII); + assert(match_name != NULL); + + n = CFArrayGetCount(interfaces); + for (i = 0; i < n; i++) { + CFStringRef bsd_name; + SCNetworkInterfaceRef interface; + + interface = CFArrayGetValueAtIndex(interfaces, i); + bsd_name = SCNetworkInterfaceGetBSDName(interface); + if (_SC_CFEqual(bsd_name, match_name)) { + // if match + ok = SCNetworkInterfaceForceConfigurationRefresh(interface); + if (!ok) { + int status; + + status = SCError(); + if (status != kSCStatusAccessError) { + SCPrint(TRUE, stderr, CFSTR("%s\n"), SCErrorString(status)); + exit(1); + } + + // ... and if can't write the SCDynamicStore, try w/prefs + } + + break; + } + } + + CFRelease(match_name); + CFRelease(interfaces); + } + + if (ok) { + exit(0); + } + } + + do_prefs_init(); /* initialization */ + do_prefs_open(0, NULL); /* open default prefs */ + + services = SCNetworkServiceCopyAll(prefs); + if (services != NULL) { + CFIndex i; + CFStringRef match_name; + CFIndex n; + + match_name = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingASCII); + assert(match_name != NULL); + + n = CFArrayGetCount(services); + for (i = 0; i < n; i++) { + CFStringRef bsd_name; + SCNetworkInterfaceRef interface; + SCNetworkServiceRef service; + + service = CFArrayGetValueAtIndex(services, i); + interface = SCNetworkServiceGetInterface(service); + if (interface == NULL) { + // if no interface + continue; + } + + bsd_name = SCNetworkInterfaceGetBSDName(interface); + if (_SC_CFEqual(bsd_name, match_name)) { + // if match + ok = SCNetworkInterfaceForceConfigurationRefresh(interface); + if (!ok) { + SCPrint(TRUE, stderr, CFSTR("%s\n"), SCErrorString(SCError())); + exit(1); + } + + break; + } + } + + CFRelease(match_name); + CFRelease(services); + } + + if (!ok) { + SCPrint(TRUE, stderr, CFSTR("No interface\n")); + exit(1); + } + + _prefs_close(); + exit(0); +} + + static void waitKeyFound() { @@ -185,25 +1168,29 @@ waitTimeout(int sigraised) } +__private_extern__ void do_wait(char *waitKey, int timeout) { struct itimerval itv; CFStringRef key; CFMutableArrayRef keys; + Boolean ok; store = SCDynamicStoreCreate(NULL, CFSTR("scutil (wait)"), waitKeyFound, NULL); - if (!store) { + if (store == NULL) { SCPrint(TRUE, stderr, CFSTR("SCDynamicStoreCreate() failed: %s\n"), SCErrorString(SCError())); exit(1); } + key = CFStringCreateWithCString(NULL, waitKey, kCFStringEncodingUTF8); + keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - key = CFStringCreateWithCString(NULL, waitKey, kCFStringEncodingMacRoman); CFArrayAppendValue(keys, key); - - if (!SCDynamicStoreSetNotificationKeys(store, keys, NULL)) { + ok = SCDynamicStoreSetNotificationKeys(store, keys, NULL); + CFRelease(keys); + if (!ok) { SCPrint(TRUE, stderr, CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s\n"), SCErrorString(SCError())); exit(1); @@ -225,11 +1212,11 @@ do_wait(char *waitKey, int timeout) } CFRelease(key); - if (waitTimeout > 0) { + if (timeout > 0) { signal(SIGALRM, waitTimeout); bzero(&itv, sizeof(itv)); itv.it_value.tv_sec = timeout; - if (setitimer(ITIMER_REAL, &itv, NULL) < 0) { + if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { SCPrint(TRUE, stderr, CFSTR("setitimer() failed: %s\n"), strerror(errno)); exit(1); @@ -238,3 +1225,37 @@ do_wait(char *waitKey, int timeout) CFRunLoopRun(); } + +#ifdef TEST_DNS_CONFIGURATION + +Boolean doDispatch = FALSE; +CFRunLoopSourceRef notifyRls = NULL; +SCDynamicStoreRef store = NULL; +CFPropertyListRef value = NULL; + +int +main(int argc, char **argv) +{ + dns_config_t *dns_config; + +fprintf(stdout, "copy configuration\n"); + dns_config = dns_configuration_copy(); + if (dns_config != NULL) { + +fprintf(stdout, "sleeping for 120 seconds\n"); +sleep(120); + +fprintf(stdout, "sending ack\n"); + _dns_configuration_ack(dns_config, "TEST_DNS_CONFIGURATION"); + +fprintf(stdout, "sleeping for 120 seconds\n"); +sleep(120); + + dns_configuration_free(dns_config); + } + + do_showDNSConfiguration(argc, argv); + exit(0); +} + +#endif // TEST_DNS_CONFIGURATION