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