mirror of
https://gitlab.isc.org/isc-projects/dhcp
synced 2025-08-31 06:15:55 +00:00
Support defining new option code names and formats.
This commit is contained in:
272
common/parse.c
272
common/parse.c
@@ -22,7 +22,7 @@
|
||||
|
||||
#ifndef lint
|
||||
static char copyright[] =
|
||||
"$Id: parse.c,v 1.17 1999/03/16 06:37:49 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n";
|
||||
"$Id: parse.c,v 1.18 1999/03/25 21:59:36 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "dhcpd.h"
|
||||
@@ -700,12 +700,13 @@ TIME parse_date (cfile)
|
||||
IDENTIFIER . IDENTIFIER
|
||||
*/
|
||||
|
||||
struct option *parse_option_name (cfile)
|
||||
struct option *parse_option_name (cfile, allocate)
|
||||
FILE *cfile;
|
||||
int allocate;
|
||||
{
|
||||
char *val;
|
||||
enum dhcp_token token;
|
||||
char *vendor;
|
||||
char *uname;
|
||||
struct universe *universe;
|
||||
struct option *option;
|
||||
|
||||
@@ -716,10 +717,10 @@ struct option *parse_option_name (cfile)
|
||||
skip_to_semi (cfile);
|
||||
return (struct option *)0;
|
||||
}
|
||||
vendor = malloc (strlen (val) + 1);
|
||||
if (!vendor)
|
||||
log_fatal ("no memory for vendor information.");
|
||||
strcpy (vendor, val);
|
||||
uname = malloc (strlen (val) + 1);
|
||||
if (!uname)
|
||||
log_fatal ("no memory for uname information.");
|
||||
strcpy (uname, val);
|
||||
token = peek_token (&val, cfile);
|
||||
if (token == DOT) {
|
||||
/* Go ahead and take the DOT token... */
|
||||
@@ -735,21 +736,21 @@ struct option *parse_option_name (cfile)
|
||||
}
|
||||
|
||||
/* Look up the option name hash table for the specified
|
||||
vendor. */
|
||||
uname. */
|
||||
universe = ((struct universe *)
|
||||
hash_lookup (&universe_hash,
|
||||
(unsigned char *)vendor, 0));
|
||||
(unsigned char *)uname, 0));
|
||||
/* If it's not there, we can't parse the rest of the
|
||||
declaration. */
|
||||
if (!universe) {
|
||||
parse_warn ("no vendor named %s.", vendor);
|
||||
parse_warn ("no option space named %s.", uname);
|
||||
skip_to_semi (cfile);
|
||||
return (struct option *)0;
|
||||
}
|
||||
} else {
|
||||
/* Use the default hash table, which contains all the
|
||||
standard dhcp option names. */
|
||||
val = vendor;
|
||||
val = uname;
|
||||
universe = &dhcp_universe;
|
||||
}
|
||||
|
||||
@@ -759,20 +760,249 @@ struct option *parse_option_name (cfile)
|
||||
|
||||
/* If we didn't get an option structure, it's an undefined option. */
|
||||
if (!option) {
|
||||
if (val == vendor)
|
||||
/* If we've been told to allocate, that means that this
|
||||
(might) be an option code definition, so we'll create
|
||||
an option structure just in case. */
|
||||
if (allocate) {
|
||||
option = new_option ("parse_option_name");
|
||||
if (val == uname)
|
||||
option -> name = val;
|
||||
else {
|
||||
free (uname);
|
||||
option -> name = dmalloc (strlen (val) + 1,
|
||||
"parse_option_name");
|
||||
if (!option -> name)
|
||||
log_fatal ("no memory for option %s.%s",
|
||||
universe -> name, val);
|
||||
strcpy (option -> name, val);
|
||||
}
|
||||
option -> universe = universe;
|
||||
option -> code = -1;
|
||||
return option;
|
||||
}
|
||||
if (val == uname)
|
||||
parse_warn ("no option named %s", val);
|
||||
else
|
||||
parse_warn ("no option named %s for vendor %s",
|
||||
val, vendor);
|
||||
parse_warn ("no option named %s in space %s",
|
||||
val, uname);
|
||||
skip_to_semi (cfile);
|
||||
return (struct option *)0;
|
||||
}
|
||||
|
||||
/* Free the initial identifier token. */
|
||||
free (vendor);
|
||||
free (uname);
|
||||
return option;
|
||||
}
|
||||
|
||||
/* This is faked up to look good right now. Ideally, this should do a
|
||||
recursive parse and allow arbitrary data structure definitions, but for
|
||||
now it just allows you to specify a single type, an array of single types,
|
||||
a sequence of types, or an array of sequences of types.
|
||||
|
||||
ocd :== NUMBER EQUALS ocsd SEMI
|
||||
|
||||
ocsd :== ocsd_type |
|
||||
ocsd_type_sequence |
|
||||
ARRAY OF ocsd_type |
|
||||
ARRAY OF ocsd_type_sequence
|
||||
|
||||
ocsd_type :== BOOLEAN |
|
||||
INTEGER NUMBER |
|
||||
SIGNED INTEGER NUMBER |
|
||||
UNSIGNED INTEGER NUMBER |
|
||||
IP-ADDRESS |
|
||||
TEXT |
|
||||
STRING
|
||||
|
||||
ocsd_type_sequence :== LBRACE ocsd_types RBRACE
|
||||
|
||||
ocsd_type :== ocsd_type |
|
||||
ocsd_types ocsd_type */
|
||||
|
||||
int parse_option_code_definition (cfile, option)
|
||||
FILE *cfile;
|
||||
struct option *option;
|
||||
{
|
||||
char *val;
|
||||
enum dhcp_token token;
|
||||
int arrayp = 0;
|
||||
int recordp = 0;
|
||||
int no_more_in_record = 0;
|
||||
char tokbuf [128];
|
||||
int tokix = 0;
|
||||
char type;
|
||||
int code;
|
||||
int is_signed;
|
||||
|
||||
/* Parse the option code. */
|
||||
token = next_token (&val, cfile);
|
||||
if (token != NUMBER) {
|
||||
parse_warn ("expecting option code number.");
|
||||
skip_to_semi (cfile);
|
||||
return 0;
|
||||
}
|
||||
option -> code = atoi (val);
|
||||
|
||||
token = next_token (&val, cfile);
|
||||
if (token != EQUAL) {
|
||||
parse_warn ("expecting \"=\"");
|
||||
skip_to_semi (cfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See if this is an array. */
|
||||
token = next_token (&val, cfile);
|
||||
if (token == ARRAY) {
|
||||
token = next_token (&val, cfile);
|
||||
if (token != OF) {
|
||||
parse_warn ("expecting \"of\".");
|
||||
skip_to_semi (cfile);
|
||||
return 0;
|
||||
}
|
||||
arrayp = 1;
|
||||
token = next_token (&val, cfile);
|
||||
}
|
||||
|
||||
if (token == LBRACE) {
|
||||
recordp = 1;
|
||||
token = next_token (&val, cfile);
|
||||
}
|
||||
|
||||
/* At this point we're expecting a data type. */
|
||||
next_type:
|
||||
switch (token) {
|
||||
case BOOLEAN:
|
||||
type = 'f';
|
||||
break;
|
||||
case INTEGER:
|
||||
is_signed = 1;
|
||||
parse_integer:
|
||||
token = next_token (&val, cfile);
|
||||
if (token != NUMBER) {
|
||||
parse_warn ("expecting number.");
|
||||
skip_to_rbrace (cfile, recordp);
|
||||
if (recordp)
|
||||
skip_to_semi (cfile);
|
||||
return 0;
|
||||
}
|
||||
switch (atoi (val)) {
|
||||
case 8:
|
||||
type = is_signed ? 'b' : 'B';
|
||||
break;
|
||||
case 16:
|
||||
type = is_signed ? 's' : 'S';
|
||||
break;
|
||||
case 32:
|
||||
type = is_signed ? 'l' : 'L';
|
||||
break;
|
||||
default:
|
||||
parse_warn ("%s bit precision is not supported.", val);
|
||||
skip_to_rbrace (cfile, recordp);
|
||||
if (recordp)
|
||||
skip_to_semi (cfile);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case SIGNED:
|
||||
is_signed = 1;
|
||||
parse_signed:
|
||||
token = next_token (&val, cfile);
|
||||
if (token != INTEGER) {
|
||||
parse_warn ("expecting \"integer\" keyword.");
|
||||
skip_to_rbrace (cfile, recordp);
|
||||
if (recordp)
|
||||
skip_to_semi (cfile);
|
||||
return 0;
|
||||
}
|
||||
goto parse_integer;
|
||||
case UNSIGNED:
|
||||
is_signed = 0;
|
||||
goto parse_signed;
|
||||
|
||||
case IP_ADDRESS:
|
||||
type = 'I';
|
||||
break;
|
||||
case TEXT:
|
||||
type = 't';
|
||||
no_arrays:
|
||||
if (arrayp) {
|
||||
parse_warn ("arrays of text strings not %s",
|
||||
"yet supported.");
|
||||
skip_to_rbrace (cfile, recordp);
|
||||
if (recordp)
|
||||
skip_to_semi (cfile);
|
||||
return 0;
|
||||
}
|
||||
no_more_in_record = 1;
|
||||
break;
|
||||
case STRING:
|
||||
type = 'X';
|
||||
goto no_arrays;
|
||||
|
||||
default:
|
||||
parse_warn ("unknown data type %s", val);
|
||||
skip_to_rbrace (cfile, recordp);
|
||||
if (recordp)
|
||||
skip_to_semi (cfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tokix == sizeof tokbuf) {
|
||||
parse_warn ("too many types in record.");
|
||||
skip_to_rbrace (cfile, recordp);
|
||||
if (recordp)
|
||||
skip_to_semi (cfile);
|
||||
return 0;
|
||||
}
|
||||
tokbuf [tokix++] = type;
|
||||
|
||||
if (recordp) {
|
||||
token = next_token (&val, cfile);
|
||||
if (token == COMMA) {
|
||||
if (no_more_in_record) {
|
||||
parse_warn ("%s must be at end of record.",
|
||||
type == 't' ? "text" : "string");
|
||||
skip_to_rbrace (cfile, 1);
|
||||
if (recordp)
|
||||
skip_to_semi (cfile);
|
||||
return 0;
|
||||
}
|
||||
token = next_token (&val, cfile);
|
||||
goto next_type;
|
||||
}
|
||||
if (token != RBRACE) {
|
||||
parse_warn ("expecting right brace.");
|
||||
skip_to_rbrace (cfile, 1);
|
||||
if (recordp)
|
||||
skip_to_semi (cfile);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!parse_semi (cfile)) {
|
||||
parse_warn ("semicolon expected.");
|
||||
skip_to_semi (cfile);
|
||||
if (recordp)
|
||||
skip_to_semi (cfile);
|
||||
return 0;
|
||||
}
|
||||
option -> format = dmalloc (tokix + arrayp + 1,
|
||||
"parse_option_code_definition");
|
||||
if (!option -> format)
|
||||
log_fatal ("no memory for option format.");
|
||||
memcpy (option -> format, tokbuf, tokix);
|
||||
if (arrayp)
|
||||
option -> format [tokix++] = 'A';
|
||||
option -> format [tokix] = 0;
|
||||
if (option -> universe -> options [option -> code]) {
|
||||
/* XXX Free the option, but we can't do that now because they
|
||||
XXX may start out static. */
|
||||
}
|
||||
option -> universe -> options [option -> code] = option;
|
||||
add_hash (option -> universe -> hash,
|
||||
(unsigned char *)option -> name, 0, (unsigned char *)option);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* colon-seperated-hex-list :== NUMBER |
|
||||
* NUMBER COLON colon-seperated-hex-list
|
||||
@@ -929,7 +1159,7 @@ struct executable_statement *parse_executable_statement (cfile, lose)
|
||||
case SUPERSEDE:
|
||||
case OPTION:
|
||||
token = next_token (&val, cfile);
|
||||
option = parse_option_name (cfile);
|
||||
option = parse_option_name (cfile, 0);
|
||||
if (!option) {
|
||||
*lose = 1;
|
||||
return (struct executable_statement *)0;
|
||||
@@ -939,7 +1169,7 @@ struct executable_statement *parse_executable_statement (cfile, lose)
|
||||
|
||||
case DEFAULT:
|
||||
token = next_token (&val, cfile);
|
||||
option = parse_option_name (cfile);
|
||||
option = parse_option_name (cfile, 0);
|
||||
if (!option) {
|
||||
*lose = 1;
|
||||
return (struct executable_statement *)0;
|
||||
@@ -949,7 +1179,7 @@ struct executable_statement *parse_executable_statement (cfile, lose)
|
||||
|
||||
case PREPEND:
|
||||
token = next_token (&val, cfile);
|
||||
option = parse_option_name (cfile);
|
||||
option = parse_option_name (cfile, 0);
|
||||
if (!option) {
|
||||
*lose = 1;
|
||||
return (struct executable_statement *)0;
|
||||
@@ -959,7 +1189,7 @@ struct executable_statement *parse_executable_statement (cfile, lose)
|
||||
|
||||
case APPEND:
|
||||
token = next_token (&val, cfile);
|
||||
option = parse_option_name (cfile);
|
||||
option = parse_option_name (cfile, 0);
|
||||
if (!option) {
|
||||
*lose = 1;
|
||||
return (struct executable_statement *)0;
|
||||
@@ -1225,7 +1455,7 @@ int parse_non_binary (expr, cfile, lose, context)
|
||||
if (!expression_allocate (expr, "parse_expression: EXISTS"))
|
||||
log_fatal ("can't allocate expression");
|
||||
(*expr) -> op = expr_exists;
|
||||
(*expr) -> data.option = parse_option_name (cfile);
|
||||
(*expr) -> data.option = parse_option_name (cfile, 0);
|
||||
if (!(*expr) -> data.option) {
|
||||
*lose = 1;
|
||||
expression_dereference (expr,
|
||||
@@ -1336,7 +1566,7 @@ int parse_non_binary (expr, cfile, lose, context)
|
||||
if (!expression_allocate (expr, "parse_expression: OPTION"))
|
||||
log_fatal ("can't allocate expression");
|
||||
(*expr) -> op = expr_option;
|
||||
(*expr) -> data.option = parse_option_name (cfile);
|
||||
(*expr) -> data.option = parse_option_name (cfile, 0);
|
||||
if (!(*expr) -> data.option) {
|
||||
*lose = 1;
|
||||
expression_dereference (expr,
|
||||
|
Reference in New Issue
Block a user