2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-29 13:28:14 +00:00

- 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.  [ISC-Bugs #8155]
This commit is contained in:
David Hankins 2007-06-07 15:52:30 +00:00
parent a546f2a714
commit dd3282251e
9 changed files with 179 additions and 12 deletions

View File

@ -54,6 +54,11 @@ suggested fixes to <dhcp-users@isc.org>.
Changes since 4.0.0a1 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). - Fix for icmp packets on 64-bit systems (bug introduced in 4.0).
- A bug was fixed in interface discovery wherein an error identifying - A bug was fixed in interface discovery wherein an error identifying

View File

@ -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) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1996-2003 by Internet Software Consortium .\" 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 The following is the current list of boolean expressions that are
supported by the DHCP distribution. supported by the DHCP distribution.
.PP .PP
.I data-expression-1 \fB=\fI data-expression-2\fR .I data-expression-1 \fB=\fR \fIdata-expression-2\fR
.RS 0.25i .RS 0.25i
.PP .PP
The \fB=\fR operator compares the values of two data expressions, 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. result is also null.
.RE .RE
.PP .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 .PP
.RS 0.25i .RS 0.25i
The \fBand\fR operator evaluates to true if the boolean expression on 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. right-hand side are null, the result is null.
.RE .RE
.PP .PP
.I boolean-expression-1 \fBor\fI boolean-expression-2\fR .I boolean-expression-1 \fBor\fR \fIboolean-expression-2\fR
.PP .PP
.RS 0.25i .RS 0.25i
The \fBor\fR operator evaluates to true if either the boolean The \fBor\fR operator evaluates to true if either the boolean

View File

@ -34,7 +34,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@ -3195,6 +3195,7 @@ int parse_if_statement (result, cfile, lose)
* NOT boolean-expression | * NOT boolean-expression |
* data-expression EQUAL data-expression | * data-expression EQUAL data-expression |
* data-expression BANG EQUAL data-expression | * data-expression BANG EQUAL data-expression |
* data-expression REGEX_MATCH data-expression |
* boolean-expression AND boolean-expression | * boolean-expression AND boolean-expression |
* boolean-expression OR boolean-expression * boolean-expression OR boolean-expression
* EXISTS OPTION-NAME * EXISTS OPTION-NAME
@ -4559,6 +4560,35 @@ int parse_expression (expr, cfile, lose, context, plhs, binop)
context = expression_context (rhs); context = expression_context (rhs);
break; 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: case AND:
next_op = expr_and; next_op = expr_and;
context = expression_context (rhs); context = expression_context (rhs);
@ -4685,6 +4715,23 @@ int parse_expression (expr, cfile, lose, context, plhs, binop)
} }
break; 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_and:
case expr_or: case expr_or:
if ((rhs_context != context_boolean) && if ((rhs_context != context_boolean) &&

View File

@ -34,7 +34,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@ -517,6 +517,21 @@ static unsigned print_subexpression (expr, buf, len)
} }
break; 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: case expr_substring:
if (len > 11) { if (len > 11) {
rv = 8; rv = 8;

View File

@ -34,7 +34,7 @@
#ifndef lint #ifndef lint
static char copyright[] = 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 */ #endif /* not lint */
#include "dhcpd.h" #include "dhcpd.h"
@ -42,6 +42,10 @@ static char copyright[] =
#include <ctype.h> #include <ctype.h>
#include <sys/wait.h> #include <sys/wait.h>
#ifdef HAVE_REGEX_H
# include <regex.h>
#endif
struct binding_scope *global_scope; struct binding_scope *global_scope;
static int do_host_lookup PROTO ((struct data_string *, 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_check:
case expr_equal: case expr_equal:
case expr_not_equal: case expr_not_equal:
case expr_regex_match:
case expr_iregex_match:
case expr_and: case expr_and:
case expr_or: case expr_or:
case expr_not: case expr_not:
@ -1061,6 +1067,10 @@ int evaluate_boolean_expression (result, packet, lease, client_state,
int sleft, sright; int sleft, sright;
struct binding *binding; struct binding *binding;
struct binding_value *bv, *obv; struct binding_value *bv, *obv;
#ifdef HAVE_REGEX_H
int reg_st, regflags = REG_EXTENDED | REG_NOSUB;
regex_t re;
#endif
switch (expr -> op) { switch (expr -> op) {
case expr_check: case expr_check:
@ -1153,6 +1163,57 @@ int evaluate_boolean_expression (result, packet, lease, client_state,
binding_value_dereference (&obv, MDL); binding_value_dereference (&obv, MDL);
return 1; 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: case expr_and:
sleft = evaluate_boolean_expression (&bleft, packet, lease, sleft = evaluate_boolean_expression (&bleft, packet, lease,
client_state, client_state,
@ -2296,6 +2357,8 @@ int evaluate_data_expression (result, packet, lease, client_state,
case expr_check: case expr_check:
case expr_equal: case expr_equal:
case expr_not_equal: case expr_not_equal:
case expr_regex_match:
case expr_iregex_match:
case expr_and: case expr_and:
case expr_or: case expr_or:
case expr_not: case expr_not:
@ -2374,6 +2437,8 @@ int evaluate_numeric_expression (result, packet, lease, client_state,
case expr_check: case expr_check:
case expr_equal: case expr_equal:
case expr_not_equal: case expr_not_equal:
case expr_regex_match:
case expr_iregex_match:
case expr_and: case expr_and:
case expr_or: case expr_or:
case expr_not: case expr_not:
@ -2990,6 +3055,8 @@ void expression_dereference (eptr, file, line)
/* All the binary operators can be handled the same way. */ /* All the binary operators can be handled the same way. */
case expr_equal: case expr_equal:
case expr_not_equal: case expr_not_equal:
case expr_regex_match:
case expr_iregex_match:
case expr_concat: case expr_concat:
case expr_and: case expr_and:
case expr_or: case expr_or:
@ -3211,6 +3278,8 @@ int is_boolean_expression (expr)
expr -> op == expr_variable_exists || expr -> op == expr_variable_exists ||
expr -> op == expr_equal || expr -> op == expr_equal ||
expr -> op == expr_not_equal || expr -> op == expr_not_equal ||
expr->op == expr_regex_match ||
expr->op == expr_iregex_match ||
expr -> op == expr_and || expr -> op == expr_and ||
expr -> op == expr_or || expr -> op == expr_or ||
expr -> op == expr_not || expr -> op == expr_not ||
@ -3350,6 +3419,8 @@ static int op_val (op)
case expr_equal: case expr_equal:
case expr_not_equal: case expr_not_equal:
case expr_regex_match:
case expr_iregex_match:
return 4; return 4;
case expr_or: case expr_or:
@ -3443,6 +3514,8 @@ enum expression_context op_context (op)
case expr_equal: case expr_equal:
case expr_not_equal: case expr_not_equal:
case expr_regex_match:
case expr_iregex_match:
return context_data; return context_data;
case expr_and: case expr_and:
@ -3498,6 +3571,14 @@ int write_expression (file, expr, col, indent, firstp)
"\"", (char *)0); "\"", (char *)0);
break; break;
case expr_regex_match:
s = "~=";
goto binary;
case expr_iregex_match:
s = "~~";
goto binary;
case expr_not_equal: case expr_not_equal:
s = "!="; s = "!=";
goto binary; goto binary;
@ -4179,6 +4260,8 @@ int data_subexpression_length (int *rv,
case expr_match: case expr_match:
case expr_check: case expr_check:
case expr_equal: case expr_equal:
case expr_regex_match:
case expr_iregex_match:
case expr_and: case expr_and:
case expr_or: case expr_or:
case expr_not: case expr_not:

View File

@ -86,9 +86,7 @@ else
fi fi
# Look for optional headers. # Look for optional headers.
AC_CHECK_HEADER(net/if_dl.h, AC_CHECK_HEADERS(net/if_dl.h regex.h)
AC_DEFINE([HAVE_IF_DL], [1],
[Define to 1 if the system has a net/if_dl.h header.]))
# find an MD5 library # find an MD5 library
AC_SEARCH_LIBS(MD5_Init, [crypto]) AC_SEARCH_LIBS(MD5_Init, [crypto])
@ -102,6 +100,9 @@ AC_SEARCH_LIBS(inet_aton, [socket nsl], ,
AC_DEFINE([NEED_INET_ATON], [1], AC_DEFINE([NEED_INET_ATON], [1],
[Define to 1 if the inet_aton() function is missing.])) [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) # check for /dev/random (declares HAVE_DEV_RANDOM)
AC_CHECK_FILE(/dev/random, AC_CHECK_FILE(/dev/random,
AC_DEFINE([HAVE_DEV_RANDOM], [1], AC_DEFINE([HAVE_DEV_RANDOM], [1],

View File

@ -59,7 +59,7 @@
#include <net/if.h> #include <net/if.h>
#include <net/route.h> #include <net/route.h>
#include <net/if_arp.h> #include <net/if_arp.h>
#if HAVE_IF_DL #if HAVE_IF_DL_H
# include <net/if_dl.h> # include <net/if_dl.h>
#endif #endif

View File

@ -43,6 +43,7 @@ enum dhcp_token {
LPAREN = '(', LPAREN = '(',
RPAREN = ')', RPAREN = ')',
EQUAL = '=', EQUAL = '=',
TILDE = '~',
BANG = '!', BANG = '!',
PERCENT = '%', PERCENT = '%',
PLUS = '+', PLUS = '+',

View File

@ -193,7 +193,9 @@ enum expr_op {
expr_binary_xor, expr_binary_xor,
expr_client_state, expr_client_state,
expr_ucase, expr_ucase,
expr_lcase expr_lcase,
expr_regex_match,
expr_iregex_match
}; };
struct expression { struct expression {