dhcptab.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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
*/
/*
* 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/byteorder.h>
#include <syslog.h>
#include <dhcp_inittab.h>
#include <dhcp_symbol.h>
#include "hash.h"
#include "dhcpd.h"
#include <locale.h>
/*
* Local constants
*/
#define OP_DELETION 2
#define OP_BOOLEAN 3
static int include_nest; /* macro nesting counter */
static int mtable_refcnt; /* Current reference count */
static int mtable_closing; /* macros are going away */
/*
* Forward declarations.
*/
static dhcp_symbol_t *sym_list;
static size_t sym_num_items;
static int check_includes(int, char *);
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 *);
/*
* Initialize the hash table.
*/
int
initmtab(void)
{
/*
* Allocate hash table
*/
return (0);
}
/*
*/
int
checktab(void)
{
int err;
switch (err) {
case DSVC_SUCCESS:
break;
case DSVC_ACCESS:
"No permission to access dhcptab in %s (%s)\n",
break;
case DSVC_NOENT:
case DSVC_NO_TABLE:
"Dhcptab table does not exist in %s (%s)\n",
break;
default:
"Error checking status of dhcptab in %s (%s)\n",
}
return (err);
}
/*
* Read dhcptab database file.
*/
int
{
int i;
int ind;
(void) mutex_lock(&mtable_mtx);
/*
* Wait for any current thread(s) to complete using macros.
*/
mtable_closing = 1;
while (mtable_refcnt > 0) {
}
/* Get the *entire* dhcptab. */
DSVC_READ)) != DSVC_SUCCESS) {
continue;
}
if (err == DSVC_NOENT) {
err = 0; /* not a "real" error */
} else
goto leave_readtab;
}
dhcptab_list = NULL;
nentries = 0;
(void **)&dhcptab_list, &nentries);
if (err != DSVC_SUCCESS) {
}
goto leave_readtab;
} else
err = 0;
if (nentries == 0) {
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.
*/
sizeof (dt_rec_list_t *));
/* Extract symbols first. */
ind = 0;
}
}
/* Copy macros */
}
}
include_nest = 0;
if (first_mac >= 0) {
sizeof (dt_rec_list_t *));
for (i = 0; i < first_mac; i++)
break;
}
if (err != 0) {
goto leave_readtab;
} else {
}
}
/*
* Now table is reordered. process as usual.
*/
records = 0;
for (i = 0; i < nentries; i++) {
continue;
continue;
}
records++;
}
if (verbose) {
"Read %d entries from DHCP macro database on %s",
}
if (dhcptab_list != NULL)
if (tab_open)
"DHCP macro database rescan failed %d, using scan: %s",
err = 0;
}
mtable_closing = 0;
(void) mutex_unlock(&mtable_mtx);
return (err);
}
/*
* Reset the dhcptab hash table, free any dynamic symbol definitions.
*/
void
{
int i;
(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) {
}
}
/* Entirely erase all hash tables. */
/*
* Dump any dynamically defined symbol definitions, and reinitialize.
*/
/*
* Free class resources for each symbol.
*/
for (i = 0; i < sym_num_items; i++) {
}
}
if (time_to_go) {
(void) mutex_unlock(&mtable_mtx);
(void) mutex_destroy(&mtable_mtx);
(void) cond_destroy(&mtable_cv);
}
return;
}
/* Allocate the inittab and class tables */
/*
* Allocate the internal INCLUDE_SYM macro include symbol.
* Since this is not part of inittab, it must be added
* manually to the list.
*/
(void) exit(1);
}
/* Verify the inittab entries */
for (i = 0; i < sym_num_items; i++) {
sizeof (dhcp_symbol_t));
}
}
mtable_closing = 0;
(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)
{
ilen = DHCP_SCRATCH;
if (**cpp == '=')
(*cpp)++;
include[ilen] = '\0';
return (0);
} else
}
return (1);
}
/*
* Return the first macro row in dhcptab. Returns -1 if no macros exist.
*/
static int
{
int i;
for (i = 0; i < nentries; i++) {
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
{
char include[DHCP_SCRATCH + 1];
int m, err = 0;
char *cp;
include_nest++;
if (include_nest > MAX_MACRO_NESTING) {
"Circular macro definition using: %s\n", mname);
err = -1;
goto leave_check_include;
}
err = 0; /* already processed */
goto leave_check_include;
}
}
/*
* is it defined someplace?
*/
current_rowp = oldtbl[m];
break;
}
}
if (current_rowp == NULL) {
err = -1;
goto leave_check_include;
}
/*
* Scan value field, looking for includes.
*/
while (cp) {
if (scan_include(&cp, include) != 0) {
/* find a free entry */
break;
}
if (m >= nentries) {
"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;
}
"Circular macro definition using: %s\n", mname);
err = -1;
break;
}
/* Recurse. */
break;
}
include_nest--;
return (err);
}
/*
* open_macros: open reference to macro table.
*/
void
open_macros(void) {
(void) mutex_lock(&mtable_mtx);
(void) mutex_unlock(&mtable_mtx);
}
/*
* close_macros: close reference to macro table.
*/
void
close_macros(void) {
(void) mutex_lock(&mtable_mtx);
(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 *
{
}
/*ARGSUSED*/
static boolean_t
{
int i;
if (mp) {
}
}
return (B_TRUE);
}
static int
{
return (B_FALSE);
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 *
{
char *cp;
"Token: %s is too long. Limit: %d characters.\n",
return (retval);
}
case DT_SYMBOL:
/* New Symbol definition */
"Bad Runtime symbol definition: %s\n",
/* Success. Treat new symbol like the predefines. */
break;
case DT_MACRO:
/* Macro definition */
while (*cp != '\0') {
return (NULL);
}
eat_whitespace(&cp);
}
break;
default:
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
{
char **clp;
char *cp;
char include[DHCP_SCRATCH + 1];
/*
* The following buffer must be aligned on a int64_t boundary.
*/
sizeof (int64_t)];
if ((*symbol)[0] == ':')
return (0);
break;
}
if (index >= sym_num_items) {
return (-1);
} else {
}
/*
* Determine the type of operation to be done on this symbol
*/
switch (**symbol) {
case '=':
(*symbol)++;
break;
case '@':
(*symbol)++;
break;
case ':':
case '\0':
optype = OP_BOOLEAN;
break;
default:
return (-1);
}
switch (optype) {
case OP_ADDITION:
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;
include[ilen] = '\0';
defined for 'Include' symbol in macro: %2$s\n",
return (0);
}
break;
/* Vendor options. */
/*
* No combining necessary. Just duplicate
* ic's vendor options - all classes.
*/
sizeof (VNDLIST));
}
} else {
/* Class and vendor code must match. */
break;
}
}
}
}
break;
default:
/*
* Get encode associated with symbol value.
*/
else
&ilen);
include[ilen] = '\0';
} else {
*cp = '\0';
/*
* Advance symbol pointer to next encode.
*/
*cp = ':';
} else {
while (*symbol != '\0')
symbol++;
}
}
if (err == 0)
err = -1;
} else {
/*
*/
} else
}
break;
}
break;
case OP_DELETION:
return (-1);
else
}
} else {
j++) {
tmp = find_encode(
continue;
} else {
}
}
}
}
}
err = 0;
break;
case OP_BOOLEAN:
return (-1);
/*
* True signified by existence, false by omission.
*/
0, NULL, ENC_DONT_COPY);
}
} else {
j++) {
tmp = find_encode(
tmp = make_encode(
NULL,
}
}
}
}
}
err = 0;
break;
}
if (err)
return (err);
}
/*
*/
static void
{
int i, j, class_exists, copy;
class_exists = 0;
class_exists = 1;
if (copy == ENC_DONT_COPY)
}
}
if (!class_exists) {
sizeof (VNDLIST **) * (j + 1));
else {
memory adding vendor class: '%1$s' for symbol: '%2$s'\n",
break;
}
if (copy == ENC_DONT_COPY) {
} else
}
}
}
/*
* 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 *
{
break;
if (**src == '"') {
(*src)++;
continue;
}
if (**src == '\\') {
(*src)++;
if (!**src)
break;
}
n++;
}
/*
* Remove that troublesome trailing whitespace. . .
*/
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
{
int i;
for (i = 0; i < DSVC_MAX_MACSYM_LEN; i++) {
**sym == '\0')
break;
}
*buf = '\0';
}
static void
{
switch (error) {
case ITAB_BAD_IPADDR:
break;
case ITAB_BAD_STRING:
break;
case ITAB_BAD_OCTET:
break;
case ITAB_BAD_NUMBER:
break;
case ITAB_BAD_BOOLEAN:
"Error processing BOOLEAN value for symbol: '%s'\n",
break;
case ITAB_SYNTAX_ERROR:
/* FALLTHRU */
default:
"Syntax error found processing value for symbol: '%s'\n",
break;
}
}
/*
* Define new symbols for things like site-wide and vendor options.
*/
static boolean_t
{
char **fields;
int last = 0;
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++) {
"definition ignored.\n", name);
return (0);
}
}
if (ret != DSYM_SUCCESS) {
switch (ret) {
case DSYM_NULL_FIELD:
"Item is missing in symbol definition: '%s'\n",
name);
break;
case DSYM_TOO_MANY_FIELDS:
"Too many items exist in symbol definition: %s\n",
name);
break;
case DSYM_NO_MEMORY:
"Ran out of memory processing symbol: '%s'\n",
name);
break;
default:
"Internal error processing symbol: '%s'\n",
name);
break;
}
return (B_FALSE);
}
if (ret != DSYM_SUCCESS) {
switch (ret) {
case DSYM_SYNTAX_ERROR:
"Syntax error parsing symbol definition: '%s'\n",
name);
break;
case DSYM_CODE_OUT_OF_RANGE:
"%d in symbol definition: '%s'\n",
break;
case DSYM_VALUE_OUT_OF_RANGE:
"Bad item, %s, in symbol definition: '%s'\n",
break;
case DSYM_INVALID_CAT:
"in symbol definition: '%s'\n", name);
break;
case DSYM_INVALID_TYPE:
"in symbol definition: '%s'\n",
break;
case DSYM_EXCEEDS_CLASS_SIZE:
"long for vendor symbol: '%s'. Must be "
break;
"vendor symbol: '%s'. Must be less than: %d\n",
break;
case DSYM_NO_MEMORY:
"Ran out of memory processing symbol: '%s'\n",
name);
break;
default:
"Internal error processing symbol: '%s'\n",
name);
break;
}
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.
*/
/*
* Now add it to the existing definitions, reallocating
* the dynamic symbol list.
*/
sizeof (dhcp_symbol_t));
} else {
"Cannot extend symbol table, using predefined table.\n");
}
return (B_TRUE);
}