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