From be6be08d6659e79264c6de7e013453ed0892b39a Mon Sep 17 00:00:00 2001 From: Ted Lemon Date: Thu, 25 Jun 1998 03:07:51 +0000 Subject: [PATCH] Nive parse-ip-addr-or-hostbname into common parser. Add executable statement parser. Add xpression parser. Unify the option statement parsers. Make the option token parser its own function. --- common/parse.c | 883 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 873 insertions(+), 10 deletions(-) diff --git a/common/parse.c b/common/parse.c index 1dc9e133..ece7e700 100644 --- a/common/parse.c +++ b/common/parse.c @@ -42,7 +42,7 @@ #ifndef lint static char copyright[] = -"$Id: parse.c,v 1.6 1998/04/20 18:01:32 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: parse.c,v 1.7 1998/06/25 03:07:51 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -134,7 +134,11 @@ char *parse_string (cfile) return s; } -/* hostname :== identifier | hostname DOT identifier */ +/* + * hostname :== IDENTIFIER + * | IDENTIFIER DOT + * | hostname DOT IDENTIFIER + */ char *parse_host_name (cfile) FILE *cfile; @@ -187,6 +191,53 @@ 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. */ + +struct expression *parse_ip_addr_or_hostname (cfile, uniform) + FILE *cfile; + int uniform; +{ + char *val; + int token; + unsigned char addr [4]; + int len = sizeof addr; + char *name; + struct expression *rv; + + token = peek_token (&val, cfile); + if (is_identifier (token)) { + name = parse_host_name (cfile); + if (!name) + return (struct expression *)0; + rv = make_host_lookup (name); + if (!uniform) + rv = make_limit (rv, 4); + } else if (token == NUMBER) { + if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8)) + return (struct expression *)0; + rv = make_const_data (addr, len, 0, 0); + } else { + if (token != RBRACE && token != LBRACE) + token = next_token (&val, cfile); + parse_warn ("%s (%d): expecting IP address or hostname", + val, token); + if (token != SEMI) + skip_to_semi (cfile); + return (struct expression *)0; + } + + return rv; +} + +/* + * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER + */ + int parse_ip_addr (cfile, addr) FILE *cfile; struct iaddr *addr; @@ -201,8 +252,10 @@ 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; @@ -479,12 +532,14 @@ void convert_num (buf, str, base, size) } } -/* 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 + * + * 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. + */ TIME parse_date (cfile) FILE *cfile; @@ -645,6 +700,11 @@ TIME parse_date (cfile) return guess; } +/* + * option-name :== IDENTIFIER | + IDENTIFIER . IDENTIFIER + */ + struct option *parse_option_name (cfile) FILE *cfile; { @@ -718,6 +778,11 @@ struct option *parse_option_name (cfile) return option; } +/* + * colon-seperated-hex-list :== NUMBER | + * NUMBER COLON colon-seperated-hex-list + */ + unsigned char *parse_cshl (cfile, plen) FILE *cfile; int *plen; @@ -779,3 +844,801 @@ unsigned char *parse_cshl (cfile, plen) *plen = ilen + tlen; return rv; } + +/* + * 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 + */ + +struct executable_statement *parse_executable_statements (cfile, lose) + FILE *cfile; + int *lose; +{ + struct executable_statement *head, **next; + + next = &head; + while ((*next = parse_executable_statement (cfile, lose))) + next = &((*next) -> next); + if (!lose) + return head; + return (struct executable_statement *)0; +} + +struct executable_statement *parse_executable_statement (cfile, lose) + FILE *cfile; + int *lose; +{ + int token; + char *val; + struct executable_statement *stmt, base; + struct class *cta; + struct option *option; + + switch (peek_token (&val, cfile)) { + case IF: + stmt = parse_if_statement (cfile, lose); + return stmt; + case ADD: + token = next_token (&val, cfile); + if (token != STRING) { + parse_warn ("expecting class name."); + skip_to_semi (cfile); + *lose = 1; + return (struct executable_statement *)0; + } + cta = find_class (val); + if (!cta) { + parse_warn ("unknown class %s.", val); + skip_to_semi (cfile); + *lose = 1; + return (struct executable_statement *)0; + } + if (!parse_semi (cfile)) { + *lose = 1; + return (struct executable_statement *)0; + } + memset (&base, 0, sizeof base); + base.op = add_statement; + base.data.add = cta; + break; + + case BREAK: + token = next_token (&val, cfile); + if (!parse_semi (cfile)) { + *lose = 1; + return (struct executable_statement *)0; + } + memset (&base, 0, sizeof base); + base.op = break_statement; + break; + + case OPTION: + token = next_token (&val, cfile); + option = parse_option_name (cfile); + if (!option) { + *lose = 1; + return (struct executable_statement *)0; + } + return parse_option_statement (cfile, 1, option, + supersede_option_statement); + + case DEFAULT: + token = next_token (&val, cfile); + option = parse_option_name (cfile); + if (!option) { + *lose = 1; + return (struct executable_statement *)0; + } + return parse_option_statement (cfile, 1, option, + default_option_statement); + + case PREPEND: + token = next_token (&val, cfile); + option = parse_option_name (cfile); + if (!option) { + *lose = 1; + return (struct executable_statement *)0; + } + return parse_option_statement (cfile, 1, option, + prepend_option_statement); + + case APPEND: + token = next_token (&val, cfile); + option = parse_option_name (cfile); + if (!option) { + *lose = 1; + return (struct executable_statement *)0; + } + return parse_option_statement (cfile, 1, option, + append_option_statement); + + default: + *lose = 0; + return (struct executable_statement *)0; + } + + stmt = ((struct executable_statement *) + dmalloc (sizeof (struct executable_statement), + "parse_executable_statement")); + if (!stmt) + error ("no memory for new statement."); + *stmt = base; + return stmt; +} + +/* + * if-statement :== boolean-expression LBRACE executable-statements RBRACE + * else-statement + * + * else-statement :== | + * ELSE LBRACE executable-statements RBRACE | + * ELSE IF if-statement | + * ELSIF if-statement + */ + +struct executable_statement *parse_if_statement (cfile, lose) + FILE *cfile; + int *lose; +{ + int token; + char *val; + struct executable_statement *stmt; + struct expression *if_condition; + struct executable_statement *true, *false; + + token = next_token (&val, cfile); + if_condition = parse_boolean_expression (cfile, lose); + if (!if_condition) { + if (!*lose) + parse_warn ("boolean expression expected."); + return (struct executable_statement *)0; + } + token = next_token (&val, cfile); + if (token != LBRACE) { + parse_warn ("left brace expected."); + skip_to_semi (cfile); + *lose = 1; + return (struct executable_statement *)0; + } + true = parse_executable_statements (cfile, lose); + if (*lose) + return (struct executable_statement *)0; + token = next_token (&val, cfile); + if (token != RBRACE) { + parse_warn ("right brace expected."); + skip_to_semi (cfile); + *lose = 1; + return (struct executable_statement *)0; + } + token = peek_token (&val, cfile); + if (token == ELSE) { + token = next_token (&val, cfile); + token = peek_token (&val, cfile); + if (token == IF) { + token = next_token (&val, cfile); + false = parse_if_statement (cfile, lose); + if (*lose) + return (struct executable_statement *)0; + } else if (token != LBRACE) { + parse_warn ("left brace or if expected."); + skip_to_semi (cfile); + *lose = 1; + return (struct executable_statement *)0; + } else { + token = next_token (&val, cfile); + false = parse_executable_statement (cfile, lose); + if (*lose) + return (struct executable_statement *)0; + } + } else if (token == ELSIF) { + token = next_token (&val, cfile); + false = parse_if_statement (cfile, lose); + if (*lose) + return (struct executable_statement *)0; + } else + false = (struct executable_statement *)0; + + stmt = ((struct executable_statement *) + dmalloc (sizeof (struct executable_statement), + "parse_if_statement")); + if (!stmt) + error ("no memory for if statement."); + memset (stmt, 0, sizeof *stmt); + stmt -> op = if_statement; + stmt -> data.ie.expr = if_condition; + stmt -> data.ie.true = true; + stmt -> data.ie.false = false; + return stmt; +} + +/* + * boolean_expression :== CHECK STRING | + * NOT boolean-expression | + * data-expression EQUAL data-expression | + * boolean-expression AND boolean-expression | + * boolean-expression OR boolean-expression + */ + + +struct expression *parse_boolean_expression (cfile, lose) + FILE *cfile; + int *lose; +{ + int token; + char *val; + struct collection *col; + struct expression buf, *rv; + struct expression *left, *right; + + token = peek_token (&val, cfile); + + /* Check for unary operators... */ + switch (token) { + case CHECK: + token = next_token (&val, cfile); + token = next_token (&val, cfile); + if (token != STRING) { + parse_warn ("string expected."); + skip_to_semi (cfile); + *lose = 1; + return (struct expression *)0; + } + for (col = collections; col; col = col -> next) + if (!strcmp (col -> name, val)) + break; + if (!col) { + parse_warn ("unknown collection."); + *lose = 1; + return (struct expression *)0; + } + buf.op = expr_check; + buf.data.check = col; + goto have_expr; + + case NOT: + token = next_token (&val, cfile); + buf.op = expr_not; + buf.data.not = parse_boolean_expression (cfile, lose); + if (!buf.data.not) { + if (!*lose) { + parse_warn ("match expression expected"); + skip_to_semi (cfile); + } + *lose = 1; + return (struct expression *)0; + } + goto have_expr; + } + + /* If we're going to find an expression at this point, it must + involve a binary operator seperating two subexpressions. */ + left = parse_data_expression (cfile, lose); + if (!left) + return left; + token = peek_token (&val, cfile); + switch (token) { + case EQUAL: + buf.op = expr_equal; + break; + case AND: + buf.op = expr_and; + break; + case OR: + buf.op = expr_or; + break; + default: + parse_warn ("Expecting a boolean expression."); + skip_to_semi (cfile); + *lose = 1; + return (struct expression *)0; + } + token = next_token (&val, cfile); + + /* Now find the RHS of the expression. */ + right = parse_data_expression (cfile, lose); + if (!right) { + if (!*lose) { + if (buf.op == expr_equal) + parse_warn ("Expecting a data expression."); + else + parse_warn ("Expecting a boolean expression."); + skip_to_semi (cfile); + } + return right; + } + + /* Store the LHS and RHS. */ + buf.data.equal [0] = left; + buf.data.equal [1] = right; + + have_expr: + rv = new_expression ("parse_boolean_expression"); + if (!rv) + error ("No memory for boolean expression."); + *rv = buf; + return rv; +} + +/* + * data_expression :== SUBSTRING LPAREN data-expression COMMA + * numeric-expression COMMA + * numeric-expression RPAREN | + * SUFFIX LPAREN data_expression COMMA + * numeric-expression | + * OPTION option_name | + * HARDWARE | + * PACKET LPAREN numeric-expression COMMA + * numeric-expression RPAREN | + * STRING | + * colon_seperated_hex_list + */ + +struct expression *parse_data_expression (cfile, lose) + FILE *cfile; + int *lose; +{ + int token; + char *val; + struct collection *col; + struct expression buf, *rv; + struct expression *left, *right; + struct option *option; + + token = peek_token (&val, cfile); + + switch (token) { + case SUBSTRING: + token = next_token (&val, cfile); + buf.op = expr_substring; + + token = next_token (&val, cfile); + if (token != LPAREN) { + nolparen: + parse_warn ("left parenthesis expected."); + *lose = 1; + return (struct expression *)0; + } + + rv = parse_data_expression (cfile, lose); + if (!rv) { + nodata: + parse_warn ("expecting data expression."); + skip_to_semi (cfile); + *lose = 1; + return (struct expression *)0; + } + + token = next_token (&val, cfile); + if (token != COMMA) { + nocomma: + parse_warn ("comma expected."); + *lose = 1; + return (struct expression *)0; + } + + left = parse_numeric_expression (cfile, lose); + if (!left) { + nonum: + if (!*lose) { + parse_warn ("expecting numeric expression."); + skip_to_semi (cfile); + *lose = 1; + } + return (struct expression *)0; + } + + token = next_token (&val, cfile); + if (token != COMMA) + goto nocomma; + + right = parse_numeric_expression (cfile, lose); + if (!right) + goto nonum; + + token = next_token (&val, cfile); + if (token != RPAREN) { + norparen: + parse_warn ("right parenthesis expected."); + *lose = 1; + return (struct expression *)0; + } + return make_substring (rv, left, right); + + case SUFFIX: + token = next_token (&val, cfile); + buf.op = expr_suffix; + + token = next_token (&val, cfile); + if (token != LPAREN) + goto nolparen; + + buf.data.suffix.expr = parse_data_expression (cfile, lose); + if (!buf.data.suffix.expr) + goto nodata; + + token = next_token (&val, cfile); + if (token != COMMA) + goto nocomma; + + buf.data.suffix.len = parse_numeric_expression (cfile, lose); + if (!buf.data.suffix.len) + goto nonum; + + token = next_token (&val, cfile); + if (token != RPAREN) + goto norparen; + goto have_expr; + + case OPTION: + token = next_token (&val, cfile); + buf.op = expr_option; + buf.data.option = parse_option_name (cfile); + if (!buf.data.option) { + *lose = 1; + return (struct expression *)0; + } + goto have_expr; + + case HARDWARE: + token = next_token (&val, cfile); + buf.op = expr_hardware; + goto have_expr; + + case PACKET: + token = next_token (&val, cfile); + buf.op = expr_packet; + + token = next_token (&val, cfile); + if (token != LPAREN) + goto nolparen; + + buf.data.packet.offset = + parse_numeric_expression (cfile, lose); + if (!buf.data.packet.offset) + goto nonum; + + token = next_token (&val, cfile); + if (token != COMMA) + goto nocomma; + + buf.data.packet.len = + parse_numeric_expression (cfile, lose); + if (!buf.data.substring.len) + goto nonum; + + token = next_token (&val, cfile); + if (token != RPAREN) + goto norparen; + goto have_expr; + + case STRING: + token = next_token (&val, cfile); + return make_const_data (val, strlen (val), 1, 1); + + case NUMBER: + case NUMBER_OR_NAME: + buf.op = expr_const_data; + memset (&buf.data, 0, sizeof buf.data); + buf.data.const_data.data = + parse_cshl (cfile, &buf.data.const_data.len); + goto have_expr; + + default: + return (struct expression *)0; + } + + have_expr: + rv = (struct expression *)dmalloc (sizeof (struct expression), + "parse_boolean_expression"); + if (!rv) + error ("No memory for boolean expression."); + *rv = buf; + return rv; +} + +/* + * numeric-expression :== EXTRACT_INT LPAREN data-expression + * COMMA number RPAREN | + * NUMBER + */ + +struct expression *parse_numeric_expression (cfile, lose) + FILE *cfile; + int *lose; +{ + int token; + char *val; + struct collection *col; + struct expression buf, *rv; + struct expression *left, *right; + struct option *option; + + token = peek_token (&val, cfile); + + switch (token) { + case EXTRACT_INT: + token = next_token (&val, cfile); + + token = next_token (&val, cfile); + if (token != LPAREN) { + parse_warn ("left parenthesis expected."); + *lose = 1; + return (struct expression *)0; + } + + buf.data.extract_int.expr = + parse_data_expression (cfile, lose); + if (!buf.data.extract_int.expr) { + parse_warn ("expecting data expression."); + skip_to_semi (cfile); + *lose = 1; + return (struct expression *)0; + } + + token = next_token (&val, cfile); + if (token != COMMA) { + parse_warn ("comma expected."); + *lose = 1; + return (struct expression *)0; + } + + token = next_token (&val, cfile); + if (token != NUMBER) { + parse_warn ("number expected."); + *lose = 1; + return (struct expression *)0; + } + buf.data.extract_int.width = (struct expression *)0; + switch (atoi (val)) { + case 8: + buf.op = expr_extract_int8; + break; + + case 16: + buf.op = expr_extract_int16; + break; + + case 32: + buf.op = expr_extract_int32; + break; + + default: + parse_warn ("unsupported integer size %d", atoi (val)); + *lose = 1; + skip_to_semi (cfile); + return (struct expression *)0; + } + + token = next_token (&val, cfile); + if (token != RPAREN) { + parse_warn ("right parenthesis expected."); + *lose = 1; + return (struct expression *)0; + } + goto have_expr; + + case NUMBER: + buf.op = expr_const_int; + buf.data.const_int = atoi (val); + goto have_expr; + + default: + return (struct expression *)0; + } + + have_expr: + rv = (struct expression *)dmalloc (sizeof (struct expression), + "parse_boolean_expression"); + if (!rv) + error ("No memory for boolean expression."); + *rv = buf; + return rv; +} + +/* option-statement :== identifier DOT identifier SEMI + | identifier 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. */ + +struct executable_statement *parse_option_statement (cfile, lookups, + option, op) + FILE *cfile; + int lookups; + struct option *option; + enum statement_op op; +{ + char *val; + int token; + char *fmt; + struct expression *expr = (struct expression *)0; + int lose; + struct executable_statement *stmt; + + token = peek_token (&val, cfile); + if (token == SEMI) { + /* Eat the semicolon... */ + token = next_token (&val, cfile); + expr = make_const_data (0, 0, 0, 0); + goto done; + } + + /* See if there's a data expression, and if so, use it rather than + the standard format. */ + expr = ((struct expression *)parse_data_expression (cfile, &lose)); + + /* Found a data expression, but it was bogus? */ + if (lose) + return (struct executable_statement *)0; + + /* We found one. */ + if (expr) + 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'; + + for (fmt = option -> format; *fmt; fmt++) { + if (*fmt == 'A') + break; + expr = parse_option_token (cfile, fmt, + expr, uniform, lookups); + } + if (*fmt == 'A') { + token = peek_token (&val, cfile); + if (token == COMMA) { + token = next_token (&val, cfile); + continue; + } + break; + } + } while (*fmt == 'A'); + + done: + token = next_token (&val, cfile); + if (token != SEMI) { + parse_warn ("semicolon expected."); + skip_to_semi (cfile); + return (struct executable_statement *)0; + } + stmt = ((struct executable_statement *) + dmalloc (sizeof *stmt, "parse_option_statement")); + stmt -> op = op; + stmt -> data.option = option_cache (expr, option); + return stmt; +} + +struct expression *parse_option_token (cfile, fmt, expr, uniform, lookups) + FILE *cfile; + char *fmt; + struct expression *expr; + int uniform; + int lookups; +{ + char *val; + int token; + struct expression *t; + unsigned char buf [4]; + int len; + unsigned char *ob; + struct iaddr addr; + + switch (*fmt) { + case 'X': + token = peek_token (&val, cfile); + if (token == NUMBER_OR_NAME || token == NUMBER) { + ob = parse_cshl (cfile, &len); + return make_concat (expr, + make_const_data (ob, len, 0, 0)); + } else if (token == STRING) { + token = next_token (&val, cfile); + return make_concat (expr, + make_const_data ((unsigned char *) + val, + strlen (val), + 1, 1)); + } else { + parse_warn ("expecting string %s.", + "or hexadecimal data"); + skip_to_semi (cfile); + return (struct expression *)0; + } + break; + + case 't': /* Text string... */ + token = next_token (&val, cfile); + if (token != STRING && !is_identifier (token)) { + parse_warn ("expecting string."); + if (token != SEMI) + skip_to_semi (cfile); + return (struct expression *)0; + } + return make_concat (expr, + make_const_data ((unsigned char *) + val, strlen (val), 1, 1)); + break; + + case 'I': /* IP address or hostname. */ + if (lookups) + t = parse_ip_addr_or_hostname (cfile, uniform); + else { + if (!parse_ip_addr (cfile, &addr)) + return (struct expression *)0; + t = make_const_data (addr.iabuf, addr.len, 0, 1); + } + if (!t) + return (struct expression *)0; + return make_concat (expr, t); + break; + + case 'L': /* Unsigned 32-bit integer... */ + case 'l': /* Signed 32-bit integer... */ + token = next_token (&val, cfile); + if (token != NUMBER) { + need_number: + parse_warn ("expecting number."); + if (token != SEMI) + skip_to_semi (cfile); + return (struct expression *)0; + } + convert_num (buf, val, 0, 32); + return make_concat (expr, make_const_data (buf, 4, 0, 1)); + break; + case 's': /* Signed 16-bit integer. */ + case 'S': /* Unsigned 16-bit integer. */ + token = next_token (&val, cfile); + if (token != NUMBER) + goto need_number; + convert_num (buf, val, 0, 16); + return make_concat (expr, make_const_data (buf, 2, 0, 1)); + break; + case 'b': /* Signed 8-bit integer. */ + case 'B': /* Unsigned 8-bit integer. */ + token = next_token (&val, cfile); + if (token != NUMBER) + goto need_number; + convert_num (buf, val, 0, 8); + return make_concat (expr, make_const_data (buf, 1, 0, 1)); + break; + case 'f': /* Boolean flag. */ + token = next_token (&val, cfile); + if (!is_identifier (token)) { + parse_warn ("expecting identifier."); + bad_flag: + if (token != SEMI) + skip_to_semi (cfile); + return (struct expression *)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 ("expecting boolean."); + goto bad_flag; + } + return make_concat (expr, make_const_data (buf, 1, 0, 1)); + break; + default: + warn ("Bad format %c in parse_option_param.", + *fmt); + skip_to_semi (cfile); + return (struct expression *)0; + } +}