xref: /titanic_50/usr/src/lib/libdhcputil/common/dhcp_inittab.c (revision 4eab410fb63816fe2c0ad0fd18b4c948613f6616)
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_bind(dh, DLPI_ANY_SAP, NULL) !=
511 			    DLPI_SUCCESS || dlpi_info(dh, &dlinfo, 0) !=
512 			    DLPI_SUCCESS) {
513 				dlpi_close(dh);
514 				goto failed;
515 			}
516 			maclen = dlinfo.di_physaddrlen;
517 			(void) memcpy(outbuf, dlinfo.di_physaddr, maclen);
518 			dlpi_close(dh);
519 			if (hwtype == -1)
520 				hwtype = dlpi_arptype(dlinfo.di_mactype);
521 		}
522 	}
523 	if (hwtype == -1)
524 		goto failed;
525 	*hwret = htons(hwtype);
526 	return (maclen);
527 
528 failed:
529 	*ierrnop = ITAB_BAD_NUMBER;
530 	return (-1);
531 }
532 
533 /*
534  * inittab_encode_e(): converts a string representation of a given datatype into
535  *		     binary; used for encoding ascii values into a form that
536  *		     can be put in DHCP packets to be sent on the wire.
537  *
538  *   input: const dhcp_symbol_t *: the entry describing the value option
539  *	    const char *: the value to convert
540  *	    uint16_t *: set to the length of the binary data returned
541  *	    boolean_t: if false, return a full DHCP option
542  *	    int *: error return value
543  *  output: uchar_t *: a dynamically allocated byte array with converted data
544  */
545 
546 uchar_t *
547 inittab_encode_e(const dhcp_symbol_t *ie, const char *value, uint16_t *lengthp,
548     boolean_t just_payload, int *ierrnop)
549 {
550 	int		hlen = 0;
551 	uint16_t	length;
552 	uchar_t		n_entries = 0;
553 	const char	*valuep;
554 	char		*currp;
555 	uchar_t		*result = NULL;
556 	uchar_t		*optstart;
557 	unsigned int	i;
558 	uint8_t		type_size = inittab_type_to_size(ie);
559 	boolean_t	is_signed;
560 	uint_t		vallen, reslen;
561 	dhcpv6_option_t	*d6o;
562 	int		type;
563 	char		*cp2;
564 
565 	*ierrnop = 0;
566 	if (type_size == 0) {
567 		*ierrnop = ITAB_SYNTAX_ERROR;
568 		return (NULL);
569 	}
570 
571 	switch (ie->ds_type) {
572 	case DSYM_ASCII:
573 		n_entries = strlen(value);		/* no NUL */
574 		break;
575 
576 	case DSYM_OCTET:
577 		vallen = strlen(value);
578 		n_entries = vallen / 2;
579 		n_entries += vallen % 2;
580 		break;
581 
582 	case DSYM_DOMAIN:
583 		/*
584 		 * Maximum (worst-case) encoded length is one byte more than
585 		 * the number of characters on input.
586 		 */
587 		n_entries = strlen(value) + 1;
588 		break;
589 
590 	case DSYM_DUID:
591 		/* Worst case is ":::::" */
592 		n_entries = strlen(value);
593 		if (n_entries < DLPI_PHYSADDR_MAX)
594 			n_entries = DLPI_PHYSADDR_MAX;
595 		n_entries += sizeof (duid_llt_t);
596 		break;
597 
598 	default:
599 		/*
600 		 * figure out the number of entries by counting the spaces
601 		 * in the value string
602 		 */
603 		for (valuep = value; valuep++ != NULL; n_entries++)
604 			valuep = strchr(valuep, ' ');
605 		break;
606 	}
607 
608 	/*
609 	 * if we're gonna return a complete option, then include the
610 	 * option length and code in the size of the packet we allocate
611 	 */
612 	if (!just_payload)
613 		hlen = ie->ds_dhcpv6 ? sizeof (*d6o) : 2;
614 
615 	length = n_entries * type_size;
616 	if (hlen + length > 0)
617 		result = malloc(hlen + length);
618 
619 	if ((optstart = result) != NULL && !just_payload)
620 		optstart += hlen;
621 
622 	switch (ie->ds_type) {
623 
624 	case DSYM_ASCII:
625 
626 		if (optstart == NULL) {
627 			*ierrnop = ITAB_NOMEM;
628 			return (NULL);
629 		}
630 
631 		(void) memcpy(optstart, value, length);
632 		break;
633 
634 	case DSYM_DOMAIN:
635 		if (optstart == NULL) {
636 			*ierrnop = ITAB_NOMEM;
637 			return (NULL);
638 		}
639 
640 		/*
641 		 * Note that this encoder always presents the trailing 0-octet
642 		 * when dealing with a list.  This means that you can't have
643 		 * non-fully-qualified members anywhere but at the end of a
644 		 * list (or as the only member of the list).
645 		 */
646 		valuep = value;
647 		while (*valuep != '\0') {
648 			int dig, val, inchr;
649 			boolean_t escape;
650 			uchar_t *flen;
651 
652 			/*
653 			 * Skip over whitespace that delimits list members.
654 			 */
655 			if (isascii(*valuep) && isspace(*valuep)) {
656 				valuep++;
657 				continue;
658 			}
659 			dig = val = 0;
660 			escape = B_FALSE;
661 			flen = optstart++;
662 			while ((inchr = *valuep) != '\0') {
663 				valuep++;
664 				/*
665 				 * Just copy non-ASCII text directly to the
666 				 * output string.  This simplifies the use of
667 				 * other ctype macros below, as, unlike the
668 				 * special isascii function, they don't handle
669 				 * non-ASCII.
670 				 */
671 				if (!isascii(inchr)) {
672 					escape = B_FALSE;
673 					*optstart++ = inchr;
674 					continue;
675 				}
676 				if (escape) {
677 					/*
678 					 * Handle any of \D, \DD, or \DDD for
679 					 * a digit escape.
680 					 */
681 					if (isdigit(inchr)) {
682 						val = val * 10 + inchr - '0';
683 						if (++dig == 3) {
684 							*optstart++ = val;
685 							dig = val = 0;
686 							escape = B_FALSE;
687 						}
688 						continue;
689 					} else if (dig > 0) {
690 						/*
691 						 * User terminated \D or \DD
692 						 * with non-digit.  An error,
693 						 * but we can assume he means
694 						 * to treat as \00D or \0DD.
695 						 */
696 						*optstart++ = val;
697 						dig = val = 0;
698 					}
699 					/* Fall through and copy character */
700 					escape = B_FALSE;
701 				} else if (inchr == '\\') {
702 					escape = B_TRUE;
703 					continue;
704 				} else if (inchr == '.') {
705 					/*
706 					 * End of component.  Write the length
707 					 * prefix.  If the component is zero
708 					 * length (i.e., ".."), the just omit
709 					 * it.
710 					 */
711 					*flen = (optstart - flen) - 1;
712 					if (*flen > 0)
713 						flen = optstart++;
714 					continue;
715 				} else if (isspace(inchr)) {
716 					/*
717 					 * Unescaped space; end of domain name
718 					 * in list.
719 					 */
720 					break;
721 				}
722 				*optstart++ = inchr;
723 			}
724 			/*
725 			 * Handle trailing escape sequence.  If string ends
726 			 * with \, then assume user wants \ at end of encoded
727 			 * string.  If it ends with \D or \DD, assume \00D or
728 			 * \0DD.
729 			 */
730 			if (escape)
731 				*optstart++ = dig > 0 ? val : '\\';
732 			*flen = (optstart - flen) - 1;
733 			/*
734 			 * If user specified FQDN with trailing '.', then above
735 			 * will result in zero for the last component length.
736 			 * We're done, and optstart already points to the start
737 			 * of the next in list.  Otherwise, we need to write a
738 			 * single zero byte to end the entry, if there are more
739 			 * entries that will be decoded.
740 			 */
741 			while (isascii(*valuep) && isspace(*valuep))
742 				valuep++;
743 			if (*flen > 0 && *valuep != '\0')
744 				*optstart++ = '\0';
745 		}
746 		length = (optstart - result) - hlen;
747 		break;
748 
749 	case DSYM_DUID:
750 		if (optstart == NULL) {
751 			*ierrnop = ITAB_NOMEM;
752 			return (NULL);
753 		}
754 
755 		errno = 0;
756 		type = strtoul(value, &currp, 0);
757 		if (errno != 0 || value == currp || type > 65535 ||
758 		    (*currp != ',' && *currp != '\0')) {
759 			free(result);
760 			*ierrnop = ITAB_BAD_NUMBER;
761 			return (NULL);
762 		}
763 		switch (type) {
764 		case DHCPV6_DUID_LLT: {
765 			duid_llt_t dllt;
766 			int hwtype;
767 			ulong_t tstamp;
768 			int maclen;
769 
770 			if ((hwtype = get_hw_type(&currp, ierrnop)) == -2) {
771 				free(result);
772 				return (NULL);
773 			}
774 			if (*currp++ != ',') {
775 				free(result);
776 				*ierrnop = ITAB_BAD_NUMBER;
777 				return (NULL);
778 			}
779 			if (*currp == ',' || *currp == '\0') {
780 				tstamp = time(NULL) - DUID_TIME_BASE;
781 			} else {
782 				tstamp = strtoul(currp, &cp2, 0);
783 				if (errno != 0 || currp == cp2) {
784 					free(result);
785 					*ierrnop = ITAB_BAD_NUMBER;
786 					return (NULL);
787 				}
788 				currp = cp2;
789 			}
790 			maclen = get_mac_addr(currp, ierrnop,
791 			    &dllt.dllt_hwtype, hwtype,
792 			    optstart + sizeof (dllt));
793 			if (maclen == -1) {
794 				free(result);
795 				return (NULL);
796 			}
797 			dllt.dllt_dutype = htons(type);
798 			dllt.dllt_time = htonl(tstamp);
799 			(void) memcpy(optstart, &dllt, sizeof (dllt));
800 			length = maclen + sizeof (dllt);
801 			break;
802 		}
803 		case DHCPV6_DUID_EN: {
804 			duid_en_t den;
805 			ulong_t enterp;
806 
807 			if (*currp++ != ',') {
808 				free(result);
809 				*ierrnop = ITAB_BAD_NUMBER;
810 				return (NULL);
811 			}
812 			enterp = strtoul(currp, &cp2, 0);
813 			DHCPV6_SET_ENTNUM(&den, enterp);
814 			if (errno != 0 || currp == cp2 ||
815 			    enterp != DHCPV6_GET_ENTNUM(&den) ||
816 			    (*cp2 != ',' && *cp2 != '\0')) {
817 				free(result);
818 				*ierrnop = ITAB_BAD_NUMBER;
819 				return (NULL);
820 			}
821 			if (*cp2 == ',')
822 				cp2++;
823 			vallen = strlen(cp2);
824 			reslen = (vallen + 1) / 2;
825 			if (hexascii_to_octet(cp2, vallen,
826 			    optstart + sizeof (den), &reslen) != 0) {
827 				free(result);
828 				*ierrnop = ITAB_BAD_NUMBER;
829 				return (NULL);
830 			}
831 			den.den_dutype = htons(type);
832 			(void) memcpy(optstart, &den, sizeof (den));
833 			length = reslen + sizeof (den);
834 			break;
835 		}
836 		case DHCPV6_DUID_LL: {
837 			duid_ll_t dll;
838 			int hwtype;
839 			int maclen;
840 
841 			if ((hwtype = get_hw_type(&currp, ierrnop)) == -2) {
842 				free(result);
843 				return (NULL);
844 			}
845 			maclen = get_mac_addr(currp, ierrnop, &dll.dll_hwtype,
846 			    hwtype, optstart + sizeof (dll));
847 			if (maclen == -1) {
848 				free(result);
849 				return (NULL);
850 			}
851 			dll.dll_dutype = htons(type);
852 			(void) memcpy(optstart, &dll, sizeof (dll));
853 			length = maclen + sizeof (dll);
854 			break;
855 		}
856 		default:
857 			if (*currp == ',')
858 				currp++;
859 			vallen = strlen(currp);
860 			reslen = (vallen + 1) / 2;
861 			if (hexascii_to_octet(currp, vallen, optstart + 2,
862 			    &reslen) != 0) {
863 				free(result);
864 				*ierrnop = ITAB_BAD_NUMBER;
865 				return (NULL);
866 			}
867 			optstart[0] = type >> 8;
868 			optstart[1] = type;
869 			length = reslen + 2;
870 			break;
871 		}
872 		break;
873 
874 	case DSYM_OCTET:
875 
876 		if (optstart == NULL) {
877 			*ierrnop = ITAB_BAD_OCTET;
878 			return (NULL);
879 		}
880 
881 		reslen = length;
882 		/* Call libinetutil function to decode */
883 		if (hexascii_to_octet(value, vallen, optstart, &reslen) != 0) {
884 			free(result);
885 			*ierrnop = ITAB_BAD_OCTET;
886 			return (NULL);
887 		}
888 		break;
889 
890 	case DSYM_IP:
891 	case DSYM_IPV6:
892 
893 		if (optstart == NULL) {
894 			*ierrnop = ITAB_BAD_IPADDR;
895 			return (NULL);
896 		}
897 		if (n_entries % ie->ds_gran != 0) {
898 			*ierrnop = ITAB_BAD_GRAN;
899 			inittab_msg("inittab_encode: number of entries "
900 			    "not compatible with option granularity");
901 			free(result);
902 			return (NULL);
903 		}
904 
905 		for (valuep = value, i = 0; i < n_entries; i++, valuep++) {
906 
907 			currp = strchr(valuep, ' ');
908 			if (currp != NULL)
909 				*currp = '\0';
910 			if (inet_pton(ie->ds_type == DSYM_IP ? AF_INET :
911 			    AF_INET6, valuep, optstart) != 1) {
912 				*ierrnop = ITAB_BAD_IPADDR;
913 				inittab_msg("inittab_encode: bogus ip address");
914 				free(result);
915 				return (NULL);
916 			}
917 
918 			valuep = currp;
919 			if (valuep == NULL) {
920 				if (i < (n_entries - 1)) {
921 					*ierrnop = ITAB_NOT_ENOUGH_IP;
922 					inittab_msg("inittab_encode: too few "
923 					    "ip addresses");
924 					free(result);
925 					return (NULL);
926 				}
927 				break;
928 			}
929 			optstart += type_size;
930 		}
931 		break;
932 
933 	case DSYM_NUMBER:				/* FALLTHRU */
934 	case DSYM_UNUMBER8:				/* FALLTHRU */
935 	case DSYM_SNUMBER8:				/* FALLTHRU */
936 	case DSYM_UNUMBER16:				/* FALLTHRU */
937 	case DSYM_SNUMBER16:				/* FALLTHRU */
938 	case DSYM_UNUMBER24:				/* FALLTHRU */
939 	case DSYM_UNUMBER32:				/* FALLTHRU */
940 	case DSYM_SNUMBER32:				/* FALLTHRU */
941 	case DSYM_UNUMBER64:				/* FALLTHRU */
942 	case DSYM_SNUMBER64:
943 
944 		if (optstart == NULL) {
945 			*ierrnop = ITAB_BAD_NUMBER;
946 			return (NULL);
947 		}
948 
949 		is_signed = (ie->ds_type == DSYM_SNUMBER64 ||
950 		    ie->ds_type == DSYM_SNUMBER32 ||
951 		    ie->ds_type == DSYM_SNUMBER16 ||
952 		    ie->ds_type == DSYM_SNUMBER8);
953 
954 		if (encode_number(n_entries, type_size, is_signed, 0, value,
955 		    optstart, ierrnop) == B_FALSE) {
956 			free(result);
957 			return (NULL);
958 		}
959 		break;
960 
961 	default:
962 		if (ie->ds_type == DSYM_BOOL)
963 			*ierrnop = ITAB_BAD_BOOLEAN;
964 		else
965 			*ierrnop = ITAB_SYNTAX_ERROR;
966 
967 		inittab_msg("inittab_encode: unsupported type `%d'",
968 		    ie->ds_type);
969 
970 		free(result);
971 		return (NULL);
972 	}
973 
974 	/*
975 	 * if just_payload is false, then we need to add the option
976 	 * code and length fields in.
977 	 */
978 	if (!just_payload) {
979 		if (ie->ds_dhcpv6) {
980 			/* LINTED: alignment */
981 			d6o = (dhcpv6_option_t *)result;
982 			d6o->d6o_code = htons(ie->ds_code);
983 			d6o->d6o_len = htons(length);
984 		} else {
985 			result[0] = ie->ds_code;
986 			result[1] = length;
987 		}
988 	}
989 
990 	if (lengthp != NULL)
991 		*lengthp = length + hlen;
992 
993 	return (result);
994 }
995 
996 /*
997  * inittab_decode_e(): converts a binary representation of a given datatype into
998  *		     a string; used for decoding DHCP options in a packet off
999  *		     the wire into ascii
1000  *
1001  *   input: dhcp_symbol_t *: the entry describing the payload option
1002  *	    uchar_t *: the payload to convert
1003  *	    uint16_t: the payload length (only used if just_payload is true)
1004  *	    boolean_t: if false, payload is assumed to be a DHCP option
1005  *	    int *: set to extended error code if error occurs.
1006  *  output: char *: a dynamically allocated string containing the converted data
1007  */
1008 
1009 char *
1010 inittab_decode_e(const dhcp_symbol_t *ie, const uchar_t *payload,
1011     uint16_t length, boolean_t just_payload, int *ierrnop)
1012 {
1013 	char		*resultp, *result = NULL;
1014 	uint_t		n_entries;
1015 	struct in_addr	in_addr;
1016 	in6_addr_t	in6_addr;
1017 	uint8_t		type_size = inittab_type_to_size(ie);
1018 	boolean_t	is_signed;
1019 	int		type;
1020 
1021 	*ierrnop = 0;
1022 	if (type_size == 0) {
1023 		*ierrnop = ITAB_SYNTAX_ERROR;
1024 		return (NULL);
1025 	}
1026 
1027 	if (!just_payload) {
1028 		if (ie->ds_dhcpv6) {
1029 			dhcpv6_option_t d6o;
1030 
1031 			(void) memcpy(&d6o, payload, sizeof (d6o));
1032 			length = ntohs(d6o.d6o_len);
1033 			payload += sizeof (d6o);
1034 		} else {
1035 			length = payload[1];
1036 			payload += 2;
1037 		}
1038 	}
1039 
1040 	/*
1041 	 * figure out the number of elements to convert.  note that
1042 	 * for ds_type NUMBER, the granularity is really 1 since the
1043 	 * value of ds_gran is the number of bytes in the number.
1044 	 */
1045 	if (ie->ds_type == DSYM_NUMBER)
1046 		n_entries = MIN(ie->ds_max, length / type_size);
1047 	else
1048 		n_entries = MIN(ie->ds_max * ie->ds_gran, length / type_size);
1049 
1050 	if (n_entries == 0)
1051 		n_entries = length / type_size;
1052 
1053 	if ((length % type_size) != 0) {
1054 		inittab_msg("inittab_decode: length of string not compatible "
1055 		    "with option type `%i'", ie->ds_type);
1056 		*ierrnop = ITAB_BAD_STRING;
1057 		return (NULL);
1058 	}
1059 
1060 	switch (ie->ds_type) {
1061 
1062 	case DSYM_ASCII:
1063 
1064 		result = malloc(n_entries + 1);
1065 		if (result == NULL) {
1066 			*ierrnop = ITAB_NOMEM;
1067 			return (NULL);
1068 		}
1069 
1070 		(void) memcpy(result, payload, n_entries);
1071 		result[n_entries] = '\0';
1072 		break;
1073 
1074 	case DSYM_DOMAIN:
1075 
1076 		/*
1077 		 * A valid, decoded RFC 1035 domain string or sequence of
1078 		 * strings is always the same size as the encoded form, but we
1079 		 * allow for RFC 1035 \DDD and \\ and \. escaping.
1080 		 *
1081 		 * Decoding stops at the end of the input or the first coding
1082 		 * violation.  Coding violations result in discarding the
1083 		 * offending list entry entirely.  Note that we ignore the 255
1084 		 * character overall limit on domain names.
1085 		 */
1086 		if ((result = malloc(4 * length + 1)) == NULL) {
1087 			*ierrnop = ITAB_NOMEM;
1088 			return (NULL);
1089 		}
1090 		resultp = result;
1091 		while (length > 0) {
1092 			char *dstart;
1093 			int slen;
1094 
1095 			dstart = resultp;
1096 			while (length > 0) {
1097 				slen = *payload++;
1098 				length--;
1099 				/* Upper two bits of length must be zero */
1100 				if ((slen & 0xc0) != 0 || slen > length) {
1101 					length = 0;
1102 					resultp = dstart;
1103 					break;
1104 				}
1105 				if (resultp != dstart)
1106 					*resultp++ = '.';
1107 				if (slen == 0)
1108 					break;
1109 				length -= slen;
1110 				while (slen > 0) {
1111 					if (!isascii(*payload) ||
1112 					    !isgraph(*payload)) {
1113 						(void) snprintf(resultp, 5,
1114 						    "\\%03d",
1115 						    *(unsigned char *)payload);
1116 						resultp += 4;
1117 						payload++;
1118 					} else {
1119 						if (*payload == '.' ||
1120 						    *payload == '\\')
1121 							*resultp++ = '\\';
1122 						*resultp++ = *payload++;
1123 					}
1124 					slen--;
1125 				}
1126 			}
1127 			if (resultp != dstart && length > 0)
1128 				*resultp++ = ' ';
1129 		}
1130 		*resultp = '\0';
1131 		break;
1132 
1133 	case DSYM_DUID:
1134 
1135 		/*
1136 		 * First, determine the type of DUID.  We need at least two
1137 		 * octets worth of data to grab the type code.  Once we have
1138 		 * that, the number of octets required for representation
1139 		 * depends on the type.
1140 		 */
1141 
1142 		if (length < 2) {
1143 			*ierrnop = ITAB_BAD_GRAN;
1144 			return (NULL);
1145 		}
1146 		type = (payload[0] << 8) + payload[1];
1147 		switch (type) {
1148 		case DHCPV6_DUID_LLT: {
1149 			duid_llt_t dllt;
1150 
1151 			if (length < sizeof (dllt)) {
1152 				*ierrnop = ITAB_BAD_GRAN;
1153 				return (NULL);
1154 			}
1155 			(void) memcpy(&dllt, payload, sizeof (dllt));
1156 			payload += sizeof (dllt);
1157 			length -= sizeof (dllt);
1158 			n_entries = sizeof ("1,65535,4294967295,") +
1159 			    length * 3;
1160 			if ((result = malloc(n_entries)) == NULL) {
1161 				*ierrnop = ITAB_NOMEM;
1162 				return (NULL);
1163 			}
1164 			(void) snprintf(result, n_entries, "%d,%u,%u,", type,
1165 			    ntohs(dllt.dllt_hwtype), ntohl(dllt.dllt_time));
1166 			break;
1167 		}
1168 		case DHCPV6_DUID_EN: {
1169 			duid_en_t den;
1170 
1171 			if (length < sizeof (den)) {
1172 				*ierrnop = ITAB_BAD_GRAN;
1173 				return (NULL);
1174 			}
1175 			(void) memcpy(&den, payload, sizeof (den));
1176 			payload += sizeof (den);
1177 			length -= sizeof (den);
1178 			n_entries = sizeof ("2,4294967295,") + length * 2;
1179 			if ((result = malloc(n_entries)) == NULL) {
1180 				*ierrnop = ITAB_NOMEM;
1181 				return (NULL);
1182 			}
1183 			(void) snprintf(result, n_entries, "%d,%u,", type,
1184 			    DHCPV6_GET_ENTNUM(&den));
1185 			break;
1186 		}
1187 		case DHCPV6_DUID_LL: {
1188 			duid_ll_t dll;
1189 
1190 			if (length < sizeof (dll)) {
1191 				*ierrnop = ITAB_BAD_GRAN;
1192 				return (NULL);
1193 			}
1194 			(void) memcpy(&dll, payload, sizeof (dll));
1195 			payload += sizeof (dll);
1196 			length -= sizeof (dll);
1197 			n_entries = sizeof ("3,65535,") + length * 3;
1198 			if ((result = malloc(n_entries)) == NULL) {
1199 				*ierrnop = ITAB_NOMEM;
1200 				return (NULL);
1201 			}
1202 			(void) snprintf(result, n_entries, "%d,%u,", type,
1203 			    ntohs(dll.dll_hwtype));
1204 			break;
1205 		}
1206 		default:
1207 			n_entries = sizeof ("0,") + length * 2;
1208 			if ((result = malloc(n_entries)) == NULL) {
1209 				*ierrnop = ITAB_NOMEM;
1210 				return (NULL);
1211 			}
1212 			(void) snprintf(result, n_entries, "%d,", type);
1213 			break;
1214 		}
1215 		resultp = result + strlen(result);
1216 		n_entries -= strlen(result);
1217 		if (type == DHCPV6_DUID_LLT || type == DHCPV6_DUID_LL) {
1218 			if (length > 0) {
1219 				resultp += snprintf(resultp, 3, "%02X",
1220 				    *payload++);
1221 				length--;
1222 			}
1223 			while (length-- > 0) {
1224 				resultp += snprintf(resultp, 4, ":%02X",
1225 				    *payload++);
1226 			}
1227 		} else {
1228 			while (length-- > 0) {
1229 				resultp += snprintf(resultp, 3, "%02X",
1230 				    *payload++);
1231 			}
1232 		}
1233 		break;
1234 
1235 	case DSYM_OCTET:
1236 
1237 		result = malloc(n_entries * (sizeof ("0xNN") + 1));
1238 		if (result == NULL) {
1239 			*ierrnop = ITAB_NOMEM;
1240 			return (NULL);
1241 		}
1242 
1243 		result[0] = '\0';
1244 		resultp = result;
1245 		if (n_entries > 0) {
1246 			resultp += sprintf(resultp, "0x%02X", *payload++);
1247 			n_entries--;
1248 		}
1249 		while (n_entries-- > 0)
1250 			resultp += sprintf(resultp, " 0x%02X", *payload++);
1251 
1252 		break;
1253 
1254 	case DSYM_IP:
1255 	case DSYM_IPV6:
1256 		if ((length / type_size) % ie->ds_gran != 0) {
1257 			*ierrnop = ITAB_BAD_GRAN;
1258 			inittab_msg("inittab_decode: number of entries "
1259 			    "not compatible with option granularity");
1260 			return (NULL);
1261 		}
1262 
1263 		result = malloc(n_entries * (ie->ds_type == DSYM_IP ?
1264 		    INET_ADDRSTRLEN : INET6_ADDRSTRLEN));
1265 		if (result == NULL) {
1266 			*ierrnop = ITAB_NOMEM;
1267 			return (NULL);
1268 		}
1269 
1270 		for (resultp = result; n_entries != 0; n_entries--) {
1271 			if (ie->ds_type == DSYM_IP) {
1272 				(void) memcpy(&in_addr.s_addr, payload,
1273 				    sizeof (ipaddr_t));
1274 				(void) strcpy(resultp, inet_ntoa(in_addr));
1275 			} else {
1276 				(void) memcpy(&in6_addr, payload,
1277 				    sizeof (in6_addr));
1278 				(void) inet_ntop(AF_INET6, &in6_addr, resultp,
1279 				    INET6_ADDRSTRLEN);
1280 			}
1281 			resultp += strlen(resultp);
1282 			if (n_entries > 1)
1283 				*resultp++ = ' ';
1284 			payload += type_size;
1285 		}
1286 		*resultp = '\0';
1287 		break;
1288 
1289 	case DSYM_NUMBER:				/* FALLTHRU */
1290 	case DSYM_UNUMBER8:				/* FALLTHRU */
1291 	case DSYM_SNUMBER8:				/* FALLTHRU */
1292 	case DSYM_UNUMBER16:				/* FALLTHRU */
1293 	case DSYM_SNUMBER16:				/* FALLTHRU */
1294 	case DSYM_UNUMBER32:				/* FALLTHRU */
1295 	case DSYM_SNUMBER32:				/* FALLTHRU */
1296 	case DSYM_UNUMBER64:				/* FALLTHRU */
1297 	case DSYM_SNUMBER64:
1298 
1299 		is_signed = (ie->ds_type == DSYM_SNUMBER64 ||
1300 		    ie->ds_type == DSYM_SNUMBER32 ||
1301 		    ie->ds_type == DSYM_SNUMBER16 ||
1302 		    ie->ds_type == DSYM_SNUMBER8);
1303 
1304 		result = malloc(n_entries * ITAB_MAX_NUMBER_LEN);
1305 		if (result == NULL) {
1306 			*ierrnop = ITAB_NOMEM;
1307 			return (NULL);
1308 		}
1309 
1310 		if (decode_number(n_entries, type_size, is_signed, ie->ds_gran,
1311 		    payload, result, ierrnop) == B_FALSE) {
1312 			free(result);
1313 			return (NULL);
1314 		}
1315 		break;
1316 
1317 	default:
1318 		inittab_msg("inittab_decode: unsupported type `%d'",
1319 		    ie->ds_type);
1320 		break;
1321 	}
1322 
1323 	return (result);
1324 }
1325 
1326 /*
1327  * inittab_encode(): converts a string representation of a given datatype into
1328  *		     binary; used for encoding ascii values into a form that
1329  *		     can be put in DHCP packets to be sent on the wire.
1330  *
1331  *   input: dhcp_symbol_t *: the entry describing the value option
1332  *	    const char *: the value to convert
1333  *	    uint16_t *: set to the length of the binary data returned
1334  *	    boolean_t: if false, return a full DHCP option
1335  *  output: uchar_t *: a dynamically allocated byte array with converted data
1336  */
1337 
1338 uchar_t *
1339 inittab_encode(const dhcp_symbol_t *ie, const char *value, uint16_t *lengthp,
1340     boolean_t just_payload)
1341 {
1342 	int ierrno;
1343 
1344 	return (inittab_encode_e(ie, value, lengthp, just_payload, &ierrno));
1345 }
1346 
1347 /*
1348  * inittab_decode(): converts a binary representation of a given datatype into
1349  *		     a string; used for decoding DHCP options in a packet off
1350  *		     the wire into ascii
1351  *
1352  *   input: dhcp_symbol_t *: the entry describing the payload option
1353  *	    uchar_t *: the payload to convert
1354  *	    uint16_t: the payload length (only used if just_payload is true)
1355  *	    boolean_t: if false, payload is assumed to be a DHCP option
1356  *  output: char *: a dynamically allocated string containing the converted data
1357  */
1358 
1359 char *
1360 inittab_decode(const dhcp_symbol_t *ie, const uchar_t *payload, uint16_t length,
1361     boolean_t just_payload)
1362 {
1363 	int ierrno;
1364 
1365 	return (inittab_decode_e(ie, payload, length, just_payload, &ierrno));
1366 }
1367 
1368 /*
1369  * inittab_msg(): prints diagnostic messages if INITTAB_DEBUG is set
1370  *
1371  *	    const char *: a printf-like format string
1372  *	    ...: arguments to the format string
1373  *  output: void
1374  */
1375 
1376 /*PRINTFLIKE1*/
1377 static void
1378 inittab_msg(const char *fmt, ...)
1379 {
1380 	enum { INITTAB_MSG_CHECK, INITTAB_MSG_RETURN, INITTAB_MSG_OUTPUT };
1381 
1382 	va_list		ap;
1383 	char		buf[512];
1384 	static int	action = INITTAB_MSG_CHECK;
1385 
1386 	/*
1387 	 * check DHCP_INITTAB_DEBUG the first time in; thereafter, use
1388 	 * the the cached result (stored in `action').
1389 	 */
1390 	switch (action) {
1391 
1392 	case INITTAB_MSG_CHECK:
1393 
1394 		if (getenv("DHCP_INITTAB_DEBUG") == NULL) {
1395 			action = INITTAB_MSG_RETURN;
1396 			return;
1397 		}
1398 
1399 		action = INITTAB_MSG_OUTPUT;
1400 
1401 		/* FALLTHRU into INITTAB_MSG_OUTPUT */
1402 
1403 	case INITTAB_MSG_OUTPUT:
1404 
1405 		va_start(ap, fmt);
1406 
1407 		(void) snprintf(buf, sizeof (buf), "inittab: %s\n", fmt);
1408 		(void) vfprintf(stderr, buf, ap);
1409 
1410 		va_end(ap);
1411 		break;
1412 
1413 	case INITTAB_MSG_RETURN:
1414 
1415 		return;
1416 	}
1417 }
1418 
1419 /*
1420  * decode_number(): decodes a sequence of numbers from binary into ascii;
1421  *		    binary is coming off of the network, so it is in nbo
1422  *
1423  *   input: uint8_t: the number of "granularity" numbers to decode
1424  *	    uint8_t: the length of each number
1425  *	    boolean_t: whether the numbers should be considered signed
1426  *	    uint8_t: the number of numbers per granularity
1427  *	    const uint8_t *: where to decode the numbers from
1428  *	    char *: where to decode the numbers to
1429  *  output: boolean_t: true on successful conversion, false on failure
1430  */
1431 
1432 static boolean_t
1433 decode_number(uint8_t n_entries, uint8_t size, boolean_t is_signed,
1434     uint8_t granularity, const uint8_t *from, char *to, int *ierrnop)
1435 {
1436 	uint16_t	uint16;
1437 	uint32_t	uint32;
1438 	uint64_t	uint64;
1439 
1440 	if (granularity != 0) {
1441 		if ((granularity % n_entries) != 0) {
1442 			inittab_msg("decode_number: number of entries "
1443 			    "not compatible with option granularity");
1444 			*ierrnop = ITAB_BAD_GRAN;
1445 			return (B_FALSE);
1446 		}
1447 	}
1448 
1449 	for (; n_entries != 0; n_entries--, from += size) {
1450 
1451 		switch (size) {
1452 
1453 		case 1:
1454 			to += sprintf(to, is_signed ? "%d" : "%u", *from);
1455 			break;
1456 
1457 		case 2:
1458 			(void) memcpy(&uint16, from, 2);
1459 			to += sprintf(to, is_signed ? "%hd" : "%hu",
1460 			    ntohs(uint16));
1461 			break;
1462 
1463 		case 3:
1464 			uint32 = 0;
1465 			(void) memcpy((uchar_t *)&uint32 + 1, from, 3);
1466 			to += sprintf(to, is_signed ? "%ld" : "%lu",
1467 			    ntohl(uint32));
1468 			break;
1469 
1470 		case 4:
1471 			(void) memcpy(&uint32, from, 4);
1472 			to += sprintf(to, is_signed ? "%ld" : "%lu",
1473 			    ntohl(uint32));
1474 			break;
1475 
1476 		case 8:
1477 			(void) memcpy(&uint64, from, 8);
1478 			to += sprintf(to, is_signed ? "%lld" : "%llu",
1479 			    ntohll(uint64));
1480 			break;
1481 
1482 		default:
1483 			*ierrnop = ITAB_BAD_NUMBER;
1484 			inittab_msg("decode_number: unknown integer size `%d'",
1485 			    size);
1486 			return (B_FALSE);
1487 		}
1488 		if (n_entries > 0)
1489 			*to++ = ' ';
1490 	}
1491 
1492 	*to = '\0';
1493 	return (B_TRUE);
1494 }
1495 
1496 /*
1497  * encode_number(): encodes a sequence of numbers from ascii into binary;
1498  *		    number will end up on the wire so it needs to be in nbo
1499  *
1500  *   input: uint8_t: the number of "granularity" numbers to encode
1501  *	    uint8_t: the length of each number
1502  *	    boolean_t: whether the numbers should be considered signed
1503  *	    uint8_t: the number of numbers per granularity
1504  *	    const uint8_t *: where to encode the numbers from
1505  *	    char *: where to encode the numbers to
1506  *	    int *: set to extended error code if error occurs.
1507  *  output: boolean_t: true on successful conversion, false on failure
1508  */
1509 
1510 static boolean_t /* ARGSUSED */
1511 encode_number(uint8_t n_entries, uint8_t size, boolean_t is_signed,
1512     uint8_t granularity, const char *from, uint8_t *to, int *ierrnop)
1513 {
1514 	uint8_t		i;
1515 	uint16_t	uint16;
1516 	uint32_t	uint32;
1517 	uint64_t	uint64;
1518 	char		*endptr;
1519 
1520 	if (granularity != 0) {
1521 		if ((granularity % n_entries) != 0) {
1522 			*ierrnop = ITAB_BAD_GRAN;
1523 			inittab_msg("encode_number: number of entries "
1524 			    "not compatible with option granularity");
1525 			return (B_FALSE);
1526 		}
1527 	}
1528 
1529 	for (i = 0; i < n_entries; i++, from++, to += size) {
1530 
1531 		/*
1532 		 * totally obscure c factoid: it is legal to pass a
1533 		 * string representing a negative number to strtoul().
1534 		 * in this case, strtoul() will return an unsigned
1535 		 * long that if cast to a long, would represent the
1536 		 * negative number.  we take advantage of this to
1537 		 * cut down on code here.
1538 		 */
1539 
1540 		errno = 0;
1541 		switch (size) {
1542 
1543 		case 1:
1544 			*to = strtoul(from, &endptr, 0);
1545 			if (errno != 0 || from == endptr) {
1546 				goto error;
1547 			}
1548 			break;
1549 
1550 		case 2:
1551 			uint16 = htons(strtoul(from, &endptr, 0));
1552 			if (errno != 0 || from == endptr) {
1553 				goto error;
1554 			}
1555 			(void) memcpy(to, &uint16, 2);
1556 			break;
1557 
1558 		case 3:
1559 			uint32 = htonl(strtoul(from, &endptr, 0));
1560 			if (errno != 0 || from == endptr) {
1561 				goto error;
1562 			}
1563 			(void) memcpy(to, (uchar_t *)&uint32 + 1, 3);
1564 			break;
1565 
1566 		case 4:
1567 			uint32 = htonl(strtoul(from, &endptr, 0));
1568 			if (errno != 0 || from == endptr) {
1569 				goto error;
1570 			}
1571 			(void) memcpy(to, &uint32, 4);
1572 			break;
1573 
1574 		case 8:
1575 			uint64 = htonll(strtoull(from, &endptr, 0));
1576 			if (errno != 0 || from == endptr) {
1577 				goto error;
1578 			}
1579 			(void) memcpy(to, &uint64, 8);
1580 			break;
1581 
1582 		default:
1583 			inittab_msg("encode_number: unsupported integer "
1584 			    "size `%d'", size);
1585 			return (B_FALSE);
1586 		}
1587 
1588 		from = strchr(from, ' ');
1589 		if (from == NULL)
1590 			break;
1591 	}
1592 
1593 	return (B_TRUE);
1594 
1595 error:
1596 	*ierrnop = ITAB_BAD_NUMBER;
1597 	inittab_msg("encode_number: cannot convert to integer");
1598 	return (B_FALSE);
1599 }
1600 
1601 /*
1602  * inittab_type_to_size(): given an inittab entry, returns size of one entry of
1603  *		      its type
1604  *
1605  *   input: dhcp_symbol_t *: an entry of the given type
1606  *  output: uint8_t: the size in bytes of an entry of that type
1607  */
1608 
1609 uint8_t
1610 inittab_type_to_size(const dhcp_symbol_t *ie)
1611 {
1612 	switch (ie->ds_type) {
1613 
1614 	case DSYM_DUID:
1615 	case DSYM_DOMAIN:
1616 	case DSYM_ASCII:
1617 	case DSYM_OCTET:
1618 	case DSYM_SNUMBER8:
1619 	case DSYM_UNUMBER8:
1620 
1621 		return (1);
1622 
1623 	case DSYM_SNUMBER16:
1624 	case DSYM_UNUMBER16:
1625 
1626 		return (2);
1627 
1628 	case DSYM_UNUMBER24:
1629 
1630 		return (3);
1631 
1632 	case DSYM_SNUMBER32:
1633 	case DSYM_UNUMBER32:
1634 	case DSYM_IP:
1635 
1636 		return (4);
1637 
1638 	case DSYM_SNUMBER64:
1639 	case DSYM_UNUMBER64:
1640 
1641 		return (8);
1642 
1643 	case DSYM_NUMBER:
1644 
1645 		return (ie->ds_gran);
1646 
1647 	case DSYM_IPV6:
1648 
1649 		return (sizeof (in6_addr_t));
1650 	}
1651 
1652 	return (0);
1653 }
1654 
1655 /*
1656  * itabcode_to_dsymcode(): maps an inittab category code to its dsym
1657  *                         representation
1658  *
1659  *   input: uchar_t: the inittab category code
1660  *  output: dsym_category_t: the dsym category code
1661  */
1662 
1663 static dsym_category_t
1664 itabcode_to_dsymcode(uchar_t itabcode)
1665 {
1666 
1667 	unsigned int	i;
1668 
1669 	for (i = 0; i < ITAB_CAT_COUNT; i++)
1670 		if (category_map[i].cme_itabcode == itabcode)
1671 			return (category_map[i].cme_dsymcode);
1672 
1673 	return (DSYM_BAD_CAT);
1674 }
1675 
1676 /*
1677  * category_to_code(): maps a category name to its numeric representation
1678  *
1679  *   input: const char *: the category name
1680  *  output: uchar_t: its internal code (numeric representation)
1681  */
1682 
1683 static uchar_t
1684 category_to_code(const char *category)
1685 {
1686 	unsigned int	i;
1687 
1688 	for (i = 0; i < ITAB_CAT_COUNT; i++)
1689 		if (strcasecmp(category_map[i].cme_name, category) == 0)
1690 			return (category_map[i].cme_itabcode);
1691 
1692 	return (0);
1693 }
1694 
1695 /*
1696  * our internal table of DHCP option values, used by inittab_verify()
1697  */
1698 static dhcp_symbol_t inittab_table[] =
1699 {
1700 { DSYM_INTERNAL,	1024,	"Hostname",	DSYM_BOOL,	0,	0 },
1701 { DSYM_INTERNAL,	1025,	"LeaseNeg",	DSYM_BOOL,	0,	0 },
1702 { DSYM_INTERNAL,	1026,	"EchoVC",	DSYM_BOOL,	0,	0 },
1703 { DSYM_INTERNAL,	1027,	"BootPath",	DSYM_ASCII,	1,	128 },
1704 { DSYM_FIELD,		0,	"Opcode",	DSYM_UNUMBER8,	1,	1 },
1705 { DSYM_FIELD,		1,	"Htype",	DSYM_UNUMBER8,	1,	1 },
1706 { DSYM_FIELD,		2,	"HLen",		DSYM_UNUMBER8,	1,	1 },
1707 { DSYM_FIELD,		3,	"Hops",		DSYM_UNUMBER8,	1,	1 },
1708 { DSYM_FIELD,		4,	"Xid",		DSYM_UNUMBER32,	1,	1 },
1709 { DSYM_FIELD,		8,	"Secs",		DSYM_UNUMBER16,	1,	1 },
1710 { DSYM_FIELD,		10,	"Flags",	DSYM_OCTET,	1,	2 },
1711 { DSYM_FIELD,		12,	"Ciaddr",	DSYM_IP,	1,	1 },
1712 { DSYM_FIELD,		16,	"Yiaddr",	DSYM_IP,	1,	1 },
1713 { DSYM_FIELD,		20,	"BootSrvA",	DSYM_IP,	1,	1 },
1714 { DSYM_FIELD,		24,	"Giaddr",	DSYM_IP,	1,	1 },
1715 { DSYM_FIELD,		28,	"Chaddr",	DSYM_OCTET,	1,	16 },
1716 { DSYM_FIELD,		44,	"BootSrvN",	DSYM_ASCII,	1,	64 },
1717 { DSYM_FIELD,		108,	"BootFile",	DSYM_ASCII,	1,	128 },
1718 { DSYM_FIELD,		236,	"Magic",	DSYM_OCTET,	1,	4 },
1719 { DSYM_FIELD,		240,	"Options",	DSYM_OCTET,	1,	60 },
1720 { DSYM_STANDARD,	1,	"Subnet",	DSYM_IP,	1,	1 },
1721 { DSYM_STANDARD,	2,	"UTCoffst",	DSYM_SNUMBER32,	1,	1 },
1722 { DSYM_STANDARD,	3,	"Router",	DSYM_IP,	1,	0 },
1723 { DSYM_STANDARD,	4,	"Timeserv",	DSYM_IP,	1,	0 },
1724 { DSYM_STANDARD,	5,	"IEN116ns",	DSYM_IP,	1,	0 },
1725 { DSYM_STANDARD,	6,	"DNSserv",	DSYM_IP,	1,	0 },
1726 { DSYM_STANDARD,	7,	"Logserv",	DSYM_IP,	1,	0 },
1727 { DSYM_STANDARD,	8,	"Cookie",	DSYM_IP,	1,	0 },
1728 { DSYM_STANDARD,	9,	"Lprserv",	DSYM_IP,	1,	0 },
1729 { DSYM_STANDARD,	10,	"Impress",	DSYM_IP,	1,	0 },
1730 { DSYM_STANDARD,	11,	"Resource",	DSYM_IP,	1,	0 },
1731 { DSYM_STANDARD,	12,	"Hostname",	DSYM_ASCII,	1,	0 },
1732 { DSYM_STANDARD,	13,	"Bootsize",	DSYM_UNUMBER16,	1,	1 },
1733 { DSYM_STANDARD,	14,	"Dumpfile",	DSYM_ASCII,	1,	0 },
1734 { DSYM_STANDARD,	15,	"DNSdmain",	DSYM_ASCII,	1,	0 },
1735 { DSYM_STANDARD,	16,	"Swapserv",	DSYM_IP,	1,	1 },
1736 { DSYM_STANDARD,	17,	"Rootpath",	DSYM_ASCII,	1,	0 },
1737 { DSYM_STANDARD,	18,	"ExtendP",	DSYM_ASCII,	1,	0 },
1738 { DSYM_STANDARD,	19,	"IpFwdF",	DSYM_UNUMBER8,	1,	1 },
1739 { DSYM_STANDARD,	20,	"NLrouteF",	DSYM_UNUMBER8,	1,	1 },
1740 { DSYM_STANDARD,	21,	"PFilter",	DSYM_IP,	2,	0 },
1741 { DSYM_STANDARD,	22,	"MaxIpSiz",	DSYM_UNUMBER16,	1,	1 },
1742 { DSYM_STANDARD,	23,	"IpTTL",	DSYM_UNUMBER8,	1,	1 },
1743 { DSYM_STANDARD,	24,	"PathTO",	DSYM_UNUMBER32,	1,	1 },
1744 { DSYM_STANDARD,	25,	"PathTbl",	DSYM_UNUMBER16,	1,	0 },
1745 { DSYM_STANDARD,	26,	"MTU",		DSYM_UNUMBER16,	1,	1 },
1746 { DSYM_STANDARD,	27,	"SameMtuF",	DSYM_UNUMBER8,	1,	1 },
1747 { DSYM_STANDARD,	28,	"Broadcst",	DSYM_IP,	1,	1 },
1748 { DSYM_STANDARD,	29,	"MaskDscF",	DSYM_UNUMBER8,	1,	1 },
1749 { DSYM_STANDARD,	30,	"MaskSupF",	DSYM_UNUMBER8,	1,	1 },
1750 { DSYM_STANDARD,	31,	"RDiscvyF",	DSYM_UNUMBER8,	1,	1 },
1751 { DSYM_STANDARD,	32,	"RSolictS",	DSYM_IP,	1,	1 },
1752 { DSYM_STANDARD,	33,	"StaticRt",	DSYM_IP,	2,	0 },
1753 { DSYM_STANDARD,	34,	"TrailerF",	DSYM_UNUMBER8,	1,	1 },
1754 { DSYM_STANDARD,	35,	"ArpTimeO",	DSYM_UNUMBER32,	1,	1 },
1755 { DSYM_STANDARD,	36,	"EthEncap",	DSYM_UNUMBER8,	1,	1 },
1756 { DSYM_STANDARD,	37,	"TcpTTL",	DSYM_UNUMBER8,	1,	1 },
1757 { DSYM_STANDARD,	38,	"TcpKaInt",	DSYM_UNUMBER32,	1,	1 },
1758 { DSYM_STANDARD,	39,	"TcpKaGbF",	DSYM_UNUMBER8,	1,	1 },
1759 { DSYM_STANDARD,	40,	"NISdmain",	DSYM_ASCII,	1,	0 },
1760 { DSYM_STANDARD,	41,	"NISservs",	DSYM_IP,	1,	0 },
1761 { DSYM_STANDARD,	42,	"NTPservs",	DSYM_IP,	1,	0 },
1762 { DSYM_STANDARD,	43,	"Vendor",	DSYM_OCTET,	1,	0 },
1763 { DSYM_STANDARD,	44,	"NetBNms",	DSYM_IP,	1,	0 },
1764 { DSYM_STANDARD,	45,	"NetBDsts",	DSYM_IP,	1,	0 },
1765 { DSYM_STANDARD,	46,	"NetBNdT",	DSYM_UNUMBER8,	1,	1 },
1766 { DSYM_STANDARD,	47,	"NetBScop",	DSYM_ASCII,	1,	0 },
1767 { DSYM_STANDARD,	48,	"XFontSrv",	DSYM_IP,	1,	0 },
1768 { DSYM_STANDARD,	49,	"XDispMgr",	DSYM_IP,	1,	0 },
1769 { DSYM_STANDARD,	50,	"ReqIP",	DSYM_IP,	1,	1 },
1770 { DSYM_STANDARD,	51,	"LeaseTim",	DSYM_UNUMBER32,	1,	1 },
1771 { DSYM_STANDARD,	52,	"OptOvrld",	DSYM_UNUMBER8,	1,	1 },
1772 { DSYM_STANDARD,	53,	"DHCPType",	DSYM_UNUMBER8,	1,	1 },
1773 { DSYM_STANDARD,	54,	"ServerID",	DSYM_IP,	1,	1 },
1774 { DSYM_STANDARD,	55,	"ReqList",	DSYM_OCTET,	1,	0 },
1775 { DSYM_STANDARD,	56,	"Message",	DSYM_ASCII,	1,	0 },
1776 { DSYM_STANDARD,	57,	"DHCP_MTU",	DSYM_UNUMBER16,	1,	1 },
1777 { DSYM_STANDARD,	58,	"T1Time",	DSYM_UNUMBER32,	1,	1 },
1778 { DSYM_STANDARD,	59,	"T2Time",	DSYM_UNUMBER32,	1,	1 },
1779 { DSYM_STANDARD,	60,	"ClassID",	DSYM_ASCII,	1,	0 },
1780 { DSYM_STANDARD,	61,	"ClientID",	DSYM_OCTET,	1,	0 },
1781 { DSYM_STANDARD,	62,	"NW_dmain",	DSYM_ASCII,	1,	0 },
1782 { DSYM_STANDARD,	63,	"NWIPOpts",	DSYM_OCTET,	1,	128 },
1783 { DSYM_STANDARD,	64,	"NIS+dom",	DSYM_ASCII,	1,	0 },
1784 { DSYM_STANDARD,	65,	"NIS+serv",	DSYM_IP,	1,	0 },
1785 { DSYM_STANDARD,	66,	"TFTPsrvN",	DSYM_ASCII,	1,	64 },
1786 { DSYM_STANDARD,	67,	"OptBootF",	DSYM_ASCII,	1,	128 },
1787 { DSYM_STANDARD,	68,	"MblIPAgt",	DSYM_IP,	1,	0 },
1788 { DSYM_STANDARD,	69,	"SMTPserv",	DSYM_IP,	1,	0 },
1789 { DSYM_STANDARD,	70,	"POP3serv",	DSYM_IP,	1,	0 },
1790 { DSYM_STANDARD,	71,	"NNTPserv",	DSYM_IP,	1,	0 },
1791 { DSYM_STANDARD,	72,	"WWWservs",	DSYM_IP,	1,	0 },
1792 { DSYM_STANDARD,	73,	"Fingersv",	DSYM_IP,	1,	0 },
1793 { DSYM_STANDARD,	74,	"IRCservs",	DSYM_IP,	1,	0 },
1794 { DSYM_STANDARD,	75,	"STservs",	DSYM_IP,	1,	0 },
1795 { DSYM_STANDARD,	76,	"STDAservs",	DSYM_IP,	1,	0 },
1796 { DSYM_STANDARD,	77,	"UserClas",	DSYM_ASCII,	1,	0 },
1797 { DSYM_STANDARD,	78,	"SLP_DA",	DSYM_OCTET,	1,	0 },
1798 { DSYM_STANDARD,	79,	"SLP_SS",	DSYM_OCTET,	1,	0 },
1799 { DSYM_STANDARD,	82,	"AgentOpt",	DSYM_OCTET,	1,	0 },
1800 { DSYM_STANDARD,	89,	"FQDN",		DSYM_OCTET,	1,	0 },
1801 { 0,			0,	"",		0,		0,	0 }
1802 };
1803