1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <string.h>
31 #include <dhcpagent_ipc.h>
32 #include <dhcp_inittab.h>
33 #include <dhcp_symbol.h>
34 
35 #define	DHCP_INFO_VENDOR_START_V4	256
36 #define	DHCP_INFO_VENDOR_START_V6	65536
37 
38 static void
usage(const char * program)39 usage(const char *program)
40 {
41 	(void) fprintf(stderr,
42 	    "usage: %s [-c] [-i interface] [-n limit] [-v {4|6}] code\n"
43 	    "       %s [-c] [-i interface] [-n limit] [-v {4|6}] identifier\n",
44 	    program, program);
45 
46 	exit(DHCP_EXIT_BADARGS);
47 }
48 
49 int
main(int argc,char ** argv)50 main(int argc, char **argv)
51 {
52 	ssize_t			max_lines = -1;
53 	size_t			gran, n_spaces = 0;
54 	dhcp_optnum_t		optnum;
55 	dhcp_ipc_request_t	*request;
56 	dhcp_ipc_reply_t	*reply;
57 	int			c, error, i;
58 	char			*ifname = "";
59 	char			*value, *valuep;
60 	dhcp_symbol_t		*entry;
61 	DHCP_OPT		*opt;
62 	size_t			opt_len;
63 	boolean_t		is_canonical = B_FALSE;
64 	long			version = 4;
65 	boolean_t		isv6;
66 	uint8_t			*valptr;
67 
68 	while ((c = getopt(argc, argv, "ci:n:v:")) != EOF) {
69 
70 		switch (c) {
71 
72 		case 'c':
73 			is_canonical = B_TRUE;
74 			break;
75 
76 		case 'i':
77 			ifname = optarg;
78 			break;
79 
80 		case 'n':
81 			max_lines = strtoul(optarg, NULL, 0);
82 			break;
83 
84 		case 'v':
85 			version = strtol(optarg, NULL, 0);
86 			if (version != 4 && version != 6)
87 				usage(argv[0]);
88 			break;
89 
90 		case '?':
91 			usage(argv[0]);
92 
93 		default:
94 			break;
95 		}
96 	}
97 
98 	if (argc - optind != 1)
99 		usage(argv[0]);
100 
101 	/*
102 	 * we either have a code or an identifer.  if we have a code,
103 	 * then values over 256 indicate a vendor option.  if we have
104 	 * an identifier, then use inittab_getbyname() to turn the
105 	 * identifier into a code, then send the request over the wire.
106 	 */
107 
108 	isv6 = (version == 6);
109 
110 	if (isalpha(*argv[optind])) {
111 
112 		entry = inittab_getbyname(ITAB_CAT_SITE | ITAB_CAT_STANDARD |
113 		    ITAB_CAT_VENDOR | ITAB_CAT_FIELD |
114 		    (isv6 ? ITAB_CAT_V6 : 0), ITAB_CONS_INFO,
115 		    argv[optind]);
116 
117 		if (entry == NULL) {
118 			(void) fprintf(stderr, "%s: unknown identifier `%s'\n",
119 			    argv[0], argv[optind]);
120 			return (DHCP_EXIT_BADARGS);
121 		}
122 
123 		optnum.code	= entry->ds_code;
124 		optnum.category = entry->ds_category;
125 
126 	} else {
127 		ulong_t start;
128 
129 		optnum.code	= strtoul(argv[optind], 0, 0);
130 		optnum.category = ITAB_CAT_STANDARD | ITAB_CAT_SITE;
131 
132 		/*
133 		 * sigh.  this is a hack, but it's needed for backward
134 		 * compatibility with the CA dhcpinfo program.
135 		 */
136 
137 		start = isv6 ? DHCP_INFO_VENDOR_START_V6 :
138 		    DHCP_INFO_VENDOR_START_V4;
139 		if (optnum.code > start) {
140 			optnum.code    -= start;
141 			optnum.category = ITAB_CAT_VENDOR;
142 		}
143 
144 		if (isv6)
145 			optnum.category |= ITAB_CAT_V6;
146 
147 		entry = inittab_getbycode(optnum.category, ITAB_CONS_INFO,
148 		    optnum.code);
149 
150 		if (entry == NULL) {
151 			(void) fprintf(stderr, "%s: unknown code `%s'\n",
152 			    argv[0], argv[optind]);
153 			return (DHCP_EXIT_BADARGS);
154 		}
155 		optnum.category = entry->ds_category;
156 	}
157 
158 	optnum.size = entry->ds_max * inittab_type_to_size(entry);
159 
160 	/*
161 	 * send the request to the agent and reap the reply
162 	 */
163 
164 	request = dhcp_ipc_alloc_request(DHCP_GET_TAG | (isv6 ? DHCP_V6 : 0),
165 	    ifname, &optnum, sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
166 
167 	if (request == NULL)
168 		return (DHCP_EXIT_SYSTEM);
169 
170 	error = dhcp_ipc_make_request(request, &reply, DHCP_IPC_WAIT_DEFAULT);
171 	if (error != 0 || reply->return_code != 0) {
172 
173 		if (error == 0)
174 			error = reply->return_code;
175 
176 		(void) fprintf(stderr, "%s: %s\n", argv[0],
177 		    dhcp_ipc_strerror(error));
178 
179 		if (error == DHCP_IPC_E_TIMEOUT)
180 			return (DHCP_EXIT_TIMEOUT);
181 
182 		return (DHCP_EXIT_FAILURE);
183 	}
184 
185 	opt = dhcp_ipc_get_data(reply, &opt_len, NULL);
186 
187 	/*
188 	 * no data means that the client has an ACK but has no information
189 	 * about the specified option; return success
190 	 */
191 
192 	if (opt_len == 0)
193 		return (DHCP_EXIT_SUCCESS);
194 
195 	/*
196 	 * check for protocol error
197 	 */
198 
199 	if (isv6) {
200 		dhcpv6_option_t d6o;
201 
202 		if (opt_len < sizeof (d6o))
203 			return (DHCP_EXIT_FAILURE);
204 		(void) memcpy(&d6o, opt, sizeof (d6o));
205 		if (opt_len != ntohs(d6o.d6o_len) + sizeof (d6o))
206 			return (DHCP_EXIT_FAILURE);
207 		valptr = (uint8_t *)opt + sizeof (d6o);
208 		opt_len -= sizeof (d6o);
209 	} else {
210 		if (opt_len < 2 || (opt_len - 2 != opt->len))
211 			return (DHCP_EXIT_FAILURE);
212 		opt_len -= 2;
213 		valptr = opt->value;
214 	}
215 
216 	if (is_canonical) {
217 
218 		value = malloc(opt_len * (sizeof ("0xNN") + 1));
219 		if (value == NULL) {
220 			(void) fprintf(stderr, "%s: out of memory\n", argv[0]);
221 			return (DHCP_EXIT_FAILURE);
222 		}
223 
224 		for (i = 0, valuep = value; i < opt_len; i++)
225 			valuep += sprintf(valuep, "0x%02X ", valptr[i]);
226 
227 		valuep[-1] = '\0';
228 		gran = 1;
229 
230 	} else {
231 
232 		value = inittab_decode(entry, valptr, opt_len, B_TRUE);
233 		if (value == NULL) {
234 			(void) fprintf(stderr, "%s: cannot decode agent's "
235 			    "reply\n", argv[0]);
236 			return (DHCP_EXIT_FAILURE);
237 		}
238 
239 		gran = entry->ds_gran;
240 	}
241 
242 	/*
243 	 * now display `gran' items per line, printing at most `max_lines'.
244 	 */
245 
246 	for (i = 0; value[i] != '\0'; i++) {
247 		if (value[i] == ' ') {
248 			if ((++n_spaces % gran) == 0) {
249 				value[i] = '\n';
250 				if (max_lines != -1 && --max_lines == 0) {
251 					value[i] = '\0';
252 					break;
253 				}
254 			}
255 		}
256 	}
257 
258 	(void) printf("%s\n", value);
259 
260 	return (DHCP_EXIT_SUCCESS);
261 }
262