diff options
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcptab.c')
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcptab.c | 1313 |
1 files changed, 1313 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcptab.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcptab.c new file mode 100644 index 0000000000..db7d2ce975 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcptab.c @@ -0,0 +1,1313 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <assert.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/byteorder.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <syslog.h> +#include <dhcp_inittab.h> +#include <dhcp_symbol.h> +#include "netinet/dhcp.h" +#include "hash.h" +#include "dhcpd.h" +#include <locale.h> + +/* + * 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); +} |