diff --git a/RELNOTES b/RELNOTES index 7c99f43d..441e5456 100644 --- a/RELNOTES +++ b/RELNOTES @@ -54,6 +54,11 @@ suggested fixes to . Changes since 4.0.0a1 +- Two new operators, ~= and ~~, have been integrated to implement + boolean matches by regular expression (such as may be used in + class matching statements). Thanks to a patch by Alexandr S. + Agranovsky, which underwent slight modification. + - Fix for icmp packets on 64-bit systems (bug introduced in 4.0). - A bug was fixed in interface discovery wherein an error identifying diff --git a/common/dhcp-eval.5 b/common/dhcp-eval.5 index 0647d27d..1bde0b2b 100644 --- a/common/dhcp-eval.5 +++ b/common/dhcp-eval.5 @@ -1,4 +1,4 @@ -.\" $Id: dhcp-eval.5,v 1.26 2007/05/19 19:16:24 dhankins Exp $ +.\" $Id: dhcp-eval.5,v 1.27 2007/06/07 15:52:29 dhankins Exp $ .\" .\" Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium @@ -114,7 +114,7 @@ treated as false in conditionals. The following is the current list of boolean expressions that are supported by the DHCP distribution. .PP -.I data-expression-1 \fB=\fI data-expression-2\fR +.I data-expression-1 \fB=\fR \fIdata-expression-2\fR .RS 0.25i .PP The \fB=\fR operator compares the values of two data expressions, @@ -123,7 +123,20 @@ either the left-hand side or the right-hand side are null, the result is also null. .RE .PP -.I boolean-expression-1 \fBand\fI boolean-expression-2\fR +.I data-expression-1 \fB~=\fR \fIdata-expression-2\fR +.I data-expression-1 \fB~~\fR \fIdata-expression-2\fR +.RS 0.25i +.PP +The \fB~=\fR and \fB~~\fR operators (not available on all systems) perform +extended regex(7) matching of the values of two data expressions, returning +true if \fIdata-expression-1\fR matches against the regular expression +evaluated by \fIdata-expression-2\fR, or false if it does not match or +encounters some error. If either the left-hand side or the right-hand side +are null, the result is also false. The \fB~~\fR operator differs from the +\fB~=\fR operator in that it is case-insensitive. +.RE +.PP +.I boolean-expression-1 \fBand\fR \fIboolean-expression-2\fR .PP .RS 0.25i The \fBand\fR operator evaluates to true if the boolean expression on @@ -133,7 +146,7 @@ the expression on the left-hand side or the expression on the right-hand side are null, the result is null. .RE .PP -.I boolean-expression-1 \fBor\fI boolean-expression-2\fR +.I boolean-expression-1 \fBor\fR \fIboolean-expression-2\fR .PP .RS 0.25i The \fBor\fR operator evaluates to true if either the boolean diff --git a/common/parse.c b/common/parse.c index 25846880..c11ce0d0 100644 --- a/common/parse.c +++ b/common/parse.c @@ -34,7 +34,7 @@ #ifndef lint static char copyright[] = -"$Id: parse.c,v 1.126 2007/05/29 18:11:55 each Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n"; +"$Id: parse.c,v 1.127 2007/06/07 15:52:29 dhankins Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -3195,6 +3195,7 @@ int parse_if_statement (result, cfile, lose) * NOT boolean-expression | * data-expression EQUAL data-expression | * data-expression BANG EQUAL data-expression | + * data-expression REGEX_MATCH data-expression | * boolean-expression AND boolean-expression | * boolean-expression OR boolean-expression * EXISTS OPTION-NAME @@ -4559,6 +4560,35 @@ int parse_expression (expr, cfile, lose, context, plhs, binop) context = expression_context (rhs); break; + case TILDE: +#ifdef HAVE_REGEX_H + token = next_token(&val, NULL, cfile); + token = peek_token(&val, NULL, cfile); + + if (token == TILDE) + next_op = expr_iregex_match; + else if (token == EQUAL) + next_op = expr_regex_match; + else { + parse_warn(cfile, "expecting ~= or ~~ operator"); + *lose = 1; + skip_to_semi(cfile); + if (lhs) + expression_dereference(&lhs, MDL); + return 0; + } + + context = expression_context(rhs); +#else + parse_warn(cfile, "No support for regex operator."); + *lose = 1; + skip_to_semi(cfile); + if (lhs != NULL) + expression_dereference(&lhs, MDL); + return 0; +#endif + break; + case AND: next_op = expr_and; context = expression_context (rhs); @@ -4685,6 +4715,23 @@ int parse_expression (expr, cfile, lose, context, plhs, binop) } break; + case expr_regex_match: +#ifdef HAVE_REGEX_H + if (expression_context(rhs) != context_data) { + parse_warn(cfile, "expecting data expression"); + skip_to_semi(cfile); + expression_dereference(&rhs, MDL); + *lose = 1; + return 0; + } +#else + /* It should not be possible to attempt to parse the right + * hand side of an operator there is no support for. + */ + log_fatal("Impossible condition at %s:%d.", MDL); +#endif + break; + case expr_and: case expr_or: if ((rhs_context != context_boolean) && diff --git a/common/print.c b/common/print.c index e9a64632..51ce2d30 100644 --- a/common/print.c +++ b/common/print.c @@ -34,7 +34,7 @@ #ifndef lint static char copyright[] = -"$Id: print.c,v 1.66 2007/05/29 18:11:55 each Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n"; +"$Id: print.c,v 1.67 2007/06/07 15:52:29 dhankins Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -517,6 +517,21 @@ static unsigned print_subexpression (expr, buf, len) } break; + case expr_regex_match: + if (len > 10) { + rv = 4; + strcpy(buf, "(regex "); + rv += print_subexpression(expr->data.equal[0], + buf + rv, len - rv - 2); + buf[rv++] = ' '; + rv += print_subexpression(expr->data.equal[1], + buf + rv, len - rv - 1); + buf[rv++] = ')'; + buf[rv] = 0; + return rv; + } + break; + case expr_substring: if (len > 11) { rv = 8; diff --git a/common/tree.c b/common/tree.c index dc3d7149..aa7cd038 100644 --- a/common/tree.c +++ b/common/tree.c @@ -34,7 +34,7 @@ #ifndef lint static char copyright[] = -"$Id: tree.c,v 1.115 2007/05/19 19:16:24 dhankins Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n"; +"$Id: tree.c,v 1.116 2007/06/07 15:52:29 dhankins Exp $ Copyright (c) 2004-2007 Internet Systems Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -42,6 +42,10 @@ static char copyright[] = #include #include +#ifdef HAVE_REGEX_H +# include +#endif + struct binding_scope *global_scope; static int do_host_lookup PROTO ((struct data_string *, @@ -968,6 +972,8 @@ int evaluate_dns_expression (result, packet, lease, client_state, in_options, case expr_check: case expr_equal: case expr_not_equal: + case expr_regex_match: + case expr_iregex_match: case expr_and: case expr_or: case expr_not: @@ -1061,6 +1067,10 @@ int evaluate_boolean_expression (result, packet, lease, client_state, int sleft, sright; struct binding *binding; struct binding_value *bv, *obv; +#ifdef HAVE_REGEX_H + int reg_st, regflags = REG_EXTENDED | REG_NOSUB; + regex_t re; +#endif switch (expr -> op) { case expr_check: @@ -1153,6 +1163,57 @@ int evaluate_boolean_expression (result, packet, lease, client_state, binding_value_dereference (&obv, MDL); return 1; + case expr_iregex_match: +#ifdef HAVE_REGEX_H + regflags |= REG_ICASE; +#endif + /* FALL THROUGH */ + case expr_regex_match: +#ifdef HAVE_REGEX_H + memset(&left, 0, sizeof left); + bleft = evaluate_data_expression(&left, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr->data.equal[0], MDL); + memset(&right, 0, sizeof right); + bright = evaluate_data_expression(&right, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr->data.equal[1], MDL); + + *result = 0; + memset(&re, 0, sizeof(re)); + if (bleft && bright && + (regcomp(&re, right.data, regflags) == 0) && + (regexec(&re, left.data, (size_t)0, NULL, 0) == 0)) + *result = 1; + +#if defined (DEBUG_EXPRESSIONS) + log_debug("bool: %s ~= %s yields %s", + bleft ? print_hex_1(left.len, left.data, 20) + : "NULL", + bright ? print_hex_2 (right.len, right.data, 20) + : "NULL", + *result ? "true" : "false"); +#endif + + if (bleft) + data_string_forget(&left, MDL); + if (bright) + data_string_forget(&right, MDL); + + regfree(&re); + return reg_st; +#else + /* It shouldn't be possible to configure a regex operator + * when there's no support. + */ + log_fatal("Impossible condition at %s:%d.", MDL); + break; +#endif + case expr_and: sleft = evaluate_boolean_expression (&bleft, packet, lease, client_state, @@ -2296,6 +2357,8 @@ int evaluate_data_expression (result, packet, lease, client_state, case expr_check: case expr_equal: case expr_not_equal: + case expr_regex_match: + case expr_iregex_match: case expr_and: case expr_or: case expr_not: @@ -2374,6 +2437,8 @@ int evaluate_numeric_expression (result, packet, lease, client_state, case expr_check: case expr_equal: case expr_not_equal: + case expr_regex_match: + case expr_iregex_match: case expr_and: case expr_or: case expr_not: @@ -2990,6 +3055,8 @@ void expression_dereference (eptr, file, line) /* All the binary operators can be handled the same way. */ case expr_equal: case expr_not_equal: + case expr_regex_match: + case expr_iregex_match: case expr_concat: case expr_and: case expr_or: @@ -3211,6 +3278,8 @@ int is_boolean_expression (expr) expr -> op == expr_variable_exists || expr -> op == expr_equal || expr -> op == expr_not_equal || + expr->op == expr_regex_match || + expr->op == expr_iregex_match || expr -> op == expr_and || expr -> op == expr_or || expr -> op == expr_not || @@ -3350,6 +3419,8 @@ static int op_val (op) case expr_equal: case expr_not_equal: + case expr_regex_match: + case expr_iregex_match: return 4; case expr_or: @@ -3443,6 +3514,8 @@ enum expression_context op_context (op) case expr_equal: case expr_not_equal: + case expr_regex_match: + case expr_iregex_match: return context_data; case expr_and: @@ -3498,6 +3571,14 @@ int write_expression (file, expr, col, indent, firstp) "\"", (char *)0); break; + case expr_regex_match: + s = "~="; + goto binary; + + case expr_iregex_match: + s = "~~"; + goto binary; + case expr_not_equal: s = "!="; goto binary; @@ -4179,6 +4260,8 @@ int data_subexpression_length (int *rv, case expr_match: case expr_check: case expr_equal: + case expr_regex_match: + case expr_iregex_match: case expr_and: case expr_or: case expr_not: diff --git a/configure.ac b/configure.ac index e6bf322e..333c39f1 100644 --- a/configure.ac +++ b/configure.ac @@ -86,9 +86,7 @@ else fi # Look for optional headers. -AC_CHECK_HEADER(net/if_dl.h, - AC_DEFINE([HAVE_IF_DL], [1], - [Define to 1 if the system has a net/if_dl.h header.])) +AC_CHECK_HEADERS(net/if_dl.h regex.h) # find an MD5 library AC_SEARCH_LIBS(MD5_Init, [crypto]) @@ -102,6 +100,9 @@ AC_SEARCH_LIBS(inet_aton, [socket nsl], , AC_DEFINE([NEED_INET_ATON], [1], [Define to 1 if the inet_aton() function is missing.])) +# Check for a standalone regex library. +AC_SEARCH_LIBS(regcomp, [regex]) + # check for /dev/random (declares HAVE_DEV_RANDOM) AC_CHECK_FILE(/dev/random, AC_DEFINE([HAVE_DEV_RANDOM], [1], diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 51ff4f71..7a62c71a 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -59,7 +59,7 @@ #include #include #include -#if HAVE_IF_DL +#if HAVE_IF_DL_H # include #endif diff --git a/includes/dhctoken.h b/includes/dhctoken.h index 7e4e1469..8bbd06ef 100644 --- a/includes/dhctoken.h +++ b/includes/dhctoken.h @@ -43,6 +43,7 @@ enum dhcp_token { LPAREN = '(', RPAREN = ')', EQUAL = '=', + TILDE = '~', BANG = '!', PERCENT = '%', PLUS = '+', diff --git a/includes/tree.h b/includes/tree.h index b357b25c..8ce66c4b 100644 --- a/includes/tree.h +++ b/includes/tree.h @@ -193,7 +193,9 @@ enum expr_op { expr_binary_xor, expr_client_state, expr_ucase, - expr_lcase + expr_lcase, + expr_regex_match, + expr_iregex_match }; struct expression {