/* $Id: eval.c,v 1.3 2019/01/28 17:12:52 dhartmei Exp $ */ /* * Copyright (c) 2004-2019 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.3 2019/01/28 17:12:52 dhartmei Exp $"; #include #include #include #include #include "eval.h" #ifndef REG_BASIC #define REG_BASIC 0 #endif extern int yyerror(const 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; struct action_list *al; 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); for (al = rs->action; al != NULL; al = al->next) res[al->action->idx] = VAL_UNDEF; 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 && val == VAL_TRUE) 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(); }