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