xref: /illumos-gate/usr/src/lib/libdhcputil/common/dhcp_inittab.c (revision d1aea6f139360e9e7f1504facb24f8521047b15c)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright (c) 2016 by Delphix. All rights reserved.
25  */
26 
27 #include <sys/types.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <stdarg.h>
33 #include <limits.h>
34 #include <ctype.h>
35 #include <libgen.h>
36 #include <sys/isa_defs.h>
37 #include <sys/socket.h>
38 #include <net/if_arp.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <sys/sysmacros.h>
42 #include <libinetutil.h>
43 #include <libdlpi.h>
44 #include <netinet/dhcp6.h>
45 
46 #include "dhcp_symbol.h"
47 #include "dhcp_inittab.h"
48 
49 static void		inittab_msg(const char *, ...);
50 static uchar_t		category_to_code(const char *);
51 static boolean_t	encode_number(uint8_t, uint8_t, boolean_t, uint8_t,
52 			    const char *, uint8_t *, int *);
53 static boolean_t	decode_number(uint8_t, uint8_t, boolean_t, uint8_t,
54 			    const uint8_t *, char *, int *);
55 static dhcp_symbol_t	*inittab_lookup(uchar_t, char, const char *, int32_t,
56 			    size_t *);
57 static dsym_category_t	itabcode_to_dsymcode(uchar_t);
58 static boolean_t	parse_entry(char *, char **);
59 
60 /*
61  * forward declaration of our internal inittab_table[].  too bulky to put
62  * up front -- check the end of this file for its definition.
63  *
64  * Note: we have only an IPv4 version here.  The inittab_verify() function is
65  * used by the DHCP server and manager.  We'll need a new function if the
66  * server is extended to DHCPv6.
67  */
68 static dhcp_symbol_t	inittab_table[];
69 
70 /*
71  * the number of fields in the inittab and names for the fields.  note that
72  * this order is meaningful to parse_entry(); other functions should just
73  * use them as indexes into the array returned from parse_entry().
74  */
75 #define	ITAB_FIELDS	7
76 enum { ITAB_NAME, ITAB_CODE, ITAB_TYPE, ITAB_GRAN, ITAB_MAX, ITAB_CONS,
77     ITAB_CAT };
78 
79 /*
80  * the category_map_entry_t is used to map the inittab category codes to
81  * the dsym codes.  the reason the codes are different is that the inittab
82  * needs to have the codes be ORable such that queries can retrieve more
83  * than one category at a time.  this map is also used to map the inittab
84  * string representation of a category to its numerical code.
85  */
86 typedef struct category_map_entry {
87 	dsym_category_t	cme_dsymcode;
88 	char		*cme_name;
89 	uchar_t		cme_itabcode;
90 } category_map_entry_t;
91 
92 static category_map_entry_t category_map[] = {
93 	{ DSYM_STANDARD,	"STANDARD",	ITAB_CAT_STANDARD },
94 	{ DSYM_FIELD,		"FIELD",	ITAB_CAT_FIELD },
95 	{ DSYM_INTERNAL,	"INTERNAL",	ITAB_CAT_INTERNAL },
96 	{ DSYM_VENDOR,		"VENDOR",	ITAB_CAT_VENDOR },
97 	{ DSYM_SITE,		"SITE",		ITAB_CAT_SITE }
98 };
99 
100 /*
101  * inittab_load(): returns all inittab entries with the specified criteria
102  *
103  *   input: uchar_t: the categories the consumer is interested in
104  *	    char: the consumer type of the caller
105  *	    size_t *: set to the number of entries returned
106  *  output: dhcp_symbol_t *: an array of dynamically allocated entries
107  *	    on success, NULL upon failure
108  */
109 
110 dhcp_symbol_t	*
111 inittab_load(uchar_t categories, char consumer, size_t *n_entries)
112 {
113 	return (inittab_lookup(categories, consumer, NULL, -1, n_entries));
114 }
115 
116 /*
117  * inittab_getbyname(): returns an inittab entry with the specified criteria
118  *
119  *   input: int: the categories the consumer is interested in
120  *	    char: the consumer type of the caller
121  *	    char *: the name of the inittab entry the consumer wants
122  *  output: dhcp_symbol_t *: a dynamically allocated dhcp_symbol structure
123  *	    on success, NULL upon failure
124  */
125 
126 dhcp_symbol_t	*
127 inittab_getbyname(uchar_t categories, char consumer, const char *name)
128 {
129 	return (inittab_lookup(categories, consumer, name, -1, NULL));
130 }
131 
132 /*
133  * inittab_getbycode(): returns an inittab entry with the specified criteria
134  *
135  *   input: uchar_t: the categories the consumer is interested in
136  *	    char: the consumer type of the caller
137  *	    uint16_t: the code of the inittab entry the consumer wants
138  *  output: dhcp_symbol_t *: a dynamically allocated dhcp_symbol structure
139  *	    on success, NULL upon failure
140  */
141 
142 dhcp_symbol_t	*
143 inittab_getbycode(uchar_t categories, char consumer, uint16_t code)
144 {
145 	return (inittab_lookup(categories, consumer, NULL, code, NULL));
146 }
147 
148 /*
149  * inittab_lookup(): returns inittab entries with the specified criteria
150  *
151  *   input: uchar_t: the categories the consumer is interested in
152  *	    char: the consumer type of the caller
153  *	    const char *: the name of the entry the caller is interested
154  *		in, or NULL if the caller doesn't care
155  *	    int32_t: the code the caller is interested in, or -1 if the
156  *		caller doesn't care
157  *	    size_t *: set to the number of entries returned
158  *  output: dhcp_symbol_t *: dynamically allocated dhcp_symbol structures
159  *	    on success, NULL upon failure
160  */
161 
162 static dhcp_symbol_t *
163 inittab_lookup(uchar_t categories, char consumer, const char *name,
164     int32_t code, size_t *n_entriesp)
165 {
166 	FILE			*inittab_fp;
167 	dhcp_symbol_t		*new_entries, *entries = NULL;
168 	dhcp_symbol_t		entry;
169 	char			buffer[ITAB_MAX_LINE_LEN];
170 	char			*fields[ITAB_FIELDS];
171 	unsigned long		line = 0;
172 	size_t			i, n_entries = 0;
173 	const char		*inittab_path;
174 	uchar_t			category_code;
175 	dsym_cdtype_t		type;
176 
177 	if (categories & ITAB_CAT_V6) {
178 		inittab_path = getenv("DHCP_INITTAB6_PATH");
179 		if (inittab_path == NULL)
180 			inittab_path = ITAB_INITTAB6_PATH;
181 	} else {
182 		inittab_path = getenv("DHCP_INITTAB_PATH");
183 		if (inittab_path == NULL)
184 			inittab_path = ITAB_INITTAB_PATH;
185 	}
186 
187 	inittab_fp = fopen(inittab_path, "r");
188 	if (inittab_fp == NULL) {
189 		inittab_msg("inittab_lookup: fopen: %s: %s",
190 		    inittab_path, strerror(errno));
191 		return (NULL);
192 	}
193 
194 	(void) bufsplit(",\n", 0, NULL);
195 	while (fgets(buffer, sizeof (buffer), inittab_fp) != NULL) {
196 
197 		line++;
198 
199 		/*
200 		 * make sure the string didn't overflow our buffer
201 		 */
202 		if (strchr(buffer, '\n') == NULL) {
203 			inittab_msg("inittab_lookup: line %li: too long, "
204 			    "skipping", line);
205 			continue;
206 		}
207 
208 		/*
209 		 * skip `pure comment' lines
210 		 */
211 		for (i = 0; buffer[i] != '\0'; i++)
212 			if (isspace(buffer[i]) == 0)
213 				break;
214 
215 		if (buffer[i] == ITAB_COMMENT_CHAR || buffer[i] == '\0')
216 			continue;
217 
218 		/*
219 		 * parse the entry out into fields.
220 		 */
221 		if (parse_entry(buffer, fields) == B_FALSE) {
222 			inittab_msg("inittab_lookup: line %li: syntax error, "
223 			    "skipping", line);
224 			continue;
225 		}
226 
227 		/*
228 		 * validate the values in the entries; skip if invalid.
229 		 */
230 		if (atoi(fields[ITAB_GRAN]) > ITAB_GRAN_MAX) {
231 			inittab_msg("inittab_lookup: line %li: granularity `%s'"
232 			    " out of range, skipping", line, fields[ITAB_GRAN]);
233 			continue;
234 		}
235 
236 		if (atoi(fields[ITAB_MAX]) > ITAB_MAX_MAX) {
237 			inittab_msg("inittab_lookup: line %li: maximum `%s' "
238 			    "out of range, skipping", line, fields[ITAB_MAX]);
239 			continue;
240 		}
241 
242 		if (dsym_get_type_id(fields[ITAB_TYPE], &type, B_FALSE) !=
243 		    DSYM_SUCCESS) {
244 			inittab_msg("inittab_lookup: line %li: type `%s' "
245 			    "is invalid, skipping", line, fields[ITAB_TYPE]);
246 			continue;
247 		}
248 
249 		/*
250 		 * find out whether this entry of interest to our consumer,
251 		 * and if so, throw it onto the set of entries we'll return.
252 		 * check categories last since it's the most expensive check.
253 		 */
254 		if (strchr(fields[ITAB_CONS], consumer) == NULL)
255 			continue;
256 
257 		if (code != -1 && atoi(fields[ITAB_CODE]) != code)
258 			continue;
259 
260 		if (name != NULL && strcasecmp(fields[ITAB_NAME], name) != 0)
261 			continue;
262 
263 		category_code = category_to_code(fields[ITAB_CAT]);
264 		if ((category_code & categories) == 0)
265 			continue;
266 
267 		/*
268 		 * looks like a match.  allocate an entry and fill it in
269 		 */
270 		new_entries = realloc(entries, (n_entries + 1) *
271 		    sizeof (dhcp_symbol_t));
272 
273 		/*
274 		 * if we run out of memory, might as well return what we can
275 		 */
276 		if (new_entries == NULL) {
277 			inittab_msg("inittab_lookup: ran out of memory "
278 			    "allocating dhcp_symbol_t's");
279 			break;
280 		}
281 
282 		entry.ds_max	  = atoi(fields[ITAB_MAX]);
283 		entry.ds_code	  = atoi(fields[ITAB_CODE]);
284 		entry.ds_type	  = type;
285 		entry.ds_gran	  = atoi(fields[ITAB_GRAN]);
286 		entry.ds_category = itabcode_to_dsymcode(category_code);
287 		entry.ds_classes.dc_cnt	  = 0;
288 		entry.ds_classes.dc_names = NULL;
289 		(void) strlcpy(entry.ds_name, fields[ITAB_NAME],
290 		    sizeof (entry.ds_name));
291 		entry.ds_dhcpv6	  = (categories & ITAB_CAT_V6) ? 1 : 0;
292 
293 		entries = new_entries;
294 		entries[n_entries++] = entry;
295 	}
296 
297 	if (ferror(inittab_fp) != 0) {
298 		inittab_msg("inittab_lookup: error on inittab stream");
299 		clearerr(inittab_fp);
300 	}
301 
302 	(void) fclose(inittab_fp);
303 
304 	if (n_entriesp != NULL)
305 		*n_entriesp = n_entries;
306 
307 	return (entries);
308 }
309 
310 /*
311  * parse_entry(): parses an entry out into its constituent fields
312  *
313  *   input: char *: the entry
314  *	    char **: an array of ITAB_FIELDS length which contains
315  *		     pointers into the entry on upon return
316  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
317  */
318 
319 static boolean_t
320 parse_entry(char *entry, char **fields)
321 {
322 	char	*category, *spacep;
323 	size_t	n_fields, i;
324 
325 	/*
326 	 * due to a mistake made long ago, the first and second fields of
327 	 * each entry are not separated by a comma, but rather by
328 	 * whitespace -- have bufsplit() treat the two fields as one, then
329 	 * pull them apart afterwards.
330 	 */
331 	n_fields = bufsplit(entry, ITAB_FIELDS - 1, fields);
332 	if (n_fields != (ITAB_FIELDS - 1))
333 		return (B_FALSE);
334 
335 	/*
336 	 * pull the first and second fields apart.  this is complicated
337 	 * since the first field can contain embedded whitespace (so we
338 	 * must separate the two fields by the last span of whitespace).
339 	 *
340 	 * first, find the initial span of whitespace.  if there isn't one,
341 	 * then the entry is malformed.
342 	 */
343 	category = strpbrk(fields[ITAB_NAME], " \t");
344 	if (category == NULL)
345 		return (B_FALSE);
346 
347 	/*
348 	 * find the last span of whitespace.
349 	 */
350 	do {
351 		while (isspace(*category))
352 			category++;
353 
354 		spacep = strpbrk(category, " \t");
355 		if (spacep != NULL)
356 			category = spacep;
357 	} while (spacep != NULL);
358 
359 	/*
360 	 * NUL-terminate the first byte of the last span of whitespace, so
361 	 * that the first field doesn't have any residual trailing
362 	 * whitespace.
363 	 */
364 	spacep = category - 1;
365 	while (isspace(*spacep))
366 		spacep--;
367 
368 	if (spacep <= fields[0])
369 		return (B_FALSE);
370 
371 	*++spacep = '\0';
372 
373 	/*
374 	 * remove any whitespace from the fields.
375 	 */
376 	for (i = 0; i < n_fields; i++) {
377 		while (isspace(*fields[i]))
378 			fields[i]++;
379 	}
380 	fields[ITAB_CAT] = category;
381 
382 	return (B_TRUE);
383 }
384 
385 /*
386  * inittab_verify(): verifies that a given inittab entry matches an internal
387  *		     definition
388  *
389  *   input: dhcp_symbol_t *: the inittab entry to verify
390  *	    dhcp_symbol_t *: if non-NULL, a place to store the internal
391  *			       inittab entry upon return
392  *  output: int: ITAB_FAILURE, ITAB_SUCCESS, or ITAB_UNKNOWN
393  *
394  *   notes: IPv4 only
395  */
396 
397 int
398 inittab_verify(const dhcp_symbol_t *inittab_ent, dhcp_symbol_t *internal_ent)
399 {
400 	unsigned int	i;
401 
402 	for (i = 0; inittab_table[i].ds_name[0] != '\0'; i++) {
403 
404 		if (inittab_ent->ds_category != inittab_table[i].ds_category)
405 			continue;
406 
407 		if (inittab_ent->ds_code == inittab_table[i].ds_code) {
408 			if (internal_ent != NULL)
409 				*internal_ent = inittab_table[i];
410 
411 			if (inittab_table[i].ds_type != inittab_ent->ds_type ||
412 			    inittab_table[i].ds_gran != inittab_ent->ds_gran ||
413 			    inittab_table[i].ds_max  != inittab_ent->ds_max)
414 				return (ITAB_FAILURE);
415 
416 			return (ITAB_SUCCESS);
417 		}
418 	}
419 
420 	return (ITAB_UNKNOWN);
421 }
422 
423 /*
424  * get_hw_type(): interpret ",hwtype" in the input string, as part of a DUID.
425  *		  The hwtype string is optional, and must be 0-65535 if
426  *		  present.
427  *
428  *   input: char **: pointer to string pointer
429  *	    int *: error return value
430  *  output: int: hardware type, or -1 for empty, or -2 for error.
431  */
432 
433 static int
434 get_hw_type(char **strp, int *ierrnop)
435 {
436 	char *str = *strp;
437 	ulong_t hwtype;
438 
439 	if (*str++ != ',') {
440 		*ierrnop = ITAB_BAD_NUMBER;
441 		return (-2);
442 	}
443 	if (*str == ',' || *str == '\0') {
444 		*strp = str;
445 		return (-1);
446 	}
447 	hwtype = strtoul(str, strp, 0);
448 	if (errno != 0 || *strp == str || hwtype > 65535) {
449 		*ierrnop = ITAB_BAD_NUMBER;
450 		return (-2);
451 	} else {
452 		return ((int)hwtype);
453 	}
454 }
455 
456 /*
457  * get_mac_addr(): interpret ",macaddr" in the input string, as part of a DUID.
458  *		   The 'macaddr' may be a hex string (in any standard format),
459  *		   or the name of a physical interface.  If an interface name
460  *		   is given, then the interface type is extracted as well.
461  *
462  *   input: const char *: input string
463  *	    int *: error return value
464  *	    uint16_t *: hardware type output (network byte order)
465  *	    int: hardware type input; -1 for empty
466  *	    uchar_t *: output buffer for MAC address
467  *  output: int: length of MAC address, or -1 for error
468  */
469 
470 static int
471 get_mac_addr(const char *str, int *ierrnop, uint16_t *hwret, int hwtype,
472     uchar_t *outbuf)
473 {
474 	int maclen;
475 	int dig, val;
476 	dlpi_handle_t dh;
477 	dlpi_info_t dlinfo;
478 	char chr;
479 
480 	if (*str != '\0') {
481 		if (*str++ != ',')
482 			goto failed;
483 		if (dlpi_open(str, &dh, 0) != DLPI_SUCCESS) {
484 			maclen = 0;
485 			dig = val = 0;
486 			/*
487 			 * Allow MAC addresses with separators matching regexp
488 			 * (:|-| *).
489 			 */
490 			while ((chr = *str++) != '\0') {
491 				if (isdigit(chr)) {
492 					val = (val << 4) + chr - '0';
493 				} else if (isxdigit(chr)) {
494 					val = (val << 4) + chr -
495 					    (isupper(chr) ? 'A' : 'a') + 10;
496 				} else if (isspace(chr) && dig == 0) {
497 					continue;
498 				} else if (chr == ':' || chr == '-' ||
499 				    isspace(chr)) {
500 					dig = 1;
501 				} else {
502 					goto failed;
503 				}
504 				if (++dig == 2) {
505 					*outbuf++ = val;
506 					maclen++;
507 					dig = val = 0;
508 				}
509 			}
510 		} else {
511 			if (dlpi_bind(dh, DLPI_ANY_SAP, NULL) !=
512 			    DLPI_SUCCESS || dlpi_info(dh, &dlinfo, 0) !=
513 			    DLPI_SUCCESS) {
514 				dlpi_close(dh);
515 				goto failed;
516 			}
517 			maclen = dlinfo.di_physaddrlen;
518 			(void) memcpy(outbuf, dlinfo.di_physaddr, maclen);
519 			dlpi_close(dh);
520 			if (hwtype == -1)
521 				hwtype = dlpi_arptype(dlinfo.di_mactype);
522 		}
523 	}
524 	if (hwtype == -1)
525 		goto failed;
526 	*hwret = htons(hwtype);
527 	return (maclen);
528 
529 failed:
530 	*ierrnop = ITAB_BAD_NUMBER;
531 	return (-1);
532 }
533 
534 /*
535  * inittab_encode_e(): converts a string representation of a given datatype into
536  *		     binary; used for encoding ascii values into a form that
537  *		     can be put in DHCP packets to be sent on the wire.
538  *
539  *   input: const dhcp_symbol_t *: the entry describing the value option
540  *	    const char *: the value to convert
541  *	    uint16_t *: set to the length of the binary data returned
542  *	    boolean_t: if false, return a full DHCP option
543  *	    int *: error return value
544  *  output: uchar_t *: a dynamically allocated byte array with converted data
545  */
546 
547 uchar_t *
548 inittab_encode_e(const dhcp_symbol_t *ie, const char *value, uint16_t *lengthp,
549     boolean_t just_payload, int *ierrnop)
550 {
551 	int		hlen = 0;
552 	uint16_t	length;
553 	uchar_t		n_entries = 0;
554 	const char	*valuep;
555 	char		*currp;
556 	uchar_t		*result = NULL;
557 	uchar_t		*optstart;
558 	unsigned int	i;
559 	uint8_t		type_size = inittab_type_to_size(ie);
560 	boolean_t	is_signed;
561 	uint_t		vallen, reslen;
562 	dhcpv6_option_t	*d6o;
563 	int		type;
564 	char		*cp2;
565 
566 	*ierrnop = 0;
567 	if (type_size == 0) {
568 		*ierrnop = ITAB_SYNTAX_ERROR;
569 		return (NULL);
570 	}
571 
572 	switch (ie->ds_type) {
573 	case DSYM_ASCII:
574 		n_entries = strlen(value);		/* no NUL */
575 		break;
576 
577 	case DSYM_OCTET:
578 		vallen = strlen(value);
579 		n_entries = vallen / 2;
580 		n_entries += vallen % 2;
581 		break;
582 
583 	case DSYM_DOMAIN:
584 		/*
585 		 * Maximum (worst-case) encoded length is one byte more than
586 		 * the number of characters on input.
587 		 */
588 		n_entries = strlen(value) + 1;
589 		break;
590 
591 	case DSYM_DUID:
592 		/* Worst case is ":::::" */
593 		n_entries = strlen(value);
594 		if (n_entries < DLPI_PHYSADDR_MAX)
595 			n_entries = DLPI_PHYSADDR_MAX;
596 		n_entries += sizeof (duid_llt_t);
597 		break;
598 
599 	default:
600 		/*
601 		 * figure out the number of entries by counting the spaces
602 		 * in the value string
603 		 */
604 		for (valuep = value; valuep++ != NULL; n_entries++)
605 			valuep = strchr(valuep, ' ');
606 		break;
607 	}
608 
609 	/*
610 	 * if we're gonna return a complete option, then include the
611 	 * option length and code in the size of the packet we allocate
612 	 */
613 	if (!just_payload)
614 		hlen = ie->ds_dhcpv6 ? sizeof (*d6o) : 2;
615 
616 	length = n_entries * type_size;
617 	if (hlen + length > 0)
618 		result = malloc(hlen + length);
619 
620 	if ((optstart = result) != NULL && !just_payload)
621 		optstart += hlen;
622 
623 	switch (ie->ds_type) {
624 
625 	case DSYM_ASCII:
626 
627 		if (optstart == NULL) {
628 			*ierrnop = ITAB_NOMEM;
629 			return (NULL);
630 		}
631 
632 		(void) memcpy(optstart, value, length);
633 		break;
634 
635 	case DSYM_DOMAIN:
636 		if (optstart == NULL) {
637 			*ierrnop = ITAB_NOMEM;
638 			return (NULL);
639 		}
640 
641 		/*
642 		 * Note that this encoder always presents the trailing 0-octet
643 		 * when dealing with a list.  This means that you can't have
644 		 * non-fully-qualified members anywhere but at the end of a
645 		 * list (or as the only member of the list).
646 		 */
647 		valuep = value;
648 		while (*valuep != '\0') {
649 			int dig, val, inchr;
650 			boolean_t escape;
651 			uchar_t *flen;
652 
653 			/*
654 			 * Skip over whitespace that delimits list members.
655 			 */
656 			if (isascii(*valuep) && isspace(*valuep)) {
657 				valuep++;
658 				continue;
659 			}
660 			dig = val = 0;
661 			escape = B_FALSE;
662 			flen = optstart++;
663 			while ((inchr = *valuep) != '\0') {
664 				valuep++;
665 				/*
666 				 * Just copy non-ASCII text directly to the
667 				 * output string.  This simplifies the use of
668 				 * other ctype macros below, as, unlike the
669 				 * special isascii function, they don't handle
670 				 * non-ASCII.
671 				 */
672 				if (!isascii(inchr)) {
673 					escape = B_FALSE;
674 					*optstart++ = inchr;
675 					continue;
676 				}
677 				if (escape) {
678 					/*
679 					 * Handle any of \D, \DD, or \DDD for
680 					 * a digit escape.
681 					 */
682 					if (isdigit(inchr)) {
683 						val = val * 10 + inchr - '0';
684 						if (++dig == 3) {
685 							*optstart++ = val;
686 							dig = val = 0;
687 							escape = B_FALSE;
688 						}
689 						continue;
690 					} else if (dig > 0) {
691 						/*
692 						 * User terminated \D or \DD
693 						 * with non-digit.  An error,
694 						 * but we can assume they mean
695 						 * to treat as \00D or \0DD.
696 						 */
697 						*optstart++ = val;
698 						dig = val = 0;
699 					}
700 					/* Fall through and copy character */
701 					escape = B_FALSE;
702 				} else if (inchr == '\\') {
703 					escape = B_TRUE;
704 					continue;
705 				} else if (inchr == '.') {
706 					/*
707 					 * End of component.  Write the length
708 					 * prefix.  If the component is zero
709 					 * length (i.e., ".."), the just omit
710 					 * it.
711 					 */
712 					*flen = (optstart - flen) - 1;
713 					if (*flen > 0)
714 						flen = optstart++;
715 					continue;
716 				} else if (isspace(inchr)) {
717 					/*
718 					 * Unescaped space; end of domain name
719 					 * in list.
720 					 */
721 					break;
722 				}
723 				*optstart++ = inchr;
724 			}
725 			/*
726 			 * Handle trailing escape sequence.  If string ends
727 			 * with \, then assume user wants \ at end of encoded
728 			 * string.  If it ends with \D or \DD, assume \00D or
729 			 * \0DD.
730 			 */
731 			if (escape)
732 				*optstart++ = dig > 0 ? val : '\\';
733 			*flen = (optstart - flen) - 1;
734 			/*
735 			 * If user specified FQDN with trailing '.', then above
736 			 * will result in zero for the last component length.
737 			 * We're done, and optstart already points to the start
738 			 * of the next in list.  Otherwise, we need to write a
739 			 * single zero byte to end the entry, if there are more
740 			 * entries that will be decoded.
741 			 */
742 			while (isascii(*valuep) && isspace(*valuep))
743 				valuep++;
744 			if (*flen > 0 && *valuep != '\0')
745 				*optstart++ = '\0';
746 		}
747 		length = (optstart - result) - hlen;
748 		break;
749 
750 	case DSYM_DUID:
751 		if (optstart == NULL) {
752 			*ierrnop = ITAB_NOMEM;
753 			return (NULL);
754 		}
755 
756 		errno = 0;
757 		type = strtoul(value, &currp, 0);
758 		if (errno != 0 || value == currp || type > 65535 ||
759 		    (*currp != ',' && *currp != '\0')) {
760 			free(result);
761 			*ierrnop = ITAB_BAD_NUMBER;
762 			return (NULL);
763 		}
764 		switch (type) {
765 		case DHCPV6_DUID_LLT: {
766 			duid_llt_t dllt;
767 			int hwtype;
768 			ulong_t tstamp;
769 			int maclen;
770 
771 			if ((hwtype = get_hw_type(&currp, ierrnop)) == -2) {
772 				free(result);
773 				return (NULL);
774 			}
775 			if (*currp++ != ',') {
776 				free(result);
777 				*ierrnop = ITAB_BAD_NUMBER;
778 				return (NULL);
779 			}
780 			if (*currp == ',' || *currp == '\0') {
781 				tstamp = time(NULL) - DUID_TIME_BASE;
782 			} else {
783 				tstamp = strtoul(currp, &cp2, 0);
784 				if (errno != 0 || currp == cp2) {
785 					free(result);
786 					*ierrnop = ITAB_BAD_NUMBER;
787 					return (NULL);
788 				}
789 				currp = cp2;
790 			}
791 			maclen = get_mac_addr(currp, ierrnop,
792 			    &dllt.dllt_hwtype, hwtype,
793 			    optstart + sizeof (dllt));
794 			if (maclen == -1) {
795 				free(result);
796 				return (NULL);
797 			}
798 			dllt.dllt_dutype = htons(type);
799 			dllt.dllt_time = htonl(tstamp);
800 			(void) memcpy(optstart, &dllt, sizeof (dllt));
801 			length = maclen + sizeof (dllt);
802 			break;
803 		}
804 		case DHCPV6_DUID_EN: {
805 			duid_en_t den;
806 			ulong_t enterp;
807 
808 			if (*currp++ != ',') {
809 				free(result);
810 				*ierrnop = ITAB_BAD_NUMBER;
811 				return (NULL);
812 			}
813 			enterp = strtoul(currp, &cp2, 0);
814 			DHCPV6_SET_ENTNUM(&den, enterp);
815 			if (errno != 0 || currp == cp2 ||
816 			    enterp != DHCPV6_GET_ENTNUM(&den) ||
817 			    (*cp2 != ',' && *cp2 != '\0')) {
818 				free(result);
819 				*ierrnop = ITAB_BAD_NUMBER;
820 				return (NULL);
821 			}
822 			if (*cp2 == ',')
823 				cp2++;
824 			vallen = strlen(cp2);
825 			reslen = (vallen + 1) / 2;
826 			if (hexascii_to_octet(cp2, vallen,
827 			    optstart + sizeof (den), &reslen) != 0) {
828 				free(result);
829 				*ierrnop = ITAB_BAD_NUMBER;
830 				return (NULL);
831 			}
832 			den.den_dutype = htons(type);
833 			(void) memcpy(optstart, &den, sizeof (den));
834 			length = reslen + sizeof (den);
835 			break;
836 		}
837 		case DHCPV6_DUID_LL: {
838 			duid_ll_t dll;
839 			int hwtype;
840 			int maclen;
841 
842 			if ((hwtype = get_hw_type(&currp, ierrnop)) == -2) {
843 				free(result);
844 				return (NULL);
845 			}
846 			maclen = get_mac_addr(currp, ierrnop, &dll.dll_hwtype,
847 			    hwtype, optstart + sizeof (dll));
848 			if (maclen == -1) {
849 				free(result);
850 				return (NULL);
851 			}
852 			dll.dll_dutype = htons(type);
853 			(void) memcpy(optstart, &dll, sizeof (dll));
854 			length = maclen + sizeof (dll);
855 			break;
856 		}
857 		default:
858 			if (*currp == ',')
859 				currp++;
860 			vallen = strlen(currp);
861 			reslen = (vallen + 1) / 2;
862 			if (hexascii_to_octet(currp, vallen, optstart + 2,
863 			    &reslen) != 0) {
864 				free(result);
865 				*ierrnop = ITAB_BAD_NUMBER;
866 				return (NULL);
867 			}
868 			optstart[0] = type >> 8;
869 			optstart[1] = type;
870 			length = reslen + 2;
871 			break;
872 		}
873 		break;
874 
875 	case DSYM_OCTET:
876 
877 		if (optstart == NULL) {
878 			*ierrnop = ITAB_BAD_OCTET;
879 			return (NULL);
880 		}
881 
882 		reslen = length;
883 		/* Call libinetutil function to decode */
884 		if (hexascii_to_octet(value, vallen, optstart, &reslen) != 0) {
885 			free(result);
886 			*ierrnop = ITAB_BAD_OCTET;
887 			return (NULL);
888 		}
889 		break;
890 
891 	case DSYM_IP:
892 	case DSYM_IPV6:
893 
894 		if (optstart == NULL) {
895 			*ierrnop = ITAB_BAD_IPADDR;
896 			return (NULL);
897 		}
898 		if (n_entries % ie->ds_gran != 0) {
899 			*ierrnop = ITAB_BAD_GRAN;
900 			inittab_msg("inittab_encode: number of entries "
901 			    "not compatible with option granularity");
902 			free(result);
903 			return (NULL);
904 		}
905 
906 		for (valuep = value, i = 0; i < n_entries; i++, valuep++) {
907 
908 			currp = strchr(valuep, ' ');
909 			if (currp != NULL)
910 				*currp = '\0';
911 			if (inet_pton(ie->ds_type == DSYM_IP ? AF_INET :
912 			    AF_INET6, valuep, optstart) != 1) {
913 				*ierrnop = ITAB_BAD_IPADDR;
914 				inittab_msg("inittab_encode: bogus ip address");
915 				free(result);
916 				return (NULL);
917 			}
918 
919 			valuep = currp;
920 			if (valuep == NULL) {
921 				if (i < (n_entries - 1)) {
922 					*ierrnop = ITAB_NOT_ENOUGH_IP;
923 					inittab_msg("inittab_encode: too few "
924 					    "ip addresses");
925 					free(result);
926 					return (NULL);
927 				}
928 				break;
929 			}
930 			optstart += type_size;
931 		}
932 		break;
933 
934 	case DSYM_NUMBER:				/* FALLTHRU */
935 	case DSYM_UNUMBER8:				/* FALLTHRU */
936 	case DSYM_SNUMBER8:				/* FALLTHRU */
937 	case DSYM_UNUMBER16:				/* FALLTHRU */
938 	case DSYM_SNUMBER16:				/* FALLTHRU */
939 	case DSYM_UNUMBER24:				/* FALLTHRU */
940 	case DSYM_UNUMBER32:				/* FALLTHRU */
941 	case DSYM_SNUMBER32:				/* FALLTHRU */
942 	case DSYM_UNUMBER64:				/* FALLTHRU */
943 	case DSYM_SNUMBER64:
944 
945 		if (optstart == NULL) {
946 			*ierrnop = ITAB_BAD_NUMBER;
947 			return (NULL);
948 		}
949 
950 		is_signed = (ie->ds_type == DSYM_SNUMBER64 ||
951 		    ie->ds_type == DSYM_SNUMBER32 ||
952 		    ie->ds_type == DSYM_SNUMBER16 ||
953 		    ie->ds_type == DSYM_SNUMBER8);
954 
955 		if (encode_number(n_entries, type_size, is_signed, 0, value,
956 		    optstart, ierrnop) == B_FALSE) {
957 			free(result);
958 			return (NULL);
959 		}
960 		break;
961 
962 	default:
963 		if (ie->ds_type == DSYM_BOOL)
964 			*ierrnop = ITAB_BAD_BOOLEAN;
965 		else
966 			*ierrnop = ITAB_SYNTAX_ERROR;
967 
968 		inittab_msg("inittab_encode: unsupported type `%d'",
969 		    ie->ds_type);
970 
971 		free(result);
972 		return (NULL);
973 	}
974 
975 	/*
976 	 * if just_payload is false, then we need to add the option
977 	 * code and length fields in.
978 	 */
979 	if (!just_payload) {
980 		if (ie->ds_dhcpv6) {
981 			/* LINTED: alignment */
982 			d6o = (dhcpv6_option_t *)result;
983 			d6o->d6o_code = htons(ie->ds_code);
984 			d6o->d6o_len = htons(length);
985 		} else {
986 			result[0] = ie->ds_code;
987 			result[1] = length;
988 		}
989 	}
990 
991 	if (lengthp != NULL)
992 		*lengthp = length + hlen;
993 
994 	return (result);
995 }
996 
997 /*
998  * inittab_decode_e(): converts a binary representation of a given datatype into
999  *		     a string; used for decoding DHCP options in a packet off
1000  *		     the wire into ascii
1001  *
1002  *   input: dhcp_symbol_t *: the entry describing the payload option
1003  *	    uchar_t *: the payload to convert
1004  *	    uint16_t: the payload length (only used if just_payload is true)
1005  *	    boolean_t: if false, payload is assumed to be a DHCP option
1006  *	    int *: set to extended error code if error occurs.
1007  *  output: char *: a dynamically allocated string containing the converted data
1008  */
1009 
1010 char *
1011 inittab_decode_e(const dhcp_symbol_t *ie, const uchar_t *payload,
1012     uint16_t length, boolean_t just_payload, int *ierrnop)
1013 {
1014 	char		*resultp, *result = NULL;
1015 	uint_t		n_entries;
1016 	struct in_addr	in_addr;
1017 	in6_addr_t	in6_addr;
1018 	uint8_t		type_size = inittab_type_to_size(ie);
1019 	boolean_t	is_signed;
1020 	int		type;
1021 
1022 	*ierrnop = 0;
1023 	if (type_size == 0) {
1024 		*ierrnop = ITAB_SYNTAX_ERROR;
1025 		return (NULL);
1026 	}
1027 
1028 	if (!just_payload) {
1029 		if (ie->ds_dhcpv6) {
1030 			dhcpv6_option_t d6o;
1031 
1032 			(void) memcpy(&d6o, payload, sizeof (d6o));
1033 			length = ntohs(d6o.d6o_len);
1034 			payload += sizeof (d6o);
1035 		} else {
1036 			length = payload[1];
1037 			payload += 2;
1038 		}
1039 	}
1040 
1041 	/*
1042 	 * figure out the number of elements to convert.  note that
1043 	 * for ds_type NUMBER, the granularity is really 1 since the
1044 	 * value of ds_gran is the number of bytes in the number.
1045 	 */
1046 	if (ie->ds_type == DSYM_NUMBER)
1047 		n_entries = MIN(ie->ds_max, length / type_size);
1048 	else
1049 		n_entries = MIN(ie->ds_max * ie->ds_gran, length / type_size);
1050 
1051 	if (n_entries == 0)
1052 		n_entries = length / type_size;
1053 
1054 	if ((length % type_size) != 0) {
1055 		inittab_msg("inittab_decode: length of string not compatible "
1056 		    "with option type `%i'", ie->ds_type);
1057 		*ierrnop = ITAB_BAD_STRING;
1058 		return (NULL);
1059 	}
1060 
1061 	switch (ie->ds_type) {
1062 
1063 	case DSYM_ASCII:
1064 
1065 		result = malloc(n_entries + 1);
1066 		if (result == NULL) {
1067 			*ierrnop = ITAB_NOMEM;
1068 			return (NULL);
1069 		}
1070 
1071 		(void) memcpy(result, payload, n_entries);
1072 		result[n_entries] = '\0';
1073 		break;
1074 
1075 	case DSYM_DOMAIN:
1076 
1077 		/*
1078 		 * A valid, decoded RFC 1035 domain string or sequence of
1079 		 * strings is always the same size as the encoded form, but we
1080 		 * allow for RFC 1035 \DDD and \\ and \. escaping.
1081 		 *
1082 		 * Decoding stops at the end of the input or the first coding
1083 		 * violation.  Coding violations result in discarding the
1084 		 * offending list entry entirely.  Note that we ignore the 255
1085 		 * character overall limit on domain names.
1086 		 */
1087 		if ((result = malloc(4 * length + 1)) == NULL) {
1088 			*ierrnop = ITAB_NOMEM;
1089 			return (NULL);
1090 		}
1091 		resultp = result;
1092 		while (length > 0) {
1093 			char *dstart;
1094 			int slen;
1095 
1096 			dstart = resultp;
1097 			while (length > 0) {
1098 				slen = *payload++;
1099 				length--;
1100 				/* Upper two bits of length must be zero */
1101 				if ((slen & 0xc0) != 0 || slen > length) {
1102 					length = 0;
1103 					resultp = dstart;
1104 					break;
1105 				}
1106 				if (resultp != dstart)
1107 					*resultp++ = '.';
1108 				if (slen == 0)
1109 					break;
1110 				length -= slen;
1111 				while (slen > 0) {
1112 					if (!isascii(*payload) ||
1113 					    !isgraph(*payload)) {
1114 						(void) snprintf(resultp, 5,
1115 						    "\\%03d",
1116 						    *(unsigned char *)payload);
1117 						resultp += 4;
1118 						payload++;
1119 					} else {
1120 						if (*payload == '.' ||
1121 						    *payload == '\\')
1122 							*resultp++ = '\\';
1123 						*resultp++ = *payload++;
1124 					}
1125 					slen--;
1126 				}
1127 			}
1128 			if (resultp != dstart && length > 0)
1129 				*resultp++ = ' ';
1130 		}
1131 		*resultp = '\0';
1132 		break;
1133 
1134 	case DSYM_DUID:
1135 
1136 		/*
1137 		 * First, determine the type of DUID.  We need at least two
1138 		 * octets worth of data to grab the type code.  Once we have
1139 		 * that, the number of octets required for representation
1140 		 * depends on the type.
1141 		 */
1142 
1143 		if (length < 2) {
1144 			*ierrnop = ITAB_BAD_GRAN;
1145 			return (NULL);
1146 		}
1147 		type = (payload[0] << 8) + payload[1];
1148 		switch (type) {
1149 		case DHCPV6_DUID_LLT: {
1150 			duid_llt_t dllt;
1151 
1152 			if (length < sizeof (dllt)) {
1153 				*ierrnop = ITAB_BAD_GRAN;
1154 				return (NULL);
1155 			}
1156 			(void) memcpy(&dllt, payload, sizeof (dllt));
1157 			payload += sizeof (dllt);
1158 			length -= sizeof (dllt);
1159 			n_entries = sizeof ("1,65535,4294967295,") +
1160 			    length * 3;
1161 			if ((result = malloc(n_entries)) == NULL) {
1162 				*ierrnop = ITAB_NOMEM;
1163 				return (NULL);
1164 			}
1165 			(void) snprintf(result, n_entries, "%d,%u,%u,", type,
1166 			    ntohs(dllt.dllt_hwtype), ntohl(dllt.dllt_time));
1167 			break;
1168 		}
1169 		case DHCPV6_DUID_EN: {
1170 			duid_en_t den;
1171 
1172 			if (length < sizeof (den)) {
1173 				*ierrnop = ITAB_BAD_GRAN;
1174 				return (NULL);
1175 			}
1176 			(void) memcpy(&den, payload, sizeof (den));
1177 			payload += sizeof (den);
1178 			length -= sizeof (den);
1179 			n_entries = sizeof ("2,4294967295,") + length * 2;
1180 			if ((result = malloc(n_entries)) == NULL) {
1181 				*ierrnop = ITAB_NOMEM;
1182 				return (NULL);
1183 			}
1184 			(void) snprintf(result, n_entries, "%d,%u,", type,
1185 			    DHCPV6_GET_ENTNUM(&den));
1186 			break;
1187 		}
1188 		case DHCPV6_DUID_LL: {
1189 			duid_ll_t dll;
1190 
1191 			if (length < sizeof (dll)) {
1192 				*ierrnop = ITAB_BAD_GRAN;
1193 				return (NULL);
1194 			}
1195 			(void) memcpy(&dll, payload, sizeof (dll));
1196 			payload += sizeof (dll);
1197 			length -= sizeof (dll);
1198 			n_entries = sizeof ("3,65535,") + length * 3;
1199 			if ((result = malloc(n_entries)) == NULL) {
1200 				*ierrnop = ITAB_NOMEM;
1201 				return (NULL);
1202 			}
1203 			(void) snprintf(result, n_entries, "%d,%u,", type,
1204 			    ntohs(dll.dll_hwtype));
1205 			break;
1206 		}
1207 		default:
1208 			n_entries = sizeof ("0,") + length * 2;
1209 			if ((result = malloc(n_entries)) == NULL) {
1210 				*ierrnop = ITAB_NOMEM;
1211 				return (NULL);
1212 			}
1213 			(void) snprintf(result, n_entries, "%d,", type);
1214 			break;
1215 		}
1216 		resultp = result + strlen(result);
1217 		n_entries -= strlen(result);
1218 		if (type == DHCPV6_DUID_LLT || type == DHCPV6_DUID_LL) {
1219 			if (length > 0) {
1220 				resultp += snprintf(resultp, 3, "%02X",
1221 				    *payload++);
1222 				length--;
1223 			}
1224 			while (length-- > 0) {
1225 				resultp += snprintf(resultp, 4, ":%02X",
1226 				    *payload++);
1227 			}
1228 		} else {
1229 			while (length-- > 0) {
1230 				resultp += snprintf(resultp, 3, "%02X",
1231 				    *payload++);
1232 			}
1233 		}
1234 		break;
1235 
1236 	case DSYM_OCTET:
1237 
1238 		result = malloc(n_entries * (sizeof ("0xNN") + 1));
1239 		if (result == NULL) {
1240 			*ierrnop = ITAB_NOMEM;
1241 			return (NULL);
1242 		}
1243 
1244 		result[0] = '\0';
1245 		resultp = result;
1246 		if (n_entries > 0) {
1247 			resultp += sprintf(resultp, "0x%02X", *payload++);
1248 			n_entries--;
1249 		}
1250 		while (n_entries-- > 0)
1251 			resultp += sprintf(resultp, " 0x%02X", *payload++);
1252 
1253 		break;
1254 
1255 	case DSYM_IP:
1256 	case DSYM_IPV6:
1257 		if ((length / type_size) % ie->ds_gran != 0) {
1258 			*ierrnop = ITAB_BAD_GRAN;
1259 			inittab_msg("inittab_decode: number of entries "
1260 			    "not compatible with option granularity");
1261 			return (NULL);
1262 		}
1263 
1264 		result = malloc(n_entries * (ie->ds_type == DSYM_IP ?
1265 		    INET_ADDRSTRLEN : INET6_ADDRSTRLEN));
1266 		if (result == NULL) {
1267 			*ierrnop = ITAB_NOMEM;
1268 			return (NULL);
1269 		}
1270 
1271 		for (resultp = result; n_entries != 0; n_entries--) {
1272 			if (ie->ds_type == DSYM_IP) {
1273 				(void) memcpy(&in_addr.s_addr, payload,
1274 				    sizeof (ipaddr_t));
1275 				(void) strcpy(resultp, inet_ntoa(in_addr));
1276 			} else {
1277 				(void) memcpy(&in6_addr, payload,
1278 				    sizeof (in6_addr));
1279 				(void) inet_ntop(AF_INET6, &in6_addr, resultp,
1280 				    INET6_ADDRSTRLEN);
1281 			}
1282 			resultp += strlen(resultp);
1283 			if (n_entries > 1)
1284 				*resultp++ = ' ';
1285 			payload += type_size;
1286 		}
1287 		*resultp = '\0';
1288 		break;
1289 
1290 	case DSYM_NUMBER:				/* FALLTHRU */
1291 	case DSYM_UNUMBER8:				/* FALLTHRU */
1292 	case DSYM_SNUMBER8:				/* FALLTHRU */
1293 	case DSYM_UNUMBER16:				/* FALLTHRU */
1294 	case DSYM_SNUMBER16:				/* FALLTHRU */
1295 	case DSYM_UNUMBER32:				/* FALLTHRU */
1296 	case DSYM_SNUMBER32:				/* FALLTHRU */
1297 	case DSYM_UNUMBER64:				/* FALLTHRU */
1298 	case DSYM_SNUMBER64:
1299 
1300 		is_signed = (ie->ds_type == DSYM_SNUMBER64 ||
1301 		    ie->ds_type == DSYM_SNUMBER32 ||
1302 		    ie->ds_type == DSYM_SNUMBER16 ||
1303 		    ie->ds_type == DSYM_SNUMBER8);
1304 
1305 		result = malloc(n_entries * ITAB_MAX_NUMBER_LEN);
1306 		if (result == NULL) {
1307 			*ierrnop = ITAB_NOMEM;
1308 			return (NULL);
1309 		}
1310 
1311 		if (decode_number(n_entries, type_size, is_signed, ie->ds_gran,
1312 		    payload, result, ierrnop) == B_FALSE) {
1313 			free(result);
1314 			return (NULL);
1315 		}
1316 		break;
1317 
1318 	default:
1319 		inittab_msg("inittab_decode: unsupported type `%d'",
1320 		    ie->ds_type);
1321 		break;
1322 	}
1323 
1324 	return (result);
1325 }
1326 
1327 /*
1328  * inittab_encode(): converts a string representation of a given datatype into
1329  *		     binary; used for encoding ascii values into a form that
1330  *		     can be put in DHCP packets to be sent on the wire.
1331  *
1332  *   input: dhcp_symbol_t *: the entry describing the value option
1333  *	    const char *: the value to convert
1334  *	    uint16_t *: set to the length of the binary data returned
1335  *	    boolean_t: if false, return a full DHCP option
1336  *  output: uchar_t *: a dynamically allocated byte array with converted data
1337  */
1338 
1339 uchar_t *
1340 inittab_encode(const dhcp_symbol_t *ie, const char *value, uint16_t *lengthp,
1341     boolean_t just_payload)
1342 {
1343 	int ierrno;
1344 
1345 	return (inittab_encode_e(ie, value, lengthp, just_payload, &ierrno));
1346 }
1347 
1348 /*
1349  * inittab_decode(): converts a binary representation of a given datatype into
1350  *		     a string; used for decoding DHCP options in a packet off
1351  *		     the wire into ascii
1352  *
1353  *   input: dhcp_symbol_t *: the entry describing the payload option
1354  *	    uchar_t *: the payload to convert
1355  *	    uint16_t: the payload length (only used if just_payload is true)
1356  *	    boolean_t: if false, payload is assumed to be a DHCP option
1357  *  output: char *: a dynamically allocated string containing the converted data
1358  */
1359 
1360 char *
1361 inittab_decode(const dhcp_symbol_t *ie, const uchar_t *payload, uint16_t length,
1362     boolean_t just_payload)
1363 {
1364 	int ierrno;
1365 
1366 	return (inittab_decode_e(ie, payload, length, just_payload, &ierrno));
1367 }
1368 
1369 /*
1370  * inittab_msg(): prints diagnostic messages if INITTAB_DEBUG is set
1371  *
1372  *	    const char *: a printf-like format string
1373  *	    ...: arguments to the format string
1374  *  output: void
1375  */
1376 
1377 /*PRINTFLIKE1*/
1378 static void
1379 inittab_msg(const char *fmt, ...)
1380 {
1381 	enum { INITTAB_MSG_CHECK, INITTAB_MSG_RETURN, INITTAB_MSG_OUTPUT };
1382 
1383 	va_list		ap;
1384 	char		buf[512];
1385 	static int	action = INITTAB_MSG_CHECK;
1386 
1387 	/*
1388 	 * check DHCP_INITTAB_DEBUG the first time in; thereafter, use
1389 	 * the the cached result (stored in `action').
1390 	 */
1391 	switch (action) {
1392 
1393 	case INITTAB_MSG_CHECK:
1394 
1395 		if (getenv("DHCP_INITTAB_DEBUG") == NULL) {
1396 			action = INITTAB_MSG_RETURN;
1397 			return;
1398 		}
1399 
1400 		action = INITTAB_MSG_OUTPUT;
1401 
1402 		/* FALLTHRU into INITTAB_MSG_OUTPUT */
1403 
1404 	case INITTAB_MSG_OUTPUT:
1405 
1406 		va_start(ap, fmt);
1407 
1408 		(void) snprintf(buf, sizeof (buf), "inittab: %s\n", fmt);
1409 		(void) vfprintf(stderr, buf, ap);
1410 
1411 		va_end(ap);
1412 		break;
1413 
1414 	case INITTAB_MSG_RETURN:
1415 
1416 		return;
1417 	}
1418 }
1419 
1420 /*
1421  * decode_number(): decodes a sequence of numbers from binary into ascii;
1422  *		    binary is coming off of the network, so it is in nbo
1423  *
1424  *   input: uint8_t: the number of "granularity" numbers to decode
1425  *	    uint8_t: the length of each number
1426  *	    boolean_t: whether the numbers should be considered signed
1427  *	    uint8_t: the number of numbers per granularity
1428  *	    const uint8_t *: where to decode the numbers from
1429  *	    char *: where to decode the numbers to
1430  *  output: boolean_t: true on successful conversion, false on failure
1431  */
1432 
1433 static boolean_t
1434 decode_number(uint8_t n_entries, uint8_t size, boolean_t is_signed,
1435     uint8_t granularity, const uint8_t *from, char *to, int *ierrnop)
1436 {
1437 	uint16_t	uint16;
1438 	uint32_t	uint32;
1439 	uint64_t	uint64;
1440 
1441 	if (granularity != 0) {
1442 		if ((granularity % n_entries) != 0) {
1443 			inittab_msg("decode_number: number of entries "
1444 			    "not compatible with option granularity");
1445 			*ierrnop = ITAB_BAD_GRAN;
1446 			return (B_FALSE);
1447 		}
1448 	}
1449 
1450 	for (; n_entries != 0; n_entries--, from += size) {
1451 
1452 		switch (size) {
1453 
1454 		case 1:
1455 			to += sprintf(to, is_signed ? "%d" : "%u", *from);
1456 			break;
1457 
1458 		case 2:
1459 			(void) memcpy(&uint16, from, 2);
1460 			to += sprintf(to, is_signed ? "%hd" : "%hu",
1461 			    ntohs(uint16));
1462 			break;
1463 
1464 		case 3:
1465 			uint32 = 0;
1466 			(void) memcpy((uchar_t *)&uint32 + 1, from, 3);
1467 			to += sprintf(to, is_signed ? "%ld" : "%lu",
1468 			    ntohl(uint32));
1469 			break;
1470 
1471 		case 4:
1472 			(void) memcpy(&uint32, from, 4);
1473 			to += sprintf(to, is_signed ? "%ld" : "%lu",
1474 			    ntohl(uint32));
1475 			break;
1476 
1477 		case 8:
1478 			(void) memcpy(&uint64, from, 8);
1479 			to += sprintf(to, is_signed ? "%lld" : "%llu",
1480 			    ntohll(uint64));
1481 			break;
1482 
1483 		default:
1484 			*ierrnop = ITAB_BAD_NUMBER;
1485 			inittab_msg("decode_number: unknown integer size `%d'",
1486 			    size);
1487 			return (B_FALSE);
1488 		}
1489 		if (n_entries > 0)
1490 			*to++ = ' ';
1491 	}
1492 
1493 	*to = '\0';
1494 	return (B_TRUE);
1495 }
1496 
1497 /*
1498  * encode_number(): encodes a sequence of numbers from ascii into binary;
1499  *		    number will end up on the wire so it needs to be in nbo
1500  *
1501  *   input: uint8_t: the number of "granularity" numbers to encode
1502  *	    uint8_t: the length of each number
1503  *	    boolean_t: whether the numbers should be considered signed
1504  *	    uint8_t: the number of numbers per granularity
1505  *	    const uint8_t *: where to encode the numbers from
1506  *	    char *: where to encode the numbers to
1507  *	    int *: set to extended error code if error occurs.
1508  *  output: boolean_t: true on successful conversion, false on failure
1509  */
1510 
1511 static boolean_t /* ARGSUSED */
1512 encode_number(uint8_t n_entries, uint8_t size, boolean_t is_signed,
1513     uint8_t granularity, const char *from, uint8_t *to, int *ierrnop)
1514 {
1515 	uint8_t		i;
1516 	uint16_t	uint16;
1517 	uint32_t	uint32;
1518 	uint64_t	uint64;
1519 	char		*endptr;
1520 
1521 	if (granularity != 0) {
1522 		if ((granularity % n_entries) != 0) {
1523 			*ierrnop = ITAB_BAD_GRAN;
1524 			inittab_msg("encode_number: number of entries "
1525 			    "not compatible with option granularity");
1526 			return (B_FALSE);
1527 		}
1528 	}
1529 
1530 	for (i = 0; i < n_entries; i++, from++, to += size) {
1531 
1532 		/*
1533 		 * totally obscure c factoid: it is legal to pass a
1534 		 * string representing a negative number to strtoul().
1535 		 * in this case, strtoul() will return an unsigned
1536 		 * long that if cast to a long, would represent the
1537 		 * negative number.  we take advantage of this to
1538 		 * cut down on code here.
1539 		 */
1540 
1541 		errno = 0;
1542 		switch (size) {
1543 
1544 		case 1:
1545 			*to = strtoul(from, &endptr, 0);
1546 			if (errno != 0 || from == endptr) {
1547 				goto error;
1548 			}
1549 			break;
1550 
1551 		case 2:
1552 			uint16 = htons(strtoul(from, &endptr, 0));
1553 			if (errno != 0 || from == endptr) {
1554 				goto error;
1555 			}
1556 			(void) memcpy(to, &uint16, 2);
1557 			break;
1558 
1559 		case 3:
1560 			uint32 = htonl(strtoul(from, &endptr, 0));
1561 			if (errno != 0 || from == endptr) {
1562 				goto error;
1563 			}
1564 			(void) memcpy(to, (uchar_t *)&uint32 + 1, 3);
1565 			break;
1566 
1567 		case 4:
1568 			uint32 = htonl(strtoul(from, &endptr, 0));
1569 			if (errno != 0 || from == endptr) {
1570 				goto error;
1571 			}
1572 			(void) memcpy(to, &uint32, 4);
1573 			break;
1574 
1575 		case 8:
1576 			uint64 = htonll(strtoull(from, &endptr, 0));
1577 			if (errno != 0 || from == endptr) {
1578 				goto error;
1579 			}
1580 			(void) memcpy(to, &uint64, 8);
1581 			break;
1582 
1583 		default:
1584 			inittab_msg("encode_number: unsupported integer "
1585 			    "size `%d'", size);
1586 			return (B_FALSE);
1587 		}
1588 
1589 		from = strchr(from, ' ');
1590 		if (from == NULL)
1591 			break;
1592 	}
1593 
1594 	return (B_TRUE);
1595 
1596 error:
1597 	*ierrnop = ITAB_BAD_NUMBER;
1598 	inittab_msg("encode_number: cannot convert to integer");
1599 	return (B_FALSE);
1600 }
1601 
1602 /*
1603  * inittab_type_to_size(): given an inittab entry, returns size of one entry of
1604  *		      its type
1605  *
1606  *   input: dhcp_symbol_t *: an entry of the given type
1607  *  output: uint8_t: the size in bytes of an entry of that type
1608  */
1609 
1610 uint8_t
1611 inittab_type_to_size(const dhcp_symbol_t *ie)
1612 {
1613 	switch (ie->ds_type) {
1614 
1615 	case DSYM_DUID:
1616 	case DSYM_DOMAIN:
1617 	case DSYM_ASCII:
1618 	case DSYM_OCTET:
1619 	case DSYM_SNUMBER8:
1620 	case DSYM_UNUMBER8:
1621 
1622 		return (1);
1623 
1624 	case DSYM_SNUMBER16:
1625 	case DSYM_UNUMBER16:
1626 
1627 		return (2);
1628 
1629 	case DSYM_UNUMBER24:
1630 
1631 		return (3);
1632 
1633 	case DSYM_SNUMBER32:
1634 	case DSYM_UNUMBER32:
1635 	case DSYM_IP:
1636 
1637 		return (4);
1638 
1639 	case DSYM_SNUMBER64:
1640 	case DSYM_UNUMBER64:
1641 
1642 		return (8);
1643 
1644 	case DSYM_NUMBER:
1645 
1646 		return (ie->ds_gran);
1647 
1648 	case DSYM_IPV6:
1649 
1650 		return (sizeof (in6_addr_t));
1651 	}
1652 
1653 	return (0);
1654 }
1655 
1656 /*
1657  * itabcode_to_dsymcode(): maps an inittab category code to its dsym
1658  *                         representation
1659  *
1660  *   input: uchar_t: the inittab category code
1661  *  output: dsym_category_t: the dsym category code
1662  */
1663 
1664 static dsym_category_t
1665 itabcode_to_dsymcode(uchar_t itabcode)
1666 {
1667 
1668 	unsigned int	i;
1669 
1670 	for (i = 0; i < ITAB_CAT_COUNT; i++)
1671 		if (category_map[i].cme_itabcode == itabcode)
1672 			return (category_map[i].cme_dsymcode);
1673 
1674 	return (DSYM_BAD_CAT);
1675 }
1676 
1677 /*
1678  * category_to_code(): maps a category name to its numeric representation
1679  *
1680  *   input: const char *: the category name
1681  *  output: uchar_t: its internal code (numeric representation)
1682  */
1683 
1684 static uchar_t
1685 category_to_code(const char *category)
1686 {
1687 	unsigned int	i;
1688 
1689 	for (i = 0; i < ITAB_CAT_COUNT; i++)
1690 		if (strcasecmp(category_map[i].cme_name, category) == 0)
1691 			return (category_map[i].cme_itabcode);
1692 
1693 	return (0);
1694 }
1695 
1696 /*
1697  * our internal table of DHCP option values, used by inittab_verify()
1698  */
1699 static dhcp_symbol_t inittab_table[] =
1700 {
1701 { DSYM_INTERNAL,	1024,	"Hostname",	DSYM_BOOL,	0,	0 },
1702 { DSYM_INTERNAL,	1025,	"LeaseNeg",	DSYM_BOOL,	0,	0 },
1703 { DSYM_INTERNAL,	1026,	"EchoVC",	DSYM_BOOL,	0,	0 },
1704 { DSYM_INTERNAL,	1027,	"BootPath",	DSYM_ASCII,	1,	128 },
1705 { DSYM_FIELD,		0,	"Opcode",	DSYM_UNUMBER8,	1,	1 },
1706 { DSYM_FIELD,		1,	"Htype",	DSYM_UNUMBER8,	1,	1 },
1707 { DSYM_FIELD,		2,	"HLen",		DSYM_UNUMBER8,	1,	1 },
1708 { DSYM_FIELD,		3,	"Hops",		DSYM_UNUMBER8,	1,	1 },
1709 { DSYM_FIELD,		4,	"Xid",		DSYM_UNUMBER32,	1,	1 },
1710 { DSYM_FIELD,		8,	"Secs",		DSYM_UNUMBER16,	1,	1 },
1711 { DSYM_FIELD,		10,	"Flags",	DSYM_OCTET,	1,	2 },
1712 { DSYM_FIELD,		12,	"Ciaddr",	DSYM_IP,	1,	1 },
1713 { DSYM_FIELD,		16,	"Yiaddr",	DSYM_IP,	1,	1 },
1714 { DSYM_FIELD,		20,	"BootSrvA",	DSYM_IP,	1,	1 },
1715 { DSYM_FIELD,		24,	"Giaddr",	DSYM_IP,	1,	1 },
1716 { DSYM_FIELD,		28,	"Chaddr",	DSYM_OCTET,	1,	16 },
1717 { DSYM_FIELD,		44,	"BootSrvN",	DSYM_ASCII,	1,	64 },
1718 { DSYM_FIELD,		108,	"BootFile",	DSYM_ASCII,	1,	128 },
1719 { DSYM_FIELD,		236,	"Magic",	DSYM_OCTET,	1,	4 },
1720 { DSYM_FIELD,		240,	"Options",	DSYM_OCTET,	1,	60 },
1721 { DSYM_STANDARD,	1,	"Subnet",	DSYM_IP,	1,	1 },
1722 { DSYM_STANDARD,	2,	"UTCoffst",	DSYM_SNUMBER32,	1,	1 },
1723 { DSYM_STANDARD,	3,	"Router",	DSYM_IP,	1,	0 },
1724 { DSYM_STANDARD,	4,	"Timeserv",	DSYM_IP,	1,	0 },
1725 { DSYM_STANDARD,	5,	"IEN116ns",	DSYM_IP,	1,	0 },
1726 { DSYM_STANDARD,	6,	"DNSserv",	DSYM_IP,	1,	0 },
1727 { DSYM_STANDARD,	7,	"Logserv",	DSYM_IP,	1,	0 },
1728 { DSYM_STANDARD,	8,	"Cookie",	DSYM_IP,	1,	0 },
1729 { DSYM_STANDARD,	9,	"Lprserv",	DSYM_IP,	1,	0 },
1730 { DSYM_STANDARD,	10,	"Impress",	DSYM_IP,	1,	0 },
1731 { DSYM_STANDARD,	11,	"Resource",	DSYM_IP,	1,	0 },
1732 { DSYM_STANDARD,	12,	"Hostname",	DSYM_ASCII,	1,	0 },
1733 { DSYM_STANDARD,	13,	"Bootsize",	DSYM_UNUMBER16,	1,	1 },
1734 { DSYM_STANDARD,	14,	"Dumpfile",	DSYM_ASCII,	1,	0 },
1735 { DSYM_STANDARD,	15,	"DNSdmain",	DSYM_ASCII,	1,	0 },
1736 { DSYM_STANDARD,	16,	"Swapserv",	DSYM_IP,	1,	1 },
1737 { DSYM_STANDARD,	17,	"Rootpath",	DSYM_ASCII,	1,	0 },
1738 { DSYM_STANDARD,	18,	"ExtendP",	DSYM_ASCII,	1,	0 },
1739 { DSYM_STANDARD,	19,	"IpFwdF",	DSYM_UNUMBER8,	1,	1 },
1740 { DSYM_STANDARD,	20,	"NLrouteF",	DSYM_UNUMBER8,	1,	1 },
1741 { DSYM_STANDARD,	21,	"PFilter",	DSYM_IP,	2,	0 },
1742 { DSYM_STANDARD,	22,	"MaxIpSiz",	DSYM_UNUMBER16,	1,	1 },
1743 { DSYM_STANDARD,	23,	"IpTTL",	DSYM_UNUMBER8,	1,	1 },
1744 { DSYM_STANDARD,	24,	"PathTO",	DSYM_UNUMBER32,	1,	1 },
1745 { DSYM_STANDARD,	25,	"PathTbl",	DSYM_UNUMBER16,	1,	0 },
1746 { DSYM_STANDARD,	26,	"MTU",		DSYM_UNUMBER16,	1,	1 },
1747 { DSYM_STANDARD,	27,	"SameMtuF",	DSYM_UNUMBER8,	1,	1 },
1748 { DSYM_STANDARD,	28,	"Broadcst",	DSYM_IP,	1,	1 },
1749 { DSYM_STANDARD,	29,	"MaskDscF",	DSYM_UNUMBER8,	1,	1 },
1750 { DSYM_STANDARD,	30,	"MaskSupF",	DSYM_UNUMBER8,	1,	1 },
1751 { DSYM_STANDARD,	31,	"RDiscvyF",	DSYM_UNUMBER8,	1,	1 },
1752 { DSYM_STANDARD,	32,	"RSolictS",	DSYM_IP,	1,	1 },
1753 { DSYM_STANDARD,	33,	"StaticRt",	DSYM_IP,	2,	0 },
1754 { DSYM_STANDARD,	34,	"TrailerF",	DSYM_UNUMBER8,	1,	1 },
1755 { DSYM_STANDARD,	35,	"ArpTimeO",	DSYM_UNUMBER32,	1,	1 },
1756 { DSYM_STANDARD,	36,	"EthEncap",	DSYM_UNUMBER8,	1,	1 },
1757 { DSYM_STANDARD,	37,	"TcpTTL",	DSYM_UNUMBER8,	1,	1 },
1758 { DSYM_STANDARD,	38,	"TcpKaInt",	DSYM_UNUMBER32,	1,	1 },
1759 { DSYM_STANDARD,	39,	"TcpKaGbF",	DSYM_UNUMBER8,	1,	1 },
1760 { DSYM_STANDARD,	40,	"NISdmain",	DSYM_ASCII,	1,	0 },
1761 { DSYM_STANDARD,	41,	"NISservs",	DSYM_IP,	1,	0 },
1762 { DSYM_STANDARD,	42,	"NTPservs",	DSYM_IP,	1,	0 },
1763 { DSYM_STANDARD,	43,	"Vendor",	DSYM_OCTET,	1,	0 },
1764 { DSYM_STANDARD,	44,	"NetBNms",	DSYM_IP,	1,	0 },
1765 { DSYM_STANDARD,	45,	"NetBDsts",	DSYM_IP,	1,	0 },
1766 { DSYM_STANDARD,	46,	"NetBNdT",	DSYM_UNUMBER8,	1,	1 },
1767 { DSYM_STANDARD,	47,	"NetBScop",	DSYM_ASCII,	1,	0 },
1768 { DSYM_STANDARD,	48,	"XFontSrv",	DSYM_IP,	1,	0 },
1769 { DSYM_STANDARD,	49,	"XDispMgr",	DSYM_IP,	1,	0 },
1770 { DSYM_STANDARD,	50,	"ReqIP",	DSYM_IP,	1,	1 },
1771 { DSYM_STANDARD,	51,	"LeaseTim",	DSYM_UNUMBER32,	1,	1 },
1772 { DSYM_STANDARD,	52,	"OptOvrld",	DSYM_UNUMBER8,	1,	1 },
1773 { DSYM_STANDARD,	53,	"DHCPType",	DSYM_UNUMBER8,	1,	1 },
1774 { DSYM_STANDARD,	54,	"ServerID",	DSYM_IP,	1,	1 },
1775 { DSYM_STANDARD,	55,	"ReqList",	DSYM_OCTET,	1,	0 },
1776 { DSYM_STANDARD,	56,	"Message",	DSYM_ASCII,	1,	0 },
1777 { DSYM_STANDARD,	57,	"DHCP_MTU",	DSYM_UNUMBER16,	1,	1 },
1778 { DSYM_STANDARD,	58,	"T1Time",	DSYM_UNUMBER32,	1,	1 },
1779 { DSYM_STANDARD,	59,	"T2Time",	DSYM_UNUMBER32,	1,	1 },
1780 { DSYM_STANDARD,	60,	"ClassID",	DSYM_ASCII,	1,	0 },
1781 { DSYM_STANDARD,	61,	"ClientID",	DSYM_OCTET,	1,	0 },
1782 { DSYM_STANDARD,	62,	"NW_dmain",	DSYM_ASCII,	1,	0 },
1783 { DSYM_STANDARD,	63,	"NWIPOpts",	DSYM_OCTET,	1,	128 },
1784 { DSYM_STANDARD,	64,	"NIS+dom",	DSYM_ASCII,	1,	0 },
1785 { DSYM_STANDARD,	65,	"NIS+serv",	DSYM_IP,	1,	0 },
1786 { DSYM_STANDARD,	66,	"TFTPsrvN",	DSYM_ASCII,	1,	64 },
1787 { DSYM_STANDARD,	67,	"OptBootF",	DSYM_ASCII,	1,	128 },
1788 { DSYM_STANDARD,	68,	"MblIPAgt",	DSYM_IP,	1,	0 },
1789 { DSYM_STANDARD,	69,	"SMTPserv",	DSYM_IP,	1,	0 },
1790 { DSYM_STANDARD,	70,	"POP3serv",	DSYM_IP,	1,	0 },
1791 { DSYM_STANDARD,	71,	"NNTPserv",	DSYM_IP,	1,	0 },
1792 { DSYM_STANDARD,	72,	"WWWservs",	DSYM_IP,	1,	0 },
1793 { DSYM_STANDARD,	73,	"Fingersv",	DSYM_IP,	1,	0 },
1794 { DSYM_STANDARD,	74,	"IRCservs",	DSYM_IP,	1,	0 },
1795 { DSYM_STANDARD,	75,	"STservs",	DSYM_IP,	1,	0 },
1796 { DSYM_STANDARD,	76,	"STDAservs",	DSYM_IP,	1,	0 },
1797 { DSYM_STANDARD,	77,	"UserClas",	DSYM_ASCII,	1,	0 },
1798 { DSYM_STANDARD,	78,	"SLP_DA",	DSYM_OCTET,	1,	0 },
1799 { DSYM_STANDARD,	79,	"SLP_SS",	DSYM_OCTET,	1,	0 },
1800 { DSYM_STANDARD,	82,	"AgentOpt",	DSYM_OCTET,	1,	0 },
1801 { DSYM_STANDARD,	89,	"FQDN",		DSYM_OCTET,	1,	0 },
1802 { 0,			0,	"",		0,		0,	0 }
1803 };
1804