c64dbbd1 |
/*
* $Id$
*
* Copyright (C) 2007 1&1 Internet AG
*
|
27642a08 |
* This file is part of Kamailio, a free SIP server.
|
c64dbbd1 |
*
|
27642a08 |
* Kamailio is free software; you can redistribute it and/or modify
|
c64dbbd1 |
* 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
*
|
27642a08 |
* Kamailio is distributed in the hope that it will be useful,
|
c64dbbd1 |
* 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
|
1f8e65ee |
/*!
* \file
* \brief USERBLACKLIST :: module definitions
* \ingroup userblacklist
* - Module: \ref userblacklist
*/
/*!
* \defgroup userblacklist USERBLACKLIST :: The Kamailio userblacklist Module
*
* The userblacklist module allows Kamailio to handle blacklists on a per user basis.
* This information is stored in a database table, which is queried to decide if the
* number (more exactly, the request URI user) is blacklisted or not.
* An additional functionality that this module provides is the ability to handle
* global blacklists. This lists are loaded on startup into memory, thus providing a
* better performance then in the userblacklist case.
*/
|
c64dbbd1 |
#include <string.h>
#include "../../parser/parse_uri.h"
#include "../../mem/shm_mem.h"
#include "../../sr_module.h"
|
3cb0009d |
#include "../../lib/kmi/mi.h"
|
c64dbbd1 |
#include "../../mem/mem.h"
#include "../../usr_avp.h"
#include "../../locking.h"
#include "../../error.h"
#include "../../ut.h"
#include "../../mod_fix.h"
|
f1a4d217 |
#include "../../lib/trie/dtrie.h"
|
c64dbbd1 |
#include "db.h"
|
9230c04c |
#include "db_userblacklist.h"
|
c64dbbd1 |
MODULE_VERSION
#define MAXNUMBERLEN 31
typedef struct _avp_check
{
int avp_flags;
int_str avp_name;
} avp_check_t;
struct check_blacklist_fs_t {
|
dc10e758 |
struct dtrie_node_t *dtrie_root;
|
c64dbbd1 |
};
|
9230c04c |
str userblacklist_db_url = str_init(DEFAULT_RODB_URL);
|
c64dbbd1 |
static int use_domain = 0;
/* ---- fixup functions: */
static int check_blacklist_fixup(void** param, int param_no);
static int check_user_blacklist_fixup(void** param, int param_no);
/* ---- exported commands: */
|
0fe12cbb |
static int check_user_blacklist(struct sip_msg *msg, char* str1, char* str2, char* str3, char* str4);
|
9a0b1825 |
static int check_user_whitelist(struct sip_msg *msg, char* str1, char* str2, char* str3, char* str4);
|
0fe12cbb |
static int check_blacklist(struct sip_msg *msg, struct check_blacklist_fs_t *arg1);
|
c64dbbd1 |
/* ---- module init functions: */
static int mod_init(void);
static int child_init(int rank);
static int mi_child_init(void);
static void mod_destroy(void);
/* --- fifo functions */
|
bb30e4a8 |
struct mi_root * mi_reload_blacklist(struct mi_root* cmd, void* param); /* usage: kamctl fifo reload_blacklist */
|
c64dbbd1 |
static cmd_export_t cmds[]={
{ "check_user_blacklist", (cmd_function)check_user_blacklist, 2, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
|
9a0b1825 |
{ "check_user_whitelist", (cmd_function)check_user_whitelist, 2, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
|
0fe12cbb |
{ "check_user_blacklist", (cmd_function)check_user_blacklist, 3, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
|
9a0b1825 |
{ "check_user_whitelist", (cmd_function)check_user_whitelist, 3, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
|
0fe12cbb |
{ "check_user_blacklist", (cmd_function)check_user_blacklist, 4, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
|
9a0b1825 |
{ "check_user_whitelist", (cmd_function)check_user_whitelist, 4, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
|
c64dbbd1 |
{ "check_blacklist", (cmd_function)check_blacklist, 1, check_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
{ 0, 0, 0, 0, 0, 0}
};
static param_export_t params[] = {
|
9230c04c |
userblacklist_DB_URL
userblacklist_DB_TABLE
globalblacklist_DB_TABLE
userblacklist_DB_COLS
globalblacklist_DB_COLS
|
c64dbbd1 |
{ "use_domain", INT_PARAM, &use_domain },
{ 0, 0, 0}
};
/* Exported MI functions */
static mi_export_t mi_cmds[] = {
{ "reload_blacklist", mi_reload_blacklist, MI_NO_INPUT_FLAG, 0, mi_child_init },
{ 0, 0, 0, 0, 0}
};
struct module_exports exports= {
"userblacklist",
|
0fe12cbb |
DEFAULT_DLFLAGS,
|
c64dbbd1 |
cmds,
params,
0,
mi_cmds,
0,
|
0fe12cbb |
0,
mod_init,
|
c64dbbd1 |
0,
mod_destroy,
child_init
};
struct source_t {
struct source_t *next;
/** prefixes to be used are stored in this table */
char *table;
/** d-tree structure: will be built from data in database */
|
dc10e758 |
struct dtrie_node_t *dtrie_root;
|
c64dbbd1 |
};
struct source_list_t {
struct source_t *head;
};
static gen_lock_t *lock = NULL;
static struct source_list_t *sources = NULL;
|
dc10e758 |
static struct dtrie_node_t *dtrie_root;
|
c64dbbd1 |
static int check_user_blacklist_fixup(void** param, int param_no)
{
pv_elem_t *model=NULL;
str s;
/* convert to str */
s.s = (char*)*param;
s.len = strlen(s.s);
|
0fe12cbb |
if (param_no > 0 && param_no <= 4) {
if(s.len == 0 && param_no != 4) {
LM_ERR("no parameter %d\n", param_no);
|
c64dbbd1 |
return E_UNSPEC;
}
if(pv_parse_format(&s, &model) < 0 || !model) {
|
0fe12cbb |
LM_ERR("wrong format [%.*s] for parameter %d\n", s.len, s.s, param_no);
|
c64dbbd1 |
return E_UNSPEC;
}
|
0fe12cbb |
|
c64dbbd1 |
if(!model->spec.getf) {
if(param_no == 1) {
|
0fe12cbb |
if(str2int(&s, (unsigned int*)&model->spec.pvp.pvn.u.isname.name.n) != 0) {
LM_ERR("wrong value [%.*s] for parameter %d\n", s.len, s.s, param_no);
|
c64dbbd1 |
return E_UNSPEC;
|
0fe12cbb |
}
} else {
if(param_no == 2 || param_no == 3) {
LM_ERR("wrong value [%.*s] for parameter %d\n", s.len, s.s, param_no);
return E_UNSPEC;
} else {
// only a string
return 0;
}
|
c64dbbd1 |
}
}
*param = (void*)model;
|
0fe12cbb |
} else {
LM_ERR("wrong number of parameters\n");
|
c64dbbd1 |
}
return 0;
}
|
9a0b1825 |
static int check_user_list(struct sip_msg *msg, char* str1, char* str2, char* str3, char* str4, int listtype)
|
c64dbbd1 |
{
str user = { .len = 0, .s = NULL };
str domain = { .len = 0, .s = NULL};
|
0fe12cbb |
str table = { .len = 0, .s = NULL};
str number = { .len = 0, .s = NULL};
|
4074d000 |
void **nodeflags;
|
0fe12cbb |
char *ptr;
|
c64dbbd1 |
char req_number[MAXNUMBERLEN+1];
|
0fe12cbb |
/* user */
|
c64dbbd1 |
if(((pv_elem_p)str1)->spec.getf) {
if(pv_printf_s(msg, (pv_elem_p)str1, &user) != 0) {
LM_ERR("cannot print user pseudo-variable\n");
return -1;
}
}
|
0fe12cbb |
/* domain */
if(((pv_elem_p)str2)->spec.getf) {
|
c64dbbd1 |
if(pv_printf_s(msg, (pv_elem_p)str2, &domain) != 0) {
LM_ERR("cannot print domain pseudo-variable\n");
return -1;
}
}
|
0fe12cbb |
/* source number */
if(str3 != NULL && ((pv_elem_p)str3)->spec.getf) {
if(pv_printf_s(msg, (pv_elem_p)str3, &number) != 0) {
LM_ERR("cannot print number pseudo-variable\n");
return -1;
}
}
/* table name */
if(str4 != NULL && strlen(str4) > 0) {
/* string */
table.s=str4;
table.len=strlen(str4);
} else {
/* use default table name */
|
9230c04c |
table.len=userblacklist_table.len;
table.s=userblacklist_table.s;
|
0fe12cbb |
}
|
c64dbbd1 |
if (msg->first_line.type != SIP_REQUEST) {
LM_ERR("SIP msg is not a request\n");
return -1;
}
|
0fe12cbb |
if(number.s == NULL) {
/* use R-URI */
if ((parse_sip_msg_uri(msg) < 0) || (!msg->parsed_uri.user.s) || (msg->parsed_uri.user.len > MAXNUMBERLEN)) {
LM_ERR("cannot parse msg URI\n");
return -1;
}
strncpy(req_number, msg->parsed_uri.user.s, msg->parsed_uri.user.len);
req_number[msg->parsed_uri.user.len] = '\0';
} else {
if (number.len > MAXNUMBERLEN) {
LM_ERR("number to long\n");
return -1;
}
strncpy(req_number, number.s, number.len);
req_number[number.len] = '\0';
|
c64dbbd1 |
}
|
0fe12cbb |
LM_DBG("check entry %s for user %.*s on domain %.*s in table %.*s\n", req_number,
user.len, user.s, domain.len, domain.s, table.len, table.s);
|
dc10e758 |
if (db_build_userbl_tree(&user, &domain, &table, dtrie_root, use_domain) < 0) {
|
c64dbbd1 |
LM_ERR("cannot build d-tree\n");
return -1;
}
|
0fe12cbb |
ptr = req_number;
/* Skip over non-digits. */
while (strlen(ptr) > 0 && !isdigit(*ptr)) {
ptr = ptr + 1;
}
|
d72deb95 |
nodeflags = dtrie_longest_match(dtrie_root, ptr, strlen(ptr), NULL, 10);
|
4074d000 |
if (nodeflags) {
if (*nodeflags == (void *)MARK_WHITELIST) {
|
c64dbbd1 |
/* LM_ERR("whitelisted"); */
return 1; /* found, but is whitelisted */
}
|
0fe12cbb |
} else {
|
9a0b1825 |
if(!listtype) {
/* LM_ERR("not found return 1"); */
return 1; /* not found is ok */
} else {
/* LM_ERR("not found return -1"); */
return -1; /* not found is not ok */
}
|
c64dbbd1 |
}
LM_DBG("entry %s is blacklisted\n", req_number);
return -1;
}
|
9a0b1825 |
static int check_user_whitelist(struct sip_msg *msg, char* str1, char* str2, char* str3, char* str4)
{
return check_user_list(msg, str1, str2, str3, str4, 1);
}
static int check_user_blacklist(struct sip_msg *msg, char* str1, char* str2, char* str3, char* str4)
{
return check_user_list(msg, str1, str2, str3, str4, 0);
}
|
c64dbbd1 |
/**
* Finds d-tree root for given table.
* \return pointer to d-tree root on success, NULL otherwise
*/
|
dc10e758 |
static struct dtrie_node_t *table2dt(const char *table)
|
c64dbbd1 |
{
struct source_t *src = sources->head;
while (src) {
|
dc10e758 |
if (strcmp(table, src->table) == 0) return src->dtrie_root;
|
c64dbbd1 |
src = src->next;
}
LM_ERR("invalid table '%s'.\n", table);
return NULL;
}
/**
* Adds a new table to the list, if the table is
* already present, nothing will be done.
* \return zero on success, negative on errors
*/
static int add_source(const char *table)
{
/* check if the table is already present */
struct source_t *src = sources->head;
while (src) {
if (strcmp(table, src->table) == 0) return 0;
src = src->next;
}
src = shm_malloc(sizeof(struct source_t));
if (!src) {
|
5e44df26 |
SHM_MEM_ERROR;
|
c64dbbd1 |
return -1;
}
memset(src, 0, sizeof(struct source_t));
src->next = sources->head;
sources->head = src;
src->table = shm_malloc(strlen(table)+1);
if (!src->table) {
|
5e44df26 |
SHM_MEM_ERROR;
|
c64dbbd1 |
shm_free(src);
return -1;
}
strcpy(src->table, table);
LM_DBG("add table %s", table);
|
d72deb95 |
src->dtrie_root = dtrie_init(10);
|
dc10e758 |
if (src->dtrie_root == NULL) {
LM_ERR("could not initialize data");
return -1;
}
return 0;
|
c64dbbd1 |
}
static int check_blacklist_fixup(void **arg, int arg_no)
{
char *table = (char *)(*arg);
|
dc10e758 |
struct dtrie_node_t *node = NULL;
|
275899b1 |
struct check_blacklist_fs_t *new_arg;
|
c64dbbd1 |
if (arg_no != 1) {
|
0fe12cbb |
LM_ERR("wrong number of parameters\n");
|
c64dbbd1 |
return -1;
}
if (!table) {
|
0fe12cbb |
LM_ERR("no table name\n");
|
c64dbbd1 |
return -1;
}
/* try to add the table */
if (add_source(table) != 0) {
LM_ERR("could not add table");
return -1;
}
/* get the node that belongs to the table */
node = table2dt(table);
if (!node) {
|
0fe12cbb |
LM_ERR("invalid table '%s'\n", table);
|
c64dbbd1 |
return -1;
}
|
275899b1 |
new_arg = pkg_malloc(sizeof(struct check_blacklist_fs_t));
|
c64dbbd1 |
if (!new_arg) {
|
5e44df26 |
PKG_MEM_ERROR;
|
c64dbbd1 |
return -1;
}
memset(new_arg, 0, sizeof(struct check_blacklist_fs_t));
|
dc10e758 |
new_arg->dtrie_root = node;
|
c64dbbd1 |
*arg=(void*)new_arg;
return 0;
}
|
0fe12cbb |
static int check_blacklist(struct sip_msg *msg, struct check_blacklist_fs_t *arg1)
|
c64dbbd1 |
{
|
4074d000 |
void **nodeflags;
|
0fe12cbb |
char *ptr;
|
c64dbbd1 |
char req_number[MAXNUMBERLEN+1];
|
8c2f0b88 |
int ret = -1;
|
c64dbbd1 |
if (msg->first_line.type != SIP_REQUEST) {
LM_ERR("SIP msg is not a request\n");
return -1;
}
if (parse_sip_msg_uri(msg) < 0) {
LM_ERR("cannot parse msg URI\n");
return -1;
}
if ((parse_sip_msg_uri(msg) < 0) || (!msg->parsed_uri.user.s) || (msg->parsed_uri.user.len > MAXNUMBERLEN)) {
LM_ERR("cannot parse msg URI\n");
return -1;
}
strncpy(req_number, msg->parsed_uri.user.s, msg->parsed_uri.user.len);
req_number[msg->parsed_uri.user.len] = '\0';
|
0fe12cbb |
ptr = req_number;
/* Skip over non-digits. */
while (strlen(ptr) > 0 && !isdigit(*ptr)) {
ptr = ptr + 1;
}
|
c64dbbd1 |
LM_DBG("check entry %s\n", req_number);
|
8c2f0b88 |
/* avoids dirty reads when updating d-tree */
lock_get(lock);
|
d72deb95 |
nodeflags = dtrie_longest_match(arg1->dtrie_root, ptr, strlen(ptr), NULL, 10);
|
4074d000 |
if (nodeflags) {
if (*nodeflags == (void *)MARK_WHITELIST) {
|
c64dbbd1 |
/* LM_DBG("whitelisted"); */
|
8c2f0b88 |
ret = 1; /* found, but is whitelisted */
|
c64dbbd1 |
}
}
else {
/* LM_ERR("not found"); */
|
8c2f0b88 |
ret = 1; /* not found is ok */
|
c64dbbd1 |
}
|
8c2f0b88 |
lock_release(lock);
|
c64dbbd1 |
LM_DBG("entry %s is blacklisted\n", req_number);
|
8c2f0b88 |
return ret;
|
c64dbbd1 |
}
/**
* Fills the d-tree for all configured and prepared sources.
* \return 0 on success, -1 otherwise
*/
static int reload_sources(void)
{
int result = 0;
str tmp;
|
275899b1 |
struct source_t *src;
int n;
|
c64dbbd1 |
/* critical section start: avoids dirty reads when updating d-tree */
lock_get(lock);
|
275899b1 |
src = sources->head;
|
c64dbbd1 |
while (src) {
tmp.s = src->table;
tmp.len = strlen(src->table);
|
275899b1 |
n = db_reload_source(&tmp, src->dtrie_root);
|
c64dbbd1 |
if (n < 0) {
|
0fe12cbb |
LM_ERR("cannot reload source from '%.*s'\n", tmp.len, tmp.s);
|
c64dbbd1 |
result = -1;
break;
}
|
0fe12cbb |
LM_INFO("got %d entries from '%.*s'\n", n, tmp.len, tmp.s);
|
c64dbbd1 |
src = src->next;
}
/* critical section end */
lock_release(lock);
return result;
}
static int init_source_list(void)
{
sources = shm_malloc(sizeof(struct source_list_t));
if (!sources) {
|
5e44df26 |
SHM_MEM_ERROR;
|
c64dbbd1 |
return -1;
}
sources->head = NULL;
return 0;
}
static void destroy_source_list(void)
{
if (sources) {
while (sources->head) {
struct source_t *src = sources->head;
sources->head = src->next;
if (src->table) shm_free(src->table);
|
d72deb95 |
dtrie_destroy(&(src->dtrie_root), NULL, 10);
|
c64dbbd1 |
shm_free(src);
}
shm_free(sources);
sources = NULL;
}
}
static int init_shmlock(void)
{
lock = lock_alloc();
if (!lock) {
LM_CRIT("cannot allocate memory for lock.\n");
return -1;
}
if (lock_init(lock) == 0) {
LM_CRIT("cannot initialize lock.\n");
return -1;
}
return 0;
}
static void destroy_shmlock(void)
{
if (lock) {
lock_destroy(lock);
lock_dealloc((void *)lock);
lock = NULL;
}
}
struct mi_root * mi_reload_blacklist(struct mi_root* cmd, void* param)
{
struct mi_root * tmp = NULL;
if(reload_sources() == 0) {
|
dc10e758 |
tmp = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
|
c64dbbd1 |
} else {
tmp = init_mi_tree( 500, "cannot reload blacklist", 21);
}
return tmp;
}
static int mod_init(void)
{
|
571bc6e4 |
if(register_mi_mod(exports.name, mi_cmds)!=0)
{
LM_ERR("failed to register MI commands\n");
return -1;
}
|
9230c04c |
userblacklist_db_vars();
|
c64dbbd1 |
|
9230c04c |
if (userblacklist_db_init() != 0) return -1;
|
c64dbbd1 |
if (init_shmlock() != 0) return -1;
if (init_source_list() != 0) return -1;
return 0;
}
static int child_init(int rank)
{
|
82a5ef65 |
if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
return 0; /* do nothing for the main process */
|
9230c04c |
if (userblacklist_db_open() != 0) return -1;
|
d72deb95 |
dtrie_root=dtrie_init(10);
|
dc10e758 |
if (dtrie_root == NULL) {
LM_ERR("could not initialize data");
return -1;
}
|
c64dbbd1 |
/* because we've added new sources during the fixup */
if (reload_sources() != 0) return -1;
return 0;
}
static int mi_child_init(void)
{
|
9230c04c |
if (userblacklist_db_open() != 0) return -1;
|
d72deb95 |
dtrie_root=dtrie_init(10);
|
dc10e758 |
if (dtrie_root == NULL) {
LM_ERR("could not initialize data");
return -1;
}
|
c64dbbd1 |
/* because we've added new sources during the fixup */
if (reload_sources() != 0) return -1;
return 0;
}
static void mod_destroy(void)
{
destroy_source_list();
destroy_shmlock();
|
9230c04c |
userblacklist_db_close();
|
d72deb95 |
dtrie_destroy(&dtrie_root, NULL, 10);
|
c64dbbd1 |
}
|