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