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