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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #include <stdarg.h>
35 #include <limits.h>
36 #include <ctype.h>
37 #include <libgen.h>
38 #include <sys/isa_defs.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <sys/sysmacros.h>
43 #include <libinetutil.h>
44 
45 #include "dhcp_symbol.h"
46 #include "dhcp_inittab.h"
47 
48 static uint64_t		dhcp_htonll(uint64_t);
49 static uint64_t		dhcp_ntohll(uint64_t);
50 static void		inittab_msg(const char *, ...);
51 static uchar_t		category_to_code(const char *);
52 static boolean_t	encode_number(uint8_t, uint8_t, boolean_t, uint8_t,
53 			    const char *, uint8_t *, int *);
54 static boolean_t	decode_number(uint8_t, uint8_t, boolean_t, uint8_t,
55 			    const uint8_t *, char *, int *);
56 static dhcp_symbol_t	*inittab_lookup(uchar_t, char, const char *, int32_t,
57 			    size_t *);
58 static dsym_category_t	itabcode_to_dsymcode(uchar_t);
59 static boolean_t	parse_entry(char *, char **);
60 
61 /*
62  * forward declaration of our internal inittab_table[].  too bulky to put
63  * up front -- check the end of this file for its definition.
64  */
65 static dhcp_symbol_t	inittab_table[];
66 
67 /*
68  * the number of fields in the inittab and names for the fields.  note that
69  * this order is meaningful to parse_entry(); other functions should just
70  * use them as indexes into the array returned from parse_entry().
71  */
72 #define	ITAB_FIELDS	7
73 enum { ITAB_NAME, ITAB_CODE, ITAB_TYPE, ITAB_GRAN, ITAB_MAX, ITAB_CONS,
74     ITAB_CAT };
75 
76 /*
77  * the category_map_entry_t is used to map the inittab category codes to
78  * the dsym codes.  the reason the codes are different is that the inittab
79  * needs to have the codes be ORable such that queries can retrieve more
80  * than one category at a time.  this map is also used to map the inittab
81  * string representation of a category to its numerical code.
82  */
83 typedef struct category_map_entry {
84 	dsym_category_t	cme_dsymcode;
85 	char		*cme_name;
86 	uchar_t		cme_itabcode;
87 } category_map_entry_t;
88 
89 static category_map_entry_t category_map[] = {
90 	{ DSYM_STANDARD,	"STANDARD",	ITAB_CAT_STANDARD },
91 	{ DSYM_FIELD,		"FIELD",	ITAB_CAT_FIELD },
92 	{ DSYM_INTERNAL,	"INTERNAL",	ITAB_CAT_INTERNAL },
93 	{ DSYM_VENDOR,		"VENDOR",	ITAB_CAT_VENDOR },
94 	{ DSYM_SITE,		"SITE",		ITAB_CAT_SITE }
95 };
96 
97 /*
98  * inittab_load(): returns all inittab entries with the specified criteria
99  *
100  *   input: uchar_t: the categories the consumer is interested in
101  *	    char: the consumer type of the caller
102  *	    size_t *: set to the number of entries returned
103  *  output: dhcp_symbol_t *: an array of dynamically allocated entries
104  *	    on success, NULL upon failure
105  */
106 dhcp_symbol_t	*
107 inittab_load(uchar_t categories, char consumer, size_t *n_entries)
108 {
109 	return (inittab_lookup(categories, consumer, NULL, -1, n_entries));
110 }
111 
112 /*
113  * inittab_getbyname(): returns an inittab entry with the specified criteria
114  *
115  *   input: int: the categories the consumer is interested in
116  *	    char: the consumer type of the caller
117  *	    char *: the name of the inittab entry the consumer wants
118  *  output: dhcp_symbol_t *: a dynamically allocated dhcp_symbol structure
119  *	    on success, NULL upon failure
120  */
121 dhcp_symbol_t	*
122 inittab_getbyname(uchar_t categories, char consumer, const char *name)
123 {
124 	return (inittab_lookup(categories, consumer, name, -1, NULL));
125 }
126 
127 /*
128  * inittab_getbycode(): returns an inittab entry with the specified criteria
129  *
130  *   input: uchar_t: the categories the consumer is interested in
131  *	    char: the consumer type of the caller
132  *	    uint16_t: the code of the inittab entry the consumer wants
133  *  output: dhcp_symbol_t *: a dynamically allocated dhcp_symbol structure
134  *	    on success, NULL upon failure
135  */
136 dhcp_symbol_t	*
137 inittab_getbycode(uchar_t categories, char consumer, uint16_t code)
138 {
139 	return (inittab_lookup(categories, consumer, NULL, code, NULL));
140 }
141 
142 /*
143  * inittab_lookup(): returns inittab entries with the specified criteria
144  *
145  *   input: uchar_t: the categories the consumer is interested in
146  *	    char: the consumer type of the caller
147  *	    const char *: the name of the entry the caller is interested
148  *		in, or NULL if the caller doesn't care
149  *	    int32_t: the code the caller is interested in, or -1 if the
150  *		caller doesn't care
151  *	    size_t *: set to the number of entries returned
152  *  output: dhcp_symbol_t *: dynamically allocated dhcp_symbol structures
153  *	    on success, NULL upon failure
154  */
155 static dhcp_symbol_t *
156 inittab_lookup(uchar_t categories, char consumer, const char *name,
157     int32_t code, size_t *n_entriesp)
158 {
159 	FILE			*inittab_fp;
160 	dhcp_symbol_t		*new_entries, *entries = NULL;
161 	dhcp_symbol_t		entry;
162 	char			buffer[ITAB_MAX_LINE_LEN];
163 	char			*fields[ITAB_FIELDS];
164 	unsigned long		line = 0;
165 	size_t			i, n_entries = 0;
166 	char			*inittab_path;
167 	uchar_t			category_code;
168 	dsym_cdtype_t		type;
169 
170 	inittab_path = getenv("DHCP_INITTAB_PATH");
171 	if (inittab_path == NULL)
172 		inittab_path = ITAB_INITTAB_PATH;
173 
174 	inittab_fp = fopen(inittab_path, "r");
175 	if (inittab_fp == NULL) {
176 		inittab_msg("inittab_lookup: fopen: %s: %s",
177 		    ITAB_INITTAB_PATH, strerror(errno));
178 		return (NULL);
179 	}
180 
181 	(void) bufsplit(",\n", 0, NULL);
182 	while (fgets(buffer, sizeof (buffer), inittab_fp) != NULL) {
183 
184 		line++;
185 
186 		/*
187 		 * make sure the string didn't overflow our buffer
188 		 */
189 		if (strchr(buffer, '\n') == NULL) {
190 			inittab_msg("inittab_lookup: line %li: too long, "
191 			    "skipping", line);
192 			continue;
193 		}
194 
195 		/*
196 		 * skip `pure comment' lines
197 		 */
198 		for (i = 0; buffer[i] != '\0'; i++)
199 			if (isspace(buffer[i]) == 0)
200 				break;
201 
202 		if (buffer[i] == ITAB_COMMENT_CHAR || buffer[i] == '\0')
203 			continue;
204 
205 		/*
206 		 * parse the entry out into fields.
207 		 */
208 		if (parse_entry(buffer, fields) == B_FALSE) {
209 			inittab_msg("inittab_lookup: line %li: syntax error, "
210 			    "skipping", line);
211 			continue;
212 		}
213 
214 		/*
215 		 * validate the values in the entries; skip if invalid.
216 		 */
217 		if (atoi(fields[ITAB_GRAN]) > ITAB_GRAN_MAX) {
218 			inittab_msg("inittab_lookup: line %li: granularity `%s'"
219 			    " out of range, skipping", line, fields[ITAB_GRAN]);
220 			continue;
221 		}
222 
223 		if (atoi(fields[ITAB_MAX]) > ITAB_MAX_MAX) {
224 			inittab_msg("inittab_lookup: line %li: maximum `%s' "
225 			    "out of range, skipping", line, fields[ITAB_MAX]);
226 			continue;
227 		}
228 
229 		if (dsym_get_type_id(fields[ITAB_TYPE], &type, B_FALSE) !=
230 		    DSYM_SUCCESS) {
231 			inittab_msg("inittab_lookup: line %li: type `%s' "
232 			    "is invalid, skipping", line, fields[ITAB_TYPE]);
233 			continue;
234 		}
235 
236 		/*
237 		 * find out whether this entry of interest to our consumer,
238 		 * and if so, throw it onto the set of entries we'll return.
239 		 * check categories last since it's the most expensive check.
240 		 */
241 		if (strchr(fields[ITAB_CONS], consumer) == NULL)
242 			continue;
243 
244 		if (code != -1 && atoi(fields[ITAB_CODE]) != code)
245 			continue;
246 
247 		if (name != NULL && strcasecmp(fields[ITAB_NAME], name) != 0)
248 			continue;
249 
250 		category_code = category_to_code(fields[ITAB_CAT]);
251 		if ((category_code & categories) == 0)
252 			continue;
253 
254 		/*
255 		 * looks like a match.  allocate an entry and fill it in
256 		 */
257 		new_entries = realloc(entries, (n_entries + 1) *
258 		    sizeof (dhcp_symbol_t));
259 
260 		/*
261 		 * if we run out of memory, might as well return what we can
262 		 */
263 		if (new_entries == NULL) {
264 			inittab_msg("inittab_lookup: ran out of memory "
265 			    "allocating dhcp_symbol_t's");
266 			break;
267 		}
268 
269 		entry.ds_max	  = atoi(fields[ITAB_MAX]);
270 		entry.ds_code	  = atoi(fields[ITAB_CODE]);
271 		entry.ds_type	  = type;
272 		entry.ds_gran	  = atoi(fields[ITAB_GRAN]);
273 		entry.ds_category = itabcode_to_dsymcode(category_code);
274 		entry.ds_classes.dc_cnt	  = 0;
275 		entry.ds_classes.dc_names = NULL;
276 		(void) strlcpy(entry.ds_name, fields[ITAB_NAME],
277 		    sizeof (entry.ds_name));
278 
279 		entries = new_entries;
280 		entries[n_entries++] = entry;
281 	}
282 
283 	if (ferror(inittab_fp) != 0) {
284 		inittab_msg("inittab_lookup: error on inittab stream");
285 		clearerr(inittab_fp);
286 	}
287 
288 	(void) fclose(inittab_fp);
289 
290 	if (n_entriesp != NULL)
291 		*n_entriesp = n_entries;
292 
293 	return (entries);
294 }
295 
296 /*
297  * parse_entry(): parses an entry out into its constituent fields
298  *
299  *   input: char *: the entry
300  *	    char **: an array of ITAB_FIELDS length which contains
301  *		     pointers into the entry on upon return
302  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
303  */
304 static boolean_t
305 parse_entry(char *entry, char **fields)
306 {
307 	char	*category, *spacep;
308 	size_t	n_fields, i;
309 
310 	/*
311 	 * due to a mistake made long ago, the first and second fields of
312 	 * each entry are not separated by a comma, but rather by
313 	 * whitespace -- have bufsplit() treat the two fields as one, then
314 	 * pull them apart afterwards.
315 	 */
316 	n_fields = bufsplit(entry, ITAB_FIELDS - 1, fields);
317 	if (n_fields != (ITAB_FIELDS - 1))
318 		return (B_FALSE);
319 
320 	/*
321 	 * pull the first and second fields apart.  this is complicated
322 	 * since the first field can contain embedded whitespace (so we
323 	 * must separate the two fields by the last span of whitespace).
324 	 *
325 	 * first, find the initial span of whitespace.  if there isn't one,
326 	 * then the entry is malformed.
327 	 */
328 	category = strpbrk(fields[ITAB_NAME], " \t");
329 	if (category == NULL)
330 		return (B_FALSE);
331 
332 	/*
333 	 * find the last span of whitespace.
334 	 */
335 	do {
336 		while (isspace(*category))
337 			category++;
338 
339 		spacep = strpbrk(category, " \t");
340 		if (spacep != NULL)
341 			category = spacep;
342 	} while (spacep != NULL);
343 
344 	/*
345 	 * NUL-terminate the first byte of the last span of whitespace, so
346 	 * that the first field doesn't have any residual trailing
347 	 * whitespace.
348 	 */
349 	spacep = category - 1;
350 	while (isspace(*spacep))
351 		spacep--;
352 
353 	if (spacep <= fields[0])
354 		return (B_FALSE);
355 
356 	*++spacep = '\0';
357 
358 	/*
359 	 * remove any whitespace from the fields.
360 	 */
361 	for (i = 0; i < n_fields; i++) {
362 		while (isspace(*fields[i]))
363 			fields[i]++;
364 	}
365 	fields[ITAB_CAT] = category;
366 
367 	return (B_TRUE);
368 }
369 
370 /*
371  * inittab_verify(): verifies that a given inittab entry matches an internal
372  *		     definition
373  *
374  *   input: dhcp_symbol_t *: the inittab entry to verify
375  *	    dhcp_symbol_t *: if non-NULL, a place to store the internal
376  *			       inittab entry upon return
377  *  output: int: ITAB_FAILURE, ITAB_SUCCESS, or ITAB_UNKNOWN
378  */
379 int
380 inittab_verify(dhcp_symbol_t *inittab_ent, dhcp_symbol_t *internal_ent)
381 {
382 	unsigned int	i;
383 
384 	for (i = 0; inittab_table[i].ds_name[0] != '\0'; i++) {
385 
386 		if (inittab_ent->ds_category != inittab_table[i].ds_category)
387 			continue;
388 
389 		if (inittab_ent->ds_code == inittab_table[i].ds_code) {
390 			if (internal_ent != NULL)
391 				*internal_ent = inittab_table[i];
392 
393 			if (inittab_table[i].ds_type != inittab_ent->ds_type ||
394 			    inittab_table[i].ds_gran != inittab_ent->ds_gran ||
395 			    inittab_table[i].ds_max  != inittab_ent->ds_max)
396 				return (ITAB_FAILURE);
397 
398 			return (ITAB_SUCCESS);
399 		}
400 	}
401 
402 	return (ITAB_UNKNOWN);
403 }
404 
405 /*
406  * inittab_encode_e(): converts a string representation of a given datatype into
407  *		     binary; used for encoding ascii values into a form that
408  *		     can be put in DHCP packets to be sent on the wire.
409  *
410  *   input: dhcp_symbol_t *: the entry describing the value option
411  *	    const char *: the value to convert
412  *	    uint16_t *: set to the length of the binary data returned
413  *	    boolean_t: if false, return a full DHCP option
414  *  output: uchar_t *: a dynamically allocated byte array with converted data
415  */
416 uchar_t *
417 inittab_encode_e(dhcp_symbol_t *ie, const char *value, uint16_t *lengthp,
418     boolean_t just_payload, int *ierrnop)
419 {
420 	uint16_t	length = 0;
421 	uchar_t		n_entries = 0;
422 	const char	*valuep;
423 	char		*currp;
424 	uchar_t		*result = NULL;
425 	unsigned int	i;
426 	uint8_t		type_size = inittab_type_to_size(ie);
427 	boolean_t	is_signed;
428 	uint_t		vallen, reslen;
429 
430 	*ierrnop = 0;
431 	if (type_size == 0) {
432 		*ierrnop = ITAB_SYNTAX_ERROR;
433 		return (NULL);
434 	}
435 
436 	if (ie->ds_type == DSYM_ASCII)
437 		n_entries = strlen(value);		/* no NUL */
438 	else if (ie->ds_type == DSYM_OCTET) {
439 		vallen = strlen(value);
440 		n_entries = vallen / 2;
441 		n_entries += vallen % 2;
442 	} else {
443 		/*
444 		 * figure out the number of entries by counting the spaces
445 		 * in the value string
446 		 */
447 		for (valuep = value; valuep++ != NULL; n_entries++)
448 			valuep = strchr(valuep, ' ');
449 	}
450 
451 	/*
452 	 * if we're gonna return a complete option, then include the
453 	 * option length and code in the size of the packet we allocate
454 	 */
455 	if (just_payload == B_FALSE)
456 		length += 2;
457 
458 	length += n_entries * type_size;
459 	if (length > 0)
460 		result = malloc(length);
461 
462 	switch (ie->ds_type) {
463 
464 	case DSYM_ASCII:
465 
466 		if (result == NULL) {
467 			*ierrnop = ITAB_NOMEM;
468 			return (NULL);
469 		}
470 
471 		if (strlen(value) > length) {
472 			free(result);
473 			*ierrnop = ITAB_BAD_STRING;
474 			return (NULL);
475 		}
476 
477 		(void) memcpy(result, value, length);
478 		break;
479 
480 	case DSYM_OCTET:
481 
482 		if (result == NULL) {
483 			*ierrnop = ITAB_BAD_OCTET;
484 			return (NULL);
485 		}
486 
487 		reslen = length;
488 		/* Call libinetutil function to decode */
489 		if (hexascii_to_octet(value, vallen, result, &reslen) != 0) {
490 			free(result);
491 			*ierrnop = ITAB_BAD_OCTET;
492 			return (NULL);
493 		}
494 		break;
495 
496 	case DSYM_IP:
497 
498 		if (result == NULL) {
499 			*ierrnop = ITAB_BAD_IPADDR;
500 			return (NULL);
501 		}
502 		if (n_entries % ie->ds_gran != 0) {
503 			*ierrnop = ITAB_BAD_GRAN;
504 			inittab_msg("inittab_encode: number of entries "
505 			    "not compatible with option granularity");
506 			free(result);
507 			return (NULL);
508 		}
509 
510 		for (valuep = value, i = 0; i < n_entries; i++, valuep++) {
511 
512 			currp = strchr(valuep, ' ');
513 			if (currp != NULL)
514 				*currp = '\0';
515 			if (inet_pton(AF_INET, valuep,
516 			    &result[i * sizeof (ipaddr_t)]) != 1) {
517 				*ierrnop = ITAB_BAD_IPADDR;
518 				inittab_msg("inittab_encode: bogus ip address");
519 				free(result);
520 				return (NULL);
521 			}
522 
523 			valuep = currp;
524 			if (valuep == NULL) {
525 				if (i < (n_entries - 1)) {
526 					*ierrnop = ITAB_NOT_ENOUGH_IP;
527 					inittab_msg("inittab_encode: too few "
528 					    "ip addresses");
529 					free(result);
530 					return (NULL);
531 				}
532 				break;
533 			}
534 		}
535 		break;
536 
537 	case DSYM_NUMBER:				/* FALLTHRU */
538 	case DSYM_UNUMBER8:				/* FALLTHRU */
539 	case DSYM_SNUMBER8:				/* FALLTHRU */
540 	case DSYM_UNUMBER16:				/* FALLTHRU */
541 	case DSYM_SNUMBER16:				/* FALLTHRU */
542 	case DSYM_UNUMBER32:				/* FALLTHRU */
543 	case DSYM_SNUMBER32:				/* FALLTHRU */
544 	case DSYM_UNUMBER64:				/* FALLTHRU */
545 	case DSYM_SNUMBER64:
546 
547 		if (result == NULL) {
548 			*ierrnop = ITAB_BAD_NUMBER;
549 			return (NULL);
550 		}
551 
552 		is_signed = (ie->ds_type == DSYM_SNUMBER64 ||
553 		    ie->ds_type == DSYM_SNUMBER32 ||
554 		    ie->ds_type == DSYM_SNUMBER16 ||
555 		    ie->ds_type == DSYM_SNUMBER8);
556 
557 		if (encode_number(n_entries, type_size, is_signed, 0, value,
558 		    result, ierrnop) == B_FALSE) {
559 			free(result);
560 			return (NULL);
561 		}
562 		break;
563 
564 	default:
565 		if (ie->ds_type == DSYM_BOOL)
566 			*ierrnop = ITAB_BAD_BOOLEAN;
567 		else
568 			*ierrnop = ITAB_SYNTAX_ERROR;
569 
570 		inittab_msg("inittab_encode: unsupported type `%d'",
571 		    ie->ds_type);
572 
573 		free(result);
574 		return (NULL);
575 	}
576 
577 	/*
578 	 * if just_payload is false, then we need to slide the option
579 	 * code and length fields in. (length includes them in its
580 	 * count, so we have to subtract 2)
581 	 */
582 	if (just_payload == B_FALSE) {
583 		(void) memmove(result + 2, result, length - 2);
584 		result[0] = ie->ds_code;
585 		result[1] = length - 2;
586 	}
587 
588 	if (lengthp != NULL)
589 		*lengthp = length;
590 
591 	return (result);
592 }
593 
594 /*
595  * inittab_decode_e(): converts a binary representation of a given datatype into
596  *		     a string; used for decoding DHCP options in a packet off
597  *		     the wire into ascii
598  *
599  *   input: dhcp_symbol_t *: the entry describing the payload option
600  *	    uchar_t *: the payload to convert
601  *	    uint16_t: the payload length (only used if just_payload is true)
602  *	    boolean_t: if false, payload is assumed to be a DHCP option
603  *	    int *: set to extended error code if error occurs.
604  *  output: char *: a dynamically allocated string containing the converted data
605  */
606 char *
607 inittab_decode_e(dhcp_symbol_t *ie, uchar_t *payload, uint16_t length,
608     boolean_t just_payload, int *ierrnop)
609 {
610 	char		*resultp, *end, *result = NULL;
611 	char		*currp;
612 	uchar_t		n_entries;
613 	struct in_addr	in_addr;
614 	uint8_t		type_size = inittab_type_to_size(ie);
615 	boolean_t	is_signed;
616 
617 	*ierrnop = 0;
618 	if (type_size == 0) {
619 		*ierrnop = ITAB_SYNTAX_ERROR;
620 		return (NULL);
621 	}
622 
623 	if (just_payload == B_FALSE) {
624 		length = payload[1];
625 		payload += 2;
626 	}
627 
628 	/*
629 	 * figure out the number of elements to convert.  note that
630 	 * for ds_type NUMBER, the granularity is really 1 since the
631 	 * value of ds_gran is the number of bytes in the number.
632 	 */
633 	if (ie->ds_type == DSYM_NUMBER)
634 		n_entries = MIN(ie->ds_max, length / type_size);
635 	else
636 		n_entries = MIN(ie->ds_max * ie->ds_gran, length / type_size);
637 
638 	if (n_entries == 0)
639 		n_entries = length / type_size;
640 
641 	if ((length % type_size) != 0) {
642 		inittab_msg("inittab_decode: length of string not compatible "
643 		    "with option type `%i'", ie->ds_type);
644 		*ierrnop = ITAB_BAD_STRING;
645 		return (NULL);
646 	}
647 
648 	switch (ie->ds_type) {
649 
650 	case DSYM_ASCII:
651 
652 		result = malloc(n_entries + 1);
653 		if (result == NULL) {
654 			*ierrnop = ITAB_NOMEM;
655 			return (NULL);
656 		}
657 
658 		(void) memcpy(result, payload, n_entries);
659 		result[n_entries] = '\0';
660 		break;
661 
662 	case DSYM_OCTET:
663 
664 		result = malloc(n_entries * (sizeof ("0xNN") + 1));
665 		if (result == NULL) {
666 			*ierrnop = ITAB_NOMEM;
667 			return (NULL);
668 		}
669 
670 		for (resultp = result; n_entries != 0; n_entries--) {
671 			currp = resultp;
672 			resultp += sprintf(resultp, "0x%02X ", *payload++);
673 			if (currp == resultp) {
674 				free(result);
675 				*ierrnop = ITAB_BAD_OCTET;
676 				return (NULL);
677 			}
678 		}
679 
680 		resultp[-1] = '\0';
681 		break;
682 
683 	case DSYM_IP:
684 
685 		if ((length / sizeof (ipaddr_t)) % ie->ds_gran != 0) {
686 			*ierrnop = ITAB_BAD_GRAN;
687 			inittab_msg("inittab_decode: number of entries "
688 			    "not compatible with option granularity");
689 			return (NULL);
690 		}
691 
692 		result = malloc(n_entries * (sizeof ("aaa.bbb.ccc.ddd") + 1));
693 		end = &result[n_entries * (sizeof ("aaa.bbb.ccc.ddd") + 1)];
694 		if (result == NULL) {
695 			*ierrnop = ITAB_NOMEM;
696 			return (NULL);
697 		}
698 
699 		for (resultp = result; n_entries != 0; n_entries--) {
700 			(void) memcpy(&in_addr.s_addr, payload,
701 			    sizeof (ipaddr_t));
702 			currp = resultp;
703 			resultp += snprintf(resultp, end - resultp, "%s ",
704 			    inet_ntoa(in_addr));
705 			if (currp == resultp) {
706 				free(result);
707 				*ierrnop = ITAB_BAD_IPADDR;
708 				return (NULL);
709 			}
710 			payload += sizeof (ipaddr_t);
711 		}
712 
713 		resultp[-1] = '\0';
714 		break;
715 
716 	case DSYM_NUMBER:				/* FALLTHRU */
717 	case DSYM_UNUMBER8:				/* FALLTHRU */
718 	case DSYM_SNUMBER8:				/* FALLTHRU */
719 	case DSYM_UNUMBER16:				/* FALLTHRU */
720 	case DSYM_SNUMBER16:				/* FALLTHRU */
721 	case DSYM_UNUMBER32:				/* FALLTHRU */
722 	case DSYM_SNUMBER32:				/* FALLTHRU */
723 	case DSYM_UNUMBER64:				/* FALLTHRU */
724 	case DSYM_SNUMBER64:
725 
726 		is_signed = (ie->ds_type == DSYM_SNUMBER64 ||
727 		    ie->ds_type == DSYM_SNUMBER32 ||
728 		    ie->ds_type == DSYM_SNUMBER16 ||
729 		    ie->ds_type == DSYM_SNUMBER8);
730 
731 		result = malloc(n_entries * ITAB_MAX_NUMBER_LEN);
732 		if (result == NULL) {
733 			*ierrnop = ITAB_NOMEM;
734 			return (NULL);
735 		}
736 
737 		if (decode_number(n_entries, type_size, is_signed, ie->ds_gran,
738 		    payload, result, ierrnop) == B_FALSE) {
739 			free(result);
740 			return (NULL);
741 		}
742 		break;
743 
744 	default:
745 		inittab_msg("inittab_decode: unsupported type `%d'",
746 		    ie->ds_type);
747 		break;
748 	}
749 
750 	return (result);
751 }
752 
753 /*
754  * inittab_encode(): converts a string representation of a given datatype into
755  *		     binary; used for encoding ascii values into a form that
756  *		     can be put in DHCP packets to be sent on the wire.
757  *
758  *   input: dhcp_symbol_t *: the entry describing the value option
759  *	    const char *: the value to convert
760  *	    uint16_t *: set to the length of the binary data returned
761  *	    boolean_t: if false, return a full DHCP option
762  *  output: uchar_t *: a dynamically allocated byte array with converted data
763  */
764 uchar_t *
765 inittab_encode(dhcp_symbol_t *ie, const char *value, uint16_t *lengthp,
766     boolean_t just_payload)
767 {
768 	int ierrno;
769 
770 	return (inittab_encode_e(ie, value, lengthp, just_payload, &ierrno));
771 }
772 
773 /*
774  * inittab_decode(): converts a binary representation of a given datatype into
775  *		     a string; used for decoding DHCP options in a packet off
776  *		     the wire into ascii
777  *
778  *   input: dhcp_symbol_t *: the entry describing the payload option
779  *	    uchar_t *: the payload to convert
780  *	    uint16_t: the payload length (only used if just_payload is true)
781  *	    boolean_t: if false, payload is assumed to be a DHCP option
782  *  output: char *: a dynamically allocated string containing the converted data
783  */
784 char *
785 inittab_decode(dhcp_symbol_t *ie, uchar_t *payload, uint16_t length,
786     boolean_t just_payload)
787 {
788 	int ierrno;
789 
790 	return (inittab_decode_e(ie, payload, length, just_payload, &ierrno));
791 }
792 
793 /*
794  * inittab_msg(): prints diagnostic messages if INITTAB_DEBUG is set
795  *
796  *	    const char *: a printf-like format string
797  *	    ...: arguments to the format string
798  *  output: void
799  */
800 /*PRINTFLIKE1*/
801 static void
802 inittab_msg(const char *fmt, ...)
803 {
804 	enum { INITTAB_MSG_CHECK, INITTAB_MSG_RETURN, INITTAB_MSG_OUTPUT };
805 
806 	va_list		ap;
807 	char		buf[512];
808 	static int	action = INITTAB_MSG_CHECK;
809 
810 	/*
811 	 * check DHCP_INITTAB_DEBUG the first time in; thereafter, use
812 	 * the the cached result (stored in `action').
813 	 */
814 	switch (action) {
815 
816 	case INITTAB_MSG_CHECK:
817 
818 		if (getenv("DHCP_INITTAB_DEBUG") == NULL) {
819 			action = INITTAB_MSG_RETURN;
820 			return;
821 		}
822 
823 		action = INITTAB_MSG_OUTPUT;
824 
825 		/* FALLTHRU into INITTAB_MSG_OUTPUT */
826 
827 	case INITTAB_MSG_OUTPUT:
828 
829 		va_start(ap, fmt);
830 
831 		(void) snprintf(buf, sizeof (buf), "inittab: %s\n", fmt);
832 		(void) vfprintf(stderr, buf, ap);
833 
834 		va_end(ap);
835 		break;
836 
837 	case INITTAB_MSG_RETURN:
838 
839 		return;
840 	}
841 }
842 
843 /*
844  * decode_number(): decodes a sequence of numbers from binary into ascii;
845  *		    binary is coming off of the network, so it is in nbo
846  *
847  *   input: uint8_t: the number of "granularity" numbers to decode
848  *	    uint8_t: the length of each number
849  *	    boolean_t: whether the numbers should be considered signed
850  *	    uint8_t: the number of numbers per granularity
851  *	    const uint8_t *: where to decode the numbers from
852  *	    char *: where to decode the numbers to
853  *  output: boolean_t: true on successful conversion, false on failure
854  */
855 static boolean_t
856 decode_number(uint8_t n_entries, uint8_t size, boolean_t is_signed,
857     uint8_t granularity, const uint8_t *from, char *to, int *ierrnop)
858 {
859 	uint16_t	uint16;
860 	uint32_t	uint32;
861 	uint64_t	uint64;
862 
863 	if (granularity != 0) {
864 		if ((granularity % n_entries) != 0) {
865 			inittab_msg("decode_number: number of entries "
866 			    "not compatible with option granularity");
867 			*ierrnop = ITAB_BAD_GRAN;
868 			return (B_FALSE);
869 		}
870 	}
871 
872 	for (; n_entries != 0; n_entries--, from += size) {
873 
874 		switch (size) {
875 
876 		case 1:
877 			to += sprintf(to, is_signed ? "%d " : "%u ", *from);
878 			break;
879 
880 		case 2:
881 			(void) memcpy(&uint16, from, 2);
882 			to += sprintf(to, is_signed ? "%hd " : "%hu ",
883 			    ntohs(uint16));
884 			break;
885 
886 		case 4:
887 			(void) memcpy(&uint32, from, 4);
888 			to += sprintf(to, is_signed ? "%ld " : "%lu ",
889 			    ntohl(uint32));
890 			break;
891 
892 		case 8:
893 			(void) memcpy(&uint64, from, 8);
894 			to += sprintf(to, is_signed ? "%lld " : "%llu ",
895 			    dhcp_ntohll(uint64));
896 			break;
897 
898 		default:
899 			*ierrnop = ITAB_BAD_NUMBER;
900 			inittab_msg("decode_number: unknown integer size `%d'",
901 			    size);
902 			return (B_FALSE);
903 		}
904 	}
905 
906 	to[-1] = '\0';
907 	return (B_TRUE);
908 }
909 
910 /*
911  * encode_number(): encodes a sequence of numbers from ascii into binary;
912  *		    number will end up on the wire so it needs to be in nbo
913  *
914  *   input: uint8_t: the number of "granularity" numbers to encode
915  *	    uint8_t: the length of each number
916  *	    boolean_t: whether the numbers should be considered signed
917  *	    uint8_t: the number of numbers per granularity
918  *	    const uint8_t *: where to encode the numbers from
919  *	    char *: where to encode the numbers to
920  *	    int *: set to extended error code if error occurs.
921  *  output: boolean_t: true on successful conversion, false on failure
922  */
923 static boolean_t /* ARGSUSED */
924 encode_number(uint8_t n_entries, uint8_t size, boolean_t is_signed,
925     uint8_t granularity, const char *from, uint8_t *to, int *ierrnop)
926 {
927 	uint8_t		i;
928 	uint16_t	uint16;
929 	uint32_t	uint32;
930 	uint64_t	uint64;
931 	char		*endptr;
932 
933 	if (granularity != 0) {
934 		if ((granularity % n_entries) != 0) {
935 			*ierrnop = ITAB_BAD_GRAN;
936 			inittab_msg("encode_number: number of entries "
937 			    "not compatible with option granularity");
938 			return (B_FALSE);
939 		}
940 	}
941 
942 	for (i = 0; i < n_entries; i++, from++) {
943 
944 		/*
945 		 * totally obscure c factoid: it is legal to pass a
946 		 * string representing a negative number to strtoul().
947 		 * in this case, strtoul() will return an unsigned
948 		 * long that if cast to a long, would represent the
949 		 * negative number.  we take advantage of this to
950 		 * cut down on code here.
951 		 */
952 
953 		errno = 0;
954 		switch (size) {
955 
956 		case 1:
957 			to[i] = strtoul(from, &endptr, 0);
958 			if (errno != 0 || from == endptr) {
959 				goto error;
960 			}
961 			break;
962 
963 		case 2:
964 			uint16 = htons(strtoul(from, &endptr, 0));
965 			if (errno != 0 || from == endptr) {
966 				goto error;
967 			}
968 			(void) memcpy(to + (i * 2), &uint16, 2);
969 			break;
970 
971 		case 4:
972 			uint32 = htonl(strtoul(from, &endptr, 0));
973 			if (errno != 0 || from == endptr) {
974 				goto error;
975 			}
976 			(void) memcpy(to + (i * 4), &uint32, 4);
977 			break;
978 
979 		case 8:
980 			uint64 = dhcp_htonll(strtoull(from, &endptr, 0));
981 			if (errno != 0 || from == endptr) {
982 				goto error;
983 			}
984 			(void) memcpy(to + (i * 8), &uint64, 8);
985 			break;
986 
987 		default:
988 			inittab_msg("encode_number: unsupported integer "
989 			    "size `%d'", size);
990 			return (B_FALSE);
991 		}
992 
993 		from = strchr(from, ' ');
994 		if (from == NULL)
995 			break;
996 	}
997 
998 	return (B_TRUE);
999 
1000 error:
1001 	*ierrnop = ITAB_BAD_NUMBER;
1002 	inittab_msg("encode_number: cannot convert to integer");
1003 	return (B_FALSE);
1004 }
1005 
1006 /*
1007  * inittab_type_to_size(): given an inittab entry, returns size of one entry of
1008  *		      its type
1009  *
1010  *   input: dhcp_symbol_t *: an entry of the given type
1011  *  output: uint8_t: the size in bytes of an entry of that type
1012  */
1013 uint8_t
1014 inittab_type_to_size(dhcp_symbol_t *ie)
1015 {
1016 	switch (ie->ds_type) {
1017 
1018 	case DSYM_ASCII:
1019 	case DSYM_OCTET:
1020 	case DSYM_SNUMBER8:
1021 	case DSYM_UNUMBER8:
1022 
1023 		return (1);
1024 
1025 	case DSYM_SNUMBER16:
1026 	case DSYM_UNUMBER16:
1027 
1028 		return (2);
1029 
1030 	case DSYM_SNUMBER32:
1031 	case DSYM_UNUMBER32:
1032 	case DSYM_IP:
1033 
1034 		return (4);
1035 
1036 	case DSYM_SNUMBER64:
1037 	case DSYM_UNUMBER64:
1038 
1039 		return (8);
1040 
1041 	case DSYM_NUMBER:
1042 
1043 		return (ie->ds_gran);
1044 	}
1045 
1046 	return (0);
1047 }
1048 
1049 /*
1050  * itabcode_to_dsymcode(): maps an inittab category code to its dsym
1051  *                         representation
1052  *
1053  *   input: uchar_t: the inittab category code
1054  *  output: dsym_category_t: the dsym category code
1055  */
1056 static dsym_category_t
1057 itabcode_to_dsymcode(uchar_t itabcode)
1058 {
1059 
1060 	unsigned int	i;
1061 
1062 	for (i = 0; i < ITAB_CAT_COUNT; i++)
1063 		if (category_map[i].cme_itabcode == itabcode)
1064 			return (category_map[i].cme_dsymcode);
1065 
1066 	return (DSYM_BAD_CAT);
1067 }
1068 
1069 /*
1070  * category_to_code(): maps a category name to its numeric representation
1071  *
1072  *   input: const char *: the category name
1073  *  output: uchar_t: its internal code (numeric representation)
1074  */
1075 static uchar_t
1076 category_to_code(const char *category)
1077 {
1078 	unsigned int	i;
1079 
1080 	for (i = 0; i < ITAB_CAT_COUNT; i++)
1081 		if (strcasecmp(category_map[i].cme_name, category) == 0)
1082 			return (category_map[i].cme_itabcode);
1083 
1084 	return (0);
1085 }
1086 
1087 /*
1088  * dhcp_htonll(): converts a 64-bit number from host to network byte order
1089  *
1090  *   input: uint64_t: the number to convert
1091  *  output: uint64_t: its value in network byte order
1092  */
1093 static uint64_t
1094 dhcp_htonll(uint64_t uint64_hbo)
1095 {
1096 	return (dhcp_ntohll(uint64_hbo));
1097 }
1098 
1099 /*
1100  * dhcp_ntohll(): converts a 64-bit number from network to host byte order
1101  *
1102  *   input: uint64_t: the number to convert
1103  *  output: uint64_t: its value in host byte order
1104  */
1105 static uint64_t
1106 dhcp_ntohll(uint64_t uint64_nbo)
1107 {
1108 #ifdef	_LITTLE_ENDIAN
1109 	return ((uint64_t)ntohl(uint64_nbo & 0xffffffff) << 32 |
1110 	    ntohl(uint64_nbo >> 32));
1111 #else
1112 	return (uint64_nbo);
1113 #endif
1114 }
1115 
1116 /*
1117  * our internal table of DHCP option values, used by inittab_verify()
1118  */
1119 static dhcp_symbol_t inittab_table[] =
1120 {
1121 { DSYM_INTERNAL,	1024,	"Hostname",	DSYM_BOOL,	0,	0 },
1122 { DSYM_INTERNAL,	1025,	"LeaseNeg",	DSYM_BOOL,	0,	0 },
1123 { DSYM_INTERNAL,	1026,	"EchoVC",	DSYM_BOOL,	0,	0 },
1124 { DSYM_INTERNAL,	1027,	"BootPath",	DSYM_ASCII,	1,	128 },
1125 { DSYM_FIELD,		0,	"Opcode",	DSYM_UNUMBER8,	1,	1 },
1126 { DSYM_FIELD,		1,	"Htype",	DSYM_UNUMBER8,	1,	1 },
1127 { DSYM_FIELD,		2,	"HLen",		DSYM_UNUMBER8,	1,	1 },
1128 { DSYM_FIELD,		3,	"Hops",		DSYM_UNUMBER8,	1,	1 },
1129 { DSYM_FIELD,		4,	"Xid",		DSYM_UNUMBER32,	1,	1 },
1130 { DSYM_FIELD,		8,	"Secs",		DSYM_UNUMBER16,	1,	1 },
1131 { DSYM_FIELD,		10,	"Flags",	DSYM_OCTET,	1,	2 },
1132 { DSYM_FIELD,		12,	"Ciaddr",	DSYM_IP,	1,	1 },
1133 { DSYM_FIELD,		16,	"Yiaddr",	DSYM_IP,	1,	1 },
1134 { DSYM_FIELD,		20,	"BootSrvA",	DSYM_IP,	1,	1 },
1135 { DSYM_FIELD,		24,	"Giaddr",	DSYM_IP,	1,	1 },
1136 { DSYM_FIELD,		28,	"Chaddr",	DSYM_OCTET, 	1,	16 },
1137 { DSYM_FIELD,		44,	"BootSrvN",	DSYM_ASCII,	1,	64 },
1138 { DSYM_FIELD,		108,	"BootFile",	DSYM_ASCII,	1,	128 },
1139 { DSYM_FIELD,		236,	"Magic",	DSYM_OCTET,	1,	4 },
1140 { DSYM_FIELD,		240,	"Options",	DSYM_OCTET,	1,	60 },
1141 { DSYM_STANDARD,	1,	"Subnet",	DSYM_IP,	1,	1 },
1142 { DSYM_STANDARD,	2,	"UTCoffst",	DSYM_SNUMBER32,	1,	1 },
1143 { DSYM_STANDARD,	3,	"Router",	DSYM_IP,	1,	0 },
1144 { DSYM_STANDARD,	4,	"Timeserv",	DSYM_IP,	1,	0 },
1145 { DSYM_STANDARD,	5,	"IEN116ns",	DSYM_IP,	1,	0 },
1146 { DSYM_STANDARD,	6,	"DNSserv",	DSYM_IP,	1,	0 },
1147 { DSYM_STANDARD,	7,	"Logserv",	DSYM_IP,	1,	0 },
1148 { DSYM_STANDARD,	8,	"Cookie",	DSYM_IP,	1,	0 },
1149 { DSYM_STANDARD,	9,	"Lprserv",	DSYM_IP,	1,	0 },
1150 { DSYM_STANDARD,	10,	"Impress",	DSYM_IP,	1,	0 },
1151 { DSYM_STANDARD,	11,	"Resource",	DSYM_IP,	1,	0 },
1152 { DSYM_STANDARD,	12,	"Hostname",	DSYM_ASCII,	1,	0 },
1153 { DSYM_STANDARD,	13,	"Bootsize",	DSYM_UNUMBER16,	1,	1 },
1154 { DSYM_STANDARD,	14,	"Dumpfile",	DSYM_ASCII,	1,	0 },
1155 { DSYM_STANDARD,	15,	"DNSdmain",	DSYM_ASCII,	1,	0 },
1156 { DSYM_STANDARD,	16,	"Swapserv",	DSYM_IP,	1,	1 },
1157 { DSYM_STANDARD,	17,	"Rootpath",	DSYM_ASCII,	1,	0 },
1158 { DSYM_STANDARD,	18,	"ExtendP",	DSYM_ASCII,	1,	0 },
1159 { DSYM_STANDARD,	19,	"IpFwdF",	DSYM_UNUMBER8,	1,	1 },
1160 { DSYM_STANDARD,	20,	"NLrouteF",	DSYM_UNUMBER8,	1,	1 },
1161 { DSYM_STANDARD,	21,	"PFilter",	DSYM_IP,	2,	0 },
1162 { DSYM_STANDARD,	22,	"MaxIpSiz",	DSYM_UNUMBER16,	1,	1 },
1163 { DSYM_STANDARD,	23,	"IpTTL",	DSYM_UNUMBER8,	1,	1 },
1164 { DSYM_STANDARD,	24,	"PathTO",	DSYM_UNUMBER32,	1,	1 },
1165 { DSYM_STANDARD,	25,	"PathTbl",	DSYM_UNUMBER16,	1,	0 },
1166 { DSYM_STANDARD,	26,	"MTU",		DSYM_UNUMBER16,	1,	1 },
1167 { DSYM_STANDARD,	27,	"SameMtuF",	DSYM_UNUMBER8,	1,	1 },
1168 { DSYM_STANDARD,	28,	"Broadcst",	DSYM_IP,	1,	1 },
1169 { DSYM_STANDARD,	29,	"MaskDscF",	DSYM_UNUMBER8,	1,	1 },
1170 { DSYM_STANDARD,	30,	"MaskSupF",	DSYM_UNUMBER8,	1,	1 },
1171 { DSYM_STANDARD,	31,	"RDiscvyF",	DSYM_UNUMBER8,	1,	1 },
1172 { DSYM_STANDARD,	32,	"RSolictS",	DSYM_IP,	1,	1 },
1173 { DSYM_STANDARD,	33,	"StaticRt",	DSYM_IP,	2,	0 },
1174 { DSYM_STANDARD,	34,	"TrailerF",	DSYM_UNUMBER8,	1,	1 },
1175 { DSYM_STANDARD,	35,	"ArpTimeO",	DSYM_UNUMBER32,	1,	1 },
1176 { DSYM_STANDARD,	36,	"EthEncap",	DSYM_UNUMBER8,	1,	1 },
1177 { DSYM_STANDARD,	37,	"TcpTTL",	DSYM_UNUMBER8,	1,	1 },
1178 { DSYM_STANDARD,	38,	"TcpKaInt",	DSYM_UNUMBER32,	1,	1 },
1179 { DSYM_STANDARD,	39,	"TcpKaGbF",	DSYM_UNUMBER8,	1,	1 },
1180 { DSYM_STANDARD,	40,	"NISdmain",	DSYM_ASCII,	1,	0 },
1181 { DSYM_STANDARD,	41,	"NISservs",	DSYM_IP,	1,	0 },
1182 { DSYM_STANDARD,	42,	"NTPservs",	DSYM_IP,	1,	0 },
1183 { DSYM_STANDARD,	43,	"Vendor",	DSYM_OCTET,	1,	0 },
1184 { DSYM_STANDARD,	44,	"NetBNms",	DSYM_IP,	1,	0 },
1185 { DSYM_STANDARD,	45,	"NetBDsts",	DSYM_IP,	1,	0 },
1186 { DSYM_STANDARD,	46,	"NetBNdT",	DSYM_UNUMBER8,	1,	1 },
1187 { DSYM_STANDARD,	47,	"NetBScop",	DSYM_ASCII,	1,	0 },
1188 { DSYM_STANDARD,	48,	"XFontSrv",	DSYM_IP,	1,	0 },
1189 { DSYM_STANDARD,	49,	"XDispMgr",	DSYM_IP,	1,	0 },
1190 { DSYM_STANDARD,	50,	"ReqIP",	DSYM_IP,	1,	1 },
1191 { DSYM_STANDARD,	51,	"LeaseTim",	DSYM_UNUMBER32,	1,	1 },
1192 { DSYM_STANDARD,	52,	"OptOvrld",	DSYM_UNUMBER8,	1,	1 },
1193 { DSYM_STANDARD,	53,	"DHCPType",	DSYM_UNUMBER8,	1,	1 },
1194 { DSYM_STANDARD,	54,	"ServerID",	DSYM_IP,	1,	1 },
1195 { DSYM_STANDARD,	55,	"ReqList",	DSYM_OCTET,	1,	0 },
1196 { DSYM_STANDARD,	56,	"Message",	DSYM_ASCII,	1,	0 },
1197 { DSYM_STANDARD,	57,	"DHCP_MTU",	DSYM_UNUMBER16,	1,	1 },
1198 { DSYM_STANDARD,	58,	"T1Time",	DSYM_UNUMBER32,	1,	1 },
1199 { DSYM_STANDARD,	59,	"T2Time",	DSYM_UNUMBER32,	1,	1 },
1200 { DSYM_STANDARD,	60,	"ClassID",	DSYM_ASCII,	1,	0 },
1201 { DSYM_STANDARD,	61,	"ClientID",	DSYM_OCTET,	1,	0 },
1202 { DSYM_STANDARD,	62,	"NW_dmain",	DSYM_ASCII,	1,	0 },
1203 { DSYM_STANDARD,	63,	"NWIPOpts",	DSYM_OCTET,	1,	128 },
1204 { DSYM_STANDARD,	64,	"NIS+dom",	DSYM_ASCII,	1,	0 },
1205 { DSYM_STANDARD,	65,	"NIS+serv",	DSYM_IP,	1,	0 },
1206 { DSYM_STANDARD,	66,	"TFTPsrvN",	DSYM_ASCII,	1,	64 },
1207 { DSYM_STANDARD,	67,	"OptBootF",	DSYM_ASCII,	1,	128 },
1208 { DSYM_STANDARD,	68,	"MblIPAgt",	DSYM_IP,	1,	0 },
1209 { DSYM_STANDARD,	69,	"SMTPserv",	DSYM_IP,	1,	0 },
1210 { DSYM_STANDARD,	70,	"POP3serv",	DSYM_IP,	1,	0 },
1211 { DSYM_STANDARD,	71,	"NNTPserv",	DSYM_IP,	1,	0 },
1212 { DSYM_STANDARD,	72,	"WWWservs",	DSYM_IP,	1,	0 },
1213 { DSYM_STANDARD,	73,	"Fingersv",	DSYM_IP,	1,	0 },
1214 { DSYM_STANDARD,	74,	"IRCservs",	DSYM_IP,	1,	0 },
1215 { DSYM_STANDARD,	75,	"STservs",	DSYM_IP,	1,	0 },
1216 { DSYM_STANDARD,	76,	"STDAservs",	DSYM_IP,	1,	0 },
1217 { DSYM_STANDARD,	77,	"UserClas",	DSYM_ASCII,	1,	0 },
1218 { DSYM_STANDARD,	78,	"SLP_DA",	DSYM_OCTET,	1,	0 },
1219 { DSYM_STANDARD,	79,	"SLP_SS",	DSYM_OCTET,	1,	0 },
1220 { DSYM_STANDARD,	82,	"AgentOpt",	DSYM_OCTET,	1,	0 },
1221 { DSYM_STANDARD,	89,	"FQDN",		DSYM_OCTET,	1,	0 },
1222 { 0,			0,	"",		0,		0,	0 }
1223 };
1224