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