/* $Id$ */ /*** This file is part of ivam2. ivam2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. ivam2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with ivam2; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "msntab.h" #define MAX_ENTRIES 100 #define MAX_INCLUDES 10 static struct tabentry *first = NULL; static struct tabentry *last = NULL; static int n_entries = 0; static int n_includes = 0; int regex_match(const char *re, const char *s) { regex_t r; int ret; char e[256]; if ((ret = regcomp(&r, re, REG_EXTENDED|REG_NOSUB)) != 0) { regerror(ret, &r, e, sizeof(e)); daemon_log(LOG_WARNING, "Failed to compile regular expression '%s', will never match: %s", re, e); return -1; } ret = regexec(&r, s, 0, NULL, 0) == 0 ? 0 : -1; regfree(&r); return ret; } int glob_match(const char *e, const char *s) { char p[256]; const char *n = e; for (;;) { size_t m, i = strcspn(n, ";,"); m = sizeof(p)-1 < i ? sizeof(p)-1 : i; strncpy(p, n, m); p[m] = 0; if (!fnmatch(p, s, 0)) return 0; if (*(n+i) == 0) return -1; n += i+1; } } struct tabentry* msntab_check_call(const char *callee, const char *caller) { struct tabentry *l = first; while (l) { int a, b; assert(l->local && l->remote); if (l->local[0] == '~') a = !regex_match(l->local+1, callee); else a = !glob_match(l->local, callee); if (l->remote[0] == '~') b = !regex_match(l->remote+1, caller); else b = !glob_match(l->remote, caller); if (a && b) { daemon_log(LOG_INFO, "MSN table entry from '%s:%u' matched.", l->filename, l->line); return msntab_ref(l); } l = l->next; } return NULL; } struct tabentry* msntab_ref(struct tabentry *t) { assert(t && t->ref_counter >= 1); t->ref_counter++; return t; } void msntab_unref(struct tabentry *t) { assert(t && t->ref_counter >= 1); t->ref_counter--; if (t->ref_counter == 0) { if (t->args) { char **a = t->args; while (*a) { free(*a); a++; } free(t->args); } free(t->local); free(t->remote); free(t->filename); free(t); } } void msntab_flush(void) { while (first) { struct tabentry *l = first; first = first->next; if (first) first->prev = NULL; if (last == l) last = NULL; msntab_unref(l); } n_entries = 0; n_includes++; } #define MAX_ARGS 16 static char** parse_args(const char *s) { char *o, **a; char *c = (char*) s; int i = 0; a = malloc(sizeof(char *)*MAX_ARGS); memset(a, 0, sizeof(char *)*MAX_ARGS); while ((o = strsep(&c, " \t"))) { a[i++] = strdup(o); if (i >= MAX_ARGS-1) break; } return a; } static int parse_options(const char *s, struct tabentry *t) { char *o; char *c = (char*) s; assert(s && t); while ((o = strsep(&c, ","))) { if (!strcmp(o, "defaults")) continue; else if (!strcmp(o, "shbuf")) { t->shbuf = 1; continue; } else if (!strcmp(o, "pipehack")) { t->pipehack = 1; continue; } else if (!strncmp(o, "rings=", 6)) { t->rings = atoi(o+6); continue; } daemon_log(LOG_INFO, "Unknown option '%s'", o); return -1; } return 0; } int msntab_load(const char *fn) { int n; struct tabentry *t = NULL; FILE *f = NULL; daemon_log(LOG_INFO, "Loading MSN table '%s'.", fn); if (!(f = fopen(fn, "r"))) { daemon_log(LOG_ERR, "Failed to open MSN table '%s'.", fn); goto fail; } n = 0; while (!feof(f)) { char l[256], *c, *e, *local, *remote, *options, *action; n++; if (!fgets(l, sizeof(l), f)) break; c = l+strspn(l, " \t"); if ((e = strchr(c, '\r'))) *e = 0; if ((e = strchr(c, '\n'))) *e = 0; e = strchr(c, 0); if (*c == '#' || *c == 0) continue; if (!(local = strsep(&c, " \t"))) { daemon_log(LOG_ERR, "Parse failure on local MSN field in '%s:%i'.", fn, n); goto fail; } if (c) c+=strspn(c, " \t"); if (!strcmp(local, "@include")) { char *include; if (n_includes ++ >= MAX_INCLUDES) { daemon_log(LOG_ERR, "Recursive include directive detected."); goto fail; } if (!(include = strsep(&c, ""))) { daemon_log(LOG_ERR, "Parse failure on include field in '%s:%i'.", fn, n); goto fail; } if (msntab_load(include) < 0) goto fail; continue; } if (!(remote = strsep(&c, " \t"))) { daemon_log(LOG_ERR, "Parse failure on remote MSN field in '%s:%i'.", fn, n); goto fail; } if (c) c+=strspn(c, " \t"); if (!(options = strsep(&c, " \t"))) { daemon_log(LOG_ERR, "Parse failure on options field in '%s:%i'.", fn, n); goto fail; } if (c) c+=strspn(c, " \t"); if (!(action = strsep(&c, ""))) { daemon_log(LOG_ERR, "Parse failure on action field in '%s:%i'.", fn, n); goto fail; } t = malloc(sizeof(struct tabentry)); assert(t); memset(t, 0, sizeof(struct tabentry)); t->line = n; t->filename = strdup(fn); t->ref_counter = 1; t->local = strdup(local); assert(t->local); t->remote = strdup(remote); assert(t->remote); if (action[0] == '@') { if (!strcmp(action, "@hangup")) t->action = CALL_ACTION_HANGUP; else if (!strcmp(action, "@ignore")) t->action = CALL_ACTION_IGNORE; else { daemon_log(LOG_ERR, "Unknown action command '%s' in '%s:%i'.", action, fn, n); goto fail; } } else { t->action = CALL_ACTION_ACCEPT; t->args = parse_args(action); } if (parse_options(options, t) < 0) { daemon_log(LOG_ERR, "Parse failure on options field in '%s:%i'.", fn, n); goto fail; } if (last) { t->prev = last; last->next = t; last = t; } else last = first = t; n_entries ++; t = NULL; if (n_entries > MAX_ENTRIES) { daemon_log(LOG_INFO, "Too many MSN table entries"); goto fail; } } fclose(f); daemon_log(LOG_INFO, "MSN table '%s' successfully read.", fn); return 0; fail: if (t) msntab_unref(t); if (f) fclose(f); return -1; } static void dump_entry(struct tabentry *t) { char s[256]; assert(t); s[0] = 0; if (t->args) { char **a = t->args; while (*a) { char *p = strchr(s, 0); snprintf(p, sizeof(s)-(p-s), a == t->args ? "%s" : " %s", *a); a++; } } else strncpy(s, "NOARGS", sizeof(s)); daemon_log(LOG_INFO, "[%s:%02u] %-12s -> %-12s; shbuf=%c; pipehack=%c; rings=%u; action=%s; args=<%s>", t->filename, t->line, t->local, t->remote, t->shbuf ? 'y' : 'n', t->pipehack ? 'y' : 'n', t->rings, t->action == CALL_ACTION_ACCEPT ? "ACCEPT" : (t->action == CALL_ACTION_HANGUP ? "HANGUP" : "IGNORE"), s); } void msntab_dump(void) { struct tabentry *l; daemon_log(LOG_INFO, "=== Dumping MSN table ==="); l = first; while (l) { dump_entry(l); l = l->next; } daemon_log(LOG_INFO, "=== MSN table dump complete ==="); }