1/*
2 * Copyright (c) 2000, 2001, 2003-2014 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 * July 9, 2001			Allan Nathanson <ajn@apple.com>
28 * - added "-r" option for checking network reachability
29 * - added "-w" option to check/wait for the presence of a
30 *   dynamic store key.
31 *
32 * June 1, 2001			Allan Nathanson <ajn@apple.com>
33 * - public API conversion
34 *
35 * November 9, 2000		Allan Nathanson <ajn@apple.com>
36 * - initial revision
37 */
38
39#include "scutil.h"
40#include "prefs.h"
41#include "tests.h"
42
43#include <netdb.h>
44#include <netdb_async.h>
45#include <notify.h>
46#include <sys/time.h>
47#include <net/if.h>
48#include <netinet/in.h>
49#include <arpa/inet.h>
50
51#include <dnsinfo.h>
52#include "dnsinfo_internal.h"
53#include <network_information.h>
54#include "SCNetworkReachabilityInternal.h"
55#include <CommonCrypto/CommonDigest.h>
56
57
58static Boolean	resolver_bypass;
59
60
61static CF_RETURNS_RETAINED CFMutableDictionaryRef
62_setupReachabilityOptions(int argc, char **argv, const char *interface)
63{
64	int			i;
65	CFMutableDictionaryRef	options;
66
67	options = CFDictionaryCreateMutable(NULL,
68					    0,
69					    &kCFTypeDictionaryKeyCallBacks,
70					    &kCFTypeDictionaryValueCallBacks);
71
72	for (i = 0; i < argc; i++) {
73		if (strcasecmp(argv[i], "interface") == 0) {
74			if (++i >= argc) {
75				SCPrint(TRUE, stderr, CFSTR("No interface\n"));
76				CFRelease(options);
77				exit(1);
78			}
79
80			interface = argv[i];
81			continue;
82		}
83
84
85		if (strcasecmp(argv[i], "server") == 0) {
86			CFDictionarySetValue(options,
87					     kSCNetworkReachabilityOptionServerBypass,
88					     kCFBooleanFalse);
89			continue;
90		} else if (strcasecmp(argv[i], "no-server") == 0) {
91			CFDictionarySetValue(options,
92					     kSCNetworkReachabilityOptionServerBypass,
93					     kCFBooleanTrue);
94			continue;
95		}
96
97
98		if (strcasecmp(argv[i], "no-connection-on-demand") == 0) {
99			CFDictionarySetValue(options,
100					     kSCNetworkReachabilityOptionConnectionOnDemandBypass,
101					     kCFBooleanTrue);
102			continue;
103		}
104
105		if (strcasecmp(argv[i], "no-resolve") == 0) {
106			CFDictionarySetValue(options,
107					     kSCNetworkReachabilityOptionResolverBypass,
108					     kCFBooleanTrue);
109			resolver_bypass = TRUE;
110			continue;
111		}
112
113		if (strcasecmp(argv[i], "ptr") == 0) {
114			CFDictionarySetValue(options,
115					     kSCNetworkReachabilityOptionPTRAddress,
116					     kCFBooleanTrue);
117			continue;
118		}
119
120		if (strlen(argv[i]) == 0) {
121			continue;
122		}
123
124		SCPrint(TRUE, stderr, CFSTR("Unrecognized option: %s\n"), argv[i]);
125		CFRelease(options);
126		exit(1);
127	}
128
129	if (interface != NULL) {
130		CFStringRef	str;
131
132		if (if_nametoindex(interface) == 0) {
133			SCPrint(TRUE, stderr, CFSTR("No interface: %s\n"), interface);
134			exit(1);
135		}
136
137		str  = CFStringCreateWithCString(NULL, interface, kCFStringEncodingASCII);
138		CFDictionarySetValue(options, kSCNetworkReachabilityOptionInterface, str);
139		CFRelease(str);
140	}
141
142	if (CFDictionaryGetCount(options) == 0) {
143		CFRelease(options);
144		options = NULL;
145	}
146
147	return options;
148}
149
150
151static SCNetworkReachabilityRef
152_setupReachability(int argc, char **argv, SCNetworkReachabilityContext *context)
153{
154	const char			*ip_address	= argv[0];
155	char				*ip_addressN	= NULL;
156	const char			*interface;
157	CFMutableDictionaryRef		options		= NULL;
158	const char			*remote_address	= NULL;
159	char				*remote_addressN= NULL;
160	const char			*remote_interface;
161	struct sockaddr_in		sin;
162	struct sockaddr_in6		sin6;
163	SCNetworkReachabilityRef	target		= NULL;
164
165	bzero(&sin, sizeof(sin));
166	sin.sin_len    = sizeof(sin);
167	sin.sin_family = AF_INET;
168
169	bzero(&sin6, sizeof(sin6));
170	sin6.sin6_len    = sizeof(sin6);
171	sin6.sin6_family = AF_INET6;
172
173	interface = strchr(ip_address, '%');
174	if (interface != NULL) {
175		ip_addressN = strdup(ip_address);
176		ip_addressN[interface - ip_address] = '\0';
177		ip_address = ip_addressN;
178		interface++;
179	}
180
181	if ((argc > 1) && (strlen(argv[1]) > 0)) {
182		remote_address = argv[1];
183
184		remote_interface = strchr(remote_address, '%');
185		if (remote_interface != NULL) {
186			remote_addressN = strdup(remote_address);
187			remote_addressN[remote_interface - remote_address] = '\0';
188			remote_address = remote_addressN;
189			remote_interface++;
190		}
191	}
192
193	if (inet_aton(ip_address, &sin.sin_addr) == 1) {
194		struct sockaddr_in	r_sin;
195
196		if (argc > 1) {
197			bzero(&r_sin, sizeof(r_sin));
198			r_sin.sin_len    = sizeof(r_sin);
199			r_sin.sin_family = AF_INET;
200		}
201
202		if ((argc == 1)
203		    || (remote_address == NULL)
204		    || (inet_aton(remote_address, &r_sin.sin_addr) == 0)) {
205			if (argc > 2) {
206				options = _setupReachabilityOptions(argc - 2, argv + 2, interface);
207			}
208			if (options == NULL) {
209				target = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&sin);
210				if (context != NULL) {
211					context->info = "by address";
212				}
213			} else {
214				CFDataRef	data;
215
216				data = CFDataCreate(NULL, (const UInt8 *)&sin, sizeof(sin));
217				CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data);
218				CFRelease(data);
219
220				if (context != NULL) {
221					if (CFDictionaryContainsKey(options,
222								    kSCNetworkReachabilityOptionInterface)) {
223						if (CFDictionaryGetCount(options) == 2) {
224							context->info = "by address w/scope";
225						} else {
226							context->info = "by address w/scope and options";
227						}
228					} else {
229						context->info = "by address w/options";
230					}
231				}
232			}
233		} else {
234			if (remote_interface != NULL) {
235				if ((interface != NULL) && (strcmp(interface, remote_interface) != 0)) {
236					SCPrint(TRUE, stderr,
237						CFSTR("Interface mismatch \"%s\" != \"%s\"\n"),
238						interface,
239						remote_interface);
240					exit(1);
241				}
242
243				interface = remote_interface;
244			}
245
246			options = _setupReachabilityOptions(argc - 2, argv + 2, interface);
247			if (options == NULL) {
248				target = SCNetworkReachabilityCreateWithAddressPair(NULL,
249										    (struct sockaddr *)&sin,
250										    (struct sockaddr *)&r_sin);
251				if (context != NULL) {
252					context->info = "by address pair";
253				}
254			} else {
255				CFDataRef	data;
256
257				data = CFDataCreate(NULL, (const UInt8 *)&sin, sizeof(sin));
258				CFDictionarySetValue(options, kSCNetworkReachabilityOptionLocalAddress, data);
259				CFRelease(data);
260				data = CFDataCreate(NULL, (const UInt8 *)&r_sin, sizeof(r_sin));
261				CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data);
262				CFRelease(data);
263
264				if (context != NULL) {
265					if (CFDictionaryContainsKey(options,
266								    kSCNetworkReachabilityOptionInterface)) {
267						if (CFDictionaryGetCount(options) == 3) {
268							context->info = "by address pair w/scope";
269						} else {
270							context->info = "by address pair w/scope and options";
271						}
272					} else {
273						context->info = "by address pair w/options";
274					}
275				}
276			}
277		}
278	} else if (inet_pton(AF_INET6, ip_address, &sin6.sin6_addr) == 1) {
279		struct sockaddr_in6	r_sin6;
280
281		if (interface != NULL) {
282			sin6.sin6_scope_id = if_nametoindex(interface);
283		}
284
285		if (argc > 1) {
286			bzero(&r_sin6, sizeof(r_sin6));
287			r_sin6.sin6_len    = sizeof(r_sin6);
288			r_sin6.sin6_family = AF_INET6;
289		}
290
291		if ((argc == 1)
292		    || (remote_address == NULL)
293		    || (inet_pton(AF_INET6, remote_address, &r_sin6.sin6_addr) == 0)) {
294			if (argc > 2) {
295				options = _setupReachabilityOptions(argc - 2, argv + 2, NULL);
296			}
297			if (options == NULL) {
298				target = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&sin6);
299				if (context != NULL) {
300					context->info = "by (v6) address";
301				}
302			} else {
303				CFDataRef	data;
304
305				data = CFDataCreate(NULL, (const UInt8 *)&sin6, sizeof(sin6));
306				CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data);
307				CFRelease(data);
308
309				if (context != NULL) {
310					context->info = "by (v6) address w/options";
311				}
312			}
313		} else {
314			if (remote_interface != NULL) {
315				r_sin6.sin6_scope_id = if_nametoindex(remote_interface);
316
317				if ((interface != NULL) && (strcmp(interface, remote_interface) != 0)) {
318					SCPrint(TRUE, stderr,
319						CFSTR("Interface mismatch \"%s\" != \"%s\"\n"),
320						interface,
321						remote_interface);
322					exit(1);
323				}
324			}
325
326			options = _setupReachabilityOptions(argc - 2, argv + 2, NULL);
327			if (options == NULL) {
328				target = SCNetworkReachabilityCreateWithAddressPair(NULL,
329										    (struct sockaddr *)&sin6,
330										    (struct sockaddr *)&r_sin6);
331				if (context != NULL) {
332					context->info = "by (v6) address pair";
333				}
334			} else {
335				CFDataRef	data;
336
337				data = CFDataCreate(NULL, (const UInt8 *)&sin6, sizeof(sin6));
338				CFDictionarySetValue(options, kSCNetworkReachabilityOptionLocalAddress, data);
339				CFRelease(data);
340				data = CFDataCreate(NULL, (const UInt8 *)&r_sin6, sizeof(r_sin6));
341				CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, data);
342				CFRelease(data);
343
344				if (context != NULL) {
345					context->info = "by (v6) address pair w/options";
346				}
347			}
348		}
349	} else {
350		if (argc == 1) {
351			target = SCNetworkReachabilityCreateWithName(NULL, argv[0]);
352			if (context != NULL) {
353				context->info = "by name";
354			}
355		} else {
356			options = _setupReachabilityOptions(argc - 1, argv + 1, NULL);
357			if (options == NULL) {
358				target = SCNetworkReachabilityCreateWithName(NULL, argv[0]);
359				if (context != NULL) {
360					context->info = "by name";
361				}
362			} else {
363				CFStringRef	str;
364
365				str  = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
366				CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, str);
367				CFRelease(str);
368
369				if (context != NULL) {
370					context->info = "by name w/options";
371				}
372			}
373		}
374	}
375
376	if (ip_addressN != NULL) {
377		free(ip_addressN);
378	}
379
380	if (remote_addressN != NULL) {
381		free(remote_addressN);
382	}
383
384	if ((target == NULL) && (options != NULL)) {
385		if (CFDictionaryContainsKey(options, kSCNetworkReachabilityOptionPTRAddress)) {
386			CFDataRef	address;
387
388			address = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionRemoteAddress);
389			if (address == NULL) {
390				SCPrint(TRUE, stderr, CFSTR("No address\n"));
391				exit(1);
392			}
393			CFDictionarySetValue(options, kSCNetworkReachabilityOptionPTRAddress, address);
394			CFDictionaryRemoveValue(options, kSCNetworkReachabilityOptionRemoteAddress);
395
396			if (context != NULL) {
397				CFIndex	n	= CFDictionaryGetCount(options);
398
399				if (n == 1) {
400					context->info = "by PTR";
401				} else if (CFDictionaryContainsKey(options,
402								   kSCNetworkReachabilityOptionInterface)) {
403					if (n == 2) {
404						context->info = "by PTR w/scope";
405					} else {
406						context->info = "by PTR w/scope and options";
407					}
408				} else {
409					context->info = "by PTR w/options";
410				}
411			}
412		}
413
414		target = SCNetworkReachabilityCreateWithOptions(NULL, options);
415		CFRelease(options);
416	}
417
418	return target;
419}
420
421
422static void
423_printReachability(SCNetworkReachabilityRef target)
424{
425	SCNetworkReachabilityFlags	flags;
426	Boolean				ok;
427
428	ok = SCNetworkReachabilityGetFlags(target, &flags);
429	if (!ok) {
430		SCPrint(TRUE, stderr, CFSTR("    could not determine reachability, %s\n"), SCErrorString(SCError()));
431		return;
432	}
433
434	SCPrint(_sc_debug, stdout, CFSTR("flags = 0x%08x ("), flags);
435	__SCNetworkReachabilityPrintFlags(flags);
436	SCPrint(_sc_debug, stdout, CFSTR(")"));
437	SCPrint(TRUE, stdout, CFSTR("\n"));
438
439	if (resolver_bypass) {
440		int	if_index;
441
442		if_index = SCNetworkReachabilityGetInterfaceIndex(target);
443		SCPrint(_sc_debug, stdout, CFSTR("interface index = %d\n"), if_index);
444	}
445
446	return;
447}
448
449
450__private_extern__
451void
452do_checkReachability(int argc, char **argv)
453{
454	SCNetworkReachabilityRef	target;
455
456	target = _setupReachability(argc, argv, NULL);
457	if (target == NULL) {
458		SCPrint(TRUE, stderr, CFSTR("  Could not determine status: %s\n"), SCErrorString(SCError()));
459		exit(1);
460	}
461
462	_printReachability(target);
463	CFRelease(target);
464	exit(0);
465}
466
467
468static void
469_printNWIFlags(nwi_ifstate_flags flags)
470{
471	if (flags == 0) {
472		return;
473	}
474
475	SCPrint(TRUE, stdout, CFSTR(" ("));
476	if (flags & NWI_IFSTATE_FLAGS_HAS_IPV4) {
477		SCPrint(TRUE, stdout, CFSTR("IPv4"));
478		flags &= ~NWI_IFSTATE_FLAGS_HAS_IPV4;
479		SCPrint(flags != 0, stdout, CFSTR(","));
480	}
481	if (flags & NWI_IFSTATE_FLAGS_HAS_IPV6) {
482		SCPrint(TRUE, stdout, CFSTR("IPv6"));
483		flags &= ~NWI_IFSTATE_FLAGS_HAS_IPV6;
484		SCPrint(flags != 0, stdout, CFSTR(","));
485	}
486	if (flags & NWI_IFSTATE_FLAGS_HAS_DNS) {
487		SCPrint(TRUE, stdout, CFSTR("DNS"));
488		flags &= ~NWI_IFSTATE_FLAGS_HAS_DNS;
489		SCPrint(flags != 0, stdout, CFSTR(","));
490	}
491	if (flags != 0) {
492		SCPrint(TRUE, stdout, CFSTR("%p"), (void *)flags);
493	}
494	SCPrint(TRUE, stdout, CFSTR(")"));
495
496	return;
497}
498
499
500static void
501_printNWIInfo(nwi_ifstate_t ifstate)
502{
503	nwi_ifstate_flags		ifstate_flags = nwi_ifstate_get_flags(ifstate);
504	SCNetworkReachabilityFlags	reach_flags = nwi_ifstate_get_reachability_flags(ifstate);
505	const uint8_t			*signature;
506	int				signature_length;
507	const struct sockaddr		*vpn_addr = nwi_ifstate_get_vpn_server(ifstate);
508
509	SCPrint(TRUE, stdout,
510		CFSTR(" %7s : flags %p"),
511		nwi_ifstate_get_ifname(ifstate),
512		(void *)ifstate_flags);
513	_printNWIFlags(ifstate_flags);
514
515	SCPrint(TRUE, stdout, CFSTR("\n           reach 0x%08x ("), reach_flags);
516	__SCNetworkReachabilityPrintFlags(reach_flags);
517	SCPrint(TRUE, stdout, CFSTR(")"));
518
519	if (vpn_addr != NULL) {
520		char vpn_ntopbuf[INET6_ADDRSTRLEN];
521
522		_SC_sockaddr_to_string(vpn_addr, vpn_ntopbuf, sizeof(vpn_ntopbuf));
523		SCPrint(TRUE, stdout, CFSTR("\n           VPN server: %s"), vpn_ntopbuf);
524	}
525
526	signature = nwi_ifstate_get_signature(ifstate, AF_UNSPEC, &signature_length);
527	if (signature != NULL) {
528		CFDataRef	digest	= NULL;
529
530		digest = CFDataCreate(NULL, signature, CC_SHA1_DIGEST_LENGTH);
531		SCPrint(TRUE, stdout, CFSTR("\n           Signature Hash: %@"), digest);
532		CFRelease(digest);
533	} else {
534		SCPrint(TRUE, stdout, CFSTR("\n           Signature Hash: <empty>"));
535	}
536
537	SCPrint(TRUE, stdout, CFSTR("\n           generation %llu\n"),
538		nwi_ifstate_get_generation(ifstate));
539
540	return;
541}
542
543
544static void
545_printNWIReachInfo(nwi_state_t state, int af)
546{
547	uint32_t	reach_flags;
548
549	reach_flags = nwi_state_get_reachability_flags(state, af);
550	SCPrint(TRUE, stdout, CFSTR("\n   REACH : flags 0x%08x ("), reach_flags);
551	__SCNetworkReachabilityPrintFlags(reach_flags);
552	SCPrint(TRUE, stdout, CFSTR(")\n"));
553
554	return;
555}
556
557
558static void
559do_printNWI(int argc, char **argv, nwi_state_t state)
560{
561	nwi_ifstate_t	ifstate;
562
563	if (state == NULL) {
564		SCPrint(TRUE, stdout, CFSTR("No network information\n"));
565		return;
566	}
567
568	if (argc > 0) {
569		ifstate = nwi_state_get_ifstate(state, argv[0]);
570		if (ifstate != NULL) {
571			_printNWIInfo(ifstate);
572		} else {
573			SCPrint(TRUE, stdout, CFSTR("No network information (for %s)\n"), argv[0]);
574		}
575		return;
576	}
577
578	SCPrint(TRUE, stdout, CFSTR("Network information (generation %llu)"),
579		nwi_state_get_generation(state));
580
581	SCPrint(TRUE, stdout, CFSTR("\nIPv4 network interface information\n"));
582
583	ifstate = nwi_state_get_first_ifstate(state, AF_INET);
584	if (ifstate == NULL) {
585		SCPrint(TRUE, stdout, CFSTR("   No IPv4 states found\n"));
586	} else {
587		while (ifstate != NULL) {
588			_printNWIInfo(ifstate);
589			ifstate = nwi_ifstate_get_next(ifstate, AF_INET);
590		}
591	}
592	_printNWIReachInfo(state, AF_INET);
593
594	SCPrint(TRUE, stdout, CFSTR("\nIPv6 network interface information\n"));
595
596	ifstate = nwi_state_get_first_ifstate(state, AF_INET6);
597	if (ifstate == NULL) {
598		SCPrint(TRUE, stdout, CFSTR("   No IPv6 states found\n"));
599	} else {
600		while (ifstate != NULL) {
601			_printNWIInfo(ifstate);
602			ifstate = nwi_ifstate_get_next(ifstate, AF_INET6);
603		}
604	}
605	_printNWIReachInfo(state, AF_INET6);
606
607	return;
608}
609
610
611__private_extern__
612void
613do_showNWI(int argc, char **argv)
614{
615	nwi_state_t	state;
616
617	state = nwi_state_copy();
618	do_printNWI(argc, argv, state);
619	if (state != NULL) {
620		nwi_state_release(state);
621	} else {
622		exit(1);
623	}
624
625	exit(0);
626}
627
628
629__private_extern__
630void
631do_watchNWI(int argc, char **argv)
632{
633	nwi_state_t	state;
634	int		status;
635	int		token;
636
637	state = nwi_state_copy();
638	do_printNWI(argc, argv, state);
639	if (state != NULL) {
640		nwi_state_release(state);
641	}
642
643	status = notify_register_dispatch(nwi_state_get_notify_key(),
644					  &token,
645					  dispatch_get_main_queue(),
646					  ^(int token){
647						  nwi_state_t		state;
648						  struct tm		tm_now;
649						  struct timeval	tv_now;
650
651						  (void)gettimeofday(&tv_now, NULL);
652						  (void)localtime_r(&tv_now.tv_sec, &tm_now);
653						  SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
654							  tm_now.tm_hour,
655							  tm_now.tm_min,
656							  tm_now.tm_sec,
657							  tv_now.tv_usec / 1000);
658
659						  state = nwi_state_copy();
660						  do_printNWI(argc, argv, state);
661						  if (state != NULL) {
662							  nwi_state_release(state);
663						  }
664					  });
665	if (status != NOTIFY_STATUS_OK) {
666		SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed for nwi changes, status=%u"), status);
667		exit(1);
668	}
669
670	CFRunLoopRun();
671	exit(0);
672}
673
674
675static void
676callout(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
677{
678	static int	n = 3;
679	struct tm	tm_now;
680	struct timeval	tv_now;
681
682	(void)gettimeofday(&tv_now, NULL);
683	(void)localtime_r(&tv_now.tv_sec, &tm_now);
684
685	SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
686		tm_now.tm_hour,
687		tm_now.tm_min,
688		tm_now.tm_sec,
689		tv_now.tv_usec / 1000);
690	SCPrint(TRUE, stdout, CFSTR("%2d: callback w/flags=0x%08x (info=\"%s\")\n"), n++, flags, (char *)info);
691	SCPrint(TRUE, stdout, CFSTR("    %@\n"), target);
692	_printReachability(target);
693	SCPrint(TRUE, stdout, CFSTR("\n"));
694	return;
695}
696
697
698__private_extern__
699void
700do_watchReachability(int argc, char **argv)
701{
702	SCNetworkReachabilityContext	context	= { 0, NULL, NULL, NULL, NULL };
703	SCNetworkReachabilityRef	target;
704	SCNetworkReachabilityRef	target_async;
705
706	target = _setupReachability(argc, argv, NULL);
707	if (target == NULL) {
708		SCPrint(TRUE, stderr, CFSTR("  Could not determine status: %s\n"), SCErrorString(SCError()));
709		exit(1);
710	}
711
712	target_async = _setupReachability(argc, argv, &context);
713	if (target_async == NULL) {
714		SCPrint(TRUE, stderr, CFSTR("  Could not determine status: %s\n"), SCErrorString(SCError()));
715		exit(1);
716	}
717
718	// Normally, we don't want to make any calls to SCNetworkReachabilityGetFlags()
719	// until after the "target" has been scheduled on a run loop.  Otherwise, we'll
720	// end up making a synchronous DNS request and that's not what we want.
721	//
722	// To test the case were an application first calls SCNetworkReachabilityGetFlags()
723	// we provide the "CHECK_REACHABILITY_BEFORE_SCHEDULING" environment variable.
724	if (getenv("CHECK_REACHABILITY_BEFORE_SCHEDULING") != NULL) {
725		CFRelease(target_async);
726		target_async = CFRetain(target);
727	}
728
729	// Direct check of reachability
730	SCPrint(TRUE, stdout, CFSTR(" 0: direct\n"));
731	SCPrint(TRUE, stdout, CFSTR("   %@\n"), target);
732	_printReachability(target);
733	CFRelease(target);
734	SCPrint(TRUE, stdout, CFSTR("\n"));
735
736	// schedule the target
737	SCPrint(TRUE, stdout, CFSTR(" 1: start\n"));
738	SCPrint(TRUE, stdout, CFSTR("   %@\n"), target_async);
739//	_printReachability(target_async);
740	SCPrint(TRUE, stdout, CFSTR("\n"));
741
742	if (!SCNetworkReachabilitySetCallback(target_async, callout, &context)) {
743		printf("SCNetworkReachabilitySetCallback() failed: %s\n", SCErrorString(SCError()));
744		exit(1);
745	}
746
747	if (doDispatch) {
748		if (!SCNetworkReachabilitySetDispatchQueue(target_async, dispatch_get_main_queue())) {
749			printf("SCNetworkReachabilitySetDispatchQueue() failed: %s\n", SCErrorString(SCError()));
750			exit(1);
751		}
752	} else {
753		if (!SCNetworkReachabilityScheduleWithRunLoop(target_async, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
754			printf("SCNetworkReachabilityScheduleWithRunLoop() failed: %s\n", SCErrorString(SCError()));
755			exit(1);
756		}
757	}
758
759	// Note: now that we are scheduled on a run loop we can call SCNetworkReachabilityGetFlags()
760	//       to get the current status.  For "names", a DNS lookup has already been initiated.
761	SCPrint(TRUE, stdout, CFSTR(" 2: on %s\n"), doDispatch ? "dispatch queue" : "runloop");
762	SCPrint(TRUE, stdout, CFSTR("   %@\n"), target_async);
763	_printReachability(target_async);
764	SCPrint(TRUE, stdout, CFSTR("\n"));
765
766	CFRunLoopRun();
767	exit(0);
768}
769
770
771static void
772do_printDNSConfiguration(int argc, char **argv, dns_config_t *dns_config)
773{
774	SCNetworkReachabilityRef	target;
775
776	if (dns_config == NULL) {
777		SCPrint(TRUE, stdout, CFSTR("No DNS configuration available\n"));
778		return;
779	}
780
781	if (argc > 1) {
782		int				dns_config_index = -1;
783		SCNetworkReachabilityFlags	flags = 0;
784		Boolean				haveDNS = FALSE;
785		Boolean				ok = FALSE;
786		dns_resolver_t			*resolver;
787		uint32_t			resolver_if_index;
788		SCNetworkReachabilityPrivateRef targetPrivate;
789
790		target = _setupReachability(argc, argv, NULL);
791
792		targetPrivate = (SCNetworkReachabilityPrivateRef)target;
793
794		if (targetPrivate->type != reachabilityTypeName) {
795			SCPrint(TRUE, stdout, CFSTR("\"%s\" is not a hostname.\n"), argv[0]);
796			exit(1);
797		}
798
799		ok = __SC_checkResolverReachabilityInternal(&store, &flags,
800							    &haveDNS, targetPrivate->name,
801							    &resolver_if_index, &dns_config_index);
802
803		if (!ok) {
804			SCPrint(TRUE, stdout, CFSTR("No DNS configuration available.\n" ));
805			return;
806		}
807
808		SCPrint(TRUE, stdout, CFSTR("DNS configuration for %s\n"),
809			targetPrivate->name);
810
811		if (targetPrivate->if_index == 0) {
812			resolver = dns_config->resolver[dns_config_index];
813		} else {
814			resolver = dns_config->scoped_resolver[dns_config_index];
815		}
816
817		_dns_resolver_print(resolver, dns_config_index + 1);
818
819		if (target != NULL) CFRelease(target);
820	} else {
821		_dns_configuration_print(dns_config);
822	}
823
824	if (_sc_debug) {
825		SCPrint(TRUE, stdout, CFSTR("\ngeneration = %llu\n"), dns_config->generation);
826	}
827
828	return;
829}
830
831
832__private_extern__
833void
834do_showDNSConfiguration(int argc, char **argv)
835{
836	dns_config_t	*dns_config;
837
838	dns_config = dns_configuration_copy();
839	do_printDNSConfiguration(argc, argv, dns_config);
840	if (dns_config != NULL) {
841		dns_configuration_free(dns_config);
842	} else {
843		exit(1);
844	}
845
846	exit(0);
847}
848
849
850__private_extern__
851void
852do_watchDNSConfiguration(int argc, char **argv)
853{
854	dns_config_t	*dns_config;
855	int		status;
856	int		token;
857
858	dns_config = dns_configuration_copy();
859	do_printDNSConfiguration(argc, argv, dns_config);
860	if (dns_config != NULL) {
861		dns_configuration_free(dns_config);
862	}
863
864	status = notify_register_dispatch(dns_configuration_notify_key(),
865					  &token,
866					  dispatch_get_main_queue(),
867					  ^(int token){
868						  dns_config_t		*dns_config;
869						  struct tm		tm_now;
870						  struct timeval	tv_now;
871
872						  (void)gettimeofday(&tv_now, NULL);
873						  (void)localtime_r(&tv_now.tv_sec, &tm_now);
874						  SCPrint(TRUE, stdout, CFSTR("\n*** %2d:%02d:%02d.%03d\n\n"),
875							  tm_now.tm_hour,
876							  tm_now.tm_min,
877							  tm_now.tm_sec,
878							  tv_now.tv_usec / 1000);
879
880						  dns_config = dns_configuration_copy();
881						  do_printDNSConfiguration(argc, argv, dns_config);
882						  if (dns_config != NULL) {
883							  dns_configuration_free(dns_config);
884						  }
885					  });
886	if (status != NOTIFY_STATUS_OK) {
887		SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed for DNS configuration changes, status=%u"), status);
888		exit(1);
889	}
890
891	CFRunLoopRun();
892	exit(0);
893}
894
895
896static void
897showProxy(CFDictionaryRef proxy)
898{
899	CFMutableDictionaryRef	cleaned	= NULL;
900
901	if (!_sc_debug) {
902		cleaned = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
903		CFDictionaryRemoveValue(cleaned, kSCPropNetProxiesScoped);
904		CFDictionaryRemoveValue(cleaned, kSCPropNetProxiesServices);
905		CFDictionaryRemoveValue(cleaned, kSCPropNetProxiesSupplemental);
906		proxy = cleaned;
907	}
908
909	SCPrint(TRUE, stdout, CFSTR("%@\n"), proxy);
910	if (cleaned != NULL) CFRelease(cleaned);
911	return;
912}
913
914
915__private_extern__
916void
917do_showProxyConfiguration(int argc, char **argv)
918{
919	CFMutableDictionaryRef	options = NULL;
920	CFDictionaryRef		proxies;
921
922	if (getenv("BYPASS_GLOBAL_PROXY") != NULL) {
923		options = CFDictionaryCreateMutable(NULL, 0,
924						    &kCFTypeDictionaryKeyCallBacks,
925						    &kCFTypeDictionaryValueCallBacks);
926		CFDictionaryAddValue(options, kSCProxiesNoGlobal, kCFBooleanTrue);
927	}
928
929	proxies = SCDynamicStoreCopyProxiesWithOptions(NULL, options);
930
931	if (options != NULL) {
932		CFRelease(options);
933	}
934
935	if (proxies != NULL) {
936		CFStringRef	interface	= NULL;
937		CFStringRef	server		= NULL;
938
939		while (argc > 0) {
940			if (strcasecmp(argv[0], "interface") == 0) {
941				argv++;
942				argc--;
943
944				if (argc < 1) {
945					SCPrint(TRUE, stderr, CFSTR("No interface\n"));
946					exit(1);
947				}
948
949				if (if_nametoindex(argv[0]) == 0) {
950					SCPrint(TRUE, stderr, CFSTR("No interface: %s\n"), argv[0]);
951					exit(1);
952				}
953
954				interface = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
955				argv++;
956				argc--;
957			} else {
958				if (server != NULL) {
959					CFRelease(server);
960				}
961
962				server = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
963				argv++;
964				argc--;
965			}
966		}
967
968		if ((server != NULL) || (interface != NULL)) {
969			CFArrayRef	matching;
970
971			matching = SCNetworkProxiesCopyMatching(proxies, server, interface);
972			if (matching != NULL) {
973				CFIndex	i;
974				CFIndex	n;
975
976				if (server != NULL) {
977					if (interface != NULL) {
978						SCPrint(TRUE, stdout,
979							CFSTR("server = %@, interface = %@\n"),
980							server,
981							interface);
982					} else {
983						SCPrint(TRUE, stdout,
984							CFSTR("server = %@\n"),
985							server);
986					}
987				} else {
988					SCPrint(TRUE, stdout,
989						CFSTR("interface = %@\n"),
990						interface);
991				}
992
993				n = CFArrayGetCount(matching);
994				for (i = 0; i < n; i++) {
995					CFDictionaryRef	proxy;
996
997					proxy = CFArrayGetValueAtIndex(matching, i);
998					SCPrint(TRUE, stdout, CFSTR("\nproxy #%ld\n"), i + 1);
999					showProxy(proxy);
1000				}
1001
1002				CFRelease(matching);
1003			} else {
1004				SCPrint(TRUE, stdout, CFSTR("No matching proxy configurations\n"));
1005			}
1006		} else {
1007			showProxy(proxies);
1008		}
1009
1010		if (interface != NULL) CFRelease(interface);
1011		if (server != NULL) CFRelease(server);
1012		CFRelease(proxies);
1013	} else {
1014		SCPrint(TRUE, stdout, CFSTR("No proxy configuration available\n"));
1015	}
1016
1017	exit(0);
1018}
1019
1020
1021__private_extern__
1022void
1023do_snapshot(int argc, char **argv)
1024{
1025	if (!SCDynamicStoreSnapshot(store)) {
1026		SCPrint(TRUE, stdout, CFSTR("  %s\n"), SCErrorString(SCError()));
1027	}
1028
1029	(void) _SCNetworkReachabilityServer_snapshot();
1030
1031	return;
1032}
1033
1034
1035__private_extern__
1036void
1037do_renew(char *if_name)
1038{
1039	CFArrayRef	services;
1040	Boolean		ok	= FALSE;
1041
1042	if ((if_name == NULL) || (strlen(if_name) == 0)) {
1043		SCPrint(TRUE, stderr, CFSTR("No interface name\n"));
1044		exit(1);
1045	}
1046
1047	if (getenv("ATTEMPT_DHCP_RENEW_WITH_SCDYNAMICSTORE") != NULL) {
1048		CFArrayRef	interfaces;
1049
1050		interfaces = SCNetworkInterfaceCopyAll();
1051		if (interfaces != NULL) {
1052			CFIndex		i;
1053			CFStringRef	match_name;
1054			CFIndex		n;
1055
1056			match_name = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingASCII);
1057			assert(match_name != NULL);
1058
1059			n = CFArrayGetCount(interfaces);
1060			for (i = 0; i < n; i++) {
1061				CFStringRef		bsd_name;
1062				SCNetworkInterfaceRef	interface;
1063
1064				interface = CFArrayGetValueAtIndex(interfaces, i);
1065				bsd_name = SCNetworkInterfaceGetBSDName(interface);
1066				if (_SC_CFEqual(bsd_name, match_name)) {
1067					// if match
1068					ok = SCNetworkInterfaceForceConfigurationRefresh(interface);
1069					if (!ok) {
1070						int	status;
1071
1072						status = SCError();
1073						if (status != kSCStatusAccessError) {
1074							SCPrint(TRUE, stderr, CFSTR("%s\n"), SCErrorString(status));
1075							exit(1);
1076						}
1077
1078						// ... and if can't write the SCDynamicStore, try w/prefs
1079					}
1080
1081					break;
1082				}
1083			}
1084
1085			CFRelease(match_name);
1086			CFRelease(interfaces);
1087		}
1088
1089		if (ok) {
1090			exit(0);
1091		}
1092	}
1093
1094	do_prefs_init();	/* initialization */
1095	do_prefs_open(0, NULL);	/* open default prefs */
1096
1097	services = SCNetworkServiceCopyAll(prefs);
1098	if (services != NULL) {
1099		CFIndex		i;
1100		CFStringRef	match_name;
1101		CFIndex		n;
1102
1103		match_name = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingASCII);
1104		assert(match_name != NULL);
1105
1106		n = CFArrayGetCount(services);
1107		for (i = 0; i < n; i++) {
1108			CFStringRef		bsd_name;
1109			SCNetworkInterfaceRef	interface;
1110			SCNetworkServiceRef	service;
1111
1112			service = CFArrayGetValueAtIndex(services, i);
1113			interface = SCNetworkServiceGetInterface(service);
1114			if (interface == NULL) {
1115				// if no interface
1116				continue;
1117			}
1118
1119			bsd_name = SCNetworkInterfaceGetBSDName(interface);
1120			if (_SC_CFEqual(bsd_name, match_name)) {
1121				// if match
1122				ok = SCNetworkInterfaceForceConfigurationRefresh(interface);
1123				if (!ok) {
1124					SCPrint(TRUE, stderr, CFSTR("%s\n"), SCErrorString(SCError()));
1125					exit(1);
1126				}
1127
1128				break;
1129			}
1130		}
1131
1132		CFRelease(match_name);
1133		CFRelease(services);
1134	}
1135
1136	if (!ok) {
1137		SCPrint(TRUE, stderr, CFSTR("No interface\n"));
1138		exit(1);
1139	}
1140
1141	_prefs_close();
1142	exit(0);
1143}
1144
1145
1146static void
1147waitKeyFound()
1148{
1149	exit(0);
1150}
1151
1152
1153static void
1154waitTimeout(int sigraised)
1155{
1156	exit(1);
1157}
1158
1159
1160__private_extern__
1161void
1162do_wait(char *waitKey, int timeout)
1163{
1164	struct itimerval	itv;
1165	CFStringRef		key;
1166	CFMutableArrayRef	keys;
1167	Boolean			ok;
1168
1169	store = SCDynamicStoreCreate(NULL, CFSTR("scutil (wait)"), waitKeyFound, NULL);
1170	if (store == NULL) {
1171		SCPrint(TRUE, stderr,
1172			CFSTR("SCDynamicStoreCreate() failed: %s\n"), SCErrorString(SCError()));
1173		exit(1);
1174	}
1175
1176	key  = CFStringCreateWithCString(NULL, waitKey, kCFStringEncodingUTF8);
1177
1178	keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1179	CFArrayAppendValue(keys, key);
1180	ok = SCDynamicStoreSetNotificationKeys(store, keys, NULL);
1181	CFRelease(keys);
1182	if (!ok) {
1183		SCPrint(TRUE, stderr,
1184			CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s\n"), SCErrorString(SCError()));
1185		exit(1);
1186	}
1187
1188	notifyRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
1189	if (!notifyRls) {
1190		SCPrint(TRUE, stderr,
1191			CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s\n"), SCErrorString(SCError()));
1192		exit(1);
1193	}
1194
1195	CFRunLoopAddSource(CFRunLoopGetCurrent(), notifyRls, kCFRunLoopDefaultMode);
1196
1197	value = SCDynamicStoreCopyValue(store, key);
1198	if (value) {
1199		/* if the key is already present */
1200		exit(0);
1201	}
1202	CFRelease(key);
1203
1204	if (timeout > 0) {
1205		signal(SIGALRM, waitTimeout);
1206		bzero(&itv, sizeof(itv));
1207		itv.it_value.tv_sec = timeout;
1208		if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
1209			SCPrint(TRUE, stderr,
1210				CFSTR("setitimer() failed: %s\n"), strerror(errno));
1211			exit(1);
1212		}
1213	}
1214
1215	CFRunLoopRun();
1216}
1217
1218#ifdef	TEST_DNS_CONFIGURATION
1219
1220Boolean			doDispatch	= FALSE;
1221CFRunLoopSourceRef	notifyRls	= NULL;
1222SCDynamicStoreRef	store		= NULL;
1223CFPropertyListRef	value		= NULL;
1224
1225int
1226main(int argc, char **argv)
1227{
1228	dns_config_t	*dns_config;
1229
1230fprintf(stdout, "copy configuration\n");
1231	dns_config = dns_configuration_copy();
1232	if (dns_config != NULL) {
1233
1234fprintf(stdout, "sleeping for 120 seconds\n");
1235sleep(120);
1236
1237fprintf(stdout, "sending ack\n");
1238		_dns_configuration_ack(dns_config, "TEST_DNS_CONFIGURATION");
1239
1240fprintf(stdout, "sleeping for 120 seconds\n");
1241sleep(120);
1242
1243		dns_configuration_free(dns_config);
1244	}
1245
1246	  do_showDNSConfiguration(argc, argv);
1247	exit(0);
1248}
1249
1250#endif	// TEST_DNS_CONFIGURATION
1251