summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcptab.c
diff options
context:
space:
mode:
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.c1313
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);
+}