/* Copyright 2016 Alex 'AdUser' Z (ad_user@runbox.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include "common.h" #include "config.h" #include "log.h" #include "mod-defs.h" #include "filter.h" #include #define REGEX_LINE_MAX 256 #define HOST_TOKEN "" #define FILTER_LIBRARY_PARAM "load" static size_t f2b_filter_load_file(f2b_filter_t *filter, const char *path) { f2b_config_param_t *param; FILE *f = NULL; size_t linenum = 0; size_t loaded = 0; char line[REGEX_LINE_MAX] = ""; char *p, *q; assert(filter != NULL); assert(path != NULL); if ((f = fopen(path, "r")) == NULL) { f2b_log_msg(log_error, "can't open regex list '%s': %s", path, strerror(errno)); return 0; } filter->flush(filter->cfg); while (1) { p = fgets(line, sizeof(line), f); if (!p && (feof(f) || ferror(f))) break; linenum++; /* strip leading spaces */ while (isblank(*p)) p++; /* strip trailing spaces */ if ((q = strchr(p, '\r')) || (q = strchr(p, '\n'))) { while(q > p && isspace(*q)) { *q = '\0'; q--; } } switch(*p) { case '\r': case '\n': case '\0': /* empty line */ break; case ';': case '#': if ((p = strstr(p, "set: ")) != NULL) { /* inline config line */ if ((param = f2b_config_param_create(p + 5)) != NULL) { filter->config(filter->cfg, param->name, param->value); free(param); } } /* else: just comment line */ break; default: if (strstr(p, HOST_TOKEN) == NULL) { f2b_log_msg(log_warn, "pattern at %s:%zu don't have '%s' marker, ignored", path, linenum, HOST_TOKEN); continue; } if (!filter->append(filter->cfg, p)) { f2b_log_msg(log_warn, "can't create regex from pattern at %s:%zu: %s", path, linenum, p); continue; } loaded++; break; } } fclose(f); return loaded; } f2b_filter_t * f2b_filter_create(const char *name, const char *init) { f2b_filter_t *filter = NULL; if ((filter = calloc(1, sizeof(f2b_filter_t))) == NULL) return NULL; strlcpy(filter->name, name, sizeof(filter->name)); strlcpy(filter->init, init, sizeof(filter->init)); return filter; } bool f2b_filter_init(f2b_filter_t *filter, f2b_config_section_t *config) { f2b_config_param_t *param = NULL; int flags = RTLD_NOW | RTLD_LOCAL; const char *dlerr = NULL; assert(config != NULL); assert(config->type == t_filter); param = f2b_config_param_find(config->param, FILTER_LIBRARY_PARAM); if (!param) { f2b_log_msg(log_error, "can't find '%s' param in filter config", FILTER_LIBRARY_PARAM); return false; } if ((filter->h = dlopen(param->value, flags)) == NULL) goto cleanup; if ((*(void **) (&filter->create) = dlsym(filter->h, "create")) == NULL) goto cleanup; if ((*(void **) (&filter->config) = dlsym(filter->h, "config")) == NULL) goto cleanup; if ((*(void **) (&filter->append) = dlsym(filter->h, "append")) == NULL) goto cleanup; if ((*(void **) (&filter->logcb) = dlsym(filter->h, "logcb")) == NULL) goto cleanup; if ((*(void **) (&filter->state) = dlsym(filter->h, "state")) == NULL) goto cleanup; if ((*(void **) (&filter->flush) = dlsym(filter->h, "flush")) == NULL) goto cleanup; if ((*(void **) (&filter->stats) = dlsym(filter->h, "stats")) == NULL) goto cleanup; if ((*(void **) (&filter->match) = dlsym(filter->h, "match")) == NULL) goto cleanup; if ((*(void **) (&filter->destroy) = dlsym(filter->h, "destroy")) == NULL) goto cleanup; if ((filter->cfg = filter->create("")) == NULL) { f2b_log_msg(log_error, "filter create config failed"); goto cleanup; } if ((filter->state(filter->cfg) & MOD_TYPE_FILTER) == 0) { f2b_log_msg(log_error, "loaded module is not filter type"); goto cleanup; } filter->logcb(filter->cfg, f2b_log_mod_cb); /* try init */ for (param = config->param; param != NULL; param = param->next) { if (strcmp(param->name, FILTER_LIBRARY_PARAM) == 0) continue; if (filter->config(filter->cfg, param->name, param->value)) continue; f2b_log_msg(log_warn, "param pair not accepted by filter '%s': %s=%s", config->name, param->name, param->value); } if (!f2b_filter_load_file(filter, filter->init)) goto cleanup; if ((filter->flags = filter->state(filter->cfg)) < 0) { f2b_log_msg(log_error, "can't get module state"); goto cleanup; } if (filter->flags & MOD_WRONG_API) { f2b_log_msg(log_error, "module reports wrong api version"); goto cleanup; } if (filter->flags & MOD_IS_READY) return true; /* still not ready */ f2b_log_msg(log_error, "filter '%s' not fully configured", config->name); cleanup: dlerr = dlerror(); if (dlerr) f2b_log_msg(log_error, "filter load error: %s", dlerr); if (filter->h) { if (filter->cfg && filter->destroy) { filter->destroy(filter->cfg); filter->cfg = NULL; } dlclose(filter->h); filter->create = NULL; filter->config = NULL; filter->append = NULL; filter->logcb = NULL; filter->state = NULL; filter->flush = NULL; filter->stats = NULL; filter->match = NULL; filter->destroy = NULL; filter->h = NULL; } return false; } void f2b_filter_destroy(f2b_filter_t *filter) { if (!filter) return; if (filter->h) { if (filter->cfg) filter->destroy(filter->cfg); dlclose(filter->h); } free(filter); } bool f2b_filter_append(f2b_filter_t *filter, const char *pattern) { assert(filter != NULL); assert(pattern != NULL); return filter->append(filter->cfg, pattern); } uint32_t f2b_filter_match(f2b_filter_t *filter, const char *line, char *buf, size_t bufsize, short int *score) { assert(filter != NULL); assert(line != NULL); return filter->match(filter->cfg, line, buf, bufsize, score); } void f2b_filter_cmd_stats(char *buf, size_t bufsize, f2b_filter_t *filter) { assert(filter != NULL); assert(buf != NULL); filter->stats(filter->cfg, buf, bufsize); } void f2b_filter_cmd_reload(char *buf, size_t bufsize, f2b_filter_t *filter) { size_t c = 0; assert(buf != NULL); assert(filter != NULL); filter->flush(filter->cfg); if ((c = f2b_filter_load_file(filter, filter->init)) > 0) { snprintf(buf, bufsize, "+loaded %zu regexps\n", c); } else { snprintf(buf, bufsize, "-can't reload filter\n"); } }