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 2002 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 <stdlib.h>
30 #include <ctype.h>
31 #include <strings.h>
32 #include <limits.h>
33 #include <errno.h>
34 #include <dhcp_impl.h>
35 
36 #include "dhcp_symbol.h"
37 
38 /*
39  * The following structure and table are used to define the attributes
40  * of a DHCP symbol category.
41  */
42 typedef struct dsym_cat {
43 	char		*dc_string;	/* string value for the category */
44 	int		dc_minlen;	/* min. chars of dc_string to match */
45 	dsym_category_t	dc_id;		/* numerical value for the category */
46 	boolean_t	dc_dhcptab;	/* valid for dhcptab use? */
47 	ushort_t	dc_min;		/* minimum valid code */
48 	ushort_t	dc_max;		/* maximum valid code */
49 } dsym_cat_t;
50 
51 static dsym_cat_t cats[DSYM_CATEGORY_NUM] = {
52 	{ "Extend", 6, DSYM_EXTEND, B_TRUE, DHCP_LAST_STD + 1,
53 		DHCP_SITE_OPT - 1 },
54 	{ "Vendor=", 6, DSYM_VENDOR, B_TRUE, DHCP_FIRST_OPT,
55 		DHCP_LAST_OPT },
56 	{ "Site", 4, DSYM_SITE, B_TRUE, DHCP_SITE_OPT, DHCP_LAST_OPT },
57 	{ "Standard", 8, DSYM_STANDARD, B_FALSE, DHCP_FIRST_OPT,
58 	    DHCP_LAST_STD },
59 	{ "Field", 5, DSYM_FIELD, B_FALSE, CD_PACKET_START,
60 		CD_PACKET_END },
61 	{ "Internal", 8, DSYM_INTERNAL, B_FALSE, CD_INTRNL_START,
62 	    CD_INTRNL_END }
63 };
64 
65 /*
66  * The following structure and table are used to define the attributes
67  * of a DHCP symbol type.
68  */
69 typedef struct dsym_type {
70 	char		*dt_string;	/* string value for the type */
71 	dsym_cdtype_t	dt_id;		/* numerical value for the type */
72 	boolean_t	dt_dhcptab;	/* valid for dhcptab use? */
73 } dsym_type_t;
74 
75 static dsym_type_t types[DSYM_CDTYPE_NUM] = {
76 	{ "ASCII", DSYM_ASCII, B_TRUE },
77 	{ "OCTET", DSYM_OCTET, B_TRUE },
78 	{ "IP", DSYM_IP, B_TRUE },
79 	{ "NUMBER", DSYM_NUMBER, B_TRUE },
80 	{ "BOOL", DSYM_BOOL, B_TRUE },
81 	{ "INCLUDE", DSYM_INCLUDE, B_FALSE },
82 	{ "UNUMBER8", DSYM_UNUMBER8, B_TRUE },
83 	{ "UNUMBER16", DSYM_UNUMBER16, B_TRUE },
84 	{ "UNUMBER32", DSYM_UNUMBER32, B_TRUE },
85 	{ "UNUMBER64", DSYM_UNUMBER64, B_TRUE },
86 	{ "SNUMBER8", DSYM_SNUMBER8, B_TRUE },
87 	{ "SNUMBER16", DSYM_SNUMBER16, B_TRUE },
88 	{ "SNUMBER32", DSYM_SNUMBER32, B_TRUE },
89 	{ "SNUMBER64", DSYM_SNUMBER64, B_TRUE }
90 };
91 
92 /*
93  * symbol delimiters and constants
94  */
95 #define	DSYM_CLASS_DEL		" \t\n"
96 #define	DSYM_FIELD_DEL		","
97 #define	DSYM_VENDOR_DEL		'='
98 #define	DSYM_QUOTE		'"'
99 
100 /*
101  * dsym_trim(): trims all whitespace from either side of a string
102  *
103  *  input: char **: a pointer to a string to trim of whitespace.
104  * output: none
105  */
106 static void
107 dsym_trim(char **str)
108 {
109 
110 	char *tmpstr = *str;
111 
112 	/*
113 	 * Trim all whitespace from the front of the string.
114 	 */
115 	while (*tmpstr != '\0' && isspace(*tmpstr)) {
116 		tmpstr++;
117 	}
118 
119 	/*
120 	 * Move the str pointer to first non-whitespace char.
121 	 */
122 	*str = tmpstr;
123 
124 	/*
125 	 * Check case where the string is nothing but whitespace.
126 	 */
127 	if (*tmpstr == '\0') {
128 
129 		/*
130 		 * Trim all whitespace from the end of the string.
131 		 */
132 		tmpstr = *str + strlen(*str) - 1;
133 		while (tmpstr >= *str && isspace(*tmpstr)) {
134 			tmpstr--;
135 		}
136 
137 		/*
138 		 * terminate after last non-whitespace char.
139 		 */
140 		*(tmpstr+1) = '\0';
141 	}
142 }
143 
144 /*
145  * dsym_get_token(): strtok_r() like routine, except consecutive delimiters
146  *                   result in an empty string
147  *
148  *   note: original string is modified
149  *
150  *  input: char *: string in which to search for tokens
151  *         char *: list of possible token delimiter characters
152  *         char **: location for next call to routine
153  *         boolean_t: should delimiters be ignored if within quoted string?
154  * output: char *: token, or NULL if no more tokens
155  */
156 static char *
157 dsym_get_token(char *str, char *dels, char **lasts, boolean_t quote_support)
158 {
159 
160 	char *ptr = str;
161 	char *del;
162 	boolean_t found = B_FALSE;
163 	boolean_t in_quote = B_FALSE;
164 
165 	/*
166 	 * If incoming string has no tokens return a NULL
167 	 * pointer to signify no more tokens.
168 	 */
169 	if (*ptr == '\0') {
170 		return (NULL);
171 	}
172 
173 	/*
174 	 * Loop until either a token has been identified or until end
175 	 * of string has been reached.
176 	 */
177 	while (!found && *ptr != '\0') {
178 
179 		/*
180 		 * If pointer currently lies within a quoted string,
181 		 * then do not check for the delimiter.
182 		 */
183 		if (!in_quote) {
184 			for (del = dels; !found && *del != '\0'; del++) {
185 				if (*del == *ptr) {
186 					*ptr++ = '\0';
187 					found = B_TRUE;
188 				}
189 			}
190 		}
191 
192 		/*
193 		 * If the pointer is pointing at a delimiter, then
194 		 * check to see if it points to at a quote and update
195 		 * the state appropriately.
196 		 */
197 		if (!found) {
198 			if (quote_support && *ptr == DSYM_QUOTE) {
199 				in_quote = !in_quote;
200 			}
201 			ptr++;
202 		}
203 	}
204 
205 	*lasts = ptr;
206 
207 	return (str);
208 }
209 
210 /*
211  * dsym_get_long(): given a numeric string, returns its long value
212  *
213  *  input: const char *: the numeric string
214  *         long *: the return location for the long value
215  * output: DSYM_SUCCESS, DSYM_VALUE_OUT_OF_RANGE or DSYM_SYNTAX_ERROR
216  */
217 static dsym_errcode_t
218 dsym_get_long(const char *str, long *val)
219 {
220 
221 	int ret = DSYM_SUCCESS;
222 	int i;
223 
224 	for (i = 0; str[i] != '\0'; i++) {
225 		if (!isdigit(str[i])) {
226 			return (DSYM_SYNTAX_ERROR);
227 		}
228 	}
229 
230 	errno = 0;
231 	*val = strtol(str, NULL, 10);
232 	if (errno != 0) {
233 		ret = DSYM_VALUE_OUT_OF_RANGE;
234 	}
235 
236 	return (ret);
237 }
238 
239 /*
240  * dsym_free_classes(): frees the classes allocated by dsym_parse_classes()
241  *
242  *  input: dhcp_classes_t *: pointer to structure containing classes to free
243  * output: none
244  */
245 void
246 dsym_free_classes(dhcp_classes_t *classes)
247 {
248 
249 	int i;
250 
251 	if (classes->dc_names == NULL) {
252 		return;
253 	}
254 
255 	for (i = 0; i < classes->dc_cnt; i++) {
256 		free(classes->dc_names[i]);
257 	}
258 
259 	free(classes->dc_names);
260 	classes->dc_names = NULL;
261 	classes->dc_cnt = 0;
262 }
263 
264 /*
265  * dsym_parse_classes(): given a "Vendor" class string, builds and returns
266  *                     the list of vendor classes
267  *
268  *  input: char *: the "Vendor" class string
269  *         dhcp_classes_t *: pointer to the classes structure
270  * output: DSYM_SUCCESS, DSYM_INVALID_CAT, DSYM_EXCEEDS_MAX_CLASS_SIZE,
271  *         DSYM_EXCEEDS_CLASS_SIZE, DSYM_SYNTAX_ERROR, or DSYM_NO_MEMORY
272  */
273 static dsym_errcode_t
274 dsym_parse_classes(char *ptr, dhcp_classes_t *classes_ret)
275 {
276 
277 	char **classes = NULL;
278 	char *cp;
279 	int len;
280 	int ret = DSYM_SUCCESS;
281 	int i;
282 
283 	while (*ptr != '\0') {
284 		if (*ptr == DSYM_VENDOR_DEL) {
285 			ptr++;
286 			break;
287 		}
288 		ptr++;
289 	}
290 
291 	if (*ptr == '\0') {
292 	    return (DSYM_INVALID_CAT);
293 	}
294 
295 	if (strlen(ptr) > DSYM_MAX_CLASS_SIZE) {
296 		return (DSYM_EXCEEDS_MAX_CLASS_SIZE);
297 	}
298 
299 	dsym_trim(&ptr);
300 	classes_ret->dc_cnt = 0;
301 	for (i = 0; ret == DSYM_SUCCESS; i++) {
302 		cp = dsym_get_token(ptr, DSYM_CLASS_DEL, &ptr, B_TRUE);
303 		if (cp == NULL) {
304 			break;
305 		}
306 
307 		len = strlen(cp);
308 
309 		if (len == 0) {
310 			continue;
311 		} else if (len > DSYM_CLASS_SIZE) {
312 			ret = DSYM_EXCEEDS_CLASS_SIZE;
313 			continue;
314 		}
315 
316 		if (cp[0] == DSYM_QUOTE && cp[len-1] != DSYM_QUOTE) {
317 			ret = DSYM_SYNTAX_ERROR;
318 			continue;
319 		}
320 
321 		/* Strip off the quotes */
322 		if (cp[0] == DSYM_QUOTE) {
323 			cp[len-1] = '\0';
324 			cp++;
325 		}
326 
327 		classes = realloc(classes_ret->dc_names,
328 		    (sizeof (char **)) * (classes_ret->dc_cnt + 1));
329 		if (classes == NULL ||
330 		    (classes[classes_ret->dc_cnt] = strdup(cp))
331 		    == NULL) {
332 			ret = DSYM_NO_MEMORY;
333 			continue;
334 		}
335 		classes_ret->dc_names = classes;
336 		classes_ret->dc_cnt++;
337 	}
338 
339 	if (ret != DSYM_SUCCESS) {
340 		dsym_free_classes(classes_ret);
341 	}
342 
343 	return (ret);
344 }
345 
346 /*
347  * dsym_get_cat_by_name(): given a category field, returns the pointer to its
348  *                         entry in the internal category table.
349  *
350  *  input: const char *: the category name
351  *         dsym_cat_t *: the return location for the pointer to the table entry
352  *         boolean_t: case-sensitive name compare
353  * output: int: DSYM_SUCCESS or DSYM_INVALID_CAT
354  */
355 static dsym_errcode_t
356 dsym_get_cat_by_name(const char *cat, dsym_cat_t **entry, boolean_t cs)
357 {
358 
359 	dsym_cat_t *entryp = NULL;
360 	int ret = DSYM_SUCCESS;
361 	int cnt = sizeof (cats) / sizeof (dsym_cat_t);
362 	int result;
363 	int len;
364 	int i;
365 
366 	for (i = 0; i < cnt; i++) {
367 
368 		len = cats[i].dc_minlen;
369 		if (cs) {
370 			result = strncmp(cat, cats[i].dc_string, len);
371 		} else {
372 			result = strncasecmp(cat, cats[i].dc_string, len);
373 		}
374 
375 		if (result == 0) {
376 			entryp = &cats[i];
377 			break;
378 		}
379 	}
380 
381 	if (entryp != NULL) {
382 		/*
383 		 * Special code required for the Vendor category, because we
384 		 * allow whitespace between the keyword and the delimiter.
385 		 * If there is no delimiter, then this is an illegal category.
386 		 */
387 		const char *ptr = cat + entryp->dc_minlen;
388 		if (entryp->dc_id == DSYM_VENDOR) {
389 			while (*ptr != '\0' && isspace(*ptr)) {
390 				ptr++;
391 			}
392 			if (*ptr != DSYM_VENDOR_DEL) {
393 				ret = DSYM_INVALID_CAT;
394 			}
395 		} else {
396 			if (*ptr != '\0') {
397 				ret = DSYM_INVALID_CAT;
398 			}
399 		}
400 	} else {
401 		ret = DSYM_INVALID_CAT;
402 	}
403 
404 	if (ret == DSYM_SUCCESS) {
405 		*entry = entryp;
406 	}
407 
408 	return (ret);
409 }
410 
411 /*
412  * dsym_parse_cat(): given a category field, returns the category value
413  *                 Note: The category must be a valid dhcptab category.
414  *
415  *  input: const char *: a category field
416  *         dsym_category_t *: the return location for the category value
417  * output: int: DSYM_SUCCESS or DSYM_INVALID_CAT
418  */
419 static dsym_errcode_t
420 dsym_parse_cat(const char *field, dsym_category_t *cat)
421 {
422 
423 	dsym_cat_t *entry;
424 	int ret;
425 
426 	ret = dsym_get_cat_by_name(field, &entry, B_TRUE);
427 	if (ret == DSYM_SUCCESS) {
428 		/*
429 		 * Since this routine is meant to be used to parse dhcptab
430 		 * symbol definitions, only a subset of the DHCP categories
431 		 * are valid in this context.
432 		 */
433 		if (entry->dc_dhcptab) {
434 			*cat = entry->dc_id;
435 		} else {
436 			ret = DSYM_INVALID_CAT;
437 		}
438 	}
439 
440 	return (ret);
441 }
442 
443 /*
444  * dsym_parse_intrange(): given a DHCP integer field, returns the value
445  *
446  *  input: const char *: a DHCP code field
447  *         int *: the return location for the value
448  *         int: the minimum valid value
449  *         int: the maximum valid value
450  * output: int: DSYM_SUCCESS, DSYM_SYNTAX_ERROR, or DSYM_VALUE_OUT_OF_RANGE
451  */
452 static dsym_errcode_t
453 dsym_parse_intrange(const char *field, int *intval, int min, int max)
454 {
455 
456 	int ret;
457 	long longval;
458 
459 	ret = dsym_get_long(field, &longval);
460 	if (ret == DSYM_SUCCESS) {
461 		if (longval < min || longval > max) {
462 			ret = DSYM_VALUE_OUT_OF_RANGE;
463 		} else {
464 			*intval = (int)longval;
465 		}
466 	}
467 	return (ret);
468 }
469 
470 /*
471  * dsym_validate_code(): given a symbol category and code, validates
472  *                       that the code is valid for the category
473  *
474  *  input: dsym_category_t: the symbol category
475  *         uint16_t: the symbol code
476  * output: DSYM_SUCCESS, DSYM_INVALID_CAT or DSYM_CODE_OUT_OF_RANGE
477  */
478 static dsym_errcode_t
479 dsym_validate_code(dsym_category_t cat, ushort_t code)
480 {
481 
482 	int cnt = sizeof (cats) / sizeof (dsym_cat_t);
483 	int i;
484 
485 	/*
486 	 * Find the category entry from the internal table.
487 	 */
488 	for (i = 0; i < cnt; i++) {
489 		dsym_cat_t *entry;
490 		if (cat == cats[i].dc_id) {
491 			entry = &cats[i];
492 			if (code < entry->dc_min || code > entry->dc_max) {
493 				return (DSYM_CODE_OUT_OF_RANGE);
494 			}
495 			return (DSYM_SUCCESS);
496 		}
497 	}
498 
499 	return (DSYM_INVALID_CAT);
500 }
501 
502 /*
503  * dsym_validate_granularity(): given a symbol type, validates
504  *                       	that the granularity is valid for the type
505  *
506  *  input: dsym_cdtype_t: the symbol type
507  *         uchar_t: the symbol granularity
508  * output: DSYM_SUCCESS or DSYM_VALUE_OUT_OF_RANGE
509  */
510 static dsym_errcode_t
511 dsym_validate_granularity(dsym_cdtype_t type, uchar_t gran)
512 {
513 	/*
514 	 * We only need to check for a 0 with non-boolean types, as
515 	 * anything else is already validated by the ranges passed to
516 	 * dsym_parse_intrange() in dsym_parse_field().
517 	 */
518 	if (gran == 0 && type != DSYM_BOOL) {
519 		return (DSYM_VALUE_OUT_OF_RANGE);
520 	}
521 	return (DSYM_SUCCESS);
522 }
523 
524 /*
525  * dsym_get_type_by_name(): given a type field, returns the pointer to its
526  *                          entry in the internal type table.
527  *
528  *  input: const char *: the type name
529  *         dsym_type_t *: the return location for the pointer to the table entry
530  *         boolean_t: case-sensitive name compare
531  * output: int: DSYM_SUCCESS or DSYM_INVALID_TYPE
532  */
533 static dsym_errcode_t
534 dsym_get_type_by_name(const char *type, dsym_type_t **entry, boolean_t cs)
535 {
536 	int cnt = sizeof (types) / sizeof (dsym_type_t);
537 	int result;
538 	int i;
539 
540 	for (i = 0; i < cnt; i++) {
541 
542 		if (cs) {
543 			result = strcmp(type, types[i].dt_string);
544 		} else {
545 			result = strcasecmp(type, types[i].dt_string);
546 		}
547 
548 		if (result == 0) {
549 			*entry = &types[i];
550 			return (DSYM_SUCCESS);
551 		}
552 	}
553 
554 	return (DSYM_INVALID_TYPE);
555 }
556 
557 /*
558  * dsym_parse_type(): given a DHCP type string, returns the type id
559  *
560  *  input: char *: a DHCP type string
561  *         dsym_cdtype_t *: the return location for the type id
562  * output: int: DSYM_SUCCESS or DSYM_INVALID_TYPE
563  */
564 static dsym_errcode_t
565 dsym_parse_type(char *field, dsym_cdtype_t *type)
566 {
567 
568 	dsym_type_t *entry;
569 	int ret;
570 
571 	ret = dsym_get_type_by_name(field, &entry, B_TRUE);
572 	if (ret == DSYM_SUCCESS) {
573 		/*
574 		 * Since this routine is meant to be used to parse dhcptab
575 		 * symbol definitions, only a subset of the DHCP type
576 		 * are valid in this context.
577 		 */
578 		if (entry->dt_dhcptab) {
579 			*type = entry->dt_id;
580 		} else {
581 			ret = DSYM_INVALID_TYPE;
582 		}
583 	}
584 
585 	return (ret);
586 }
587 
588 /*
589  * dsym_free_fields(): frees an array of fields allocated by
590  *                     dsym_init_parser().
591  *
592  *  input: char **: array of fields to free
593  * output: none
594  */
595 void
596 dsym_free_fields(char **fields)
597 {
598 	int i;
599 	if (fields != NULL) {
600 		for (i = 0; i < DSYM_NUM_FIELDS; i++) {
601 			free(fields[i]);
602 		}
603 		free(fields);
604 	}
605 }
606 
607 /*
608  * dsym_close_parser(): free up all resources associated with the parser
609  *
610  *  input: char **: the fields allocated by dsym_init_parser()
611  *         dhcp_symbol_t *: the structure populated by dsym_init_parser()
612  * output: none
613  */
614 void
615 dsym_close_parser(char **fields, dhcp_symbol_t *sym)
616 {
617 	dsym_free_fields(fields);
618 	dsym_free_classes(&sym->ds_classes);
619 }
620 
621 /*
622  * dsym_init_parser(): initializes the structures used to parse a symbol
623  *                     value.
624  *
625  *  input: const char *: the symbol name
626  *         const char *: the symbol value in dhcptab format
627  *         char ***: the return location for the symbol fields
628  *         dhcp_symbol_t *: the structure which eventually will
629  *                          be the repository for the parsed symbol data
630  * output: int: DSYM_SUCCESS, DYSM_NO_MEMORY, DSYM_NULL_FIELD or
631  *              DSYM_TOO_MANY_FIELDS
632  */
633 dsym_errcode_t
634 dsym_init_parser(const char *name, const char *value, char ***fields_ret,
635     dhcp_symbol_t *sym)
636 {
637 
638 	int ret = DSYM_SUCCESS;
639 	char *cp;
640 	char *next;
641 	char *field;
642 	char **fields;
643 	int i;
644 
645 	/*
646 	 * Initialize the symbol structure.
647 	 */
648 	sym->ds_category = 0;
649 	sym->ds_code = 0;
650 	(void) strlcpy(sym->ds_name, name, DSYM_MAX_SYM_LEN);
651 	sym->ds_type = 0;
652 	sym->ds_gran = 0;
653 	sym->ds_max = 0;
654 	sym->ds_classes.dc_names = NULL;
655 	sym->ds_classes.dc_cnt = 0;
656 
657 	if ((cp = strdup(value)) == NULL ||
658 	    (fields = calloc(DSYM_NUM_FIELDS, sizeof (char *))) == NULL) {
659 		ret = DSYM_NO_MEMORY;
660 	}
661 
662 	next = cp;
663 	for (i = 0; ret == DSYM_SUCCESS && i < DSYM_NUM_FIELDS; i++) {
664 
665 		field = dsym_get_token(next, DSYM_FIELD_DEL, &next,
666 			B_FALSE);
667 
668 		if (field == NULL) {
669 			ret = DSYM_NULL_FIELD;
670 			continue;
671 		}
672 
673 		dsym_trim(&field);
674 
675 		if (strlen(field) == 0) {
676 			ret = DSYM_NULL_FIELD;
677 			continue;
678 		}
679 
680 		if ((fields[i] = strdup(field)) == NULL) {
681 			ret = DSYM_NO_MEMORY;
682 			continue;
683 		}
684 	}
685 
686 	if (ret == DSYM_SUCCESS &&
687 	    dsym_get_token(next, DSYM_FIELD_DEL, &next, B_FALSE) != NULL) {
688 		ret = DSYM_TOO_MANY_FIELDS;
689 	}
690 
691 	if (ret != DSYM_SUCCESS) {
692 		dsym_free_fields(fields);
693 	} else {
694 		*fields_ret = fields;
695 	}
696 
697 	free(cp);
698 	return (ret);
699 }
700 
701 /*
702  * dsym_parse_field(): parses the specified symbol field.
703  *
704  *  input: int: the field number to be parsed.
705  *         char **: symbol fields initialized by dsym_init_parser()
706  *         dhcp_symbol_t *: the structure which will be the repository
707  *                          for the parsed field
708  * output: int: DSYM_SUCCESS, DSYM_SYNTAX_ERROR, DSYM_CODE_OUT_OF_RANGE,
709  *              DSYM_INVALID_CAT, DSYM_INVALID_TYPE, DSYM_EXCEEDS_CLASS_SIZE,
710  *              DSYM_EXCEEDS_MAX_CLASS_SIZE, DSYM_NO_MEMORY,
711  *              DSYM_INVALID_FIELD_NUM, DSYM_VALUE_OUT_OF_RANGE
712  */
713 dsym_errcode_t
714 dsym_parse_field(int field_num, char **fields, dhcp_symbol_t *sym)
715 {
716 
717 	int 	ret = DSYM_SUCCESS;
718 	int	intval;
719 
720 	switch (field_num) {
721 
722 	case DSYM_CAT_FIELD:
723 		ret = dsym_parse_cat(fields[field_num], &sym->ds_category);
724 		if (ret == DSYM_SUCCESS && sym->ds_category == DSYM_VENDOR) {
725 			ret = dsym_parse_classes(fields[field_num],
726 			    &sym->ds_classes);
727 		}
728 		break;
729 
730 	case DSYM_CODE_FIELD:
731 		ret = dsym_parse_intrange(fields[field_num], &intval, 0,
732 		    USHRT_MAX);
733 		if (ret == DSYM_SUCCESS) {
734 			sym->ds_code = (ushort_t)intval;
735 			ret = dsym_validate_code(sym->ds_category,
736 			    sym->ds_code);
737 		}
738 		break;
739 
740 	case DSYM_TYPE_FIELD:
741 		ret = dsym_parse_type(fields[field_num], &sym->ds_type);
742 		break;
743 
744 	case DSYM_GRAN_FIELD:
745 		ret = dsym_parse_intrange(fields[field_num], &intval, 0,
746 		    UCHAR_MAX);
747 		if (ret == DSYM_SUCCESS) {
748 			sym->ds_gran = (uchar_t)intval;
749 			ret = dsym_validate_granularity(sym->ds_type,
750 			    sym->ds_gran);
751 		}
752 		break;
753 
754 	case DSYM_MAX_FIELD:
755 		ret = dsym_parse_intrange(fields[field_num], &intval, 0,
756 		    UCHAR_MAX);
757 		if (ret == DSYM_SUCCESS) {
758 			sym->ds_max = (uchar_t)intval;
759 		}
760 		break;
761 	default:
762 		ret = DSYM_INVALID_FIELD_NUM;
763 	}
764 
765 	return (ret);
766 }
767 
768 /*
769  * dsym_parser(): parses a DHCP symbol value
770  *
771  *  input: char **: symbol fields initialized by dsym_init_parser()
772  *         dhcp_symbol_t *: the structure which will be the repository
773  *                          for the parsed field
774  *         int *: last field processed
775  *         boolean_t: parse all fields even though errors occur?
776  * output: int: DSYM_SUCCESS, DSYM_SYNTAX_ERROR, DSYM_CODE_OUT_OF_RANGE,
777  *              DSYM_INVALID_CAT, DSYM_INVALID_TYPE, DSYM_EXCEEDS_CLASS_SIZE,
778  *              DSYM_EXCEEDS_MAX_CLASS_SIZE, DSYM_NO_MEMORY
779  *              DSYM_INVALID_FIELD_NUM, DSYM_VALUE_OUT_OF_RANGE
780  */
781 dsym_errcode_t
782 dsym_parser(char **fields, dhcp_symbol_t *sym, int *lastField,
783     boolean_t bestEffort)
784 {
785 
786 	int ret = DSYM_SUCCESS;
787 	int tret = DSYM_SUCCESS;
788 	int i;
789 
790 	*lastField = -1;
791 	for (i = DSYM_FIRST_FIELD;
792 	    tret == DSYM_SUCCESS && i < DSYM_NUM_FIELDS; i++) {
793 
794 		tret = dsym_parse_field(i, fields, sym);
795 		if (tret != DSYM_SUCCESS) {
796 			if (ret == DSYM_SUCCESS) {
797 				ret = tret;
798 			}
799 			if (bestEffort) {
800 				*lastField = i;
801 				tret = DSYM_SUCCESS;
802 			}
803 		}
804 	}
805 
806 	if (*lastField == -1) {
807 		*lastField = i - 1;
808 	}
809 
810 	return (ret);
811 }
812 
813 /*
814  * dsym_get_cat_id(): given a category string, return the associated id.
815  *
816  *  input: const char *: the category name
817  *         dsym_category_t *: the return location for the id
818  *         boolean_t: case-sensitive name compare
819  * output: int: DSYM_SUCCESS or DSYM_INVALID_CAT
820  */
821 dsym_errcode_t
822 dsym_get_cat_id(const char *cat, dsym_category_t *id, boolean_t cs)
823 {
824 
825 	dsym_cat_t *entry;
826 	int ret;
827 
828 	ret = dsym_get_cat_by_name(cat, &entry, cs);
829 	if (ret == DSYM_SUCCESS) {
830 		*id = entry->dc_id;
831 	}
832 
833 	return (ret);
834 }
835 
836 /*
837  * dsym_get_code_ranges(): given a category field, returns its valid code
838  *                         ranges.
839  *
840  *  input: const char *: the category name
841  *         ushort *: return location for the minimum code value.
842  *         ushort *: return location for the maximum code value.
843  *         boolean_t: case-sensitive name compare
844  * output: int: DSYM_SUCCESS or DSYM_INVALID_CAT
845  */
846 dsym_errcode_t
847 dsym_get_code_ranges(const char *cat, ushort_t *min, ushort_t *max,
848     boolean_t cs)
849 {
850 
851 	dsym_cat_t *entry;
852 	int ret;
853 
854 	ret = dsym_get_cat_by_name(cat, &entry, cs);
855 	if (ret == DSYM_SUCCESS) {
856 		*min = entry->dc_min;
857 		*max = entry->dc_max;
858 	}
859 
860 	return (ret);
861 }
862 
863 /*
864  * dsym_get_type_id(): given a type string, return the associated type id.
865  *
866  *  input: const char *: the type name
867  *         dsym_cdtype_t *: the return location for the id
868  *         boolean_t: case-sensitive name compare
869  * output: int: DSYM_SUCCESS or DSYM_INVALID_TYPE
870  */
871 dsym_errcode_t
872 dsym_get_type_id(const char *type, dsym_cdtype_t *id, boolean_t cs)
873 {
874 
875 	dsym_type_t *entry;
876 	int ret;
877 
878 	ret = dsym_get_type_by_name(type, &entry, cs);
879 	if (ret == DSYM_SUCCESS) {
880 		*id = entry->dt_id;
881 	}
882 
883 	return (ret);
884 }
885