1/*
2 * Copyright (c) 1999-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 * client.c
25 * - client side program to talk to ipconfigd
26 */
27/*
28 * Modification History
29 *
30 * September, 1999 	Dieter Siegmund (dieter@apple.com)
31 * - initial revision
32 * July 31, 2000	Dieter Siegmund (dieter@apple.com)
33 * - changed to add set, and new implementation of waitall
34 * - removed waitif
35 * February 24, 2003	Dieter Siegmund (dieter@apple.com)
36 * - added support to retrieve information about the netboot (bsdp)
37 *   packet stored in the device tree
38 * September 10, 2009	Dieter Siegmund (dieter@apple.com)
39 * - added IPv6 support
40 */
41#include <stdio.h>
42#include <unistd.h>
43#include <stdlib.h>
44#include <mach/mach.h>
45#include <mach/mach_error.h>
46#include <servers/bootstrap.h>
47#include <ctype.h>
48#include <string.h>
49#include <sysexits.h>
50
51#include <netinet/in.h>
52#include <netinet/udp.h>
53#include <netinet/in_systm.h>
54#include <netinet/ip.h>
55#include <netinet/bootp.h>
56#include <arpa/inet.h>
57#include <SystemConfiguration/SystemConfiguration.h>
58#include <SystemConfiguration/SCValidation.h>
59
60#include "ipconfig_ext.h"
61#include "ipconfig_types.h"
62#include "dhcp_options.h"
63#include "dhcplib.h"
64#include "bsdp.h"
65#include "bsdplib.h"
66#include "ioregpath.h"
67#include "ipconfig.h"
68#include "cfutil.h"
69#include "DHCPv6.h"
70#include "DHCPv6Options.h"
71#include "IPConfigurationControlPrefs.h"
72
73#define METHOD_LIST_V4			"BOOTP, MANUAL, DHCP, INFORM"
74#define METHOD_LIST_V4_WITH_NONE	METHOD_LIST_V4 ", NONE"
75#define METHOD_LIST_V6			"AUTOMATIC-V6, MANUAL-V6, 6TO4"
76#define METHOD_LIST_V6_WITH_NONE	METHOD_LIST_V6 ", NONE-V6"
77#define METHOD_LIST			METHOD_LIST_V4 ", " METHOD_LIST_V6
78#define METHOD_LIST_WITH_NONE		METHOD_LIST_V4_WITH_NONE ", " METHOD_LIST_V6_WITH_NONE
79
80typedef int func_t(mach_port_t server, int argc, char * argv[]);
81typedef func_t * funcptr_t;
82
83static char * progname;
84static char * command_name;
85
86#define STARTUP_KEY	CFSTR("Plugin:IPConfiguration")
87
88#define SHADOW_MOUNT_PATH_COMMAND	"shadow_mount_path"
89#define SHADOW_FILE_PATH_COMMAND	"shadow_file_path"
90#define MACHINE_NAME_COMMAND		"machine_name"
91
92static void
93on_alarm(int sigraised)
94{
95    exit(0);
96}
97
98#define WAIT_ALL_DEFAULT_TIMEOUT	90
99#define WAIT_ALL_MAX_TIMEOUT		120
100
101static void
102key_appeared(SCDynamicStoreRef session, CFArrayRef changes, void * arg)
103{
104    exit(0);
105}
106
107static int
108S_wait_all(mach_port_t server, int argc, char * argv[])
109{
110    CFMutableArrayRef	keys;
111    SCDynamicStoreRef 	session;
112    CFRunLoopSourceRef	rls;
113    unsigned long	t = WAIT_ALL_DEFAULT_TIMEOUT;
114    CFPropertyListRef	value;
115    struct itimerval 	v;
116
117    if (argc > 0) {
118	t = strtoul(argv[0], 0, 0);
119	if (t > WAIT_ALL_MAX_TIMEOUT) {
120	    t = WAIT_ALL_MAX_TIMEOUT;
121	}
122    }
123
124    session = SCDynamicStoreCreate(NULL, CFSTR("ipconfig command"),
125				   key_appeared, NULL);
126    if (session == NULL) {
127	fprintf(stderr, "SCDynamicStoreCreate failed: %s\n",
128		SCErrorString(SCError()));
129	return (0);
130    }
131    keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
132    CFArrayAppendValue(keys, STARTUP_KEY);
133    SCDynamicStoreSetNotificationKeys(session, keys, NULL);
134    CFRelease(keys);
135
136    rls = SCDynamicStoreCreateRunLoopSource(NULL, session, 0);
137    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
138    CFRelease(rls);
139
140    signal(SIGALRM, on_alarm);
141    bzero(&v, sizeof(v));
142    v.it_value.tv_sec = t;
143    if (setitimer(ITIMER_REAL, &v, NULL) < 0) {
144	perror("setitimer");
145	return (0);
146    }
147    value = SCDynamicStoreCopyValue(session, STARTUP_KEY);
148    if (value == NULL) {
149	CFRunLoopRun();
150	return (0);
151    }
152    CFRelease(value);
153    CFRelease(session);
154    return (0);
155}
156
157static int
158S_bsdp_get_packet(mach_port_t server, int argc, char * argv[])
159{
160    CFDictionaryRef	chosen = NULL;
161    struct dhcp *	dhcp;
162    int			length;
163    CFDataRef		response = NULL;
164    int			ret = 1;
165
166    if (getuid() != 0) {
167	return (EX_NOPERM);
168    }
169
170    chosen = myIORegistryEntryCopyValue("IODeviceTree:/chosen");
171    if (chosen == NULL) {
172	goto done;
173    }
174    response = CFDictionaryGetValue(chosen, CFSTR("bsdp-response"));
175    if (isA_CFData(response) == NULL) {
176	response = CFDictionaryGetValue(chosen, CFSTR("bootp-response"));
177	if (isA_CFData(response) == NULL) {
178	    goto done;
179	}
180    }
181    /* ALIGN: CFDataGetBytePtr is aligned to at least sizeof(uint64) */
182    dhcp = (struct dhcp *)(void *)CFDataGetBytePtr(response);
183    length = (int)CFDataGetLength(response);
184    bsdp_print_packet(dhcp, length, 0);
185    ret = 0;
186 done:
187    if (chosen != NULL) {
188	CFRelease(chosen);
189    }
190    return (ret);
191}
192
193static int
194S_bsdp_option(mach_port_t server, int argc, char * argv[])
195{
196    CFDictionaryRef	chosen = NULL;
197    void *		data = NULL;
198    int			data_len;
199    struct dhcp *	dhcp;
200    int			length;
201    dhcpol_t		options;
202    CFDataRef		response = NULL;
203    int			ret = 1;
204    int			tag = 0;
205    dhcpol_t		vendor_options;
206    int			vendor_tag = 0;
207
208    if (getuid() != 0) {
209	return (EX_NOPERM);
210    }
211
212    chosen = myIORegistryEntryCopyValue("IODeviceTree:/chosen");
213    if (chosen == NULL) {
214	goto done;
215    }
216    response = CFDictionaryGetValue(chosen, CFSTR("bsdp-response"));
217    if (isA_CFData(response) == NULL) {
218	response = CFDictionaryGetValue(chosen, CFSTR("bootp-response"));
219	if (isA_CFData(response) == NULL) {
220	    goto done;
221	}
222    }
223
224    /* ALIGN: CFDataGetBytePtr is aligned to at least sizeof(uint64) */
225    dhcp = (struct dhcp *)(void *)CFDataGetBytePtr(response);
226    length = (int)CFDataGetLength(response);
227    if (dhcpol_parse_packet(&options, dhcp, length, NULL) == FALSE) {
228	goto done;
229    }
230    if (strcmp(argv[0], SHADOW_MOUNT_PATH_COMMAND) == 0) {
231	tag = dhcptag_vendor_specific_e;
232	vendor_tag = bsdptag_shadow_mount_path_e;
233    }
234    else if (strcmp(argv[0], SHADOW_FILE_PATH_COMMAND) == 0) {
235	tag = dhcptag_vendor_specific_e;
236	vendor_tag = bsdptag_shadow_file_path_e;
237    }
238    else if (strcmp(argv[0], MACHINE_NAME_COMMAND) == 0) {
239	tag = dhcptag_vendor_specific_e;
240	vendor_tag = bsdptag_machine_name_e;
241    }
242    else {
243	tag = atoi(argv[0]);
244	if (argc == 2) {
245	    vendor_tag = atoi(argv[1]);
246	}
247    }
248    if (tag == dhcptag_vendor_specific_e && vendor_tag != 0) {
249	if (dhcpol_parse_vendor(&vendor_options, &options, NULL) == FALSE) {
250	    goto done;
251	}
252	data = dhcpol_option_copy(&vendor_options, vendor_tag, &data_len);
253	if (data != NULL) {
254	    dhcptype_print(bsdptag_type(vendor_tag), data, data_len);
255	    ret = 0;
256	}
257	dhcpol_free(&vendor_options);
258    }
259    else {
260	const dhcptag_info_t * entry;
261
262	entry = dhcptag_info(tag);
263	if (entry == NULL) {
264	    goto done;
265	}
266	data = dhcpol_option_copy(&options, tag, &data_len);
267	if (data != NULL) {
268	    dhcptype_print(entry->type, data, data_len);
269	    ret = 0;
270	}
271    }
272 done:
273    if (data != NULL) {
274	free(data);
275    }
276    if (chosen != NULL) {
277	CFRelease(chosen);
278    }
279    return (ret);
280}
281
282static int
283S_if_addr(mach_port_t server, int argc, char * argv[])
284{
285    struct in_addr	ip;
286    kern_return_t	kret;
287    if_name_t		name;
288    ipconfig_status_t	status;
289
290    strlcpy(name, argv[0], sizeof(name));
291    kret = ipconfig_if_addr(server, name, (ip_address_t *)&ip, &status);
292    if (kret != KERN_SUCCESS) {
293	fprintf(stderr, "get if addr %s failed, %s\n", name,
294		mach_error_string(kret));
295    }
296    if (status == ipconfig_status_success_e) {
297	printf("%s\n", inet_ntoa(ip));
298	return (0);
299    }
300    return (1);
301}
302
303static int
304S_if_count(mach_port_t server, int argc, char * argv[])
305{
306    int 		count = 0;
307    kern_return_t	status;
308
309    status = ipconfig_if_count(server, &count);
310    if (status == KERN_SUCCESS) {
311	printf("%d\n", count);
312	return (0);
313    }
314    fprintf(stderr, "get if count failed, %s\n", mach_error_string(status));
315    return (1);
316}
317
318static int
319S_get_option(mach_port_t server, int argc, char * argv[])
320{
321    char		buf[1024];
322    inline_data_t 	data;
323    unsigned int 	data_len = sizeof(data);
324    dhcpo_err_str_t	err;
325    kern_return_t	kret;
326    if_name_t		name;
327    ipconfig_status_t	status;
328    int			tag;
329
330    strlcpy(name, argv[0], sizeof(name));
331
332    tag = dhcptag_with_name(argv[1]);
333    if (tag == -1) {
334	tag = atoi(argv[1]);
335    }
336    kret = ipconfig_get_option(server, name, tag, data, &data_len,
337			       &status);
338    if (kret != KERN_SUCCESS) {
339	fprintf(stderr, "ipconfig_get_option failed, %s\n",
340		mach_error_string(kret));
341	goto done;
342    }
343    if (status != ipconfig_status_success_e) {
344	goto done;
345    }
346    if (dhcptag_to_str(buf, sizeof(buf), tag, data, data_len, &err)) {
347	printf("%s\n", buf);
348	return (0);
349    }
350    fprintf(stderr, "couldn't convert the option, %s\n",
351	    err.str);
352 done:
353    return (1);
354}
355
356static int
357S_get_packet(mach_port_t server, int argc, char * argv[])
358{
359    uint32_t		data[sizeof(inline_data_t)/sizeof(uint32_t)];
360    unsigned int 	data_len = sizeof(data);
361    kern_return_t	kret;
362    if_name_t		name;
363    int			ret;
364    ipconfig_status_t	status;
365
366    ret = 1;
367    strlcpy(name, argv[0], sizeof(name));
368    kret = ipconfig_get_packet(server, name, (void *)data, &data_len, &status);
369    if (kret != KERN_SUCCESS) {
370	fprintf(stderr, "ipconfig_get_packet failed, %s\n",
371		mach_error_string(kret));
372	goto done;
373    }
374    if (status != ipconfig_status_success_e) {
375	goto done;
376    }
377    /* ALIGN: inline_data_t is aligned at least sizeof(uint32_t) bytes */
378    dhcp_packet_print((struct dhcp *)(void *)data, data_len);
379    ret = 0;
380
381 done:
382    return (ret);
383}
384
385static int
386S_get_v6_packet(mach_port_t server, int argc, char * argv[])
387{
388    inline_data_t 		data;
389    unsigned int 		data_len = sizeof(data);
390    DHCPv6OptionErrorString 	err;
391    kern_return_t		kret;
392    if_name_t			name;
393    DHCPv6OptionListRef		options;
394    int				ret;
395    ipconfig_status_t		status;
396
397    ret = 1;
398    strlcpy(name, argv[0], sizeof(name));
399    kret = ipconfig_get_v6_packet(server, name, data, &data_len, &status);
400    if (kret != KERN_SUCCESS) {
401	fprintf(stderr, "ipconfig_get_v6_packet failed, %s\n",
402		mach_error_string(kret));
403	goto done;
404    }
405    if (status != ipconfig_status_success_e) {
406	goto done;
407    }
408    DHCPv6PacketFPrint(stdout, (DHCPv6PacketRef)data, data_len);
409    options = DHCPv6OptionListCreateWithPacket((DHCPv6PacketRef)data,
410					       data_len, &err);
411    if (options != NULL) {
412	DHCPv6OptionListFPrint(stdout, options);
413	DHCPv6OptionListRelease(&options);
414    }
415    ret = 0;
416
417 done:
418    return (ret);
419}
420
421#ifndef kSCValNetIPv6ConfigMethodLinkLocal
422static const CFStringRef kIPConfigurationIPv6ConfigMethodLinkLocal = CFSTR("LinkLocal");
423#define kSCValNetIPv6ConfigMethodLinkLocal kIPConfigurationIPv6ConfigMethodLinkLocal
424#endif /* kSCValNetIPv6ConfigMethodLinkLocal */
425
426#ifndef kSCValNetIPv4ConfigMethodFailover
427static const CFStringRef kIPConfigurationConfigMethodFailover = CFSTR("Failover");
428#define kSCValNetIPv4ConfigMethodFailover kIPConfigurationConfigMethodFailover
429#endif /* kSCValNetIPv4ConfigMethodFailover */
430
431#ifndef kSCPropNetIPv4FailoverAddressTimeout
432static const CFStringRef kIPConfigurationFailoverAddressTimeout = CFSTR("FailoverAddressTimeout");
433#define kSCPropNetIPv4FailoverAddressTimeout	kIPConfigurationFailoverAddressTimeout
434#endif /* kSCPropNetIPv4FailoverAddressTimeout */
435
436static CFStringRef
437IPv4ConfigMethodGet(const char * m)
438{
439    if (strcasecmp("bootp", m) == 0) {
440	return (kSCValNetIPv4ConfigMethodBOOTP);
441    }
442    if (strcasecmp("dhcp", m) == 0) {
443	return (kSCValNetIPv4ConfigMethodDHCP);
444    }
445    if (strcasecmp("manual", m) == 0) {
446	return (kSCValNetIPv4ConfigMethodManual);
447    }
448    if (strcasecmp("inform", m) == 0) {
449	return (kSCValNetIPv4ConfigMethodINFORM);
450    }
451    if (strcasecmp("linklocal", m) == 0) {
452	return (kSCValNetIPv4ConfigMethodLinkLocal);
453    }
454    if (strcasecmp("failover", m) == 0) {
455	return (kSCValNetIPv4ConfigMethodFailover);
456    }
457    return (NULL);
458}
459
460static CFDictionaryRef
461IPv4ConfigDictCreate(const char * ifname,
462		     int argc, char * argv[], const char * cmd,
463		     const char * method_name,
464		     CFStringRef config_method)
465{
466    CFMutableDictionaryRef	dict;
467
468    dict = CFDictionaryCreateMutable(NULL, 0,
469				     &kCFTypeDictionaryKeyCallBacks,
470				     &kCFTypeDictionaryValueCallBacks);
471    CFDictionarySetValue(dict, kSCPropNetIPv4ConfigMethod,
472			 config_method);
473    if (config_method == kSCValNetIPv4ConfigMethodFailover
474	|| config_method == kSCValNetIPv4ConfigMethodINFORM
475	|| config_method == kSCValNetIPv4ConfigMethodManual) {
476	struct in_addr		ip_address;
477
478	if (config_method == kSCValNetIPv4ConfigMethodFailover) {
479	    if (argc < 1 || argc > 3) {
480		fprintf(stderr, "usage: ipconfig %s %s "
481			"%s <ip-address> [ <subnet-mask> [ <timeout> ] ]\n",
482			cmd, ifname, method_name);
483		goto failed;
484	    }
485	}
486	else if (argc < 1 || argc > 2) {
487	    fprintf(stderr, "usage: ipconfig %s %s "
488		    "%s <ip-address> [ <subnet-mask> ]\n",
489		    cmd, ifname, method_name);
490	    goto failed;
491	}
492	if (inet_aton(argv[0], &ip_address) == 0) {
493	    fprintf(stderr, "Invalid IP address %s\n", argv[0]);
494	    goto failed;
495	}
496	my_CFDictionarySetIPAddressAsArrayValue(dict,
497						kSCPropNetIPv4Addresses,
498						ip_address);
499	if (argc >= 2) {
500	    if (inet_aton(argv[1], &ip_address) != 1) {
501		fprintf(stderr, "Invalid IP mask %s\n", argv[1]);
502		goto failed;
503	    }
504	    my_CFDictionarySetIPAddressAsArrayValue(dict,
505						    kSCPropNetIPv4SubnetMasks,
506						    ip_address);
507	}
508	if (argc >= 3) {
509	    int32_t	timeout;
510
511	    timeout = (int32_t)strtol(argv[2], NULL, 0);
512	    if (timeout > 0) {
513		CFNumberRef	num;
514
515		num = CFNumberCreate(NULL, kCFNumberSInt32Type, &timeout);
516		CFDictionarySetValue(dict,
517				     kSCPropNetIPv4FailoverAddressTimeout,
518				     num);
519		CFRelease(num);
520	    }
521	}
522    }
523    else if (argc != 0) {
524	fprintf(stderr, "too many arguments for method\n");
525	goto failed;
526    }
527    return (dict);
528
529 failed:
530    my_CFRelease(&dict);
531    return (NULL);
532}
533
534static CFStringRef
535IPv6ConfigMethodGet(const char * m)
536{
537    if (strcasecmp(m, "automatic-v6") == 0) {
538	return (kSCValNetIPv6ConfigMethodAutomatic);
539    }
540    else if (strcasecmp(m, "manual-v6") == 0) {
541	return (kSCValNetIPv6ConfigMethodManual);
542    }
543    else if (strcasecmp(m, "6to4") == 0) {
544	return (kSCValNetIPv6ConfigMethod6to4);
545    }
546    if (strcasecmp(m, "linklocal-v6") == 0) {
547	return (kSCValNetIPv6ConfigMethodLinkLocal);
548    }
549    return (NULL);
550}
551
552static void
553my_CFDictionarySetIPv6AddressAsArrayValue(CFMutableDictionaryRef dict,
554					  CFStringRef prop,
555					  const struct in6_addr * ipaddr_p)
556{
557    CFStringRef		str;
558
559    str = my_CFStringCreateWithIPv6Address(ipaddr_p);
560    my_CFDictionarySetTypeAsArrayValue(dict, prop, str);
561    CFRelease(str);
562    return;
563}
564
565static void
566my_CFDictionarySetIntegerAsArrayValue(CFMutableDictionaryRef dict,
567				      CFStringRef prop, int val)
568{
569    CFNumberRef		num;
570
571    num = CFNumberCreate(NULL, kCFNumberIntType, &val);
572    my_CFDictionarySetTypeAsArrayValue(dict, prop, num);
573    CFRelease(num);
574    return;
575}
576
577
578static CFDictionaryRef
579IPv6ConfigDictCreate(const char * ifname,
580		     int argc, char * argv[], const char * cmd,
581		     const char * method_name,
582		     CFStringRef config_method)
583{
584    CFDictionaryRef		dict;
585    CFMutableDictionaryRef	ipv6_dict;
586    CFStringRef			ipv6_key;
587
588    ipv6_dict = CFDictionaryCreateMutable(NULL, 0,
589					  &kCFTypeDictionaryKeyCallBacks,
590					  &kCFTypeDictionaryValueCallBacks);
591    CFDictionarySetValue(ipv6_dict, kSCPropNetIPv6ConfigMethod,
592			 config_method);
593    if (config_method == kSCValNetIPv6ConfigMethodManual) {
594	struct in6_addr		ip_address;
595	int			prefix_length;
596
597	if (argc != 2) {
598	    fprintf(stderr, "usage: ipconfig %s %s "
599		    "%s6 <ipv6-address> <prefix-length>\n",
600		    cmd, ifname, method_name);
601	    goto failed;
602	}
603	if (inet_pton(AF_INET6, argv[0], &ip_address) != 1) {
604	    fprintf(stderr, "Invalid IPv6 address %s\n", argv[0]);
605	    goto failed;
606	}
607	my_CFDictionarySetIPv6AddressAsArrayValue(ipv6_dict,
608						  kSCPropNetIPv6Addresses,
609						  &ip_address);
610	prefix_length = (int)strtol(argv[1], NULL, 0);
611	if (prefix_length < 0 || prefix_length > 128) {
612	    fprintf(stderr, "Invalid prefix_length %s\n", argv[1]);
613	    goto failed;
614	}
615	my_CFDictionarySetIntegerAsArrayValue(ipv6_dict,
616					      kSCPropNetIPv6PrefixLength,
617					      prefix_length);
618    }
619    else if (argc != 0) {
620	fprintf(stderr, "too many arguments for method\n");
621	goto failed;
622    }
623    ipv6_key = kSCEntNetIPv6;
624    dict = CFDictionaryCreate(NULL,
625			      (const void * *)&ipv6_key,
626			      (const void * *)&ipv6_dict, 1,
627			      &kCFTypeDictionaryKeyCallBacks,
628			      &kCFTypeDictionaryValueCallBacks);
629    CFRelease(ipv6_dict);
630    return (dict);
631
632 failed:
633    my_CFRelease(&ipv6_dict);
634    return (NULL);
635}
636
637static CFDictionaryRef
638ConfigDictCreate(const char * ifname, int argc, char * argv[], const char * cmd,
639		 const char * method_name, boolean_t allow_none)
640{
641    CFStringRef		config_method;
642
643    config_method = IPv4ConfigMethodGet(method_name);
644    if (config_method != NULL) {
645	return (IPv4ConfigDictCreate(ifname, argc, argv, cmd, method_name,
646				     config_method));
647    }
648    config_method = IPv6ConfigMethodGet(method_name);
649    if (config_method == NULL) {
650	fprintf(stderr,
651		"ipconfig %s: invalid method '%s'\n<method> is one of %s\n",
652		cmd, method_name,
653		(allow_none) ? METHOD_LIST_WITH_NONE : METHOD_LIST);
654	return (NULL);
655    }
656    return (IPv6ConfigDictCreate(ifname, argc, argv, cmd,
657				 method_name, config_method));
658}
659
660static int
661S_set(mach_port_t server, int argc, char * argv[])
662{
663    CFDataRef			data = NULL;
664    CFDictionaryRef		dict = NULL;
665    const char *		method_name;
666    if_name_t			if_name;
667    kern_return_t		kret;
668    ipconfig_status_t		status = ipconfig_status_success_e;
669    void *			xml_data_ptr = NULL;
670    int				xml_data_len = 0;
671
672    strlcpy(if_name, argv[0], sizeof(if_name));
673    method_name = argv[1];
674    argv += 2;
675    argc -= 2;
676
677    if (strcasecmp(method_name, "NONE") == 0) {
678	/* nothing to do, NONE implies NULL method data */
679    }
680    else if (strcasecmp(method_name, "NONE-V6") == 0
681	     || strcasecmp(method_name, "NONE-V4") == 0) {
682
683	CFDictionaryRef		empty_dict;
684	CFStringRef		ip_key;
685
686	/* NONE-V{4,6} is represented as an empty IPv{4,6} dictionary */
687	empty_dict = CFDictionaryCreate(NULL,
688					NULL, NULL, 0,
689					&kCFTypeDictionaryKeyCallBacks,
690					&kCFTypeDictionaryValueCallBacks);
691	if (strcasecmp(method_name, "NONE-V6") == 0) {
692	    ip_key = kSCEntNetIPv6;
693	}
694	else {
695	    ip_key = kSCEntNetIPv4;
696	}
697	dict = CFDictionaryCreate(NULL,
698				  (const void * *)&ip_key,
699				  (const void * *)&empty_dict, 1,
700				  &kCFTypeDictionaryKeyCallBacks,
701				  &kCFTypeDictionaryValueCallBacks);
702	CFRelease(empty_dict);
703    }
704    else {
705	dict = ConfigDictCreate(if_name, argc, argv, command_name, method_name,
706				TRUE);
707	if (dict == NULL) {
708	    return (1);
709	}
710    }
711    if (dict != NULL) {
712	data = CFPropertyListCreateData(NULL,
713					dict,
714					kCFPropertyListBinaryFormat_v1_0,
715					0,
716					NULL);
717	if (data == NULL) {
718	    CFRelease(dict);
719	    fprintf(stderr, "failed to allocate memory\n");
720	    return (1);
721	}
722	xml_data_ptr = (void *)CFDataGetBytePtr(data);
723	xml_data_len = (int)CFDataGetLength(data);
724    }
725    kret = ipconfig_set(server, if_name, xml_data_ptr, xml_data_len, &status);
726    my_CFRelease(&dict);
727    my_CFRelease(&data);
728    if (kret != KERN_SUCCESS) {
729	mach_error("ipconfig_set failed", kret);
730	return (1);
731    }
732    if (status != ipconfig_status_success_e) {
733	fprintf(stderr, "ipconfig_set %s %s failed: %s\n",
734		if_name, method_name, ipconfig_status_string(status));
735	return (1);
736    }
737    return (0);
738}
739
740static int
741S_set_verbose(mach_port_t server, int argc, char * argv[])
742{
743    int			verbose;
744
745    verbose = (int)strtol(argv[0], NULL, 0);
746    errno = 0;
747    if (verbose == 0 && errno != 0) {
748	fprintf(stderr, "conversion to integer of %s failed\n", argv[0]);
749	return (1);
750    }
751    if (IPConfigurationControlPrefsSetVerbose(verbose != 0) == FALSE) {
752	fprintf(stderr, "failed to set verbose\n");
753	return (1);
754    }
755    return (0);
756}
757
758#ifdef IPCONFIG_TEST_NO_ENTRY
759static int
760S_set_something(mach_port_t server, int argc, char * argv[])
761{
762    kern_return_t		kret;
763    ipconfig_status_t		status = ipconfig_status_success_e;
764    int				verbose;
765
766    verbose = strtol(argv[0], NULL, 0);
767    errno = 0;
768    if (verbose == 0 && errno != 0) {
769	fprintf(stderr, "conversion to integer of %s failed\n", argv[0]);
770	return (1);
771    }
772    kret = ipconfig_set_something(server, verbose, &status);
773    if (kret != KERN_SUCCESS) {
774	return (1);
775    }
776    if (status != ipconfig_status_success_e) {
777	fprintf(stderr, "setsomething failed: %s\n",
778		ipconfig_status_string(status));
779	return (1);
780    }
781    return (0);
782}
783#endif /* IPCONFIG_TEST_NO_ENTRY */
784
785static int
786S_add_or_set_service(mach_port_t server, int argc, char * argv[], bool add)
787{
788    CFDataRef			data = NULL;
789    CFDictionaryRef		dict;
790    char *			method_name;
791    if_name_t			if_name;
792    kern_return_t		kret;
793    inline_data_t 		service_id;
794    unsigned int 		service_id_len;
795    ipconfig_status_t		status = ipconfig_status_success_e;
796    void *			xml_data_ptr = NULL;
797    int				xml_data_len = 0;
798
799    strlcpy(if_name, argv[0], sizeof(if_name));
800    method_name = argv[1];
801    argv += 2;
802    argc -= 2;
803
804    dict = ConfigDictCreate(if_name, argc, argv, command_name, method_name,
805			    FALSE);
806    if (dict == NULL) {
807	return (1);
808    }
809    data = CFPropertyListCreateData(NULL,
810				    dict,
811				    kCFPropertyListBinaryFormat_v1_0,
812				    0,
813				    NULL);
814    if (data == NULL) {
815	CFRelease(dict);
816	fprintf(stderr, "failed to allocate memory\n");
817	return (1);
818    }
819    xml_data_ptr = (void *)CFDataGetBytePtr(data);
820    xml_data_len = (int)CFDataGetLength(data);
821    if (add) {
822	kret = ipconfig_add_service(server, if_name, xml_data_ptr, xml_data_len,
823				    service_id, &service_id_len, &status);
824    }
825    else {
826	kret = ipconfig_set_service(server, if_name, xml_data_ptr, xml_data_len,
827				    service_id, &service_id_len, &status);
828    }
829    CFRelease(dict);
830    CFRelease(data);
831
832    if (kret != KERN_SUCCESS) {
833	fprintf(stderr, "ipconfig_%s_service failed, %s\n", add ? "add" : "set",
834		mach_error_string(kret));
835	return (1);
836    }
837    if (status != ipconfig_status_success_e) {
838	fprintf(stderr, "ipconfig_%s_service %s %s failed: %s\n",
839		add ? "add" : "set",
840		if_name, method_name, ipconfig_status_string(status));
841	return (1);
842    }
843    printf("%.*s\n", service_id_len, service_id);
844    return (0);
845}
846
847static int
848S_add_service(mach_port_t server, int argc, char * argv[])
849{
850    return (S_add_or_set_service(server, argc, argv, TRUE));
851}
852
853static int
854S_set_service(mach_port_t server, int argc, char * argv[])
855{
856    return (S_add_or_set_service(server, argc, argv, FALSE));
857}
858
859static int
860S_remove_service_with_id(mach_port_t server, int argc, char * argv[])
861{
862    if_name_t			if_name;
863    kern_return_t		kret;
864    inline_data_t 		service_id;
865    unsigned int 		service_id_len;
866    ipconfig_status_t		status = ipconfig_status_success_e;
867
868    service_id_len = (int)strlen(argv[0]);
869    if (service_id_len > sizeof(service_id)) {
870	service_id_len = sizeof(service_id);
871    }
872    memcpy(service_id, argv[0], service_id_len);
873    if (argc > 1) {
874	strlcpy(if_name, argv[1], sizeof(if_name));
875    }
876    else {
877	bzero(if_name, sizeof(if_name));
878    }
879    kret = ipconfig_remove_service_on_interface(server,
880						if_name,
881						service_id, service_id_len,
882						&status);
883    if (kret != KERN_SUCCESS) {
884	mach_error("ipconfig_remove_service_on_interface failed", kret);
885	return (1);
886    }
887    if (status != ipconfig_status_success_e) {
888	fprintf(stderr, "ipconfig_remove_service_on_interface %s failed: %s\n",
889		argv[0], ipconfig_status_string(status));
890	return (1);
891    }
892    return (0);
893}
894
895static int
896S_find_service(mach_port_t server, int argc, char * argv[])
897{
898    CFDataRef			data = NULL;
899    CFDictionaryRef		dict;
900    boolean_t			exact = FALSE;
901    char *			method_name;
902    if_name_t			if_name;
903    kern_return_t		kret;
904    inline_data_t 		service_id;
905    unsigned int 		service_id_len = sizeof(service_id);
906    ipconfig_status_t		status = ipconfig_status_success_e;
907    void *			xml_data_ptr = NULL;
908    int				xml_data_len = 0;
909
910    strlcpy(if_name, argv[0], sizeof(if_name));
911    argv++;
912    argc--;
913    if (argc > 1 && strcasecmp(argv[0], "exact") == 0) {
914	exact = TRUE;
915	argc--;
916	argv++;
917    }
918    method_name = argv[0];
919    argc--;
920    argv++;
921    dict = ConfigDictCreate(if_name, argc, argv, command_name, method_name,
922			    FALSE);
923    if (dict == NULL) {
924	return (1);
925    }
926    data = CFPropertyListCreateData(NULL,
927				    dict,
928				    kCFPropertyListBinaryFormat_v1_0,
929				    0,
930				    NULL);
931    if (data == NULL) {
932	CFRelease(dict);
933	fprintf(stderr, "failed to allocate memory\n");
934	return (1);
935    }
936    xml_data_ptr = (void *)CFDataGetBytePtr(data);
937    xml_data_len = (int)CFDataGetLength(data);
938    kret = ipconfig_find_service(server, if_name, exact,
939				 xml_data_ptr, xml_data_len,
940				 service_id, &service_id_len, &status);
941    my_CFRelease(&dict);
942    my_CFRelease(&data);
943    if (kret != KERN_SUCCESS) {
944	mach_error("ipconfig_find_service failed", kret);
945	return (1);
946    }
947    if (status != ipconfig_status_success_e) {
948	fprintf(stderr, "ipconfig_find_service %s %s failed: %s\n",
949		if_name, method_name, ipconfig_status_string(status));
950	return (1);
951    }
952    printf("%.*s\n", service_id_len, service_id);
953    return (0);
954}
955
956static int
957S_remove_service(mach_port_t server, int argc, char * argv[])
958{
959    CFDataRef			data = NULL;
960    CFDictionaryRef		dict;
961    char *			method_name;
962    if_name_t			if_name;
963    kern_return_t		kret;
964    ipconfig_status_t		status = ipconfig_status_success_e;
965    void *			xml_data_ptr = NULL;
966    int				xml_data_len = 0;
967
968    strlcpy(if_name, argv[0], sizeof(if_name));
969    method_name = argv[1];
970    argv += 2;
971    argc -= 2;
972
973    dict = ConfigDictCreate(if_name, argc, argv, command_name, method_name,
974			    FALSE);
975    if (dict == NULL) {
976	return (1);
977    }
978    data = CFPropertyListCreateData(NULL,
979				    dict,
980				    kCFPropertyListBinaryFormat_v1_0,
981				    0,
982				    NULL);
983    if (data == NULL) {
984	CFRelease(dict);
985	fprintf(stderr, "failed to allocate memory\n");
986	return (1);
987    }
988    xml_data_ptr = (void *)CFDataGetBytePtr(data);
989    xml_data_len = (int)CFDataGetLength(data);
990    kret = ipconfig_remove_service(server, if_name, xml_data_ptr, xml_data_len,
991				   &status);
992    my_CFRelease(&dict);
993    my_CFRelease(&data);
994    if (kret != KERN_SUCCESS) {
995	mach_error("ipconfig_remove_service failed", kret);
996	return (1);
997    }
998    if (status != ipconfig_status_success_e) {
999	fprintf(stderr, "ipconfig_remove_service %s %s failed: %s\n",
1000		if_name, method_name, ipconfig_status_string(status));
1001	return (1);
1002    }
1003    return (0);
1004}
1005
1006static int
1007S_refresh_service(mach_port_t server, int argc, char * argv[])
1008{
1009    if_name_t			if_name;
1010    kern_return_t		kret;
1011    inline_data_t 		service_id;
1012    unsigned int 		service_id_len;
1013    ipconfig_status_t		status = ipconfig_status_success_e;
1014
1015    service_id_len = (int)strlen(argv[0]);
1016    if (service_id_len > sizeof(service_id)) {
1017	service_id_len = sizeof(service_id);
1018    }
1019    memcpy(service_id, argv[0], service_id_len);
1020    strlcpy(if_name, argv[1], sizeof(if_name));
1021    kret = ipconfig_refresh_service(server,
1022				    if_name,
1023				    service_id, service_id_len,
1024				    &status);
1025    if (kret != KERN_SUCCESS) {
1026	mach_error("ipconfig_refresh_service", kret);
1027	return (1);
1028    }
1029    if (status != ipconfig_status_success_e) {
1030	fprintf(stderr, "ipconfig_refresh_service(%s, %s) failed: %s\n",
1031		argv[0], argv[1], ipconfig_status_string(status));
1032	return (1);
1033    }
1034    return (0);
1035}
1036
1037
1038static const struct command_info {
1039    const char *command;
1040    funcptr_t	func;
1041    int		argc;
1042    const char *usage;
1043    int		display;
1044    int		no_server;
1045} commands[] = {
1046    { "waitall", S_wait_all, 0, "[ timeout secs ]", 1, 1 },
1047    { "getifaddr", S_if_addr, 1, "<interface name>", 1, 0 },
1048#if 0
1049    { "waitif", S_wait_if, 1, " <interface name>", 1, 0 },
1050#endif /* 0 */
1051    { "ifcount", S_if_count, 0, "", 1, 0 },
1052    { "getoption", S_get_option, 2,
1053      " <interface name | \"\" > <option name> | <option code>", 1, 0 },
1054    { "getpacket", S_get_packet, 1, " <interface name>", 1, 0 },
1055    { "getv6packet", S_get_v6_packet, 1, " <interface name>", 1, 0 },
1056    { "set", S_set, 2,
1057      "<interface name> <method> <method args>\n"
1058      "<method> is one of " METHOD_LIST_WITH_NONE,
1059      1, 0 },
1060    { "netbootoption", S_bsdp_option, 1, "<option> [<vendor option>] | "
1061      SHADOW_MOUNT_PATH_COMMAND " | " SHADOW_FILE_PATH_COMMAND
1062      " | " MACHINE_NAME_COMMAND, 0, 1 },
1063    { "netbootpacket", S_bsdp_get_packet, 0, "", 0, 1 },
1064    { "setverbose", S_set_verbose, 1, "0 | 1", 1, 1 },
1065#ifdef IPCONFIG_TEST_NO_ENTRY
1066    { "setsomething", S_set_something, 1, "0 | 1", 1, 0 },
1067#endif /* IPCONFIG_TEST_NO_ENTRY */
1068    { "addService", S_add_service, 2,
1069      "<interface name> <method> <method args>\n"
1070      "<method> is one of " METHOD_LIST,
1071      0, 0 },
1072    { "setService", S_set_service, 2,
1073      "<interface name> <method> <method args>\n"
1074      "<method> is one of " METHOD_LIST,
1075      0, 0 },
1076    { "findService", S_find_service, 2,
1077      "<interface name> [ exact ] <method> <method args>\n"
1078      "<method> is one of " METHOD_LIST,
1079      0, 0 },
1080    { "removeServiceWithId", S_remove_service_with_id, 1,
1081      "<service ID> [ <interface name> ]", 0, 0 },
1082    { "removeService", S_remove_service, 2,
1083      "<interface name> <method> <method args>\n"
1084      "<method> is one of " METHOD_LIST,
1085      0, 0 },
1086    { "refreshService", S_refresh_service, 2,
1087      "<service ID> <interface name>", 0, 0 },
1088    { NULL, NULL, 0, NULL, 0, 0 },
1089};
1090
1091void
1092usage()
1093{
1094    int i;
1095    fprintf(stderr, "usage: %s <command> <args>\n", progname);
1096    fprintf(stderr, "where <command> is one of ");
1097    for (i = 0; commands[i].command; i++) {
1098	if (commands[i].display) {
1099	    fprintf(stderr, "%s%s",  i == 0 ? "" : ", ",
1100		    commands[i].command);
1101	}
1102    }
1103    fprintf(stderr, "\n");
1104    exit(1);
1105}
1106
1107static const struct command_info *
1108S_lookup_command(char * cmd, int argc)
1109{
1110    int i;
1111
1112    for (i = 0; commands[i].command; i++) {
1113	if (strcasecmp(cmd, commands[i].command) == 0) {
1114	    if (argc < commands[i].argc) {
1115		fprintf(stderr, "usage: ipconfig %s %s\n", commands[i].command,
1116			commands[i].usage ? commands[i].usage : "");
1117		exit(1);
1118	    }
1119	    return commands + i;
1120	}
1121    }
1122    return (NULL);
1123}
1124
1125int
1126main(int argc, char * argv[])
1127{
1128    const struct command_info *	command;
1129    mach_port_t			server = MACH_PORT_NULL;
1130    kern_return_t		kret;
1131
1132    progname = argv[0];
1133    if (argc < 2)
1134	usage();
1135
1136    argv++; argc--;
1137    command_name = argv[0];
1138    command = S_lookup_command(command_name, argc - 1);
1139    if (command == NULL) {
1140	usage();
1141	exit(1);
1142    }
1143    argv++; argc--;
1144    if (command->no_server == 0) {
1145	kret = ipconfig_server_port(&server);
1146	switch (kret) {
1147	case BOOTSTRAP_SUCCESS:
1148	    break;
1149	case BOOTSTRAP_UNKNOWN_SERVICE:
1150	    fprintf(stderr, "ipconfig server not active\n");
1151	    /* start it maybe??? */
1152	    exit(1);
1153	default:
1154	    mach_error("ipconfig_server_port failed", kret);
1155	    exit(1);
1156	}
1157    }
1158    exit ((*command->func)(server, argc, argv));
1159}
1160