userblacklist.c

Go to the documentation of this file.
00001 /*
00002  * $Id: userblacklist.c 5227 2008-11-19 13:16:15Z henningw $
00003  *
00004  * Copyright (C) 2007 1&1 Internet AG
00005  *
00006  * This file is part of Kamailio, a free SIP server.
00007  *
00008  * Kamailio is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version
00012  *
00013  * Kamailio is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License 
00019  * along with this program; if not, write to the Free Software 
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  */
00022 
00023 /*!
00024  * \file
00025  * \brief USERBLACKLIST :: module definitions
00026  * \ingroup userblacklist
00027  * - Module: \ref userblacklist
00028  */
00029 
00030 /*!
00031  * \defgroup userblacklist USERBLACKLIST :: The Kamailio userblacklist Module
00032  *
00033  * The userblacklist module allows Kamailio to handle blacklists on a per user basis.
00034  * This information is stored in a database table, which is queried to decide if the
00035  * number (more exactly, the request URI user) is blacklisted or not.
00036  * An additional functionality that this module provides is the ability to handle
00037  * global blacklists. This lists are loaded on startup into memory, thus providing a
00038  * better performance then in the userblacklist case.
00039  */
00040 
00041 #include <string.h>
00042 
00043 #include "../../parser/parse_uri.h"
00044 #include "../../mem/shm_mem.h"
00045 #include "../../sr_module.h"
00046 #include "../../mi/mi.h"
00047 #include "../../mem/mem.h"
00048 #include "../../usr_avp.h"
00049 #include "../../locking.h"
00050 #include "../../error.h"
00051 #include "../../ut.h"
00052 #include "../../mod_fix.h"
00053 
00054 #include "../../trie/dtrie.h"
00055 #include "db.h"
00056 #include "db_userblacklist.h"
00057 
00058 MODULE_VERSION
00059 
00060 
00061 #define MAXNUMBERLEN 31
00062 
00063 
00064 typedef struct _avp_check
00065 {
00066    int avp_flags;
00067    int_str avp_name;
00068 } avp_check_t;
00069 
00070 
00071 struct check_blacklist_fs_t {
00072   struct dtrie_node_t *dtrie_root;
00073 };
00074 
00075 str userblacklist_db_url = str_init(DEFAULT_RODB_URL);
00076 static int use_domain   = 0;
00077 
00078 /* ---- fixup functions: */
00079 static int check_blacklist_fixup(void** param, int param_no);
00080 static int check_user_blacklist_fixup(void** param, int param_no);
00081 
00082 /* ---- exported commands: */
00083 static int check_user_blacklist(struct sip_msg *msg, char* str1, char* str2, char* str3, char* str4);
00084 static int check_blacklist(struct sip_msg *msg, struct check_blacklist_fs_t *arg1);
00085 
00086 /* ---- module init functions: */
00087 static int mod_init(void);
00088 static int child_init(int rank);
00089 static int mi_child_init(void);
00090 static void mod_destroy(void);
00091 
00092 /* --- fifo functions */
00093 struct mi_root * mi_reload_blacklist(struct mi_root* cmd, void* param);  /* usage: kamctl fifo reload_blacklist */
00094 
00095 
00096 static cmd_export_t cmds[]={
00097    { "check_user_blacklist", (cmd_function)check_user_blacklist, 2, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
00098    { "check_user_blacklist", (cmd_function)check_user_blacklist, 3, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
00099    { "check_user_blacklist", (cmd_function)check_user_blacklist, 4, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
00100    { "check_blacklist", (cmd_function)check_blacklist, 1, check_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
00101    { 0, 0, 0, 0, 0, 0}
00102 };
00103 
00104 
00105 static param_export_t params[] = {
00106    userblacklist_DB_URL
00107    userblacklist_DB_TABLE
00108    globalblacklist_DB_TABLE
00109    userblacklist_DB_COLS
00110    globalblacklist_DB_COLS
00111    { "use_domain",      INT_PARAM, &use_domain },
00112    { 0, 0, 0}
00113 };
00114 
00115 
00116 /* Exported MI functions */
00117 static mi_export_t mi_cmds[] = {
00118    { "reload_blacklist", mi_reload_blacklist, MI_NO_INPUT_FLAG, 0, mi_child_init },
00119    { 0, 0, 0, 0, 0}
00120 };
00121 
00122 
00123 struct module_exports exports= {
00124    "userblacklist",
00125    DEFAULT_DLFLAGS,
00126    cmds,
00127    params,
00128    0,
00129    mi_cmds,
00130    0,
00131    0,
00132    mod_init,
00133    0,
00134    mod_destroy,
00135    child_init
00136 };
00137 
00138 
00139 struct source_t {
00140    struct source_t *next;
00141    /** prefixes to be used are stored in this table */
00142    char *table;
00143    /** d-tree structure: will be built from data in database */
00144    struct dtrie_node_t *dtrie_root;
00145 };
00146 
00147 
00148 struct source_list_t {
00149   struct source_t *head;
00150 };
00151 
00152 
00153 static gen_lock_t *lock = NULL;
00154 static struct source_list_t *sources = NULL;
00155 static struct dtrie_node_t *dtrie_root;
00156 
00157 
00158 static int check_user_blacklist_fixup(void** param, int param_no)
00159 {
00160    pv_elem_t *model=NULL;
00161    str s;
00162 
00163    /* convert to str */
00164    s.s = (char*)*param;
00165    s.len = strlen(s.s);
00166 
00167    if (param_no > 0 && param_no <= 4) {
00168       if(s.len == 0 && param_no != 4) {
00169          LM_ERR("no parameter %d\n", param_no);
00170          return E_UNSPEC;
00171       }
00172 
00173       if(pv_parse_format(&s, &model) < 0 || !model) {
00174          LM_ERR("wrong format [%.*s] for parameter %d\n", s.len, s.s, param_no);
00175          return E_UNSPEC;
00176       }
00177 
00178       if(!model->spec.getf) {
00179          if(param_no == 1) {
00180             if(str2int(&s, (unsigned int*)&model->spec.pvp.pvn.u.isname.name.n) != 0) {
00181                LM_ERR("wrong value [%.*s] for parameter %d\n", s.len, s.s, param_no);
00182                return E_UNSPEC;
00183             }
00184          } else {
00185             if(param_no == 2 || param_no == 3) {
00186                LM_ERR("wrong value [%.*s] for parameter %d\n", s.len, s.s, param_no);
00187                return E_UNSPEC;
00188             } else {
00189                // only a string
00190                return 0;
00191             }
00192          }
00193       }
00194       *param = (void*)model;
00195    } else {
00196       LM_ERR("wrong number of parameters\n");
00197    }
00198 
00199    return 0;
00200 }
00201 
00202 
00203 static int check_user_blacklist(struct sip_msg *msg, char* str1, char* str2, char* str3, char* str4)
00204 {
00205    str user = { .len = 0, .s = NULL };
00206    str domain = { .len = 0, .s = NULL};
00207    str table = { .len = 0, .s = NULL};
00208    str number = { .len = 0, .s = NULL};
00209 
00210    void **nodeflags;
00211    char *ptr;
00212    char req_number[MAXNUMBERLEN+1];
00213 
00214    /* user */
00215    if(((pv_elem_p)str1)->spec.getf) {
00216       if(pv_printf_s(msg, (pv_elem_p)str1, &user) != 0) {
00217          LM_ERR("cannot print user pseudo-variable\n");
00218          return -1;
00219       }
00220    }
00221    /* domain */
00222    if(((pv_elem_p)str2)->spec.getf) {
00223       if(pv_printf_s(msg, (pv_elem_p)str2, &domain) != 0) {
00224          LM_ERR("cannot print domain pseudo-variable\n");
00225          return -1;
00226       }
00227    }
00228    /* source number */
00229    if(str3 != NULL && ((pv_elem_p)str3)->spec.getf) {
00230       if(pv_printf_s(msg, (pv_elem_p)str3, &number) != 0) {
00231          LM_ERR("cannot print number pseudo-variable\n");
00232          return -1;
00233       }
00234    }
00235    /* table name */
00236    if(str4 != NULL && strlen(str4) > 0) {
00237       /* string */
00238       table.s=str4;
00239       table.len=strlen(str4);
00240    } else {
00241       /* use default table name */
00242       table.len=userblacklist_table.len;
00243       table.s=userblacklist_table.s;
00244    }
00245 
00246    if (msg->first_line.type != SIP_REQUEST) {
00247       LM_ERR("SIP msg is not a request\n");
00248       return -1;
00249    }
00250 
00251    if(number.s == NULL) {
00252       /* use R-URI */
00253       if ((parse_sip_msg_uri(msg) < 0) || (!msg->parsed_uri.user.s) || (msg->parsed_uri.user.len > MAXNUMBERLEN)) {
00254          LM_ERR("cannot parse msg URI\n");
00255          return -1;
00256       }
00257       strncpy(req_number, msg->parsed_uri.user.s, msg->parsed_uri.user.len);
00258       req_number[msg->parsed_uri.user.len] = '\0';
00259    } else {
00260       if (number.len > MAXNUMBERLEN) {
00261          LM_ERR("number to long\n");
00262          return -1;
00263       }
00264       strncpy(req_number, number.s, number.len);
00265       req_number[number.len] = '\0';
00266    }
00267 
00268    LM_DBG("check entry %s for user %.*s on domain %.*s in table %.*s\n", req_number,
00269       user.len, user.s, domain.len, domain.s, table.len, table.s);
00270    if (db_build_userbl_tree(&user, &domain, &table, dtrie_root, use_domain) < 0) {
00271       LM_ERR("cannot build d-tree\n");
00272       return -1;
00273    }
00274 
00275    ptr = req_number;
00276    /* Skip over non-digits.  */
00277    while (strlen(ptr) > 0 && !isdigit(*ptr)) {
00278       ptr = ptr + 1;
00279    }
00280 
00281    nodeflags = dtrie_longest_match(dtrie_root, ptr, strlen(ptr), NULL, 10);
00282    if (nodeflags) {
00283       if (*nodeflags == (void *)MARK_WHITELIST) {
00284          /* LM_ERR("whitelisted"); */
00285          return 1; /* found, but is whitelisted */
00286       }
00287    } else {
00288       /* LM_ERR("not found"); */
00289       return 1; /* not found is ok */
00290    }
00291 
00292    LM_DBG("entry %s is blacklisted\n", req_number);
00293    return -1;
00294 }
00295 
00296 
00297 /**
00298  * Finds d-tree root for given table.
00299  * \return pointer to d-tree root on success, NULL otherwise
00300  */
00301 static struct dtrie_node_t *table2dt(const char *table)
00302 {
00303    struct source_t *src = sources->head;
00304    while (src) {
00305       if (strcmp(table, src->table) == 0) return src->dtrie_root;
00306       src = src->next;
00307    }
00308 
00309    LM_ERR("invalid table '%s'.\n", table);
00310    return NULL;
00311 }
00312 
00313 
00314 /**
00315  * Adds a new table to the list, if the table is
00316  * already present, nothing will be done.
00317  * \return zero on success, negative on errors
00318  */
00319 static int add_source(const char *table)
00320 {
00321    /* check if the table is already present */
00322    struct source_t *src = sources->head;
00323    while (src) {
00324       if (strcmp(table, src->table) == 0) return 0;
00325       src = src->next;
00326    }
00327 
00328    src = shm_malloc(sizeof(struct source_t));
00329    if (!src) {
00330       SHM_MEM_ERROR;
00331       return -1;
00332    }
00333    memset(src, 0, sizeof(struct source_t));
00334 
00335    src->next = sources->head;
00336    sources->head = src;
00337 
00338    src->table = shm_malloc(strlen(table)+1);
00339    if (!src->table) {
00340       SHM_MEM_ERROR;
00341       shm_free(src);
00342       return -1;
00343    }
00344    strcpy(src->table, table);
00345    LM_DBG("add table %s", table);
00346 
00347    src->dtrie_root = dtrie_init(10);
00348 
00349    if (src->dtrie_root == NULL) {
00350       LM_ERR("could not initialize data");
00351       return -1;
00352    }
00353 
00354    return 0;
00355 }
00356 
00357 
00358 static int check_blacklist_fixup(void **arg, int arg_no)
00359 {
00360    char *table = (char *)(*arg);
00361    struct dtrie_node_t *node = NULL;
00362    if (arg_no != 1) {
00363       LM_ERR("wrong number of parameters\n");
00364       return -1;
00365    }
00366 
00367    if (!table) {
00368       LM_ERR("no table name\n");
00369       return -1;
00370    }
00371    /* try to add the table */
00372    if (add_source(table) != 0) {
00373       LM_ERR("could not add table");
00374       return -1;
00375    }  
00376 
00377    /* get the node that belongs to the table */
00378    node = table2dt(table);
00379    if (!node) {
00380       LM_ERR("invalid table '%s'\n", table);
00381       return -1;
00382    }
00383 
00384    struct check_blacklist_fs_t *new_arg = (struct check_blacklist_fs_t*)pkg_malloc(sizeof(struct check_blacklist_fs_t));
00385    if (!new_arg) {
00386       PKG_MEM_ERROR;
00387       return -1;
00388    }
00389    memset(new_arg, 0, sizeof(struct check_blacklist_fs_t));
00390    new_arg->dtrie_root = node;
00391    *arg=(void*)new_arg;
00392 
00393    return 0;
00394 }
00395 
00396 
00397 static int check_blacklist(struct sip_msg *msg, struct check_blacklist_fs_t *arg1)
00398 {
00399    void **nodeflags;
00400    char *ptr;
00401    char req_number[MAXNUMBERLEN+1];
00402    int ret = -1;
00403 
00404    if (msg->first_line.type != SIP_REQUEST) {
00405       LM_ERR("SIP msg is not a request\n");
00406       return -1;
00407    }
00408 
00409    if (parse_sip_msg_uri(msg) < 0) {
00410       LM_ERR("cannot parse msg URI\n");
00411       return -1;
00412    }
00413 
00414    if ((parse_sip_msg_uri(msg) < 0) || (!msg->parsed_uri.user.s) || (msg->parsed_uri.user.len > MAXNUMBERLEN)) {
00415       LM_ERR("cannot parse msg URI\n");
00416       return -1;
00417    }
00418    strncpy(req_number, msg->parsed_uri.user.s, msg->parsed_uri.user.len);
00419    req_number[msg->parsed_uri.user.len] = '\0';
00420 
00421    ptr = req_number;
00422    /* Skip over non-digits.  */
00423    while (strlen(ptr) > 0 && !isdigit(*ptr)) {
00424       ptr = ptr + 1;
00425    }
00426 
00427    LM_DBG("check entry %s\n", req_number);
00428 
00429    /* avoids dirty reads when updating d-tree */
00430    lock_get(lock);
00431    nodeflags = dtrie_longest_match(arg1->dtrie_root, ptr, strlen(ptr), NULL, 10);
00432    if (nodeflags) {
00433       if (*nodeflags == (void *)MARK_WHITELIST) {
00434          /* LM_DBG("whitelisted"); */
00435          ret = 1; /* found, but is whitelisted */
00436       }
00437    }
00438    else {
00439       /* LM_ERR("not found"); */
00440       ret = 1; /* not found is ok */
00441    }
00442    lock_release(lock);
00443 
00444    LM_DBG("entry %s is blacklisted\n", req_number);
00445    return ret;
00446 }
00447 
00448 
00449 /**
00450  * Fills the d-tree for all configured and prepared sources.
00451  * \return 0 on success, -1 otherwise
00452  */
00453 static int reload_sources(void)
00454 {
00455    int result = 0;
00456    str tmp;
00457 
00458    /* critical section start: avoids dirty reads when updating d-tree */
00459    lock_get(lock);
00460 
00461    struct source_t *src = sources->head;
00462    while (src) {
00463       tmp.s = src->table;
00464       tmp.len = strlen(src->table);
00465       int n = db_reload_source(&tmp, src->dtrie_root);
00466       if (n < 0) {
00467          LM_ERR("cannot reload source from '%.*s'\n", tmp.len, tmp.s);
00468          result = -1;
00469          break;
00470       }
00471       LM_INFO("got %d entries from '%.*s'\n", n, tmp.len, tmp.s);
00472       src = src->next;
00473    }
00474 
00475    /* critical section end */
00476    lock_release(lock);
00477 
00478    return result;
00479 }
00480 
00481 
00482 static int init_source_list(void)
00483 {
00484    sources = shm_malloc(sizeof(struct source_list_t));
00485    if (!sources) {
00486       SHM_MEM_ERROR;
00487       return -1;
00488    }
00489    sources->head = NULL;
00490    return 0;
00491 }
00492 
00493 
00494 static void destroy_source_list(void)
00495 {
00496    if (sources) {
00497       while (sources->head) {
00498          struct source_t *src = sources->head;
00499          sources->head = src->next;
00500 
00501          if (src->table) shm_free(src->table);
00502          dtrie_destroy(&(src->dtrie_root), NULL, 10);
00503          shm_free(src);
00504       }
00505 
00506       shm_free(sources);
00507       sources = NULL;
00508    }
00509 }
00510 
00511 
00512 static int init_shmlock(void)
00513 {
00514    lock = lock_alloc();
00515    if (!lock) {
00516       LM_CRIT("cannot allocate memory for lock.\n");
00517       return -1;
00518    }
00519    if (lock_init(lock) == 0) {
00520       LM_CRIT("cannot initialize lock.\n");
00521       return -1;
00522    }
00523 
00524    return 0;
00525 }
00526 
00527 
00528 static void destroy_shmlock(void)
00529 {
00530    if (lock) {
00531       lock_destroy(lock);
00532       lock_dealloc((void *)lock);
00533       lock = NULL;
00534    }
00535 }
00536 
00537 
00538 struct mi_root * mi_reload_blacklist(struct mi_root* cmd, void* param)
00539 {
00540    struct mi_root * tmp = NULL;
00541    if(reload_sources() == 0) {
00542       tmp = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00543    } else {
00544       tmp = init_mi_tree( 500, "cannot reload blacklist", 21);
00545    }
00546 
00547    return tmp;
00548 }
00549 
00550 
00551 static int mod_init(void)
00552 {
00553    userblacklist_db_vars();
00554 
00555    if (userblacklist_db_init() != 0) return -1;
00556    if (init_shmlock() != 0) return -1;
00557    if (init_source_list() != 0) return -1;
00558    return 0;
00559 }
00560 
00561 
00562 static int child_init(int rank)
00563 {
00564    if (userblacklist_db_open() != 0) return -1;
00565    dtrie_root=dtrie_init(10);
00566    if (dtrie_root == NULL) {
00567       LM_ERR("could not initialize data");
00568       return -1;
00569    }
00570    /* because we've added new sources during the fixup */
00571    if (reload_sources() != 0) return -1;
00572 
00573    return 0;
00574 }
00575 
00576 
00577 static int mi_child_init(void)
00578 {
00579    if (userblacklist_db_open() != 0) return -1;
00580    dtrie_root=dtrie_init(10);
00581    if (dtrie_root == NULL) {
00582       LM_ERR("could not initialize data");
00583       return -1;
00584    }
00585    /* because we've added new sources during the fixup */
00586    if (reload_sources() != 0) return -1;
00587 
00588    return 0;
00589 }
00590 
00591 
00592 static void mod_destroy(void)
00593 {
00594    destroy_source_list();
00595    destroy_shmlock();
00596    userblacklist_db_close();
00597    dtrie_destroy(&dtrie_root, NULL, 10);
00598 }

Generated on Thu May 24 22:00:35 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6