/* * Routines and structures which are used from CMU's 2.2 bootp implementation * are labelled as such. Code not labelled is: * * Copyright 1997-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Copyright 1988, 1991 by Carnegie Mellon University * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Carnegie Mellon University not be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF * THIS SOFTWARE. */ /* * in.dhcpd configuration file reading code. * * The routines in this file deal with reading, interpreting, and storing * the information found in the in.dhcpd configuration file (usually * /etc/dhcptab). */ /* * TODO: What's missing: Symbol code is very generic, but doesn't allow * per symbol granularity checking - ie, using goodname() to check the * hostname, for example. Perhaps each symbol should have a verifier * function possibly associated with it (null is ok), which would return * B_TRUE if ok, B_FALSE if not, and print out a nasty message. * * Option overload. If set, then NO BOOTFILE or SNAME values can exist. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netinet/dhcp.h" #include "hash.h" #include "dhcpd.h" #include /* * Local constants */ #define OP_ADDITION 1 /* Operations on tags */ #define OP_DELETION 2 #define OP_BOOLEAN 3 #define MAXENTRYLEN 3072 /* Max size of an entire entry */ #define MAX_ITEMS 16 /* Max number of items in entry */ #define MAX_MACRO_NESTING 20 /* Max number of nested includes */ #define DB_DHCP_MAC 'm' /* Like TBL_DHCP_{MACRO,SYMBOL} */ #define DB_DHCP_SYM 's' /* But not strings! */ static time_t mtable_time; /* load time of hash table */ static uint_t nentries; /* Total number of entries */ static int include_nest; /* macro nesting counter */ static dt_rec_list_t **newtbl; /* reordered Tbl. */ static dt_rec_list_t **oldtbl; /* The original tbl. */ static hash_tbl *mtable; static mutex_t mtable_mtx; /* Reinitialization mutex */ static cond_t mtable_cv; /* Reinitialization cv */ static int mtable_refcnt; /* Current reference count */ static int mtable_closing; /* macros are going away */ #define INCLUDE_SYM "Include" /* symbol for macro include */ /* * Forward declarations. */ static dhcp_symbol_t *sym_list; static size_t sym_num_items; static int check_includes(int, char *); static MACRO *process_entry(dt_rec_list_t *); static int eval_symbol(char **, MACRO *); static char *get_string(char **, char *, uchar_t *); static void adjust(char **); static void eat_whitespace(char **); static int first_macro_row(dt_rec_list_t **); static void get_sym_name(char *, char **); static void print_error_msg(int, uchar_t); static boolean_t define_symbol(char **, char *); static int scan_include(char **, char *); static boolean_t free_macro(MACRO *, boolean_t); static int macro_cmp(MACRO *, MACRO *); static void add_vndlist(ENCODE *, MACRO *, dhcp_symbol_t *); /* * Initialize the hash table. */ int initmtab(void) { /* * Allocate hash table */ mtable = hash_Init(0, NULL, 0, B_FALSE); assert(mtable != NULL); (void) mutex_init(&mtable_mtx, USYNC_THREAD, NULL); (void) cond_init(&mtable_cv, USYNC_THREAD, 0); return (0); } /* * Check presence/access to dhcptab database file. */ int checktab(void) { int err; dsvc_handle_t dh; err = open_dd(&dh, &datastore, DSVC_DHCPTAB, DT_DHCPTAB, DSVC_READ); switch (err) { case DSVC_SUCCESS: (void) close_dd(&dh); break; case DSVC_ACCESS: dhcpmsg(LOG_ERR, "No permission to access dhcptab in %s (%s)\n", datastore.d_resource, datastore.d_location); err = EACCES; break; case DSVC_NOENT: case DSVC_NO_TABLE: dhcpmsg(LOG_INFO, "Dhcptab table does not exist in %s (%s)\n", datastore.d_resource, datastore.d_location); err = ENOENT; break; default: dhcpmsg(LOG_ERR, "Error checking status of dhcptab in %s (%s)\n", datastore.d_resource, datastore.d_location); } return (err); } /* * Read dhcptab database file. */ int readtab(int preserve) { int err = 0, first_mac; MACRO *mc; uint32_t query; dt_rec_t dt; dt_rec_list_t **dhcptab = NULL; dt_rec_list_t *dhcptab_list = NULL; dt_rec_list_t *dtep; int i; int ind; uint_t records; timestruc_t tm; dsvc_handle_t dh; boolean_t tab_open = B_FALSE; (void) mutex_lock(&mtable_mtx); /* * Wait for any current thread(s) to complete using macros. */ mtable_closing = 1; while (mtable_refcnt > 0) { tm.tv_sec = 1; tm.tv_nsec = 0; (void) cond_reltimedwait(&mtable_cv, &mtable_mtx, &tm); } /* Get the *entire* dhcptab. */ while ((err = open_dd(&dh, &datastore, DSVC_DHCPTAB, DT_DHCPTAB, DSVC_READ)) != DSVC_SUCCESS) { if (err == DSVC_BUSY) { continue; } if (err == DSVC_NOENT) { dhcpmsg(LOG_INFO, "Empty dhcptab macro database.\n"); err = 0; /* not a "real" error */ } else dhcpmsg(LOG_ERR, "Error opening macro database: %s\n", dhcpsvc_errmsg(err)); goto leave_readtab; } tab_open = B_TRUE; DSVC_QINIT(query); (void) memset(&dt, 0, sizeof (dt_rec_t)); dhcptab_list = NULL; nentries = 0; err = lookup_dd(dh, B_FALSE, query, -1, (const void *)&dt, (void **)&dhcptab_list, &nentries); if (err != DSVC_SUCCESS) { if (verbose && err == DSVC_NOENT) { dhcpmsg(LOG_INFO, "Error access macro database: %s\n", dhcpsvc_errmsg(err)); } goto leave_readtab; } else err = 0; if (nentries == 0) { dhcpmsg(LOG_INFO, "Empty dhcptab macro database.\n"); err = 0; /* not a "real" error */ goto leave_readtab; } /* * Because libdhcpsvc doesn't guarantee any order, we need to * preprocess the macro list to guarantee that macros which are * included in other macro definitions are already defined prior * to their use. This means that macro processing is a two step process. */ dhcptab = (dt_rec_list_t **)smalloc((nentries + 1) * sizeof (dt_rec_list_t *)); /* Extract symbols first. */ ind = 0; for (i = 0, dtep = dhcptab_list; dtep != NULL; i++, dtep = dtep->dtl_next) { if (dtep->dtl_rec->dt_type == DT_SYMBOL) { dhcptab[ind++] = dtep; } } /* Copy macros */ for (i = 0, dtep = dhcptab_list; dtep != NULL; i++, dtep = dtep->dtl_next) { if (dtep->dtl_rec->dt_type == DT_MACRO) { dhcptab[ind++] = dtep; } } first_mac = first_macro_row(dhcptab); include_nest = 0; if (first_mac >= 0) { oldtbl = dhcptab; newtbl = (dt_rec_list_t **)smalloc((nentries + 1) * sizeof (dt_rec_list_t *)); for (i = 0; i < first_mac; i++) newtbl[i] = oldtbl[i]; /* copy symdefs */ for (i = first_mac; i < nentries; i++) { if ((err = check_includes(first_mac, oldtbl[i]->dtl_rec->dt_key)) != 0) break; } if (err != 0) { free(newtbl); free(dhcptab); goto leave_readtab; } else { free(dhcptab); dhcptab = newtbl; } } resettab(B_FALSE); /* * Now table is reordered. process as usual. */ records = 0; for (i = 0; i < nentries; i++) { if ((mc = process_entry(dhcptab[i])) == (MACRO *)NULL) continue; if (hash_Insert(mtable, mc->nm, strlen(mc->nm), macro_cmp, mc->nm, mc) == NULL) { dhcpmsg(LOG_WARNING, "Duplicate macro definition: %s\n", mc->nm); continue; } records++; } mtable_time = time(NULL); if (verbose) { dhcpmsg(LOG_INFO, "Read %d entries from DHCP macro database on %s", records, ctime(&mtable_time)); } free(dhcptab); leave_readtab: if (dhcptab_list != NULL) free_dd_list(dh, dhcptab_list); if (tab_open) (void) close_dd(&dh); if (preserve && err != 0) { dhcpmsg(LOG_WARNING, "DHCP macro database rescan failed %d, using scan: %s", err, ctime(&mtable_time)); err = 0; } mtable_closing = 0; (void) mutex_unlock(&mtable_mtx); return (err); } /* * Reset the dhcptab hash table, free any dynamic symbol definitions. */ void resettab(boolean_t lck) { int i; dhcp_symbol_t tmp; timestruc_t tm; if (lck == B_TRUE) (void) mutex_lock(&mtable_mtx); /* * Wait for any current thread(s) to complete using macros. */ if (mtable_closing == 0) { mtable_closing = 1; while (mtable_refcnt > 0) { tm.tv_sec = 1; tm.tv_nsec = 0; (void) cond_reltimedwait(&mtable_cv, &mtable_mtx, &tm); } } /* Entirely erase all hash tables. */ hash_Reset(mtable, free_macro); /* * Dump any dynamically defined symbol definitions, and reinitialize. */ if (sym_list != NULL) { /* * Free class resources for each symbol. */ for (i = 0; i < sym_num_items; i++) { dsym_free_classes(&sym_list[i].ds_classes); } free(sym_list); sym_list = NULL; } if (time_to_go) { if (lck == B_TRUE) { (void) mutex_unlock(&mtable_mtx); (void) mutex_destroy(&mtable_mtx); (void) cond_destroy(&mtable_cv); } return; } /* Allocate the inittab and class tables */ sym_list = inittab_load(ITAB_CAT_STANDARD|ITAB_CAT_FIELD| ITAB_CAT_INTERNAL|ITAB_CAT_VENDOR, ITAB_CONS_SERVER, &sym_num_items); /* * Allocate the internal INCLUDE_SYM macro include symbol. * Since this is not part of inittab, it must be added * manually to the list. */ sym_list = (dhcp_symbol_t *)realloc(sym_list, (sym_num_items + 1) * sizeof (dhcp_symbol_t)); if (sym_num_items == 0 || sym_list == NULL) { dhcpmsg(LOG_ERR, "Cannot allocate inittab, exiting\n"); (void) exit(1); } (void) memset(&sym_list[sym_num_items], 0, sizeof (dhcp_symbol_t)); (void) strcpy(sym_list[sym_num_items].ds_name, INCLUDE_SYM); sym_list[sym_num_items].ds_type = DSYM_INCLUDE; sym_list[sym_num_items].ds_max = 32; sym_num_items++; /* Verify the inittab entries */ for (i = 0; i < sym_num_items; i++) { if (inittab_verify(&sym_list[i], &tmp) == ITAB_FAILURE) { print_error_msg(ITAB_SYNTAX_ERROR, i); (void) memcpy(&sym_list[i], &tmp, sizeof (dhcp_symbol_t)); } } mtable_closing = 0; if (lck == B_TRUE) (void) mutex_unlock(&mtable_mtx); } /* * Given an value field pptr, return the first INCLUDE_SYM value found in * include, updating pptr along the way. Returns nonzero if no INCLUDE_SYM * symbol is found (pptr is still updated). */ static int scan_include(char **cpp, char *include) { char t_sym[DSVC_MAX_MACSYM_LEN + 1]; uchar_t ilen; while (*cpp && **cpp != '\0') { eat_whitespace(cpp); get_sym_name(t_sym, cpp); if (strcmp(t_sym, INCLUDE_SYM) == 0) { ilen = DHCP_SCRATCH; if (**cpp == '=') (*cpp)++; (void) get_string(cpp, include, &ilen); include[ilen] = '\0'; return (0); } else adjust(cpp); } return (1); } /* * Return the first macro row in dhcptab. Returns -1 if no macros exist. */ static int first_macro_row(dt_rec_list_t **tblp) { int i; for (i = 0; i < nentries; i++) { if (tolower(tblp[i]->dtl_rec->dt_type) == (int)DT_MACRO) return (i); } return (-1); } /* * RECURSIVE function: Scans for included macros, and reorders Tbl to * ensure macro definitions occur in the correct order. * * Returns 0 for success, nonzero otherwise. */ static int check_includes(int first, char *mname) { char include[DHCP_SCRATCH + 1]; int m, err = 0; dt_rec_list_t *current_rowp = NULL; char *cp; include_nest++; if (include_nest > MAX_MACRO_NESTING) { dhcpmsg(LOG_ERR, "Circular macro definition using: %s\n", mname); err = -1; goto leave_check_include; } for (m = first; m < nentries; m++) { if (newtbl[m] != NULL && strcmp(newtbl[m]->dtl_rec->dt_key, mname) == 0) { err = 0; /* already processed */ goto leave_check_include; } } /* * is it defined someplace? */ for (m = first; m < nentries; m++) { if (strcmp(oldtbl[m]->dtl_rec->dt_key, mname) == 0) { current_rowp = oldtbl[m]; break; } } if (current_rowp == NULL) { dhcpmsg(LOG_ERR, "Undefined macro: %s\n", mname); err = -1; goto leave_check_include; } /* * Scan value field, looking for includes. */ cp = current_rowp->dtl_rec->dt_value; while (cp) { adjust(&cp); if (scan_include(&cp, include) != 0) { /* find a free entry */ for (m = first; m < nentries; m++) { if (newtbl[m] == NULL) break; } if (m >= nentries) { dhcpmsg(LOG_ERR, "Macro expansion (Include=%s) error!\n", mname); err = -1; } else { newtbl[m] = current_rowp; err = 0; } break; } if (*include == '\0') { /* * Null value for macro name. We can safely ignore * this entry. An error message will be generated * later during encode processing. */ continue; } if (strcmp(mname, include) == 0) { dhcpmsg(LOG_ERR, "Circular macro definition using: %s\n", mname); err = -1; break; } /* Recurse. */ if ((err = check_includes(first, include)) != 0) break; } leave_check_include: include_nest--; return (err); } /* * open_macros: open reference to macro table. */ void open_macros(void) { (void) mutex_lock(&mtable_mtx); mtable_refcnt++; (void) mutex_unlock(&mtable_mtx); } /* * close_macros: close reference to macro table. */ void close_macros(void) { (void) mutex_lock(&mtable_mtx); mtable_refcnt--; (void) cond_signal(&mtable_cv); (void) mutex_unlock(&mtable_mtx); } /* * Given a macro name, look it up in the hash table. * Returns ptr to MACRO structure, NULL if error occurs. */ MACRO * get_macro(char *mnamep) { if (mnamep == (char *)NULL) return ((MACRO *)NULL); return ((MACRO *)hash_Lookup(mtable, mnamep, strlen(mnamep), macro_cmp, mnamep, B_FALSE)); } /*ARGSUSED*/ static boolean_t free_macro(MACRO *mp, boolean_t force) { int i; if (mp) { free_encode_list(mp->head); for (i = 0; i < mp->classes; i++) { if (mp->list[i]->head != NULL) free_encode_list(mp->list[i]->head); free(mp->list[i]); } free(mp->list); free(mp); } return (B_TRUE); } static int macro_cmp(MACRO *m1, MACRO *m2) { if (!m1 || !m2) return (B_FALSE); if (strcmp(m1->nm, m2->nm) == 0) return (B_TRUE); else return (B_FALSE); } /* * Parse out all the various tags and parameters in the row entry pointed * to by "src". * * Returns 0 for success, nozero otherwise. */ static MACRO * process_entry(dt_rec_list_t *src) { char *cp; MACRO *mc, *retval = NULL; assert(src != NULL); if (strlen(src->dtl_rec->dt_key) > DSVC_MAX_MACSYM_LEN) { dhcpmsg(LOG_ERR, "Token: %s is too long. Limit: %d characters.\n", src->dtl_rec->dt_key, DSVC_MAX_MACSYM_LEN); return (retval); } switch (tolower(src->dtl_rec->dt_type)) { case DT_SYMBOL: /* New Symbol definition */ cp = src->dtl_rec->dt_value; if (!define_symbol(&cp, src->dtl_rec->dt_key)) dhcpmsg(LOG_ERR, "Bad Runtime symbol definition: %s\n", src->dtl_rec->dt_key); /* Success. Treat new symbol like the predefines. */ break; case DT_MACRO: /* Macro definition */ mc = (MACRO *)smalloc(sizeof (MACRO)); (void) strcpy(mc->nm, src->dtl_rec->dt_key); cp = src->dtl_rec->dt_value; adjust(&cp); while (*cp != '\0') { if (eval_symbol(&cp, mc) != 0) { dhcpmsg(LOG_ERR, "Error processing macro: %s\n", mc->nm); (void) free_macro(mc, B_TRUE); return (NULL); } adjust(&cp); eat_whitespace(&cp); } retval = mc; break; default: dhcpmsg(LOG_ERR, "Unrecognized token: %s.\n", src->dtl_rec->dt_key); break; } return (retval); } /* * This function processes the parameter name pointed to by "symbol" and * updates the appropriate ENCODE structure in data if one already exists, * or allocates a new one for this parameter. */ static int eval_symbol(char **symbol, MACRO *mc) { int index, optype, i, j, err = 0; dhcp_symbol_t *sp; char **clp; ENCODE *tmp; VNDLIST **mpp, **ipp; MACRO *ic; char *cp; uchar_t ilen; uint16_t len; char t_sym[DSVC_MAX_MACSYM_LEN + 1]; char include[DHCP_SCRATCH + 1]; /* * The following buffer must be aligned on a int64_t boundary. */ uint64_t scratch[(UCHAR_MAX + sizeof (int64_t) - 1) / sizeof (int64_t)]; if ((*symbol)[0] == ':') return (0); eat_whitespace(symbol); get_sym_name(t_sym, symbol); for (index = 0; index < sym_num_items; index++) { if (strcmp(t_sym, sym_list[index].ds_name) == 0) break; } if (index >= sym_num_items) { dhcpmsg(LOG_ERR, "Unrecognized symbol name: '%s'\n", t_sym); return (-1); } else { sp = &sym_list[index]; clp = sp->ds_classes.dc_names; } /* * Determine the type of operation to be done on this symbol */ switch (**symbol) { case '=': optype = OP_ADDITION; (*symbol)++; break; case '@': optype = OP_DELETION; (*symbol)++; break; case ':': case '\0': optype = OP_BOOLEAN; break; default: dhcpmsg(LOG_ERR, "Syntax error: symbol: '%s' in macro: %s\n", t_sym, mc->nm); return (-1); } switch (optype) { case OP_ADDITION: switch (sp->ds_type) { case DSYM_BOOL: err = -1; break; case DSYM_INCLUDE: /* * If symbol type is INCLUDE, then walk the encode * list, replacing any previous encodes with those * from the INCLUDed macro. Vendor options are also * merged, if their class and vendor codes match. */ ilen = DHCP_SCRATCH; (void) get_string(symbol, include, &ilen); include[ilen] = '\0'; ic = get_macro(include); if (ic == (MACRO *)NULL) { dhcpmsg(LOG_ERR, "WARNING: No macro: '%1$s' \ defined for 'Include' symbol in macro: %2$s\n", include, mc->nm); adjust(symbol); return (0); } mc->head = combine_encodes(mc->head, ic->head, ENC_DONT_COPY); if (ic->list == NULL && mc->list == NULL) break; /* Vendor options. */ if (mc->list == NULL) { /* * No combining necessary. Just duplicate * ic's vendor options - all classes. */ mc->list = (VNDLIST **)smalloc( sizeof (VNDLIST **) * ic->classes); for (i = 0; i < ic->classes; i++) { mc->list[i] = (VNDLIST *)smalloc( sizeof (VNDLIST)); (void) strcpy(mc->list[i]->class, ic->list[i]->class); mc->list[i]->head = dup_encode_list( ic->list[i]->head); } mc->classes = ic->classes; } else { /* Class and vendor code must match. */ for (i = 0, ipp = ic->list; ipp && i < ic->classes; i++) { for (j = 0, mpp = mc->list; j < mc->classes; j++) { if (strcmp(mpp[j]->class, ipp[i]->class) == 0) { mpp[j]->head = combine_encodes( mpp[j]->head, ipp[i]->head, ENC_DONT_COPY); break; } } } } break; default: /* * Get encode associated with symbol value. */ tmp = (ENCODE *)smalloc(sizeof (ENCODE)); if (sp->ds_type == DSYM_ASCII) { if (sp->ds_max) ilen = sp->ds_max; else ilen = UCHAR_MAX; (void) get_string(symbol, (char *)scratch, &ilen); include[ilen] = '\0'; tmp->data = inittab_encode_e(sp, (char *)scratch, &len, B_TRUE, &err); } else { if ((cp = strchr(*symbol, ':')) != NULL) *cp = '\0'; tmp->data = inittab_encode_e(sp, *symbol, &len, B_TRUE, &err); /* * Advance symbol pointer to next encode. */ if (cp != NULL) { *cp = ':'; *symbol = cp; } else { while (*symbol != '\0') symbol++; } } tmp->len = len; tmp->category = sp->ds_category; tmp->code = sp->ds_code; if (err != 0 || tmp->data == NULL) { if (err == 0) err = -1; free_encode(tmp); } else { /* * Find/replace/add encode. */ if (sp->ds_category != DSYM_VENDOR) { replace_encode(&mc->head, tmp, ENC_DONT_COPY); } else add_vndlist(tmp, mc, sp); } break; } break; case OP_DELETION: if (sp->ds_type == DSYM_INCLUDE) return (-1); if (sp->ds_category != DSYM_VENDOR) { tmp = find_encode(mc->head, sp->ds_category, sp->ds_code); if (tmp != (ENCODE *)NULL) { if (tmp->prev != (ENCODE *)NULL) tmp->prev->next = tmp->next; else mc->head = mc->head->next; free_encode(tmp); } } else { for (i = 0; i < sp->ds_classes.dc_cnt; i++) { for (j = 0; mc->list && j < mc->classes; j++) { if (strcmp(clp[i], mc->list[j]->class) == 0) { tmp = find_encode( mc->list[j]->head, sp->ds_category, sp->ds_code); if (tmp == NULL) continue; if (tmp->prev != NULL) { tmp->prev->next = tmp->next; } else { mc->list[j]->head = mc->list[j]-> head->next; } free_encode(tmp); } } } } err = 0; break; case OP_BOOLEAN: if (sp->ds_type == DSYM_INCLUDE) return (-1); /* * True signified by existence, false by omission. */ if (sp->ds_category != DSYM_VENDOR) { tmp = find_encode(mc->head, sp->ds_category, sp->ds_code); if (tmp == (ENCODE *)NULL) { tmp = make_encode(sp->ds_category, sp->ds_code, 0, NULL, ENC_DONT_COPY); replace_encode(&mc->head, tmp, ENC_DONT_COPY); } } else { for (i = 0; i < sp->ds_classes.dc_cnt; i++) { for (j = 0; mc->list && j < mc->classes; j++) { if (strcmp((const char *)clp[i], mc->list[j]->class) == 0) { tmp = find_encode( mc->list[j]->head, sp->ds_category, sp->ds_code); if (tmp == NULL) { tmp = make_encode( sp->ds_category, sp->ds_code, 0, NULL, ENC_DONT_COPY); replace_encode( &mc->list[j]-> head, tmp, ENC_DONT_COPY); } } } } } err = 0; break; } if (err) print_error_msg(ITAB_SYNTAX_ERROR, index); return (err); } /* * Find/add option to appropriate client classes. */ static void add_vndlist(ENCODE *vp, MACRO *mp, dhcp_symbol_t *sp) { int i, j, class_exists, copy; VNDLIST **tmp; char **cp = sp->ds_classes.dc_names; copy = ENC_DONT_COPY; for (i = 0; i < sp->ds_classes.dc_cnt; i++) { class_exists = 0; for (j = 0; mp->list && j < mp->classes; j++) { if (strcmp(cp[i], mp->list[j]->class) == 0) { class_exists = 1; replace_encode(&mp->list[j]->head, vp, copy); if (copy == ENC_DONT_COPY) copy = ENC_COPY; } } if (!class_exists) { tmp = (VNDLIST **)realloc(mp->list, sizeof (VNDLIST **) * (j + 1)); if (tmp != NULL) mp->list = tmp; else { dhcpmsg(LOG_ERR, "Warning: ran out of \ memory adding vendor class: '%1$s' for symbol: '%2$s'\n", cp[i], sp->ds_name); break; } mp->list[j] = (VNDLIST *)smalloc(sizeof (VNDLIST)); (void) strcpy(mp->list[j]->class, cp[i]); if (copy == ENC_DONT_COPY) { mp->list[j]->head = vp; copy = ENC_COPY; } else mp->list[j]->head = dup_encode(vp); mp->classes++; } } } /* * CMU 2.2 routine. * * Read a string from the buffer indirectly pointed to through "src" and * move it into the buffer pointed to by "dest". A pointer to the maximum * allowable length of the string (including null-terminator) is passed as * "length". The actual length of the string which was read is returned in * the unsigned integer pointed to by "length". This value is the same as * that which would be returned by applying the strlen() function on the * destination string (i.e the terminating null is not counted as a * character). Trailing whitespace is removed from the string. For * convenience, the function returns the new value of "dest". * * The string is read until the maximum number of characters, an unquoted * colon (:), or a null character is read. The return string in "dest" is * null-terminated. */ static char * get_string(char **src, char *dest, uchar_t *length) { int n = 0, len, quoteflag; quoteflag = B_FALSE; len = *length - 1; while ((n < len) && (**src)) { if (quoteflag == B_FALSE && (**src == ':')) break; if (**src == '"') { (*src)++; quoteflag = !quoteflag; continue; } if (**src == '\\') { (*src)++; if (!**src) break; } *dest++ = *(*src)++; n++; } /* * Remove that troublesome trailing whitespace. . . */ while ((n > 0) && isspace(*(char *)(dest - 1))) { dest--; n--; } *dest = '\0'; *length = n; return (dest); } /* * This function adjusts the caller's pointer to point just past the * first-encountered colon. If it runs into a null character, it leaves * the pointer pointing to it. */ static void adjust(char **s) { char *t; t = *s; while (*t && (*t != ':')) t++; if (*t) t++; *s = t; } /* * This function adjusts the caller's pointer to point to the first * non-whitespace character. If it runs into a null character, it leaves * the pointer pointing to it. */ static void eat_whitespace(char **s) { char *t; t = *s; while (*t && isspace(*t)) t++; *s = t; } /* * Copy symbol name into buffer. Sym ends up pointing to the end of the * token. */ static void get_sym_name(char *buf, char **sym) { int i; for (i = 0; i < DSVC_MAX_MACSYM_LEN; i++) { if (**sym == ':' || **sym == '=' || **sym == '@' || **sym == '\0') break; *buf++ = *(*sym)++; } *buf = '\0'; } static void print_error_msg(int error, uchar_t index) { switch (error) { case ITAB_BAD_IPADDR: dhcpmsg(LOG_ERR, "Error processing Internet address \ value(s) for symbol: '%s'\n", sym_list[index].ds_name); break; case ITAB_BAD_STRING: dhcpmsg(LOG_ERR, "Error processing ASCII string value for \ symbol: '%s'\n", sym_list[index].ds_name); break; case ITAB_BAD_OCTET: dhcpmsg(LOG_ERR, "Error processing OCTET string value for \ symbol: '%s'\n", sym_list[index].ds_name); break; case ITAB_BAD_NUMBER: dhcpmsg(LOG_ERR, "Error processing NUMBER value for \ symbol: '%s'\n", sym_list[index].ds_name); break; case ITAB_BAD_BOOLEAN: dhcpmsg(LOG_ERR, "Error processing BOOLEAN value for symbol: '%s'\n", sym_list[index].ds_name); break; case ITAB_SYNTAX_ERROR: /* FALLTHRU */ default: dhcpmsg(LOG_ERR, "Syntax error found processing value for symbol: '%s'\n", sym_list[index].ds_name); break; } } /* * Define new symbols for things like site-wide and vendor options. */ static boolean_t define_symbol(char **ptr, char *name) { dhcp_symbol_t sym; char **fields; int last = 0; dsym_errcode_t ret = DSYM_SUCCESS; ushort_t min; ushort_t max; int i; /* * Only permit new symbol definitions, not old ones. I suppose we * could allow the administrator to redefine symbols, but what if * they redefine subnetmask to be a new brownie recipe? Let's stay * out of that rat hole for now. */ for (i = 0; i < sym_num_items; i++) { if (strcmp(name, sym_list[i].ds_name) == 0) { dhcpmsg(LOG_ERR, "Symbol: %s already defined. New " "definition ignored.\n", name); adjust(ptr); return (0); } } ret = dsym_init_parser(name, *ptr, &fields, &sym); if (ret != DSYM_SUCCESS) { switch (ret) { case DSYM_NULL_FIELD: dhcpmsg(LOG_ERR, "Item is missing in symbol definition: '%s'\n", name); break; case DSYM_TOO_MANY_FIELDS: dhcpmsg(LOG_ERR, "Too many items exist in symbol definition: %s\n", name); break; case DSYM_NO_MEMORY: dhcpmsg(LOG_ERR, "Ran out of memory processing symbol: '%s'\n", name); break; default: dhcpmsg(LOG_ERR, "Internal error processing symbol: '%s'\n", name); break; } return (B_FALSE); } ret = dsym_parser(fields, &sym, &last, B_FALSE); if (ret != DSYM_SUCCESS) { switch (ret) { case DSYM_SYNTAX_ERROR: dhcpmsg(LOG_ERR, "Syntax error parsing symbol definition: '%s'\n", name); break; case DSYM_CODE_OUT_OF_RANGE: (void) dsym_get_code_ranges(fields[DSYM_CAT_FIELD], &min, &max, B_TRUE); dhcpmsg(LOG_ERR, "Out of range (%d-%d) option code: " "%d in symbol definition: '%s'\n", min, max, sym.ds_code, name); break; case DSYM_VALUE_OUT_OF_RANGE: dhcpmsg(LOG_ERR, "Bad item, %s, in symbol definition: '%s'\n", fields[last], name); break; case DSYM_INVALID_CAT: dhcpmsg(LOG_ERR, "Missing/Incorrect Site/Vendor flag " "in symbol definition: '%s'\n", name); break; case DSYM_INVALID_TYPE: dhcpmsg(LOG_ERR, "Unrecognized value descriptor: %s " "in symbol definition: '%s'\n", fields[DSYM_TYPE_FIELD], name); break; case DSYM_EXCEEDS_CLASS_SIZE: dhcpmsg(LOG_ERR, "Client class is too " "long for vendor symbol: '%s'. Must be " "less than: %d\n", name, DSYM_CLASS_SIZE); break; case DSYM_EXCEEDS_MAX_CLASS_SIZE: dhcpmsg(LOG_ERR, "Client class is too long for " "vendor symbol: '%s'. Must be less than: %d\n", name, DSYM_MAX_CLASS_SIZE); break; case DSYM_NO_MEMORY: dhcpmsg(LOG_ERR, "Ran out of memory processing symbol: '%s'\n", name); break; default: dhcpmsg(LOG_ERR, "Internal error processing symbol: '%s'\n", name); break; } dsym_close_parser(fields, &sym); return (B_FALSE); } /* * Don't free the symbol structure resources, we need those. * Just free the fields memory. We will free the symbol structure * resources later. */ dsym_free_fields(fields); /* * Now add it to the existing definitions, reallocating * the dynamic symbol list. */ sym_list = (dhcp_symbol_t *)realloc(sym_list, (sym_num_items + 1) * sizeof (dhcp_symbol_t)); if (sym_list != (dhcp_symbol_t *)NULL) { sym_num_items++; (void) memcpy(&sym_list[sym_num_items - 1], &sym, sizeof (dhcp_symbol_t)); } else { dhcpmsg(LOG_ERR, "Cannot extend symbol table, using predefined table.\n"); resettab(B_FALSE); } return (B_TRUE); }