summaryrefslogtreecommitdiffstats
path: root/contrib/isc-dhcp/common/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/isc-dhcp/common/parse.c')
-rw-r--r--contrib/isc-dhcp/common/parse.c4558
1 files changed, 4365 insertions, 193 deletions
diff --git a/contrib/isc-dhcp/common/parse.c b/contrib/isc-dhcp/common/parse.c
index fe02689..a2d13f8 100644
--- a/contrib/isc-dhcp/common/parse.c
+++ b/contrib/isc-dhcp/common/parse.c
@@ -3,7 +3,7 @@
Common parser code for dhcpd and dhclient. */
/*
- * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * Copyright (c) 1995-2001 Internet Software Consortium.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,19 +34,58 @@
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
*/
#ifndef lint
static char copyright[] =
-"$Id: parse.c,v 1.2.2.4 1999/03/29 22:18:53 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
+"$Id: parse.c,v 1.104.2.8 2002/01/10 19:37:51 mellon Exp $ Copyright (c) 1995-2001 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
-#include "dhctoken.h"
+
+/* Enumerations can be specified in option formats, and are used for
+ parsing, so we define the routines that manage them here. */
+
+struct enumeration *enumerations;
+
+void add_enumeration (struct enumeration *enumeration)
+{
+ enumeration -> next = enumerations;
+ enumerations = enumeration;
+}
+
+struct enumeration *find_enumeration (const char *name, int length)
+{
+ struct enumeration *e;
+
+ for (e = enumerations; e; e = e -> next)
+ if (strlen (e -> name) == length &&
+ !memcmp (e -> name, name, (unsigned)length))
+ return e;
+ return (struct enumeration *)0;
+}
+
+struct enumeration_value *find_enumeration_value (const char *name,
+ int length,
+ const char *value)
+{
+ struct enumeration *e;
+ int i;
+
+ e = find_enumeration (name, length);
+ if (e) {
+ for (i = 0; e -> values [i].name; i++) {
+ if (!strcmp (value, e -> values [i].name))
+ return &e -> values [i];
+ }
+ }
+ return (struct enumeration_value *)0;
+}
/* Skip to the semicolon ending the current statement. If we encounter
braces, the matching closing brace terminates the statement. If we
@@ -63,17 +102,26 @@ static char copyright[] =
...et cetera. */
void skip_to_semi (cfile)
- FILE *cfile;
+ struct parse *cfile;
{
- int token;
- char *val;
- int brace_count = 0;
+ skip_to_rbrace (cfile, 0);
+}
+void skip_to_rbrace (cfile, brace_count)
+ struct parse *cfile;
+ int brace_count;
+{
+ enum dhcp_token token;
+ const char *val;
+
+#if defined (DEBUG_TOKEN)
+ log_error ("skip_to_rbrace: %d\n", brace_count);
+#endif
do {
- token = peek_token (&val, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
if (token == RBRACE) {
+ token = next_token (&val, (unsigned *)0, cfile);
if (brace_count) {
- token = next_token (&val, cfile);
if (!--brace_count)
return;
} else
@@ -81,28 +129,28 @@ void skip_to_semi (cfile)
} else if (token == LBRACE) {
brace_count++;
} else if (token == SEMI && !brace_count) {
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
return;
} else if (token == EOL) {
/* EOL only happens when parsing /etc/resolv.conf,
and we treat it like a semicolon because the
resolv.conf file is line-oriented. */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
return;
}
- token = next_token (&val, cfile);
- } while (token != EOF);
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (token != END_OF_FILE);
}
int parse_semi (cfile)
- FILE *cfile;
+ struct parse *cfile;
{
- int token;
- char *val;
+ enum dhcp_token token;
+ const char *val;
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != SEMI) {
- parse_warn ("semicolon expected.");
+ parse_warn (cfile, "semicolon expected.");
skip_to_semi (cfile);
return 0;
}
@@ -111,76 +159,100 @@ int parse_semi (cfile)
/* string-parameter :== STRING SEMI */
-char *parse_string (cfile)
- FILE *cfile;
+int parse_string (cfile, sptr, lptr)
+ struct parse *cfile;
+ char **sptr;
+ unsigned *lptr;
{
- char *val;
- int token;
+ const char *val;
+ enum dhcp_token token;
char *s;
+ unsigned len;
- token = next_token (&val, cfile);
+ token = next_token (&val, &len, cfile);
if (token != STRING) {
- parse_warn ("filename must be a string");
+ parse_warn (cfile, "expecting a string");
skip_to_semi (cfile);
- return (char *)0;
+ return 0;
}
- s = (char *)malloc (strlen (val) + 1);
+ s = (char *)dmalloc (len + 1, MDL);
if (!s)
- error ("no memory for string %s.", val);
- strcpy (s, val);
+ log_fatal ("no memory for string %s.", val);
+ memcpy (s, val, len + 1);
- if (!parse_semi (cfile))
- return (char *)0;
- return s;
+ if (!parse_semi (cfile)) {
+ dfree (s, MDL);
+ return 0;
+ }
+ if (sptr)
+ *sptr = s;
+ else
+ dfree (s, MDL);
+ if (lptr)
+ *lptr = len;
+ return 1;
}
-/* hostname :== identifier | hostname DOT identifier */
+/*
+ * hostname :== IDENTIFIER
+ * | IDENTIFIER DOT
+ * | hostname DOT IDENTIFIER
+ */
char *parse_host_name (cfile)
- FILE *cfile;
+ struct parse *cfile;
{
- char *val;
- int token;
- int len = 0;
+ const char *val;
+ enum dhcp_token token;
+ unsigned len = 0;
char *s;
char *t;
pair c = (pair)0;
+ int ltid = 0;
/* Read a dotted hostname... */
do {
/* Read a token, which should be an identifier. */
- token = next_token (&val, cfile);
- if (!is_identifier (token) && token != NUMBER) {
- parse_warn ("expecting an identifier in hostname");
- skip_to_semi (cfile);
- return (char *)0;
- }
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token) && token != NUMBER)
+ break;
+ token = next_token (&val, (unsigned *)0, cfile);
+
/* Store this identifier... */
- if (!(s = (char *)malloc (strlen (val) + 1)))
- error ("can't allocate temp space for hostname.");
+ if (!(s = (char *)dmalloc (strlen (val) + 1, MDL)))
+ log_fatal ("can't allocate temp space for hostname.");
strcpy (s, val);
c = cons ((caddr_t)s, c);
len += strlen (s) + 1;
/* Look for a dot; if it's there, keep going, otherwise
we're done. */
- token = peek_token (&val, cfile);
- if (token == DOT)
- token = next_token (&val, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == DOT) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ ltid = 1;
+ } else
+ ltid = 0;
} while (token == DOT);
+ /* Should be at least one token. */
+ if (!len)
+ return (char *)0;
+
/* Assemble the hostname together into a string. */
- if (!(s = (char *)malloc (len)))
- error ("can't allocate space for hostname.");
- t = s + len;
+ if (!(s = (char *)dmalloc (len + ltid, MDL)))
+ log_fatal ("can't allocate space for hostname.");
+ t = s + len + ltid;
*--t = 0;
+ if (ltid)
+ *--t = '.';
while (c) {
pair cdr = c -> cdr;
- int l = strlen ((char *)(c -> car));
+ unsigned l = strlen ((char *)(c -> car));
t -= l;
memcpy (t, (char *)(c -> car), l);
/* Free up temp space. */
- free (c -> car);
- free (c);
+ dfree (c -> car, MDL);
+ dfree (c, MDL);
c = cdr;
if (t != s)
*--t = '.';
@@ -188,10 +260,66 @@ char *parse_host_name (cfile)
return s;
}
+/* ip-addr-or-hostname :== ip-address | hostname
+ ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
+
+ Parse an ip address or a hostname. If uniform is zero, put in
+ an expr_substring node to limit hostnames that evaluate to more
+ than one IP address. */
+
+int parse_ip_addr_or_hostname (expr, cfile, uniform)
+ struct expression **expr;
+ struct parse *cfile;
+ int uniform;
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned char addr [4];
+ unsigned len = sizeof addr;
+ char *name;
+ struct expression *x = (struct expression *)0;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (is_identifier (token)) {
+ name = parse_host_name (cfile);
+ if (!name)
+ return 0;
+ if (!make_host_lookup (expr, name))
+ return 0;
+ if (!uniform) {
+ if (!make_limit (&x, *expr, 4))
+ return 0;
+ expression_dereference (expr, MDL);
+ *expr = x;
+ }
+ } else if (token == NUMBER) {
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return 0;
+ return make_const_data (expr, addr, len, 0, 1, MDL);
+ } else {
+ if (token != RBRACE && token != LBRACE)
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_warn (cfile, "%s (%d): expecting IP address or hostname",
+ val, token);
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
+ */
+
int parse_ip_addr (cfile, addr)
- FILE *cfile;
+ struct parse *cfile;
struct iaddr *addr;
{
+ const char *val;
+ enum dhcp_token token;
+
addr -> len = 4;
if (parse_numeric_aggregate (cfile, addr -> iabuf,
&addr -> len, DOT, 10, 8))
@@ -199,33 +327,41 @@ int parse_ip_addr (cfile, addr)
return 0;
}
-/* hardware-parameter :== HARDWARE ETHERNET csns SEMI
- csns :== NUMBER | csns COLON NUMBER */
+/*
+ * hardware-parameter :== HARDWARE hardware-type colon-seperated-hex-list SEMI
+ * hardware-type :== ETHERNET | TOKEN_RING
+ */
void parse_hardware_param (cfile, hardware)
- FILE *cfile;
+ struct parse *cfile;
struct hardware *hardware;
{
- char *val;
- int token;
- int hlen;
+ const char *val;
+ enum dhcp_token token;
+ unsigned hlen;
unsigned char *t;
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
switch (token) {
case ETHERNET:
- hardware -> htype = HTYPE_ETHER;
+ hardware -> hbuf [0] = HTYPE_ETHER;
break;
case TOKEN_RING:
- hardware -> htype = HTYPE_IEEE802;
+ hardware -> hbuf [0] = HTYPE_IEEE802;
break;
case FDDI:
- hardware -> htype = HTYPE_FDDI;
+ hardware -> hbuf [0] = HTYPE_FDDI;
break;
default:
- parse_warn ("expecting a network hardware type");
- skip_to_semi (cfile);
- return;
+ if (!strncmp (val, "unknown-", 8)) {
+ hardware -> hbuf [0] = atoi (&val [8]);
+ } else {
+ parse_warn (cfile,
+ "expecting a network hardware type");
+ skip_to_semi (cfile);
+
+ return;
+ }
}
/* Parse the hardware address information. Technically,
@@ -236,26 +372,33 @@ void parse_hardware_param (cfile, hardware)
that data in the lease file rather than simply failing on such
clients. Yuck. */
hlen = 0;
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == SEMI) {
+ hardware -> hlen = 1;
+ goto out;
+ }
t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen,
COLON, 16, 8);
- if (!t)
+ if (!t) {
+ hardware -> hlen = 1;
return;
- if (hlen > sizeof hardware -> haddr) {
- free (t);
- parse_warn ("hardware address too long");
+ }
+ if (hlen + 1 > sizeof hardware -> hbuf) {
+ dfree (t, MDL);
+ parse_warn (cfile, "hardware address too long");
} else {
- hardware -> hlen = hlen;
- memcpy ((unsigned char *)&hardware -> haddr [0],
- t, hardware -> hlen);
- if (hlen < sizeof hardware -> haddr)
- memset (&hardware -> haddr [hlen], 0,
- (sizeof hardware -> haddr) - hlen);
- free (t);
+ hardware -> hlen = hlen + 1;
+ memcpy ((unsigned char *)&hardware -> hbuf [1], t, hlen);
+ if (hlen + 1 < sizeof hardware -> hbuf)
+ memset (&hardware -> hbuf [hlen + 1], 0,
+ (sizeof hardware -> hbuf) - hlen - 1);
+ dfree (t, MDL);
}
- token = next_token (&val, cfile);
+ out:
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != SEMI) {
- parse_warn ("expecting semicolon.");
+ parse_warn (cfile, "expecting semicolon.");
skip_to_semi (cfile);
}
}
@@ -263,19 +406,19 @@ void parse_hardware_param (cfile, hardware)
/* lease-time :== NUMBER SEMI */
void parse_lease_time (cfile, timep)
- FILE *cfile;
+ struct parse *cfile;
TIME *timep;
{
- char *val;
- int token;
+ const char *val;
+ enum dhcp_token token;
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("Expecting numeric lease time");
+ parse_warn (cfile, "Expecting numeric lease time");
skip_to_semi (cfile);
return;
}
- convert_num ((unsigned char *)timep, val, 10, 32);
+ convert_num (cfile, (unsigned char *)timep, val, 10, 32);
/* Unswap the number - convert_num returns stuff in NBO. */
*timep = ntohl (*timep); /* XXX */
@@ -291,97 +434,100 @@ void parse_lease_time (cfile, timep)
unsigned char *parse_numeric_aggregate (cfile, buf,
max, seperator, base, size)
- FILE *cfile;
+ struct parse *cfile;
unsigned char *buf;
- int *max;
+ unsigned *max;
int seperator;
int base;
- int size;
+ unsigned size;
{
- char *val;
- int token;
- unsigned char *bufp = buf, *s;
- char *t;
- int count = 0;
+ const char *val;
+ enum dhcp_token token;
+ unsigned char *bufp = buf, *s, *t;
+ unsigned count = 0;
pair c = (pair)0;
if (!bufp && *max) {
- bufp = (unsigned char *)malloc (*max * size / 8);
+ bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
if (!bufp)
- error ("can't allocate space for numeric aggregate");
+ log_fatal ("no space for numeric aggregate");
+ s = 0;
} else
s = bufp;
do {
if (count) {
- token = peek_token (&val, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
if (token != seperator) {
if (!*max)
break;
if (token != RBRACE && token != LBRACE)
- token = next_token (&val, cfile);
- parse_warn ("too few numbers.");
+ token = next_token (&val,
+ (unsigned *)0,
+ cfile);
+ parse_warn (cfile, "too few numbers.");
if (token != SEMI)
skip_to_semi (cfile);
return (unsigned char *)0;
}
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
}
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
- if (token == EOF) {
- parse_warn ("unexpected end of file");
+ if (token == END_OF_FILE) {
+ parse_warn (cfile, "unexpected end of file");
break;
}
/* Allow NUMBER_OR_NAME if base is 16. */
if (token != NUMBER &&
(base != 16 || token != NUMBER_OR_NAME)) {
- parse_warn ("expecting numeric value.");
+ parse_warn (cfile, "expecting numeric value.");
skip_to_semi (cfile);
return (unsigned char *)0;
}
/* If we can, convert the number now; otherwise, build
a linked list of all the numbers. */
if (s) {
- convert_num (s, val, base, size);
+ convert_num (cfile, s, val, base, size);
s += size / 8;
} else {
- t = (char *)malloc (strlen (val) + 1);
+ t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
if (!t)
- error ("no temp space for number.");
- strcpy (t, val);
- c = cons (t, c);
+ log_fatal ("no temp space for number.");
+ strcpy ((char *)t, val);
+ c = cons ((caddr_t)t, c);
}
} while (++count != *max);
/* If we had to cons up a list, convert it now. */
if (c) {
- bufp = (unsigned char *)malloc (count * size / 8);
+ bufp = (unsigned char *)dmalloc (count * size / 8, MDL);
if (!bufp)
- error ("can't allocate space for numeric aggregate.");
+ log_fatal ("no space for numeric aggregate.");
s = bufp + count - size / 8;
*max = count;
}
while (c) {
pair cdr = c -> cdr;
- convert_num (s, (char *)(c -> car), base, size);
+ convert_num (cfile, s, (char *)(c -> car), base, size);
s -= size / 8;
/* Free up temp space. */
- free (c -> car);
- free (c);
+ dfree (c -> car, MDL);
+ dfree (c, MDL);
c = cdr;
}
return bufp;
}
-void convert_num (buf, str, base, size)
+void convert_num (cfile, buf, str, base, size)
+ struct parse *cfile;
unsigned char *buf;
- char *str;
+ const char *str;
int base;
- int size;
+ unsigned size;
{
- char *ptr = str;
+ const char *ptr = str;
int negative = 0;
u_int32_t val = 0;
int tval;
@@ -419,12 +565,13 @@ void convert_num (buf, str, base, size)
else if (tval >= '0')
tval -= '0';
else {
- warn ("Bogus number: %s.", str);
+ parse_warn (cfile, "Bogus number: %s.", str);
break;
}
if (tval >= base) {
- warn ("Bogus number: %s: digit %d not in base %d\n",
- str, tval, base);
+ parse_warn (cfile,
+ "Bogus number %s: digit %d not in base %d",
+ str, tval, base);
break;
}
val = val * base + tval;
@@ -437,16 +584,22 @@ void convert_num (buf, str, base, size)
if (val > max) {
switch (base) {
case 8:
- warn ("value %s%o exceeds max (%d) for precision.",
- negative ? "-" : "", val, max);
+ parse_warn (cfile,
+ "%s%lo exceeds max (%d) for precision.",
+ negative ? "-" : "",
+ (unsigned long)val, max);
break;
case 16:
- warn ("value %s%x exceeds max (%d) for precision.",
- negative ? "-" : "", val, max);
+ parse_warn (cfile,
+ "%s%lx exceeds max (%d) for precision.",
+ negative ? "-" : "",
+ (unsigned long)val, max);
break;
default:
- warn ("value %s%u exceeds max (%d) for precision.",
- negative ? "-" : "", val, max);
+ parse_warn (cfile,
+ "%s%lu exceeds max (%d) for precision.",
+ negative ? "-" : "",
+ (unsigned long)val, max);
break;
}
}
@@ -457,13 +610,14 @@ void convert_num (buf, str, base, size)
*buf = -(unsigned long)val;
break;
case 16:
- putShort (buf, -(unsigned long)val);
+ putShort (buf, -(long)val);
break;
case 32:
- putLong (buf, -(unsigned long)val);
+ putLong (buf, -(long)val);
break;
default:
- warn ("Unexpected integer size: %d\n", size);
+ parse_warn (cfile,
+ "Unexpected integer size: %d\n", size);
break;
}
} else {
@@ -478,160 +632,182 @@ void convert_num (buf, str, base, size)
putULong (buf, val);
break;
default:
- warn ("Unexpected integer size: %d\n", size);
+ parse_warn (cfile,
+ "Unexpected integer size: %d\n", size);
break;
}
}
}
-/* date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
- NUMBER COLON NUMBER COLON NUMBER SEMI
-
- Dates are always in GMT; first number is day of week; next is
- year/month/day; next is hours:minutes:seconds on a 24-hour
- clock. */
+/*
+ * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
+ * NUMBER COLON NUMBER COLON NUMBER SEMI |
+ * NUMBER NUMBER SLASH NUMBER SLASH NUMBER
+ * NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI |
+ * NEVER
+ *
+ * Dates are stored in GMT or with a timezone offset; first number is day
+ * of week; next is year/month/day; next is hours:minutes:seconds on a
+ * 24-hour clock, followed by the timezone offset in seconds, which is
+ * optional.
+ */
TIME parse_date (cfile)
- FILE *cfile;
+ struct parse *cfile;
{
struct tm tm;
int guess;
- char *val;
- int token;
+ int tzoff, wday, year, mon, mday, hour, min, sec;
+ const char *val;
+ enum dhcp_token token;
static int months [11] = { 31, 59, 90, 120, 151, 181,
212, 243, 273, 304, 334 };
- /* Day of week... */
- token = next_token (&val, cfile);
+ /* Day of week, or "never"... */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == NEVER) {
+ if (!parse_semi (cfile))
+ return 0;
+ return MAX_TIME;
+ }
+
if (token != NUMBER) {
- parse_warn ("numeric day of week expected.");
+ parse_warn (cfile, "numeric day of week expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_wday = atoi (val);
+ wday = atoi (val);
/* Year... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("numeric year expected.");
+ parse_warn (cfile, "numeric year expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_year = atoi (val);
- if (tm.tm_year > 1900)
- tm.tm_year -= 1900;
+
+ /* Note: the following is not a Y2K bug - it's a Y1.9K bug. Until
+ somebody invents a time machine, I think we can safely disregard
+ it. This actually works around a stupid Y2K bug that was present
+ in a very early beta release of dhcpd. */
+ year = atoi (val);
+ if (year > 1900)
+ year -= 1900;
/* Slash seperating year from month... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != SLASH) {
- parse_warn ("expected slash seperating year from month.");
+ parse_warn (cfile,
+ "expected slash seperating year from month.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
/* Month... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("numeric month expected.");
+ parse_warn (cfile, "numeric month expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_mon = atoi (val) - 1;
+ mon = atoi (val) - 1;
/* Slash seperating month from day... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != SLASH) {
- parse_warn ("expected slash seperating month from day.");
+ parse_warn (cfile,
+ "expected slash seperating month from day.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
/* Month... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("numeric day of month expected.");
+ parse_warn (cfile, "numeric day of month expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_mday = atoi (val);
+ mday = atoi (val);
/* Hour... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("numeric hour expected.");
+ parse_warn (cfile, "numeric hour expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_hour = atoi (val);
+ hour = atoi (val);
/* Colon seperating hour from minute... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != COLON) {
- parse_warn ("expected colon seperating hour from minute.");
+ parse_warn (cfile,
+ "expected colon seperating hour from minute.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
/* Minute... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("numeric minute expected.");
+ parse_warn (cfile, "numeric minute expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_min = atoi (val);
+ min = atoi (val);
/* Colon seperating minute from second... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != COLON) {
- parse_warn ("expected colon seperating hour from minute.");
+ parse_warn (cfile,
+ "expected colon seperating hour from minute.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
/* Minute... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("numeric minute expected.");
+ parse_warn (cfile, "numeric minute expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_sec = atoi (val);
- tm.tm_isdst = 0;
+ sec = atoi (val);
- /* XXX */ /* We assume that mktime does not use tm_yday. */
- tm.tm_yday = 0;
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == NUMBER) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ tzoff = atoi (val);
+ } else
+ tzoff = 0;
/* Make sure the date ends in a semicolon... */
- token = next_token (&val, cfile);
- if (token != SEMI) {
- parse_warn ("semicolon expected.");
- skip_to_semi (cfile);
+ if (!parse_semi (cfile))
return 0;
- }
/* Guess the time value... */
- guess = ((((((365 * (tm.tm_year - 70) + /* Days in years since '70 */
- (tm.tm_year - 69) / 4 + /* Leap days since '70 */
- (tm.tm_mon /* Days in months this year */
- ? months [tm.tm_mon - 1]
+ guess = ((((((365 * (year - 70) + /* Days in years since '70 */
+ (year - 69) / 4 + /* Leap days since '70 */
+ (mon /* Days in months this year */
+ ? months [mon - 1]
: 0) +
- (tm.tm_mon > 1 && /* Leap day this year */
- !((tm.tm_year - 72) & 3)) +
- tm.tm_mday - 1) * 24) + /* Day of month */
- tm.tm_hour) * 60) +
- tm.tm_min) * 60) + tm.tm_sec;
+ (mon > 1 && /* Leap day this year */
+ !((year - 72) & 3)) +
+ mday - 1) * 24) + /* Day of month */
+ hour) * 60) +
+ min) * 60) + sec + tzoff;
/* This guess could be wrong because of leap seconds or other
weirdness we don't know about that the system does. For
@@ -644,3 +820,3999 @@ TIME parse_date (cfile)
return guess;
}
+
+/*
+ * option-name :== IDENTIFIER |
+ IDENTIFIER . IDENTIFIER
+ */
+
+struct option *parse_option_name (cfile, allocate, known)
+ struct parse *cfile;
+ int allocate;
+ int *known;
+{
+ const char *val;
+ enum dhcp_token token;
+ char *uname;
+ struct universe *universe;
+ struct option *option;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile,
+ "expecting identifier after option keyword.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (struct option *)0;
+ }
+ uname = dmalloc (strlen (val) + 1, MDL);
+ if (!uname)
+ log_fatal ("no memory for uname information.");
+ strcpy (uname, val);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == DOT) {
+ /* Go ahead and take the DOT token... */
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* The next token should be an identifier... */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile, "expecting identifier after '.'");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (struct option *)0;
+ }
+
+ /* Look up the option name hash table for the specified
+ uname. */
+ universe = (struct universe *)0;
+ if (!universe_hash_lookup (&universe, universe_hash,
+ uname, 0, MDL)) {
+ parse_warn (cfile, "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 = uname;
+ universe = &dhcp_universe;
+ }
+
+ /* Look up the actual option info... */
+ option = (struct option *)0;
+ option_hash_lookup (&option, universe -> hash, val, 0, MDL);
+
+ /* If we didn't get an option structure, it's an undefined option. */
+ if (option) {
+ if (known)
+ *known = 1;
+ } else {
+ /* 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 (MDL);
+ if (val == uname)
+ option -> name = val;
+ else {
+ char *s;
+ dfree (uname, MDL);
+ s = dmalloc (strlen (val) + 1, MDL);
+ if (!s)
+ log_fatal ("no memory for option %s.%s",
+ universe -> name, val);
+ strcpy (s, val);
+ option -> name = s;
+ }
+ option -> universe = universe;
+ option -> code = 0;
+ return option;
+ }
+ if (val == uname)
+ parse_warn (cfile, "no option named %s", val);
+ else
+ parse_warn (cfile, "no option named %s in space %s",
+ val, uname);
+ skip_to_semi (cfile);
+ return (struct option *)0;
+ }
+
+ /* Free the initial identifier token. */
+ dfree (uname, MDL);
+ return option;
+}
+
+/* IDENTIFIER SEMI */
+
+void parse_option_space_decl (cfile)
+ struct parse *cfile;
+{
+ int token;
+ const char *val;
+ struct universe **ua, *nu;
+ char *s;
+
+ next_token (&val, (unsigned *)0, cfile); /* Discard the SPACE token,
+ which was checked by the
+ caller. */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile, "expecting identifier.");
+ skip_to_semi (cfile);
+ return;
+ }
+ nu = new_universe (MDL);
+ if (!nu)
+ log_fatal ("No memory for new option space.");
+
+ /* Set up the server option universe... */
+ s = dmalloc (strlen (val) + 1, MDL);
+ if (!s)
+ log_fatal ("No memory for new option space name.");
+ strcpy (s, val);
+ nu -> name = s;
+ nu -> lookup_func = lookup_hashed_option;
+ nu -> option_state_dereference =
+ hashed_option_state_dereference;
+ nu -> foreach = hashed_option_space_foreach;
+ nu -> save_func = save_hashed_option;
+ nu -> delete_func = delete_hashed_option;
+ nu -> encapsulate = hashed_option_space_encapsulate;
+ nu -> decode = parse_option_buffer;
+ nu -> length_size = 1;
+ nu -> tag_size = 1;
+ nu -> store_tag = putUChar;
+ nu -> store_length = putUChar;
+ nu -> index = universe_count++;
+ if (nu -> index >= universe_max) {
+ ua = dmalloc (universe_max * 2 * sizeof *ua, MDL);
+ if (!ua)
+ log_fatal ("No memory to expand option space array.");
+ memcpy (ua, universes, universe_max * sizeof *ua);
+ universe_max *= 2;
+ dfree (universes, MDL);
+ universes = ua;
+ }
+ universes [nu -> index] = nu;
+ option_new_hash (&nu -> hash, 1, MDL);
+ if (!nu -> hash)
+ log_fatal ("Can't allocate %s option hash table.", nu -> name);
+ universe_hash_add (universe_hash, nu -> name, 0, nu, MDL);
+ parse_semi (cfile);
+}
+
+/* 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_simple_type_sequence
+
+ ocsd_type_sequence :== LBRACE ocsd_types RBRACE
+
+ ocsd_simple_type_sequence :== LBRACE ocsd_simple_types RBRACE
+
+ ocsd_types :== ocsd_type |
+ ocsd_types ocsd_type
+
+ ocsd_type :== ocsd_simple_type |
+ ARRAY OF ocsd_simple_type
+
+ ocsd_simple_types :== ocsd_simple_type |
+ ocsd_simple_types ocsd_simple_type
+
+ ocsd_simple_type :== BOOLEAN |
+ INTEGER NUMBER |
+ SIGNED INTEGER NUMBER |
+ UNSIGNED INTEGER NUMBER |
+ IP-ADDRESS |
+ TEXT |
+ STRING |
+ ENCAPSULATE identifier */
+
+int parse_option_code_definition (cfile, option)
+ struct parse *cfile;
+ struct option *option;
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned arrayp = 0;
+ int recordp = 0;
+ int no_more_in_record = 0;
+ char tokbuf [128];
+ unsigned tokix = 0;
+ char type;
+ int code;
+ int is_signed;
+ char *s;
+ int has_encapsulation = 0;
+
+ /* Parse the option code. */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "expecting option code number.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ option -> code = atoi (val);
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != EQUAL) {
+ parse_warn (cfile, "expecting \"=\"");
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ /* See if this is an array. */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == ARRAY) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != OF) {
+ parse_warn (cfile, "expecting \"of\".");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ arrayp = 1;
+ token = next_token (&val, (unsigned *)0, cfile);
+ }
+
+ if (token == LBRACE) {
+ recordp = 1;
+ token = next_token (&val, (unsigned *)0, cfile);
+ }
+
+ /* At this point we're expecting a data type. */
+ next_type:
+ if (has_encapsulation) {
+ parse_warn (cfile,
+ "encapsulate must always be the last item.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ switch (token) {
+ case ARRAY:
+ if (arrayp) {
+ parse_warn (cfile, "no nested arrays.");
+ skip_to_rbrace (cfile, recordp);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != OF) {
+ parse_warn (cfile, "expecting \"of\".");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ arrayp = recordp + 1;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if ((recordp) && (token == LBRACE)) {
+ parse_warn (cfile,
+ "only uniform array inside record.");
+ skip_to_rbrace (cfile, recordp + 1);
+ skip_to_semi (cfile);
+ return 0;
+ }
+ goto next_type;
+ case BOOLEAN:
+ type = 'f';
+ break;
+ case INTEGER:
+ is_signed = 1;
+ parse_integer:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "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 (cfile,
+ "%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, (unsigned *)0, cfile);
+ if (token != INTEGER) {
+ parse_warn (cfile, "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 DOMAIN_NAME:
+ type = 'd';
+ goto no_arrays;
+ case TEXT:
+ type = 't';
+ no_arrays:
+ if (arrayp) {
+ parse_warn (cfile, "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_TOKEN:
+ type = 'X';
+ goto no_arrays;
+
+ case ENCAPSULATE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile,
+ "expecting option space identifier");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (strlen (val) + tokix + 2 > sizeof (tokbuf))
+ goto toobig;
+ tokbuf [tokix++] = 'E';
+ strcpy (&tokbuf [tokix], val);
+ tokix += strlen (val);
+ type = '.';
+ has_encapsulation = 1;
+ break;
+
+ default:
+ parse_warn (cfile, "unknown data type %s", val);
+ skip_to_rbrace (cfile, recordp);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ if (tokix == sizeof tokbuf) {
+ toobig:
+ parse_warn (cfile, "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, (unsigned *)0, cfile);
+ if (arrayp > recordp) {
+ if (tokix == sizeof tokbuf) {
+ parse_warn (cfile,
+ "too many types in record.");
+ skip_to_rbrace (cfile, 1);
+ skip_to_semi (cfile);
+ return 0;
+ }
+ arrayp = 0;
+ tokbuf[tokix++] = 'a';
+ }
+ if (token == COMMA) {
+ if (no_more_in_record) {
+ parse_warn (cfile,
+ "%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, (unsigned *)0, cfile);
+ goto next_type;
+ }
+ if (token != RBRACE) {
+ parse_warn (cfile, "expecting right brace.");
+ skip_to_rbrace (cfile, 1);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ }
+ if (!parse_semi (cfile)) {
+ parse_warn (cfile, "semicolon expected.");
+ skip_to_semi (cfile);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (has_encapsulation && arrayp) {
+ parse_warn (cfile,
+ "Arrays of encapsulations don't make sense.");
+ return 0;
+ }
+ if (has_encapsulation && tokbuf [0] == 'E')
+ has_encapsulation = 0;
+ s = dmalloc (tokix +
+ (arrayp ? 1 : 0) +
+ (has_encapsulation ? 1 : 0) + 1, MDL);
+ if (!s)
+ log_fatal ("no memory for option format.");
+ if (has_encapsulation)
+ s [0] = 'e';
+ memcpy (s + has_encapsulation, tokbuf, tokix);
+ tokix += has_encapsulation;
+ if (arrayp)
+ s [tokix++] = (arrayp > recordp) ? 'a' : 'A';
+ s [tokix] = 0;
+ option -> format = s;
+ 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;
+ option_hash_add (option -> universe -> hash,
+ (const char *)option -> name,
+ 0, option, MDL);
+ return 1;
+}
+
+/*
+ * base64 :== NUMBER_OR_STRING
+ */
+
+int parse_base64 (data, cfile)
+ struct data_string *data;
+ struct parse *cfile;
+{
+ enum dhcp_token token;
+ const char *val;
+ int i, j, k;
+ unsigned acc = 0;
+ static unsigned char
+ from64 [] = {64, 64, 64, 64, 64, 64, 64, 64, /* \"#$%&' */
+ 64, 64, 64, 62, 64, 64, 64, 63, /* ()*+,-./ */
+ 52, 53, 54, 55, 56, 57, 58, 59, /* 01234567 */
+ 60, 61, 64, 64, 64, 64, 64, 64, /* 89:;<=>? */
+ 64, 0, 1, 2, 3, 4, 5, 6, /* @ABCDEFG */
+ 7, 8, 9, 10, 11, 12, 13, 14, /* HIJKLMNO */
+ 15, 16, 17, 18, 19, 20, 21, 22, /* PQRSTUVW */
+ 23, 24, 25, 64, 64, 64, 64, 64, /* XYZ[\]^_ */
+ 64, 26, 27, 28, 29, 30, 31, 32, /* 'abcdefg */
+ 33, 34, 35, 36, 37, 38, 39, 40, /* hijklmno */
+ 41, 42, 43, 44, 45, 46, 47, 48, /* pqrstuvw */
+ 49, 50, 51, 64, 64, 64, 64, 64}; /* xyz{|}~ */
+ struct string_list *bufs = (struct string_list *)0,
+ *last = (struct string_list *)0,
+ *t;
+ int cc = 0;
+ int terminated = 0;
+
+ /* It's possible for a + or a / to cause a base64 quantity to be
+ tokenized into more than one token, so we have to parse them all
+ in before decoding. */
+ do {
+ unsigned l;
+
+ token = next_token (&val, &l, cfile);
+ t = dmalloc (l + sizeof *t, MDL);
+ if (!t)
+ log_fatal ("no memory for base64 buffer.");
+ memset (t, 0, (sizeof *t) - 1);
+ memcpy (t -> string, val, l + 1);
+ cc += l;
+ if (last)
+ last -> next = t;
+ else
+ bufs = t;
+ last = t;
+ token = peek_token (&val, (unsigned *)0, cfile);
+ } while (token == NUMBER_OR_NAME || token == NAME || token == EQUAL ||
+ token == NUMBER || token == PLUS || token == SLASH ||
+ token == STRING);
+
+ data -> len = cc;
+ data -> len = (data -> len * 3) / 4;
+ if (!buffer_allocate (&data -> buffer, data -> len, MDL)) {
+ parse_warn (cfile, "can't allocate buffer for base64 data.");
+ data -> len = 0;
+ data -> data = (unsigned char *)0;
+ return 0;
+ }
+
+ j = k = 0;
+ for (t = bufs; t; t = t -> next) {
+ for (i = 0; t -> string [i]; i++) {
+ unsigned foo = t -> string [i];
+ if (terminated && foo != '=') {
+ parse_warn (cfile,
+ "stuff after base64 '=' terminator: %s.",
+ &t -> string [i]);
+ goto bad;
+ }
+ if (foo < ' ' || foo > 'z') {
+ bad64:
+ parse_warn (cfile,
+ "invalid base64 character %d.",
+ t -> string [i]);
+ bad:
+ data_string_forget (data, MDL);
+ goto out;
+ }
+ if (foo == '=')
+ terminated = 1;
+ else {
+ foo = from64 [foo - ' '];
+ if (foo == 64)
+ goto bad64;
+ acc = (acc << 6) + foo;
+ switch (k % 4) {
+ case 0:
+ break;
+ case 1:
+ data -> buffer -> data [j++] = (acc >> 4);
+ acc = acc & 0x0f;
+ break;
+
+ case 2:
+ data -> buffer -> data [j++] = (acc >> 2);
+ acc = acc & 0x03;
+ break;
+ case 3:
+ data -> buffer -> data [j++] = acc;
+ acc = 0;
+ break;
+ }
+ }
+ k++;
+ }
+ }
+ if (k % 4) {
+ if (acc) {
+ parse_warn (cfile,
+ "partial base64 value left over: %d.",
+ acc);
+ }
+ }
+ data -> len = j;
+ data -> data = data -> buffer -> data;
+ out:
+ for (t = bufs; t; t = last) {
+ last = t -> next;
+ dfree (t, MDL);
+ }
+ if (data -> len)
+ return 1;
+ else
+ return 0;
+}
+
+
+/*
+ * colon-seperated-hex-list :== NUMBER |
+ * NUMBER COLON colon-seperated-hex-list
+ */
+
+int parse_cshl (data, cfile)
+ struct data_string *data;
+ struct parse *cfile;
+{
+ u_int8_t ibuf [128];
+ unsigned ilen = 0;
+ unsigned tlen = 0;
+ struct option_tag *sl = (struct option_tag *)0;
+ struct option_tag *next, **last = &sl;
+ enum dhcp_token token;
+ const char *val;
+ unsigned char *rvp;
+
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER && token != NUMBER_OR_NAME) {
+ parse_warn (cfile, "expecting hexadecimal number.");
+ skip_to_semi (cfile);
+ for (; sl; sl = next) {
+ next = sl -> next;
+ dfree (sl, MDL);
+ }
+ return 0;
+ }
+ if (ilen == sizeof ibuf) {
+ next = (struct option_tag *)
+ dmalloc (ilen - 1 +
+ sizeof (struct option_tag), MDL);
+ if (!next)
+ log_fatal ("no memory for string list.");
+ memcpy (next -> data, ibuf, ilen);
+ *last = next;
+ last = &next -> next;
+ tlen += ilen;
+ ilen = 0;
+ }
+ convert_num (cfile, &ibuf [ilen++], val, 16, 8);
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != COLON)
+ break;
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (1);
+
+ if (!buffer_allocate (&data -> buffer, tlen + ilen, MDL))
+ log_fatal ("no memory to store octet data.");
+ data -> data = &data -> buffer -> data [0];
+ data -> len = tlen + ilen;
+ data -> terminated = 0;
+
+ rvp = &data -> buffer -> data [0];
+ while (sl) {
+ next = sl -> next;
+ memcpy (rvp, sl -> data, sizeof ibuf);
+ rvp += sizeof ibuf;
+ dfree (sl, MDL);
+ sl = next;
+ }
+
+ memcpy (rvp, ibuf, ilen);
+ return 1;
+}
+
+/*
+ * executable-statements :== executable-statement executable-statements |
+ * executable-statement
+ *
+ * executable-statement :==
+ * IF if-statement |
+ * ADD class-name SEMI |
+ * BREAK SEMI |
+ * OPTION option-parameter SEMI |
+ * SUPERSEDE option-parameter SEMI |
+ * PREPEND option-parameter SEMI |
+ * APPEND option-parameter SEMI
+ */
+
+int parse_executable_statements (statements, cfile, lose, case_context)
+ struct executable_statement **statements;
+ struct parse *cfile;
+ int *lose;
+ enum expression_context case_context;
+{
+ struct executable_statement **next;
+
+ next = statements;
+ while (parse_executable_statement (next, cfile, lose, case_context))
+ next = &((*next) -> next);
+ if (!*lose)
+ return 1;
+ return 0;
+}
+
+int parse_executable_statement (result, cfile, lose, case_context)
+ struct executable_statement **result;
+ struct parse *cfile;
+ int *lose;
+ enum expression_context case_context;
+{
+ enum dhcp_token token;
+ const char *val;
+ struct executable_statement base;
+ struct class *cta;
+ struct option *option;
+ struct option_cache *cache;
+ int known;
+ int flag;
+ int i;
+ struct dns_zone *zone;
+ isc_result_t status;
+ char *s;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case IF:
+ next_token (&val, (unsigned *)0, cfile);
+ return parse_if_statement (result, cfile, lose);
+
+ case TOKEN_ADD:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "expecting class name.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+ cta = (struct class *)0;
+ status = find_class (&cta, val, MDL);
+ if (status != ISC_R_SUCCESS) {
+ parse_warn (cfile, "class %s: %s",
+ val, isc_result_totext (status));
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ return 0;
+ }
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for new statement.");
+ (*result) -> op = add_statement;
+ (*result) -> data.add = cta;
+ break;
+
+ case BREAK:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ return 0;
+ }
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for new statement.");
+ (*result) -> op = break_statement;
+ break;
+
+ case SEND:
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ option = parse_option_name (cfile, 0, &known);
+ if (!option) {
+ *lose = 1;
+ return 0;
+ }
+ return parse_option_statement (result, cfile, 1, option,
+ send_option_statement);
+
+ case SUPERSEDE:
+ case OPTION:
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ option = parse_option_name (cfile, 0, &known);
+ if (!option) {
+ *lose = 1;
+ return 0;
+ }
+ return parse_option_statement (result, cfile, 1, option,
+ supersede_option_statement);
+
+ case ALLOW:
+ flag = 1;
+ goto pad;
+ case DENY:
+ flag = 0;
+ goto pad;
+ case IGNORE:
+ flag = 2;
+ pad:
+ token = next_token (&val, (unsigned *)0, cfile);
+ cache = (struct option_cache *)0;
+ if (!parse_allow_deny (&cache, cfile, flag))
+ return 0;
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for new statement.");
+ (*result) -> op = supersede_option_statement;
+ (*result) -> data.option = cache;
+ break;
+
+ case DEFAULT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == COLON)
+ goto switch_default;
+ known = 0;
+ option = parse_option_name (cfile, 0, &known);
+ if (!option) {
+ *lose = 1;
+ return 0;
+ }
+ return parse_option_statement (result, cfile, 1, option,
+ default_option_statement);
+
+ case PREPEND:
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ option = parse_option_name (cfile, 0, &known);
+ if (!option) {
+ *lose = 1;
+ return 0;
+ }
+ return parse_option_statement (result, cfile, 1, option,
+ prepend_option_statement);
+
+ case APPEND:
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ option = parse_option_name (cfile, 0, &known);
+ if (!option) {
+ *lose = 1;
+ return 0;
+ }
+ return parse_option_statement (result, cfile, 1, option,
+ append_option_statement);
+
+ case ON:
+ token = next_token (&val, (unsigned *)0, cfile);
+ return parse_on_statement (result, cfile, lose);
+
+ case SWITCH:
+ token = next_token (&val, (unsigned *)0, cfile);
+ return parse_switch_statement (result, cfile, lose);
+
+ case CASE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (case_context == context_any) {
+ parse_warn (cfile,
+ "case statement in inappropriate scope.");
+ *lose = 1;
+ skip_to_semi (cfile);
+ return 0;
+ }
+ return parse_case_statement (result,
+ cfile, lose, case_context);
+
+ switch_default:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (case_context == context_any) {
+ parse_warn (cfile, "switch default statement in %s",
+ "inappropriate scope.");
+
+ *lose = 1;
+ return 0;
+ } else {
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for default statement.");
+ (*result) -> op = default_statement;
+ return 1;
+ }
+
+ case DEFINE:
+ case TOKEN_SET:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == DEFINE)
+ flag = 1;
+ else
+ flag = 0;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NAME && token != NUMBER_OR_NAME) {
+ parse_warn (cfile,
+ "%s can't be a variable name", val);
+ badset:
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for set statement.");
+ (*result) -> op = flag ? define_statement : set_statement;
+ (*result) -> data.set.name = dmalloc (strlen (val) + 1, MDL);
+ if (!(*result)->data.set.name)
+ log_fatal ("can't allocate variable name");
+ strcpy ((*result) -> data.set.name, val);
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (token == LPAREN) {
+ struct string_list *head, *cur, *new;
+ struct expression *expr;
+ head = cur = (struct string_list *)0;
+ do {
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if (token == RPAREN)
+ break;
+ if (token != NAME && token != NUMBER_OR_NAME) {
+ parse_warn (cfile,
+ "expecting argument name");
+ skip_to_rbrace (cfile, 0);
+ *lose = 1;
+ executable_statement_dereference
+ (result, MDL);
+ return 0;
+ }
+ new = ((struct string_list *)
+ dmalloc (sizeof (struct string_list) +
+ strlen (val), MDL));
+ if (!new)
+ log_fatal ("can't allocate string.");
+ memset (new, 0, sizeof *new);
+ strcpy (new -> string, val);
+ if (cur) {
+ cur -> next = new;
+ cur = new;
+ } else {
+ head = cur = new;
+ }
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ } while (token == COMMA);
+
+ if (token != RPAREN) {
+ parse_warn (cfile, "expecting right paren.");
+ badx:
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "expecting left brace.");
+ goto badx;
+ }
+
+ expr = (struct expression *)0;
+ if (!(expression_allocate (&expr, MDL)))
+ log_fatal ("can't allocate expression.");
+ expr -> op = expr_function;
+ if (!fundef_allocate (&expr -> data.func, MDL))
+ log_fatal ("can't allocate fundef.");
+ expr -> data.func -> args = head;
+ (*result) -> data.set.expr = expr;
+
+ if (!(parse_executable_statements
+ (&expr -> data.func -> statements, cfile, lose,
+ case_context))) {
+ if (*lose)
+ goto badx;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RBRACE) {
+ parse_warn (cfile, "expecting rigt brace.");
+ goto badx;
+ }
+ } else {
+ if (token != EQUAL) {
+ parse_warn (cfile,
+ "expecting '=' in %s statement.",
+ flag ? "define" : "set");
+ goto badset;
+ }
+
+ if (!parse_expression (&(*result) -> data.set.expr,
+ cfile, lose, context_any,
+ (struct expression **)0,
+ expr_none)) {
+ if (!*lose)
+ parse_warn (cfile,
+ "expecting expression.");
+ else
+ *lose = 1;
+ skip_to_semi (cfile);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ if (parse_semi (cfile)) {
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ }
+ break;
+
+ case UNSET:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NAME && token != NUMBER_OR_NAME) {
+ parse_warn (cfile,
+ "%s can't be a variable name", val);
+ badunset:
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for set statement.");
+ (*result) -> op = unset_statement;
+ (*result) -> data.unset = dmalloc (strlen (val) + 1, MDL);
+ if (!(*result)->data.unset)
+ log_fatal ("can't allocate variable name");
+ strcpy ((*result) -> data.unset, val);
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ break;
+
+ case EVAL:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for eval statement.");
+ (*result) -> op = eval_statement;
+
+ if (!parse_expression (&(*result) -> data.eval,
+ cfile, lose, context_data, /* XXX */
+ (struct expression **)0, expr_none)) {
+ if (!*lose)
+ parse_warn (cfile,
+ "expecting data expression.");
+ else
+ *lose = 1;
+ skip_to_semi (cfile);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ }
+ break;
+
+ case RETURN:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for return statement.");
+ (*result) -> op = return_statement;
+
+ if (!parse_expression (&(*result) -> data.retval,
+ cfile, lose, context_data,
+ (struct expression **)0, expr_none)) {
+ if (!*lose)
+ parse_warn (cfile,
+ "expecting data expression.");
+ else
+ *lose = 1;
+ skip_to_semi (cfile);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ break;
+
+ case LOG:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for log statement.");
+ (*result) -> op = log_statement;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN) {
+ parse_warn (cfile, "left parenthesis expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ i = 1;
+ if (token == FATAL) {
+ (*result) -> data.log.priority = log_priority_fatal;
+ } else if (token == ERROR) {
+ (*result) -> data.log.priority = log_priority_error;
+ } else if (token == TOKEN_DEBUG) {
+ (*result) -> data.log.priority = log_priority_debug;
+ } else if (token == INFO) {
+ (*result) -> data.log.priority = log_priority_info;
+ } else {
+ (*result) -> data.log.priority = log_priority_debug;
+ i = 0;
+ }
+ if (i) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA) {
+ parse_warn (cfile, "comma expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+ }
+
+ if (!(parse_data_expression
+ (&(*result) -> data.log.expr, cfile, lose))) {
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ parse_warn (cfile, "right parenthesis expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != SEMI) {
+ parse_warn (cfile, "semicolon expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+ break;
+
+ /* Not really a statement, but we parse it here anyway
+ because it's appropriate for all DHCP agents with
+ parsers. */
+ case ZONE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ zone = (struct dns_zone *)0;
+ if (!dns_zone_allocate (&zone, MDL))
+ log_fatal ("no memory for new zone.");
+ zone -> name = parse_host_name (cfile);
+ if (!zone -> name) {
+ badzone:
+ parse_warn (cfile, "expecting hostname.");
+ *lose = 1;
+ skip_to_semi (cfile);
+ dns_zone_dereference (&zone, MDL);
+ return 0;
+ }
+ i = strlen (zone -> name);
+ if (zone -> name [i - 1] != '.') {
+ s = dmalloc ((unsigned)i + 2, MDL);
+ if (!s)
+ goto badzone;
+ strcpy (s, zone -> name);
+ s [i] = '.';
+ s [i + 1] = 0;
+ dfree (zone -> name, MDL);
+ zone -> name = s;
+ }
+ if (!parse_zone (zone, cfile))
+ goto badzone;
+ status = enter_dns_zone (zone);
+ if (status != ISC_R_SUCCESS) {
+ if (parse_semi (cfile))
+ parse_warn (cfile, "dns zone key %s: %s",
+ zone -> name,
+ isc_result_totext (status));
+ dns_zone_dereference (&zone, MDL);
+ return 0;
+ }
+ dns_zone_dereference (&zone, MDL);
+ return 1;
+
+ /* Also not really a statement, but same idea as above. */
+ case KEY:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_key (cfile)) {
+ *lose = 1;
+ return 0;
+ }
+ return 1;
+
+ default:
+ if (config_universe && is_identifier (token)) {
+ option = (struct option *)0;
+ option_hash_lookup (&option, config_universe -> hash,
+ val, 0, MDL);
+ if (option) {
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ return parse_option_statement
+ (result, cfile, 1, option,
+ supersede_option_statement);
+ }
+ }
+
+ if (token == NUMBER_OR_NAME || token == NAME) {
+ /* This is rather ugly. Since function calls are
+ data expressions, fake up an eval statement. */
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for eval statement.");
+ (*result) -> op = eval_statement;
+
+ if (!parse_expression (&(*result) -> data.eval,
+ cfile, lose, context_data,
+ (struct expression **)0,
+ expr_none)) {
+ if (!*lose)
+ parse_warn (cfile, "expecting "
+ "function call.");
+ else
+ *lose = 1;
+ skip_to_semi (cfile);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ break;
+ }
+
+ *lose = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* zone-statements :== zone-statement |
+ zone-statement zone-statements
+ zone-statement :==
+ PRIMARY ip-addresses SEMI |
+ SECONDARY ip-addresses SEMI |
+ key-reference SEMI
+ ip-addresses :== ip-addr-or-hostname |
+ ip-addr-or-hostname COMMA ip-addresses
+ key-reference :== KEY STRING |
+ KEY identifier */
+
+int parse_zone (struct dns_zone *zone, struct parse *cfile)
+{
+ int token;
+ const char *val;
+ char *key_name;
+ struct option_cache *oc;
+ int done = 0;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "expecting left brace");
+ return 0;
+ }
+
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case PRIMARY:
+ if (zone -> primary) {
+ parse_warn (cfile,
+ "more than one primary.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (!option_cache_allocate (&zone -> primary, MDL))
+ log_fatal ("can't allocate primary option cache.");
+ oc = zone -> primary;
+ goto consemup;
+
+ case SECONDARY:
+ if (zone -> secondary) {
+ parse_warn (cfile, "more than one secondary.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (!option_cache_allocate (&zone -> secondary, MDL))
+ log_fatal ("can't allocate secondary.");
+ oc = zone -> secondary;
+ consemup:
+ token = next_token (&val, (unsigned *)0, cfile);
+ do {
+ struct expression *expr = (struct expression *)0;
+ if (!parse_ip_addr_or_hostname (&expr, cfile, 0)) {
+ parse_warn (cfile,
+ "expecting IP addr or hostname.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (oc -> expression) {
+ struct expression *old =
+ (struct expression *)0;
+ expression_reference (&old,
+ oc -> expression,
+ MDL);
+ expression_dereference (&oc -> expression,
+ MDL);
+ if (!make_concat (&oc -> expression,
+ old, expr))
+ log_fatal ("no memory for concat.");
+ expression_dereference (&expr, MDL);
+ expression_dereference (&old, MDL);
+ } else {
+ expression_reference (&oc -> expression,
+ expr, MDL);
+ expression_dereference (&expr, MDL);
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (token == COMMA);
+ if (token != SEMI) {
+ parse_warn (cfile, "expecting semicolon.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ break;
+
+ case KEY:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == STRING) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ key_name = (char *)0;
+ } else {
+ key_name = parse_host_name (cfile);
+ if (!key_name) {
+ parse_warn (cfile, "expecting key name.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ val = key_name;
+ }
+ if (omapi_auth_key_lookup_name (&zone -> key, val) !=
+ ISC_R_SUCCESS)
+ parse_warn (cfile, "unknown key %s", val);
+ if (key_name)
+ dfree (key_name, MDL);
+ if (!parse_semi (cfile))
+ return 0;
+ break;
+
+ default:
+ done = 1;
+ break;
+ }
+ } while (!done);
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RBRACE) {
+ parse_warn (cfile, "expecting right brace.");
+ return 0;
+ }
+ return 1;
+}
+
+/* key-statements :== key-statement |
+ key-statement key-statements
+ key-statement :==
+ ALGORITHM host-name SEMI |
+ secret-definition SEMI
+ secret-definition :== SECRET base64val |
+ SECRET STRING */
+
+int parse_key (struct parse *cfile)
+{
+ int token;
+ const char *val;
+ int done = 0;
+ struct auth_key *key;
+ struct data_string ds;
+ isc_result_t status;
+ char *s;
+
+ key = (struct auth_key *)0;
+ if (omapi_auth_key_new (&key, MDL) != ISC_R_SUCCESS)
+ log_fatal ("no memory for key");
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == STRING) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ key -> name = dmalloc (strlen (val) + 1, MDL);
+ if (!key -> name)
+ log_fatal ("no memory for key name.");
+ strcpy (key -> name, val);
+
+ } else {
+ key -> name = parse_host_name (cfile);
+ if (!key -> name) {
+ parse_warn (cfile, "expecting key name.");
+ skip_to_semi (cfile);
+ goto bad;
+ }
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "expecting left brace");
+ goto bad;
+ }
+
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case ALGORITHM:
+ if (key -> algorithm) {
+ parse_warn (cfile,
+ "key %s: too many algorithms",
+ key -> name);
+ goto rbad;
+ }
+ key -> algorithm = parse_host_name (cfile);
+ if (!key -> algorithm) {
+ parse_warn (cfile,
+ "expecting key algorithm name.");
+ goto rbad;
+ }
+ if (!parse_semi (cfile))
+ goto rbad;
+ /* If the algorithm name isn't an FQDN, tack on
+ the .SIG-ALG.REG.NET. domain. */
+ s = strrchr (key -> algorithm, '.');
+ if (!s) {
+ static char add [] = ".SIG-ALG.REG.INT.";
+ s = dmalloc (strlen (key -> algorithm) +
+ sizeof (add), MDL);
+ if (!s) {
+ log_error ("no memory for key %s.",
+ "algorithm");
+ goto rbad;
+ }
+ strcpy (s, key -> algorithm);
+ strcat (s, add);
+ dfree (key -> algorithm, MDL);
+ key -> algorithm = s;
+ } else if (s [1]) {
+ /* If there is no trailing '.', hack one in. */
+ s = dmalloc (strlen (key -> algorithm) + 2, MDL);
+ if (!s) {
+ log_error ("no memory for key %s.",
+ key -> algorithm);
+ goto rbad;
+ }
+ strcpy (s, key -> algorithm);
+ strcat (s, ".");
+ dfree (key -> algorithm, MDL);
+ key -> algorithm = s;
+ }
+ break;
+
+ case SECRET:
+ if (key -> key) {
+ parse_warn (cfile, "key %s: too many secrets",
+ key -> name);
+ goto rbad;
+ }
+
+ memset (&ds, 0, sizeof(ds));
+ if (!parse_base64 (&ds, cfile))
+ goto rbad;
+ status = omapi_data_string_new (&key -> key, ds.len,
+ MDL);
+ if (status != ISC_R_SUCCESS)
+ goto rbad;
+ memcpy (key -> key -> value,
+ ds.buffer -> data, ds.len);
+ data_string_forget (&ds, MDL);
+
+ if (!parse_semi (cfile))
+ goto rbad;
+ break;
+
+ default:
+ done = 1;
+ break;
+ }
+ } while (!done);
+ if (token != RBRACE) {
+ parse_warn (cfile, "expecting right brace.");
+ goto rbad;
+ }
+ /* Allow the BIND 8 syntax, which has a semicolon after each
+ closing brace. */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == SEMI)
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* Remember the key. */
+ status = omapi_auth_key_enter (key);
+ if (status != ISC_R_SUCCESS) {
+ parse_warn (cfile, "tsig key %s: %s",
+ key -> name, isc_result_totext (status));
+ goto bad;
+ }
+ omapi_auth_key_dereference (&key, MDL);
+ return 1;
+
+ rbad:
+ skip_to_rbrace (cfile, 1);
+ bad:
+ omapi_auth_key_dereference (&key, MDL);
+ return 0;
+}
+
+/*
+ * on-statement :== event-types LBRACE executable-statements RBRACE
+ * event-types :== event-type OR event-types |
+ * event-type
+ * event-type :== EXPIRY | COMMIT | RELEASE
+ */
+
+int parse_on_statement (result, cfile, lose)
+ struct executable_statement **result;
+ struct parse *cfile;
+ int *lose;
+{
+ enum dhcp_token token;
+ const char *val;
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for new statement.");
+ (*result) -> op = on_statement;
+
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case EXPIRY:
+ (*result) -> data.on.evtypes |= ON_EXPIRY;
+ break;
+
+ case COMMIT:
+ (*result) -> data.on.evtypes |= ON_COMMIT;
+ break;
+
+ case RELEASE:
+ (*result) -> data.on.evtypes |= ON_RELEASE;
+ break;
+
+ case TRANSMISSION:
+ (*result) -> data.on.evtypes |= ON_TRANSMISSION;
+ break;
+
+ default:
+ parse_warn (cfile, "expecting a lease event type");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (token == OR);
+
+ /* Semicolon means no statements. */
+ if (token == SEMI)
+ return 1;
+
+ if (token != LBRACE) {
+ parse_warn (cfile, "left brace expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ if (!parse_executable_statements (&(*result) -> data.on.statements,
+ cfile, lose, context_any)) {
+ if (*lose) {
+ /* Try to even things up. */
+ do {
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ } while (token != END_OF_FILE && token != RBRACE);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RBRACE) {
+ parse_warn (cfile, "right brace expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * switch-statement :== LPAREN expr RPAREN LBRACE executable-statements RBRACE
+ *
+ */
+
+int parse_switch_statement (result, cfile, lose)
+ struct executable_statement **result;
+ struct parse *cfile;
+ int *lose;
+{
+ enum dhcp_token token;
+ const char *val;
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for new statement.");
+ (*result) -> op = switch_statement;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN) {
+ parse_warn (cfile, "expecting left brace.");
+ pfui:
+ *lose = 1;
+ skip_to_semi (cfile);
+ gnorf:
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+
+ if (!parse_expression (&(*result) -> data.s_switch.expr,
+ cfile, lose, context_data_or_numeric,
+ (struct expression **)0, expr_none)) {
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting data or numeric expression.");
+ goto pfui;
+ }
+ goto gnorf;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ parse_warn (cfile, "right paren expected.");
+ goto pfui;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "left brace expected.");
+ goto pfui;
+ }
+ if (!(parse_executable_statements
+ (&(*result) -> data.s_switch.statements, cfile, lose,
+ (is_data_expression ((*result) -> data.s_switch.expr)
+ ? context_data : context_numeric)))) {
+ if (*lose) {
+ skip_to_rbrace (cfile, 1);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RBRACE) {
+ parse_warn (cfile, "right brace expected.");
+ goto pfui;
+ }
+ return 1;
+}
+
+/*
+ * case-statement :== CASE expr COLON
+ *
+ */
+
+int parse_case_statement (result, cfile, lose, case_context)
+ struct executable_statement **result;
+ struct parse *cfile;
+ int *lose;
+ enum expression_context case_context;
+{
+ enum dhcp_token token;
+ const char *val;
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for new statement.");
+ (*result) -> op = case_statement;
+
+ if (!parse_expression (&(*result) -> data.c_case,
+ cfile, lose, case_context,
+ (struct expression **)0, expr_none))
+ {
+ if (!*lose) {
+ parse_warn (cfile, "expecting %s expression.",
+ (case_context == context_data
+ ? "data" : "numeric"));
+ }
+ pfui:
+ *lose = 1;
+ skip_to_semi (cfile);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COLON) {
+ parse_warn (cfile, "colon expected.");
+ goto pfui;
+ }
+ return 1;
+}
+
+/*
+ * if-statement :== boolean-expression LBRACE executable-statements RBRACE
+ * else-statement
+ *
+ * else-statement :== <null> |
+ * ELSE LBRACE executable-statements RBRACE |
+ * ELSE IF if-statement |
+ * ELSIF if-statement
+ */
+
+int parse_if_statement (result, cfile, lose)
+ struct executable_statement **result;
+ struct parse *cfile;
+ int *lose;
+{
+ enum dhcp_token token;
+ const char *val;
+ int parenp;
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for if statement.");
+
+ (*result) -> op = if_statement;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == LPAREN) {
+ parenp = 1;
+ next_token (&val, (unsigned *)0, cfile);
+ } else
+ parenp = 0;
+
+
+ if (!parse_boolean_expression (&(*result) -> data.ie.expr,
+ cfile, lose)) {
+ if (!*lose)
+ parse_warn (cfile, "boolean expression expected.");
+ executable_statement_dereference (result, MDL);
+ *lose = 1;
+ return 0;
+ }
+#if defined (DEBUG_EXPRESSION_PARSE)
+ print_expression ("if condition", (*result) -> data.ie.expr);
+#endif
+ if (parenp) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ parse_warn (cfile, "expecting right paren.");
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "left brace expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ if (!parse_executable_statements (&(*result) -> data.ie.tc,
+ cfile, lose, context_any)) {
+ if (*lose) {
+ /* Try to even things up. */
+ do {
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ } while (token != END_OF_FILE && token != RBRACE);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RBRACE) {
+ parse_warn (cfile, "right brace expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == ELSE) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == IF) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_if_statement (&(*result) -> data.ie.fc,
+ cfile, lose)) {
+ if (!*lose)
+ parse_warn (cfile,
+ "expecting if statement");
+ executable_statement_dereference (result, MDL);
+ *lose = 1;
+ return 0;
+ }
+ } else if (token != LBRACE) {
+ parse_warn (cfile, "left brace or if expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ } else {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!(parse_executable_statements
+ (&(*result) -> data.ie.fc,
+ cfile, lose, context_any))) {
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RBRACE) {
+ parse_warn (cfile, "right brace expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ }
+ } else if (token == ELSIF) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_if_statement (&(*result) -> data.ie.fc,
+ cfile, lose)) {
+ if (!*lose)
+ parse_warn (cfile,
+ "expecting conditional.");
+ executable_statement_dereference (result, MDL);
+ *lose = 1;
+ return 0;
+ }
+ } else
+ (*result) -> data.ie.fc = (struct executable_statement *)0;
+
+ return 1;
+}
+
+/*
+ * boolean_expression :== CHECK STRING |
+ * NOT boolean-expression |
+ * data-expression EQUAL data-expression |
+ * data-expression BANG EQUAL data-expression |
+ * boolean-expression AND boolean-expression |
+ * boolean-expression OR boolean-expression
+ * EXISTS OPTION-NAME
+ */
+
+int parse_boolean_expression (expr, cfile, lose)
+ struct expression **expr;
+ struct parse *cfile;
+ int *lose;
+{
+ /* Parse an expression... */
+ if (!parse_expression (expr, cfile, lose, context_boolean,
+ (struct expression **)0, expr_none))
+ return 0;
+
+ if (!is_boolean_expression (*expr) &&
+ (*expr) -> op != expr_variable_reference &&
+ (*expr) -> op != expr_funcall) {
+ parse_warn (cfile, "Expecting a boolean expression.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * data_expression :== SUBSTRING LPAREN data-expression COMMA
+ * numeric-expression COMMA
+ * numeric-expression RPAREN |
+ * CONCAT LPAREN data-expression COMMA
+ data-expression RPAREN
+ * SUFFIX LPAREN data_expression COMMA
+ * numeric-expression RPAREN |
+ * OPTION option_name |
+ * HARDWARE |
+ * PACKET LPAREN numeric-expression COMMA
+ * numeric-expression RPAREN |
+ * STRING |
+ * colon_seperated_hex_list
+ */
+
+int parse_data_expression (expr, cfile, lose)
+ struct expression **expr;
+ struct parse *cfile;
+ int *lose;
+{
+ /* Parse an expression... */
+ if (!parse_expression (expr, cfile, lose, context_data,
+ (struct expression **)0, expr_none))
+ return 0;
+
+ if (!is_data_expression (*expr) &&
+ (*expr) -> op != expr_variable_reference &&
+ (*expr) -> op != expr_funcall) {
+ parse_warn (cfile, "Expecting a data expression.");
+ *lose = 1;
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * numeric-expression :== EXTRACT_INT LPAREN data-expression
+ * COMMA number RPAREN |
+ * NUMBER
+ */
+
+int parse_numeric_expression (expr, cfile, lose)
+ struct expression **expr;
+ struct parse *cfile;
+ int *lose;
+{
+ /* Parse an expression... */
+ if (!parse_expression (expr, cfile, lose, context_numeric,
+ (struct expression **)0, expr_none))
+ return 0;
+
+ if (!is_numeric_expression (*expr) &&
+ (*expr) -> op != expr_variable_reference &&
+ (*expr) -> op != expr_funcall) {
+ parse_warn (cfile, "Expecting a numeric expression.");
+ *lose = 1;
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * dns-expression :==
+ * UPDATE LPAREN ns-class COMMA ns-type COMMA data-expression COMMA
+ * data-expression COMMA numeric-expression RPAREN
+ * DELETE LPAREN ns-class COMMA ns-type COMMA data-expression COMMA
+ * data-expression RPAREN
+ * EXISTS LPAREN ns-class COMMA ns-type COMMA data-expression COMMA
+ * data-expression RPAREN
+ * NOT EXISTS LPAREN ns-class COMMA ns-type COMMA data-expression COMMA
+ * data-expression RPAREN
+ * ns-class :== IN | CHAOS | HS | NUMBER
+ * ns-type :== A | PTR | MX | TXT | NUMBER
+ */
+
+int parse_dns_expression (expr, cfile, lose)
+ struct expression **expr;
+ struct parse *cfile;
+ int *lose;
+{
+ /* Parse an expression... */
+ if (!parse_expression (expr, cfile, lose, context_dns,
+ (struct expression **)0, expr_none))
+ return 0;
+
+ if (!is_dns_expression (*expr) &&
+ (*expr) -> op != expr_variable_reference &&
+ (*expr) -> op != expr_funcall) {
+ parse_warn (cfile, "Expecting a dns update subexpression.");
+ *lose = 1;
+ return 0;
+ }
+ return 1;
+}
+
+/* Parse a subexpression that does not contain a binary operator. */
+
+int parse_non_binary (expr, cfile, lose, context)
+ struct expression **expr;
+ struct parse *cfile;
+ int *lose;
+ enum expression_context context;
+{
+ enum dhcp_token token;
+ const char *val;
+ struct collection *col;
+ struct option *option;
+ struct expression *nexp, **ep;
+ int known;
+ enum expr_op opcode;
+ const char *s;
+ char *cptr;
+ struct executable_statement *stmt;
+ int i;
+ unsigned long u;
+ isc_result_t status, code;
+ unsigned len;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+
+ /* Check for unary operators... */
+ switch (token) {
+ case CHECK:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "string expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+ for (col = collections; col; col = col -> next)
+ if (!strcmp (col -> name, val))
+ break;
+ if (!col) {
+ parse_warn (cfile, "unknown collection.");
+ *lose = 1;
+ return 0;
+ }
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_check;
+ (*expr) -> data.check = col;
+ break;
+
+ case TOKEN_NOT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (context == context_dns) {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ goto not_exists;
+ }
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_not;
+ if (!parse_non_binary (&(*expr) -> data.not,
+ cfile, lose, context_boolean)) {
+ if (!*lose) {
+ parse_warn (cfile, "expression expected");
+ skip_to_semi (cfile);
+ }
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ if (!is_boolean_expression ((*expr) -> data.not)) {
+ *lose = 1;
+ parse_warn (cfile, "boolean expression expected");
+ skip_to_semi (cfile);
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case LPAREN:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_expression (expr, cfile, lose, context,
+ (struct expression **)0, expr_none)) {
+ if (!*lose) {
+ parse_warn (cfile, "expression expected");
+ skip_to_semi (cfile);
+ }
+ *lose = 1;
+ return 0;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ *lose = 1;
+ parse_warn (cfile, "right paren expected");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ break;
+
+ case EXISTS:
+ if (context == context_dns)
+ goto ns_exists;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_exists;
+ known = 0;
+ (*expr) -> data.option = parse_option_name (cfile, 0, &known);
+ if (!(*expr) -> data.option) {
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case STATIC:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_static;
+ break;
+
+ case KNOWN:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_known;
+ break;
+
+ case SUBSTRING:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_substring;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN) {
+ nolparen:
+ expression_dereference (expr, MDL);
+ parse_warn (cfile, "left parenthesis expected.");
+ *lose = 1;
+ return 0;
+ }
+
+ if (!parse_data_expression (&(*expr) -> data.substring.expr,
+ cfile, lose)) {
+ nodata:
+ expression_dereference (expr, MDL);
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting data expression.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ }
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA) {
+ nocomma:
+ expression_dereference (expr, MDL);
+ parse_warn (cfile, "comma expected.");
+ *lose = 1;
+
+ return 0;
+ }
+
+ if (!parse_numeric_expression
+ (&(*expr) -> data.substring.offset,cfile, lose)) {
+ nonum:
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting numeric expression.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ }
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!parse_numeric_expression
+ (&(*expr) -> data.substring.len, cfile, lose))
+ goto nonum;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ norparen:
+ parse_warn (cfile, "right parenthesis expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case SUFFIX:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_suffix;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!parse_data_expression (&(*expr) -> data.suffix.expr,
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!parse_numeric_expression (&(*expr) -> data.suffix.len,
+ cfile, lose))
+ goto nonum;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case CONCAT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_concat;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!parse_data_expression (&(*expr) -> data.concat [0],
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ concat_another:
+ if (!parse_data_expression (&(*expr) -> data.concat [1],
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (token == COMMA) {
+ nexp = (struct expression *)0;
+ if (!expression_allocate (&nexp, MDL))
+ log_fatal ("can't allocate at CONCAT2");
+ nexp -> op = expr_concat;
+ expression_reference (&nexp -> data.concat [0],
+ *expr, MDL);
+ expression_dereference (expr, MDL);
+ expression_reference (expr, nexp, MDL);
+ expression_dereference (&nexp, MDL);
+ goto concat_another;
+ }
+
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case BINARY_TO_ASCII:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_binary_to_ascii;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!parse_numeric_expression (&(*expr) -> data.b2a.base,
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!parse_numeric_expression (&(*expr) -> data.b2a.width,
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!parse_data_expression (&(*expr) -> data.b2a.seperator,
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!parse_data_expression (&(*expr) -> data.b2a.buffer,
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case REVERSE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_reverse;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!(parse_numeric_expression
+ (&(*expr) -> data.reverse.width, cfile, lose)))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!(parse_data_expression
+ (&(*expr) -> data.reverse.buffer, cfile, lose)))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case PICK:
+ /* pick (a, b, c) actually produces an internal representation
+ that looks like pick (a, pick (b, pick (c, nil))). */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!(expression_allocate (expr, MDL)))
+ log_fatal ("can't allocate expression");
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ nexp = (struct expression *)0;
+ expression_reference (&nexp, *expr, MDL);
+ do {
+ nexp -> op = expr_pick_first_value;
+ if (!(parse_data_expression
+ (&nexp -> data.pick_first_value.car,
+ cfile, lose)))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == COMMA) {
+ struct expression *foo = (struct expression *)0;
+ if (!expression_allocate (&foo, MDL))
+ log_fatal ("can't allocate expr");
+ expression_reference
+ (&nexp -> data.pick_first_value.cdr, foo, MDL);
+ expression_dereference (&nexp, MDL);
+ expression_reference (&nexp, foo, MDL);
+ expression_dereference (&foo, MDL);
+ }
+ } while (token == COMMA);
+ expression_dereference (&nexp, MDL);
+
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ /* dns-update and dns-delete are present for historical
+ purposes, but are deprecated in favor of ns-update
+ in combination with update, delete, exists and not
+ exists. */
+ case DNS_UPDATE:
+ case DNS_DELETE:
+#if !defined (NSUPDATE)
+ parse_warn (cfile,
+ "Please rebuild dhcpd with --with-nsupdate.");
+#endif
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == DNS_UPDATE)
+ opcode = expr_ns_add;
+ else
+ opcode = expr_ns_delete;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile,
+ "parse_expression: expecting string.");
+ badnsupdate:
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ if (!strcasecmp (val, "a"))
+ u = T_A;
+ else if (!strcasecmp (val, "ptr"))
+ u = T_PTR;
+ else if (!strcasecmp (val, "mx"))
+ u = T_MX;
+ else if (!strcasecmp (val, "cname"))
+ u = T_CNAME;
+ else if (!strcasecmp (val, "TXT"))
+ u = T_TXT;
+ else {
+ parse_warn (cfile, "unexpected rrtype: %s", val);
+ goto badnsupdate;
+ }
+
+ s = (opcode == expr_ns_add
+ ? "old-dns-update"
+ : "old-dns-delete");
+ cptr = dmalloc (strlen (s) + 1, MDL);
+ if (!cptr)
+ log_fatal ("can't allocate name for %s", s);
+ strcpy (cptr, s);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_funcall;
+ (*expr) -> data.funcall.name = cptr;
+
+ /* Fake up a function call. */
+ ep = &(*expr) -> data.funcall.arglist;
+ if (!expression_allocate (ep, MDL))
+ log_fatal ("can't allocate expression");
+ (*ep) -> op = expr_arg;
+ if (!make_const_int (&(*ep) -> data.arg.val, u))
+ log_fatal ("can't allocate rrtype value.");
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+ ep = &((*ep) -> data.arg.next);
+ if (!expression_allocate (ep, MDL))
+ log_fatal ("can't allocate expression");
+ (*ep) -> op = expr_arg;
+ if (!(parse_data_expression (&(*ep) -> data.arg.val,
+ cfile, lose)))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ ep = &((*ep) -> data.arg.next);
+ if (!expression_allocate (ep, MDL))
+ log_fatal ("can't allocate expression");
+ (*ep) -> op = expr_arg;
+ if (!(parse_data_expression (&(*ep) -> data.arg.val,
+ cfile, lose)))
+ goto nodata;
+
+ if (opcode == expr_ns_add) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ ep = &((*ep) -> data.arg.next);
+ if (!expression_allocate (ep, MDL))
+ log_fatal ("can't allocate expression");
+ (*ep) -> op = expr_arg;
+ if (!(parse_numeric_expression (&(*ep) -> data.arg.val,
+ cfile, lose))) {
+ parse_warn (cfile,
+ "expecting numeric expression.");
+ goto badnsupdate;
+ }
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case NS_UPDATE:
+#if !defined (NSUPDATE)
+ parse_warn (cfile,
+ "Please rebuild dhcpd with --with-nsupdate.");
+#endif
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ nexp = *expr;
+ do {
+ nexp -> op = expr_dns_transaction;
+ if (!(parse_dns_expression
+ (&nexp -> data.dns_transaction.car,
+ cfile, lose)))
+ {
+ if (!*lose)
+ parse_warn
+ (cfile,
+ "expecting dns expression.");
+ badnstrans:
+ expression_dereference (expr, MDL);
+ *lose = 1;
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (token == COMMA) {
+ if (!(expression_allocate
+ (&nexp -> data.dns_transaction.cdr,
+ MDL)))
+ log_fatal
+ ("can't allocate expression");
+ nexp = nexp -> data.dns_transaction.cdr;
+ }
+ } while (token == COMMA);
+
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ /* NOT EXISTS is special cased above... */
+ not_exists:
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != EXISTS) {
+ parse_warn (cfile, "expecting DNS prerequisite.");
+ *lose = 1;
+ return 0;
+ }
+ opcode = expr_ns_not_exists;
+ goto nsupdatecode;
+ case TOKEN_ADD:
+ opcode = expr_ns_add;
+ goto nsupdatecode;
+ case TOKEN_DELETE:
+ opcode = expr_ns_delete;
+ goto nsupdatecode;
+ ns_exists:
+ opcode = expr_ns_exists;
+ nsupdatecode:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+#if !defined (NSUPDATE)
+ parse_warn (cfile,
+ "Please rebuild dhcpd with --with-nsupdate.");
+#endif
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = opcode;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token) && token != NUMBER) {
+ parse_warn (cfile, "expecting identifier or number.");
+ badnsop:
+ expression_dereference (expr, MDL);
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ if (token == NUMBER)
+ (*expr) -> data.ns_add.rrclass = atoi (val);
+ else if (!strcasecmp (val, "in"))
+ (*expr) -> data.ns_add.rrclass = C_IN;
+ else if (!strcasecmp (val, "chaos"))
+ (*expr) -> data.ns_add.rrclass = C_CHAOS;
+ else if (!strcasecmp (val, "hs"))
+ (*expr) -> data.ns_add.rrclass = C_HS;
+ else {
+ parse_warn (cfile, "unexpected rrclass: %s", val);
+ goto badnsop;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token) && token != NUMBER) {
+ parse_warn (cfile, "expecting identifier or number.");
+ goto badnsop;
+ }
+
+ if (token == NUMBER)
+ (*expr) -> data.ns_add.rrtype = atoi (val);
+ else if (!strcasecmp (val, "a"))
+ (*expr) -> data.ns_add.rrtype = T_A;
+ else if (!strcasecmp (val, "ptr"))
+ (*expr) -> data.ns_add.rrtype = T_PTR;
+ else if (!strcasecmp (val, "mx"))
+ (*expr) -> data.ns_add.rrtype = T_MX;
+ else if (!strcasecmp (val, "cname"))
+ (*expr) -> data.ns_add.rrtype = T_CNAME;
+ else if (!strcasecmp (val, "TXT"))
+ (*expr) -> data.ns_add.rrtype = T_TXT;
+ else {
+ parse_warn (cfile, "unexpected rrtype: %s", val);
+ goto badnsop;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!(parse_data_expression
+ (&(*expr) -> data.ns_add.rrname, cfile, lose)))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!(parse_data_expression
+ (&(*expr) -> data.ns_add.rrdata, cfile, lose)))
+ goto nodata;
+
+ if (opcode == expr_ns_add) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!(parse_numeric_expression
+ (&(*expr) -> data.ns_add.ttl, cfile,
+ lose))) {
+ if (!*lose)
+ parse_warn (cfile,
+ "expecting numeric expression.");
+ goto badnsupdate;
+ }
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case OPTION:
+ case CONFIG_OPTION:
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = (token == OPTION
+ ? expr_option
+ : expr_config_option);
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ (*expr) -> data.option = parse_option_name (cfile, 0, &known);
+ if (!(*expr) -> data.option) {
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case HARDWARE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_hardware;
+ break;
+
+ case LEASED_ADDRESS:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_leased_address;
+ break;
+
+ case CLIENT_STATE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_client_state;
+ break;
+
+ case FILENAME:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_filename;
+ break;
+
+ case SERVER_NAME:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_sname;
+ break;
+
+ case LEASE_TIME:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_lease_time;
+ break;
+
+ case TOKEN_NULL:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_null;
+ break;
+
+ case HOST_DECL_NAME:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_host_decl_name;
+ break;
+
+ case UPDATED_DNS_RR:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "expecting string.");
+ bad_rrtype:
+ *lose = 1;
+ return 0;
+ }
+ if (!strcasecmp (val, "a"))
+ s = "ddns-fwd-name";
+ else if (!strcasecmp (val, "ptr"))
+ s = "ddns-rev-name";
+ else {
+ parse_warn (cfile, "invalid DNS rrtype: %s", val);
+ goto bad_rrtype;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_variable_reference;
+ (*expr) -> data.variable =
+ dmalloc (strlen (s) + 1, MDL);
+ if (!(*expr) -> data.variable)
+ log_fatal ("can't allocate variable name.");
+ strcpy ((*expr) -> data.variable, s);
+ break;
+
+ case PACKET:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_packet;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!parse_numeric_expression (&(*expr) -> data.packet.offset,
+ cfile, lose))
+ goto nonum;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!parse_numeric_expression (&(*expr) -> data.packet.len,
+ cfile, lose))
+ goto nonum;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case STRING:
+ token = next_token (&val, &len, cfile);
+ if (!make_const_data (expr, (const unsigned char *)val,
+ len, 1, 1, MDL))
+ log_fatal ("can't make constant string expression.");
+ break;
+
+ case EXTRACT_INT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN) {
+ parse_warn (cfile, "left parenthesis expected.");
+ *lose = 1;
+ return 0;
+ }
+
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+
+ if (!parse_data_expression (&(*expr) -> data.extract_int,
+ cfile, lose)) {
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting data expression.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ }
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA) {
+ parse_warn (cfile, "comma expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "number expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ switch (atoi (val)) {
+ case 8:
+ (*expr) -> op = expr_extract_int8;
+ break;
+
+ case 16:
+ (*expr) -> op = expr_extract_int16;
+ break;
+
+ case 32:
+ (*expr) -> op = expr_extract_int32;
+ break;
+
+ default:
+ parse_warn (cfile,
+ "unsupported integer size %d", atoi (val));
+ *lose = 1;
+ skip_to_semi (cfile);
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ parse_warn (cfile, "right parenthesis expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case ENCODE_INT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN) {
+ parse_warn (cfile, "left parenthesis expected.");
+ *lose = 1;
+ return 0;
+ }
+
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+
+ if (!parse_numeric_expression (&(*expr) -> data.encode_int,
+ cfile, lose)) {
+ parse_warn (cfile, "expecting numeric expression.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA) {
+ parse_warn (cfile, "comma expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "number expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ switch (atoi (val)) {
+ case 8:
+ (*expr) -> op = expr_encode_int8;
+ break;
+
+ case 16:
+ (*expr) -> op = expr_encode_int16;
+ break;
+
+ case 32:
+ (*expr) -> op = expr_encode_int32;
+ break;
+
+ default:
+ parse_warn (cfile,
+ "unsupported integer size %d", atoi (val));
+ *lose = 1;
+ skip_to_semi (cfile);
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ parse_warn (cfile, "right parenthesis expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case NUMBER:
+ /* If we're in a numeric context, this should just be a
+ number, by itself. */
+ if (context == context_numeric ||
+ context == context_data_or_numeric) {
+ next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_const_int;
+ (*expr) -> data.const_int = atoi (val);
+ break;
+ }
+
+ case NUMBER_OR_NAME:
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+
+ (*expr) -> op = expr_const_data;
+ if (!parse_cshl (&(*expr) -> data.const_data, cfile)) {
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case NS_FORMERR:
+ known = FORMERR;
+ goto ns_const;
+ ns_const:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_const_int;
+ (*expr) -> data.const_int = known;
+ break;
+
+ case NS_NOERROR:
+ known = ISC_R_SUCCESS;
+ goto ns_const;
+
+ case NS_NOTAUTH:
+ known = ISC_R_NOTAUTH;
+ goto ns_const;
+
+ case NS_NOTIMP:
+ known = ISC_R_NOTIMPLEMENTED;
+ goto ns_const;
+
+ case NS_NOTZONE:
+ known = ISC_R_NOTZONE;
+ goto ns_const;
+
+ case NS_NXDOMAIN:
+ known = ISC_R_NXDOMAIN;
+ goto ns_const;
+
+ case NS_NXRRSET:
+ known = ISC_R_NXRRSET;
+ goto ns_const;
+
+ case NS_REFUSED:
+ known = ISC_R_REFUSED;
+ goto ns_const;
+
+ case NS_SERVFAIL:
+ known = ISC_R_SERVFAIL;
+ goto ns_const;
+
+ case NS_YXDOMAIN:
+ known = ISC_R_YXDOMAIN;
+ goto ns_const;
+
+ case NS_YXRRSET:
+ known = ISC_R_YXRRSET;
+ goto ns_const;
+
+ case BOOTING:
+ known = S_INIT;
+ goto ns_const;
+
+ case REBOOT:
+ known = S_REBOOTING;
+ goto ns_const;
+
+ case SELECT:
+ known = S_SELECTING;
+ goto ns_const;
+
+ case REQUEST:
+ known = S_REQUESTING;
+ goto ns_const;
+
+ case BOUND:
+ known = S_BOUND;
+ goto ns_const;
+
+ case RENEW:
+ known = S_RENEWING;
+ goto ns_const;
+
+ case REBIND:
+ known = S_REBINDING;
+ goto ns_const;
+
+ case DEFINED:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NAME && token != NUMBER_OR_NAME) {
+ parse_warn (cfile, "%s can't be a variable name", val);
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_variable_exists;
+ (*expr) -> data.variable = dmalloc (strlen (val) + 1, MDL);
+ if (!(*expr)->data.variable)
+ log_fatal ("can't allocate variable name");
+ strcpy ((*expr) -> data.variable, val);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ /* Not a valid start to an expression... */
+ default:
+ if (token != NAME && token != NUMBER_OR_NAME)
+ return 0;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* Save the name of the variable being referenced. */
+ cptr = dmalloc (strlen (val) + 1, MDL);
+ if (!cptr)
+ log_fatal ("can't allocate variable name");
+ strcpy (cptr, val);
+
+ /* Simple variable reference, as far as we can tell. */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN) {
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_variable_reference;
+ (*expr) -> data.variable = cptr;
+ break;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_funcall;
+ (*expr) -> data.funcall.name = cptr;
+
+ /* Now parse the argument list. */
+ ep = &(*expr) -> data.funcall.arglist;
+ do {
+ if (!expression_allocate (ep, MDL))
+ log_fatal ("can't allocate expression");
+ (*ep) -> op = expr_arg;
+ if (!parse_expression (&(*ep) -> data.arg.val,
+ cfile, lose, context_any,
+ (struct expression **)0,
+ expr_none)) {
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting expression.");
+ *lose = 1;
+ }
+ skip_to_semi (cfile);
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ ep = &((*ep) -> data.arg.next);
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (token == COMMA);
+ if (token != RPAREN) {
+ parse_warn (cfile, "Right parenthesis expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+ }
+ return 1;
+}
+
+/* Parse an expression. */
+
+int parse_expression (expr, cfile, lose, context, plhs, binop)
+ struct expression **expr;
+ struct parse *cfile;
+ int *lose;
+ enum expression_context context;
+ struct expression **plhs;
+ enum expr_op binop;
+{
+ enum dhcp_token token;
+ const char *val;
+ struct expression *rhs = (struct expression *)0, *tmp;
+ struct expression *lhs = (struct expression *)0;
+ enum expr_op next_op;
+ enum expression_context
+ lhs_context = context_any,
+ rhs_context = context_any;
+
+ /* Consume the left hand side we were passed. */
+ if (plhs) {
+ expression_reference (&lhs, *plhs, MDL);
+ expression_dereference (plhs, MDL);
+ }
+
+ new_rhs:
+ if (!parse_non_binary (&rhs, cfile, lose, context)) {
+ /* If we already have a left-hand side, then it's not
+ okay for there not to be a right-hand side here, so
+ we need to flag it as an error. */
+ if (lhs) {
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting right-hand side.");
+ *lose = 1;
+ skip_to_semi (cfile);
+ }
+ expression_dereference (&lhs, MDL);
+ }
+ return 0;
+ }
+
+ /* At this point, rhs contains either an entire subexpression,
+ or at least a left-hand-side. If we do not see a binary token
+ as the next token, we're done with the expression. */
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case BANG:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != EQUAL) {
+ parse_warn (cfile, "! in boolean context without =");
+ *lose = 1;
+ skip_to_semi (cfile);
+ if (lhs)
+ expression_dereference (&lhs, MDL);
+ return 0;
+ }
+ next_op = expr_not_equal;
+ context = expression_context (rhs);
+ break;
+
+ case EQUAL:
+ next_op = expr_equal;
+ context = expression_context (rhs);
+ break;
+
+ case AND:
+ next_op = expr_and;
+ context = expression_context (rhs);
+ if (context != context_boolean) {
+ needbool:
+ parse_warn (cfile, "expecting boolean expressions");
+ skip_to_semi (cfile);
+ expression_dereference (&rhs, MDL);
+ *lose = 1;
+ return 0;
+ }
+ break;
+
+ case OR:
+ next_op = expr_or;
+ context = expression_context (rhs);
+ if (context != context_boolean)
+ goto needbool;
+ break;
+
+ case PLUS:
+ next_op = expr_add;
+ context = expression_context (rhs);
+ if (context != context_numeric) {
+ neednum:
+ parse_warn (cfile, "expecting numeric expressions");
+ skip_to_semi (cfile);
+ expression_dereference (&rhs, MDL);
+ *lose = 1;
+ return 0;
+ }
+ break;
+
+ case MINUS:
+ next_op = expr_subtract;
+ context = expression_context (rhs);
+ if (context != context_numeric)
+ goto neednum;
+ break;
+
+ case SLASH:
+ next_op = expr_divide;
+ context = expression_context (rhs);
+ if (context != context_numeric)
+ goto neednum;
+ break;
+
+ case ASTERISK:
+ next_op = expr_multiply;
+ context = expression_context (rhs);
+ if (context != context_numeric)
+ goto neednum;
+ break;
+
+ case PERCENT:
+ next_op = expr_remainder;
+ context = expression_context (rhs);
+ if (context != context_numeric)
+ goto neednum;
+ break;
+
+ case AMPERSAND:
+ next_op = expr_binary_and;
+ context = expression_context (rhs);
+ if (context != context_numeric)
+ goto neednum;
+ break;
+
+ case PIPE:
+ next_op = expr_binary_or;
+ context = expression_context (rhs);
+ if (context != context_numeric)
+ goto neednum;
+ break;
+
+ case CARET:
+ next_op = expr_binary_xor;
+ context = expression_context (rhs);
+ if (context != context_numeric)
+ goto neednum;
+ break;
+
+ default:
+ next_op = expr_none;
+ }
+
+ /* If we have no lhs yet, we just parsed it. */
+ if (!lhs) {
+ /* If there was no operator following what we just parsed,
+ then we're done - return it. */
+ if (next_op == expr_none) {
+ *expr = rhs;
+ return 1;
+ }
+ lhs = rhs;
+ rhs = (struct expression *)0;
+ binop = next_op;
+ next_token (&val, (unsigned *)0, cfile);
+ goto new_rhs;
+ }
+
+ /* Now, if we didn't find a binary operator, we're done parsing
+ this subexpression, so combine it with the preceding binary
+ operator and return the result. */
+ if (next_op == expr_none) {
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("Can't allocate expression!");
+
+ (*expr) -> op = binop;
+ /* All the binary operators' data union members
+ are the same, so we'll cheat and use the member
+ for the equals operator. */
+ (*expr) -> data.equal [0] = lhs;
+ (*expr) -> data.equal [1] = rhs;
+ return 1;
+ }
+
+ /* Eat the operator token - we now know it was a binary operator... */
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* If the binary operator we saw previously has a lower precedence
+ than the next operator, then the rhs we just parsed for that
+ operator is actually the lhs of the operator with the higher
+ precedence - to get the real rhs, we need to recurse on the
+ new operator. */
+ if (binop != expr_none &&
+ op_precedence (binop, next_op) < 0) {
+ tmp = rhs;
+ rhs = (struct expression *)0;
+ if (!parse_expression (&rhs, cfile, lose, op_context (next_op),
+ &tmp, next_op)) {
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting a subexpression");
+ *lose = 1;
+ }
+ return 0;
+ }
+ next_op = expr_none;
+ }
+
+ /* Now combine the LHS and the RHS using binop. */
+ tmp = (struct expression *)0;
+ if (!expression_allocate (&tmp, MDL))
+ log_fatal ("No memory for equal precedence combination.");
+
+ /* Store the LHS and RHS. */
+ tmp -> data.equal [0] = lhs;
+ tmp -> data.equal [1] = rhs;
+ tmp -> op = binop;
+
+ lhs = tmp;
+ tmp = (struct expression *)0;
+ rhs = (struct expression *)0;
+
+ /* Recursions don't return until we have parsed the end of the
+ expression, so if we recursed earlier, we can now return what
+ we got. */
+ if (next_op == expr_none) {
+ *expr = lhs;
+ return 1;
+ }
+
+ binop = next_op;
+ goto new_rhs;
+}
+
+/* option-statement :== identifier DOT identifier <syntax> SEMI
+ | identifier <syntax> SEMI
+
+ Option syntax is handled specially through format strings, so it
+ would be painful to come up with BNF for it. However, it always
+ starts as above and ends in a SEMI. */
+
+int parse_option_statement (result, cfile, lookups, option, op)
+ struct executable_statement **result;
+ struct parse *cfile;
+ int lookups;
+ struct option *option;
+ enum statement_op op;
+{
+ const char *val;
+ enum dhcp_token token;
+ const char *fmt = NULL;
+ struct expression *expr = (struct expression *)0;
+ struct expression *tmp;
+ int lose;
+ struct executable_statement *stmt;
+ int ftt = 1;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == SEMI) {
+ /* Eat the semicolon... */
+ token = next_token (&val, (unsigned *)0, cfile);
+ goto done;
+ }
+
+ if (token == EQUAL) {
+ /* Eat the equals sign. */
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* Parse a data expression and use its value for the data. */
+ if (!parse_data_expression (&expr, cfile, &lose)) {
+ /* In this context, we must have an executable
+ statement, so if we found something else, it's
+ still an error. */
+ if (!lose) {
+ parse_warn (cfile,
+ "expecting a data expression.");
+ skip_to_semi (cfile);
+ }
+ return 0;
+ }
+
+ /* We got a valid expression, so use it. */
+ goto done;
+ }
+
+ /* Parse the option data... */
+ do {
+ /* Set a flag if this is an array of a simple type (i.e.,
+ not an array of pairs of IP addresses, or something
+ like that. */
+ int uniform = option -> format [1] == 'A';
+
+ and_again:
+ /* Set fmt to start of format for 'A' and one char back
+ for 'a' */
+ if ((fmt != NULL) &&
+ (fmt != option -> format) && (*fmt == 'a'))
+ fmt -= 1;
+ else
+ fmt = ((fmt == NULL) ||
+ (*fmt == 'A')) ? option -> format : fmt;
+
+ /* 'a' means always uniform */
+ uniform |= (fmt [1] == 'a');
+
+ for ( ; *fmt; fmt++) {
+ if ((*fmt == 'A') || (*fmt == 'a'))
+ break;
+ if (*fmt == 'o')
+ continue;
+ tmp = expr;
+ expr = (struct expression *)0;
+ if (!parse_option_token (&expr, cfile, &fmt,
+ tmp, uniform, lookups)) {
+ if (fmt [1] != 'o') {
+ if (tmp)
+ expression_dereference (&tmp,
+ MDL);
+ return 0;
+ }
+ expr = tmp;
+ tmp = (struct expression *)0;
+ }
+ if (tmp)
+ expression_dereference (&tmp, MDL);
+ }
+ if ((*fmt == 'A') || (*fmt == 'a')) {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ /* Comma means: continue with next element in array */
+ if (token == COMMA) {
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ continue;
+ }
+ /* no comma: end of array.
+ 'A' or end of string means: leave the loop */
+ if ((*fmt == 'A') || (fmt[1] == '\0'))
+ break;
+ /* 'a' means: go on with next char */
+ if (*fmt == 'a') {
+ fmt++;
+ goto and_again;
+ }
+ }
+ } while ((*fmt == 'A') || (*fmt == 'a'));
+
+ done:
+ if (!parse_semi (cfile))
+ return 0;
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for option statement.");
+ (*result) -> op = op;
+ if (expr && !option_cache (&(*result) -> data.option,
+ (struct data_string *)0, expr, option, MDL))
+ log_fatal ("no memory for option cache");
+ if (expr)
+ expression_dereference (&expr, MDL);
+ return 1;
+}
+
+int parse_option_token (rv, cfile, fmt, expr, uniform, lookups)
+ struct expression **rv;
+ struct parse *cfile;
+ const char **fmt;
+ struct expression *expr;
+ int uniform;
+ int lookups;
+{
+ const char *val;
+ enum dhcp_token token;
+ struct expression *t = (struct expression *)0;
+ unsigned char buf [4];
+ unsigned len;
+ unsigned char *ob;
+ struct iaddr addr;
+ int num;
+ const char *f, *g;
+ struct enumeration_value *e;
+
+ switch (**fmt) {
+ case 'U':
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ if ((*fmt) [1] != 'o') {
+ parse_warn (cfile, "expecting identifier.");
+ skip_to_semi (cfile);
+ }
+ return 0;
+ }
+ token = next_token (&val, &len, cfile);
+ if (!make_const_data (&t, (const unsigned char *)val,
+ len, 1, 1, MDL))
+ log_fatal ("No memory for %s", val);
+ break;
+
+ case 'E':
+ g = strchr (*fmt, '.');
+ if (!g) {
+ parse_warn (cfile,
+ "malformed encapsulation format (bug!)");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ *fmt = g;
+ case 'X':
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == NUMBER_OR_NAME || token == NUMBER) {
+ if (!expression_allocate (&t, MDL))
+ return 0;
+ if (!parse_cshl (&t -> data.const_data, cfile)) {
+ expression_dereference (&t, MDL);
+ return 0;
+ }
+ t -> op = expr_const_data;
+ } else if (token == STRING) {
+ token = next_token (&val, &len, cfile);
+ if (!make_const_data (&t, (const unsigned char *)val,
+ len, 1, 1, MDL))
+ log_fatal ("No memory for \"%s\"", val);
+ } else {
+ if ((*fmt) [1] != 'o') {
+ parse_warn (cfile, "expecting string %s.",
+ "or hexadecimal data");
+ skip_to_semi (cfile);
+ }
+ return 0;
+ }
+ break;
+
+ case 'd': /* Domain name... */
+ val = parse_host_name (cfile);
+ if (!val) {
+ parse_warn (cfile, "not a valid domain name.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ len = strlen (val);
+ goto make_string;
+
+ case 't': /* Text string... */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != STRING && !is_identifier (token)) {
+ if ((*fmt) [1] != 'o') {
+ parse_warn (cfile, "expecting string.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ }
+ return 0;
+ }
+ token = next_token (&val, &len, cfile);
+ make_string:
+ if (!make_const_data (&t, (const unsigned char *)val,
+ len, 1, 1, MDL))
+ log_fatal ("No memory for concatenation");
+ break;
+
+ case 'N':
+ f = (*fmt) + 1;
+ g = strchr (*fmt, '.');
+ if (!g) {
+ parse_warn (cfile, "malformed %s (bug!)",
+ "enumeration format");
+ foo:
+ skip_to_semi (cfile);
+ return 0;
+ }
+ *fmt = g;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile,
+ "identifier expected");
+ goto foo;
+ }
+ e = find_enumeration_value (f, (*fmt) - f, val);
+ if (!e) {
+ parse_warn (cfile, "unknown value");
+ goto foo;
+ }
+ if (!make_const_data (&t, &e -> value, 1, 0, 1, MDL))
+ return 0;
+ break;
+
+ case 'I': /* IP address or hostname. */
+ if (lookups) {
+ if (!parse_ip_addr_or_hostname (&t, cfile, uniform))
+ return 0;
+ } else {
+ if (!parse_ip_addr (cfile, &addr))
+ return 0;
+ if (!make_const_data (&t, addr.iabuf, addr.len,
+ 0, 1, MDL))
+ return 0;
+ }
+ break;
+
+ case 'T': /* Lease interval. */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != INFINITE)
+ goto check_number;
+ token = next_token (&val, (unsigned *)0, cfile);
+ putLong (buf, -1);
+ if (!make_const_data (&t, buf, 4, 0, 1, MDL))
+ return 0;
+ break;
+
+ case 'L': /* Unsigned 32-bit integer... */
+ case 'l': /* Signed 32-bit integer... */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ check_number:
+ if (token != NUMBER) {
+ need_number:
+ if ((*fmt) [1] != 'o') {
+ parse_warn (cfile, "expecting number.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ }
+ return 0;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ convert_num (cfile, buf, val, 0, 32);
+ if (!make_const_data (&t, buf, 4, 0, 1, MDL))
+ return 0;
+ break;
+
+ case 's': /* Signed 16-bit integer. */
+ case 'S': /* Unsigned 16-bit integer. */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER)
+ goto need_number;
+ token = next_token (&val, (unsigned *)0, cfile);
+ convert_num (cfile, buf, val, 0, 16);
+ if (!make_const_data (&t, buf, 2, 0, 1, MDL))
+ return 0;
+ break;
+
+ case 'b': /* Signed 8-bit integer. */
+ case 'B': /* Unsigned 8-bit integer. */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER)
+ goto need_number;
+ token = next_token (&val, (unsigned *)0, cfile);
+ convert_num (cfile, buf, val, 0, 8);
+ if (!make_const_data (&t, buf, 1, 0, 1, MDL))
+ return 0;
+ break;
+
+ case 'f': /* Boolean flag. */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ if ((*fmt) [1] != 'o')
+ parse_warn (cfile, "expecting identifier.");
+ bad_flag:
+ if ((*fmt) [1] != 'o') {
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ }
+ return 0;
+ }
+ if (!strcasecmp (val, "true")
+ || !strcasecmp (val, "on"))
+ buf [0] = 1;
+ else if (!strcasecmp (val, "false")
+ || !strcasecmp (val, "off"))
+ buf [0] = 0;
+ else if (!strcasecmp (val, "ignore"))
+ buf [0] = 2;
+ else {
+ if ((*fmt) [1] != 'o')
+ parse_warn (cfile, "expecting boolean.");
+ goto bad_flag;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!make_const_data (&t, buf, 1, 0, 1, MDL))
+ return 0;
+ break;
+
+ default:
+ parse_warn (cfile, "Bad format %c in parse_option_token.",
+ **fmt);
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (expr) {
+ if (!make_concat (rv, expr, t))
+ return 0;
+ } else
+ expression_reference (rv, t, MDL);
+ expression_dereference (&t, MDL);
+ return 1;
+}
+
+int parse_option_decl (oc, cfile)
+ struct option_cache **oc;
+ struct parse *cfile;
+{
+ const char *val;
+ int token;
+ u_int8_t buf [4];
+ u_int8_t hunkbuf [1024];
+ unsigned hunkix = 0;
+ const char *fmt, *f;
+ struct option *option;
+ struct iaddr ip_addr;
+ u_int8_t *dp;
+ unsigned len;
+ int nul_term = 0;
+ struct buffer *bp;
+ int known = 0;
+ struct enumeration_value *e;
+
+ option = parse_option_name (cfile, 0, &known);
+ if (!option)
+ return 0;
+
+ /* Parse the option data... */
+ do {
+ /* Set a flag if this is an array of a simple type (i.e.,
+ not an array of pairs of IP addresses, or something
+ like that. */
+ int uniform = option -> format [1] == 'A';
+
+ for (fmt = option -> format; *fmt; fmt++) {
+ if (*fmt == 'A')
+ break;
+ switch (*fmt) {
+ case 'E':
+ fmt = strchr (fmt, '.');
+ if (!fmt) {
+ parse_warn (cfile,
+ "malformed %s (bug!)",
+ "encapsulation format");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ case 'X':
+ len = parse_X (cfile, &hunkbuf [hunkix],
+ sizeof hunkbuf - hunkix);
+ hunkix += len;
+ break;
+
+ case 't': /* Text string... */
+ token = next_token (&val,
+ &len, cfile);
+ if (token != STRING) {
+ parse_warn (cfile,
+ "expecting string.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (hunkix + len + 1 > sizeof hunkbuf) {
+ parse_warn (cfile,
+ "option data buffer %s",
+ "overflow");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ memcpy (&hunkbuf [hunkix], val, len + 1);
+ nul_term = 1;
+ hunkix += len;
+ break;
+
+ case 'N':
+ f = fmt;
+ fmt = strchr (fmt, '.');
+ if (!fmt) {
+ parse_warn (cfile,
+ "malformed %s (bug!)",
+ "enumeration format");
+ foo:
+ skip_to_semi (cfile);
+ return 0;
+ }
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile,
+ "identifier expected");
+ goto foo;
+ }
+ e = find_enumeration_value (f, fmt - f, val);
+ if (!e) {
+ parse_warn (cfile,
+ "unknown value");
+ goto foo;
+ }
+ len = 1;
+ dp = &e -> value;
+ goto alloc;
+
+ case 'I': /* IP address. */
+ if (!parse_ip_addr (cfile, &ip_addr))
+ return 0;
+ len = ip_addr.len;
+ dp = ip_addr.iabuf;
+
+ alloc:
+ if (hunkix + len > sizeof hunkbuf) {
+ parse_warn (cfile,
+ "option data buffer %s",
+ "overflow");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ memcpy (&hunkbuf [hunkix], dp, len);
+ hunkix += len;
+ break;
+
+ case 'L': /* Unsigned 32-bit integer... */
+ case 'l': /* Signed 32-bit integer... */
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ need_number:
+ parse_warn (cfile,
+ "expecting number.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ convert_num (cfile, buf, val, 0, 32);
+ len = 4;
+ dp = buf;
+ goto alloc;
+
+ case 's': /* Signed 16-bit integer. */
+ case 'S': /* Unsigned 16-bit integer. */
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if (token != NUMBER)
+ goto need_number;
+ convert_num (cfile, buf, val, 0, 16);
+ len = 2;
+ dp = buf;
+ goto alloc;
+
+ case 'b': /* Signed 8-bit integer. */
+ case 'B': /* Unsigned 8-bit integer. */
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if (token != NUMBER)
+ goto need_number;
+ convert_num (cfile, buf, val, 0, 8);
+ len = 1;
+ dp = buf;
+ goto alloc;
+
+ case 'f': /* Boolean flag. */
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile,
+ "expecting identifier.");
+ bad_flag:
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (!strcasecmp (val, "true")
+ || !strcasecmp (val, "on"))
+ buf [0] = 1;
+ else if (!strcasecmp (val, "false")
+ || !strcasecmp (val, "off"))
+ buf [0] = 0;
+ else {
+ parse_warn (cfile,
+ "expecting boolean.");
+ goto bad_flag;
+ }
+ len = 1;
+ dp = buf;
+ goto alloc;
+
+ default:
+ log_error ("parse_option_param: Bad format %c",
+ *fmt);
+ skip_to_semi (cfile);
+ return 0;
+ }
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (*fmt == 'A' && token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn (cfile, "semicolon expected.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ bp = (struct buffer *)0;
+ if (!buffer_allocate (&bp, hunkix + nul_term, MDL))
+ log_fatal ("no memory to store option declaration.");
+ if (!bp -> data)
+ log_fatal ("out of memory allocating option data.");
+ memcpy (bp -> data, hunkbuf, hunkix + nul_term);
+
+ if (!option_cache_allocate (oc, MDL))
+ log_fatal ("out of memory allocating option cache.");
+
+ (*oc) -> data.buffer = bp;
+ (*oc) -> data.data = &bp -> data [0];
+ (*oc) -> data.terminated = nul_term;
+ (*oc) -> data.len = hunkix;
+ (*oc) -> option = option;
+ return 1;
+}
+
+/* Consider merging parse_cshl into this. */
+
+int parse_X (cfile, buf, max)
+ struct parse *cfile;
+ u_int8_t *buf;
+ unsigned max;
+{
+ int token;
+ const char *val;
+ unsigned len;
+ u_int8_t *s;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == NUMBER_OR_NAME || token == NUMBER) {
+ len = 0;
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER && token != NUMBER_OR_NAME) {
+ parse_warn (cfile,
+ "expecting hexadecimal constant.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ convert_num (cfile, &buf [len], val, 16, 8);
+ if (len++ > max) {
+ parse_warn (cfile,
+ "hexadecimal constant too long.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == COLON)
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ } while (token == COLON);
+ val = (char *)buf;
+ } else if (token == STRING) {
+ token = next_token (&val, &len, cfile);
+ if (len + 1 > max) {
+ parse_warn (cfile, "string constant too long.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ memcpy (buf, val, len + 1);
+ } else {
+ parse_warn (cfile, "expecting string or hexadecimal data");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ return len;
+}
+
+int parse_warn (struct parse *cfile, const char *fmt, ...)
+{
+ va_list list;
+ char lexbuf [256];
+ char mbuf [1024];
+ char fbuf [1024];
+ unsigned i, lix;
+
+ do_percentm (mbuf, fmt);
+#ifndef NO_SNPRINTF
+ snprintf (fbuf, sizeof fbuf, "%s line %d: %s",
+ cfile -> tlname, cfile -> lexline, mbuf);
+#else
+ sprintf (fbuf, "%s line %d: %s",
+ cfile -> tlname, cfile -> lexline, mbuf);
+#endif
+
+ va_start (list, fmt);
+ vsnprintf (mbuf, sizeof mbuf, fbuf, list);
+ va_end (list);
+
+ lix = 0;
+ for (i = 0;
+ cfile -> token_line [i] && i < (cfile -> lexchar - 1); i++) {
+ if (lix < (sizeof lexbuf) - 1)
+ lexbuf [lix++] = ' ';
+ if (cfile -> token_line [i] == '\t') {
+ for (lix;
+ lix < (sizeof lexbuf) - 1 && (lix & 7); lix++)
+ lexbuf [lix] = ' ';
+ }
+ }
+ lexbuf [lix] = 0;
+
+#ifndef DEBUG
+ syslog (log_priority | LOG_ERR, "%s", mbuf);
+ syslog (log_priority | LOG_ERR, "%s", cfile -> token_line);
+ if (cfile -> lexchar < 81)
+ syslog (log_priority | LOG_ERR, "%s^", lexbuf);
+#endif
+
+ if (log_perror) {
+ write (2, mbuf, strlen (mbuf));
+ write (2, "\n", 1);
+ write (2, cfile -> token_line, strlen (cfile -> token_line));
+ write (2, "\n", 1);
+ if (cfile -> lexchar < 81)
+ write (2, lexbuf, lix);
+ write (2, "^\n", 2);
+ }
+
+ cfile -> warnings_occurred = 1;
+
+ return 0;
+}
OpenPOWER on IntegriCloud