Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 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