/* $Id: eval.c,v 1.1.1.1 2007/01/11 15:49:52 dhartmei Exp $ */
/*
* Copyright (c) 2004-2006 Daniel Hartmeier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
static const char rcsid[] = "$Id: eval.c,v 1.1.1.1 2007/01/11 15:49:52 dhartmei Exp $";
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include "eval.h"
#ifndef REG_BASIC
#define REG_BASIC 0
#endif
extern int yyerror(char *, ...);
extern void die(const char *);
static void mutex_lock(void);
static void mutex_unlock(void);
static int check_cond(struct cond *, const char *, const char *);
static void push_expr_result(struct expr *, int, int *);
static void push_cond_result(struct cond *, int, int *);
static int build_regex(struct cond_arg *);
static void free_expr_list(struct expr_list *, struct expr *);
static pthread_mutex_t mutex;
static struct action default_action;
int
eval_init(int type)
{
memset(&default_action, 0, sizeof(default_action));
default_action.type = type;
if (pthread_mutex_init(&mutex, 0))
return (1);
else
return (0);
}
static void
mutex_lock(void)
{
if (pthread_mutex_lock(&mutex))
die("pthread_mutex_lock");
}
static void
mutex_unlock(void)
{
if (pthread_mutex_unlock(&mutex))
die("pthread_mutex_unlock");
}
struct ruleset *
create_ruleset(void)
{
struct ruleset *rs;
rs = calloc(1, sizeof(struct ruleset));
if (rs == NULL) {
yyerror("create_ruleset: calloc: %s", strerror(errno));
return (NULL);
}
return (rs);
}
struct expr *
create_cond(struct ruleset *rs, int type, const char *a, const char *b)
{
struct cond *c = NULL;
struct cond_list *cl = NULL;
struct expr *e = NULL;
struct expr_list *elc = NULL;
mutex_lock();
e = calloc(1, sizeof(struct expr));
if (e == NULL)
goto error;
elc = calloc(1, sizeof(struct expr_list));
if (elc == NULL)
goto error;
for (cl = rs->cond[type]; cl != NULL; cl = cl->next) {
if ((cl->cond->args[0].src == NULL) != (a == NULL) ||
(cl->cond->args[1].src == NULL) != (b == NULL) ||
(a != NULL && strcmp(a, cl->cond->args[0].src)) ||
(b != NULL && strcmp(b, cl->cond->args[1].src)))
continue;
break;
}
if (cl != NULL)
c = cl->cond;
else {
cl = calloc(1, sizeof(struct cond_list));
if (cl == NULL)
goto error;
c = calloc(1, sizeof(struct cond));
if (c == NULL)
goto error;
if (a != NULL) {
c->args[0].src = strdup(a);
if (c->args[0].src == NULL)
goto error;
if (build_regex(&c->args[0]))
goto error;
}
if (b != NULL) {
c->args[1].src = strdup(b);
if (c->args[1].src == NULL)
goto error;
if (build_regex(&c->args[1]))
goto error;
}
c->idx = rs->maxidx++;
cl->cond = c;
cl->next = rs->cond[type];
rs->cond[type] = cl;
}
e->type = EXPR_COND;
e->cond = c;
e->idx = rs->maxidx++;
elc->expr = e;
elc->next = c->expr;
c->expr = elc;
mutex_unlock();
return (e);
error:
if (elc != NULL)
free(elc);
if (e != NULL)
free(e);
if (cl != NULL)
free(cl);
if (c != NULL) {
if (!c->args[1].empty)
regfree(&c->args[1].re);
if (c->args[1].src != NULL)
free(c->args[1].src);
if (!c->args[0].empty)
regfree(&c->args[0].re);
if (c->args[0].src != NULL)
free(c->args[0].src);
free(c);
}
mutex_unlock();
return (NULL);
}
struct expr *
create_expr(struct ruleset *rs, int type, struct expr *a, struct expr *b)
{
struct expr *e = NULL;
struct expr_list *ela = NULL, *elb = NULL;
mutex_lock();
e = calloc(1, sizeof(struct expr));
if (e == NULL)
goto error;
if (a != NULL) {
ela = calloc(1, sizeof(struct expr_list));
if (ela == NULL)
goto error;
}
if (b != NULL) {
elb = calloc(1, sizeof(struct expr_list));
if (elb == NULL)
goto error;
}
e->type = type;
e->idx = rs->maxidx++;
if (a != NULL) {
e->args[0] = a;
ela->expr = e;
ela->next = a->expr;
a->expr = ela;
}
if (b != NULL) {
e->args[1] = b;
elb->expr = e;
elb->next = b->expr;
b->expr = elb;
}
mutex_unlock();
return (e);
error:
yyerror("create_expr: calloc: %s", strerror(errno));
if (elb != NULL)
free(elb);
if (ela != NULL)
free(ela);
if (e != NULL)
free(e);
mutex_unlock();
return (NULL);
}
struct action *
create_action(struct ruleset *rs, int type, const char *msg)
{
struct action *a = NULL;
struct action_list *al = NULL;
mutex_lock();
a = calloc(1, sizeof(struct action));
if (a == NULL)
goto error;
al = calloc(1, sizeof(struct action_list));
if (al == NULL)
goto error;
a->type = type;
a->msg = msg == NULL ? NULL : strdup(msg);
a->idx = rs->maxidx++;
al->action = a;
/* tail insert, so actions have same order as file */
if (rs->action == NULL)
rs->action = al;
else {
struct action_list *t = rs->action;
while (t->next != NULL)
t = t->next;
t->next = al;
}
mutex_unlock();
return (a);
error:
yyerror("create_action: calloc: %s", strerror(errno));
if (al != NULL)
free(al);
if (a != NULL)
free(a);
mutex_unlock();
return (NULL);
}
struct action *
eval_cond(struct ruleset *rs, int *res, int type,
const char *a, const char *b)
{
struct cond_list *cl;
struct action_list *al;
mutex_lock();
for (cl = rs->cond[type]; cl != NULL; cl = cl->next) {
int r;
if (res[cl->cond->idx] != VAL_UNDEF)
continue;
r = check_cond(cl->cond, a, b);
if (r < 0) {
mutex_unlock();
return (NULL);
} else if (!r)
push_cond_result(cl->cond, VAL_TRUE, res);
}
for (al = rs->action; al != NULL; al = al->next)
if (res[al->action->idx] == VAL_TRUE) {
mutex_unlock();
return (al->action);
}
mutex_unlock();
return (NULL);
}
struct action *
eval_end(struct ruleset *rs, int *res, int type, int max)
{
struct cond_list *cl;
struct action_list *al;
mutex_lock();
for (cl = rs->cond[type]; cl != NULL; cl = cl->next)
if (res[cl->cond->idx] == VAL_UNDEF)
push_cond_result(cl->cond, VAL_FALSE, res);
for (al = rs->action; al != NULL; al = al->next)
if (res[al->action->idx] == VAL_TRUE) {
mutex_unlock();
return (al->action);
}
for (type = max; type < COND_MAX; ++type)
for (cl = rs->cond[type]; cl != NULL; cl = cl->next)
if (res[cl->cond->idx] == VAL_UNDEF) {
mutex_unlock();
return (NULL);
}
mutex_unlock();
return (&default_action);
}
void
eval_clear(struct ruleset *rs, int *res, int type)
{
struct cond_list *cl;
mutex_lock();
for (; type < COND_MAX; ++type)
for (cl = rs->cond[type]; cl != NULL; cl = cl->next)
push_cond_result(cl->cond, VAL_UNDEF, res);
mutex_unlock();
}
static int
check_cond(struct cond *c, const char *a, const char *b)
{
int i;
for (i = 0; i < 2; ++i) {
const char *d = i ? b : a;
int r;
if (c->args[i].src == NULL || c->args[i].empty)
continue;
if (d == NULL)
return (-1);
r = regexec(&c->args[i].re, d, 0, NULL, 0);
if (r && r != REG_NOMATCH)
return (-1);
if ((r == REG_NOMATCH) != c->args[i].not)
return (1);
}
return (0);
}
static void
push_expr_result(struct expr *e, int val, int *res)
{
struct expr_list *el;
if (res[e->idx] == val)
return;
res[e->idx] = val;
if (e->action != NULL)
res[e->action->idx] = val;
for (el = e->expr; el != NULL; el = el->next) {
struct expr *p = el->expr;
switch (p->type) {
case EXPR_AND:
if (res[p->args[0]->idx] == VAL_TRUE &&
res[p->args[1]->idx] == VAL_TRUE)
push_expr_result(p, VAL_TRUE, res);
else if (res[p->args[0]->idx] == VAL_FALSE ||
res[p->args[1]->idx] == VAL_FALSE)
push_expr_result(p, VAL_FALSE, res);
else
push_expr_result(p, VAL_UNDEF, res);
break;
case EXPR_OR:
if (res[p->args[0]->idx] == VAL_TRUE ||
res[p->args[1]->idx] == VAL_TRUE)
push_expr_result(p, VAL_TRUE, res);
else if (res[p->args[0]->idx] == VAL_FALSE &&
res[p->args[1]->idx] == VAL_FALSE)
push_expr_result(p, VAL_FALSE, res);
else
push_expr_result(p, VAL_UNDEF, res);
break;
case EXPR_NOT:
if (val == VAL_TRUE)
push_expr_result(p, VAL_FALSE, res);
else if (val == VAL_FALSE)
push_expr_result(p, VAL_TRUE, res);
else
push_expr_result(p, VAL_UNDEF, res);
break;
default:
break;
}
}
}
static void
push_cond_result(struct cond *c, int val, int *res)
{
struct expr_list *el;
if (res[c->idx] == val)
return;
res[c->idx] = val;
for (el = c->expr; el != NULL; el = el->next) {
struct expr *e = el->expr;
if (e->type != EXPR_COND)
continue;
push_expr_result(e, val, res);
}
}
static int
build_regex(struct cond_arg *a)
{
char del;
const char *s = a->src, *t;
a->empty = 1;
a->not = 0;
while (*s == ' ' || *s == '\t')
s++;
if (!*s) {
yyerror("build_regex: empty argument");
return (1);
}
del = *s++;
t = s;
while (*s && *s != del)
s++;
if (!*s) {
yyerror("build_regex: missing closing delimiter %s", a->src);
return (1);
}
if (s == t) {
if (s[1]) {
yyerror("build_regex: empty expression with flags %s",
a->src);
return (1);
}
} else {
char *u;
int flags = 0, r;
u = malloc(s - t + 1);
if (u == NULL) {
yyerror("build_regex: malloc: %s", strerror(errno));
return (1);
}
memcpy(u, t, s - t);
u[s - t] = 0;
s++;
while (*s) {
switch (*s) {
case 'e':
flags |= REG_EXTENDED;
break;
case 'i':
flags |= REG_ICASE;
break;
case 'n':
a->not = 1;
break;
default:
yyerror("invalid flag %c in %s", *s, a->src);
free(u);
return (1);
}
++s;
}
if (!(flags & REG_EXTENDED))
flags |= REG_BASIC;
r = regcomp(&a->re, u, flags);
if (r) {
char e[8192];
regerror(r, &a->re, e, sizeof(e));
yyerror("regcomp: %s: %s\n", u, e);
free(u);
return (1);
}
free(u);
a->empty = 0;
}
return (0);
}
static void
free_expr_list(struct expr_list *el, struct expr *a)
{
struct expr_list *eln;
while (el != NULL) {
struct expr *e = el->expr;
eln = el->next;
if (e != NULL) {
int i, used = 0;
for (i = 0; i < 2; ++i)
if (e->args[i] != NULL) {
if (e->args[i] == a)
e->args[i] = NULL;
else
used = 1;
}
if (!used) {
free_expr_list(e->expr, e);
free(e);
}
}
free(el);
el = eln;
}
}
void
free_ruleset(struct ruleset *rs)
{
int i;
struct action_list *al, *aln;
mutex_lock();
if (rs == NULL || rs->refcnt) {
mutex_unlock();
return;
}
for (i = 0; i < COND_MAX; ++i) {
struct cond_list *cl = rs->cond[i], *cln;
while (cl != NULL) {
struct cond *c = cl->cond;
cln = cl->next;
if (c != NULL) {
int j;
for (j = 0; j < 2; ++j)
if (c->args[j].src != NULL) {
free(c->args[j].src);
if (!c->args[j].empty)
regfree(&c->args[j].re);
}
free_expr_list(c->expr, NULL);
free(c);
}
free(cl);
cl = cln;
}
}
al = rs->action;
while (al != NULL) {
struct action *a = al->action;
aln = al->next;
if (a != NULL) {
if (a->msg != NULL)
free(a->msg);
free(a);
}
free(al);
al = aln;
}
free(rs);
mutex_unlock();
}
syntax highlighted by Code2HTML, v. 0.9.1