modules/userblacklist/userblacklist.c
c64dbbd1
 /*
  * 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 
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
c64dbbd1
  */
 
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
 
932a5e8a
 #define BLACKLISTED_S		"blacklisted"
 #define BLACKLISTED_LEN		(sizeof(BLACKLISTED_S)-1)
 #define WHITELISTED_S		"whitelisted"
 #define WHITELISTED_LEN		(sizeof(WHITELISTED_S)-1)
 #define TRUE_S			"true"
 #define TRUE_LEN		(sizeof(TRUE_S)-1)
 #define FALSE_S			"false"
 #define FALSE_LEN		(sizeof(FALSE_S)-1)
c64dbbd1
 
 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);
932a5e8a
 int use_domain = 0;
2f8f8e58
 int match_mode = 10; /* numeric */
9858546e
 static struct dtrie_node_t *gnode = NULL;
c64dbbd1
 
 /* ---- fixup functions: */
 static int check_blacklist_fixup(void** param, int param_no);
 static int check_user_blacklist_fixup(void** param, int param_no);
9858546e
 static int check_globalblacklist_fixup(void** param, int param_no);
c64dbbd1
 
 /* ---- 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);
ae18879b
 static int check_user_blacklist2(struct sip_msg *msg, char* str1, char* str2);
 static int check_user_whitelist2(struct sip_msg *msg, char* str1, char* str2);
 static int check_user_blacklist3(struct sip_msg *msg, char* str1, char* str2, char* str3);
 static int check_user_whitelist3(struct sip_msg *msg, char* str1, char* str2, char* str3);
0fe12cbb
 static int check_blacklist(struct sip_msg *msg, struct check_blacklist_fs_t *arg1);
4b335f64
 static int check_whitelist(struct sip_msg *msg, struct check_blacklist_fs_t *arg1);
9858546e
 static int check_globalblacklist(struct sip_msg *msg);
 
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 */
932a5e8a
 struct mi_root * mi_dump_blacklist(struct mi_root* cmd, void* param);  /* usage: kamctl fifo dump_blacklist */
 struct mi_root * mi_check_blacklist(struct mi_root* cmd, void* param);  /* usage: kamctl fifo check_blacklist prefix */
 struct mi_root * mi_check_whitelist(struct mi_root* cmd, void* param);  /* usage: kamctl fifo check_whitelist prefix */
 struct mi_root * mi_check_userblacklist(struct mi_root* cmd, void* param);  /* usage: kamctl fifo check_userblacklist */
 struct mi_root * mi_check_userwhitelist(struct mi_root* cmd, void* param);  /* usage: kamctl fifo check_userwhitelist */
c64dbbd1
 
 
 static cmd_export_t cmds[]={
ae18879b
 	{ "check_user_blacklist", (cmd_function)check_user_blacklist2, 2, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{ "check_user_whitelist", (cmd_function)check_user_whitelist2, 2, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{ "check_user_blacklist", (cmd_function)check_user_blacklist3, 3, check_user_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{ "check_user_whitelist", (cmd_function)check_user_whitelist3, 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 },
4b335f64
 	{ "check_whitelist", (cmd_function)check_whitelist, 1, check_blacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
9858546e
 	{ "check_blacklist", (cmd_function)check_globalblacklist, 0, check_globalblacklist_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
c64dbbd1
 	{ 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 },
932a5e8a
 	{ "match_mode",	     INT_PARAM, &match_mode },
 	{ 0, 0, 0 }
c64dbbd1
 };
 
 
 /* Exported MI functions */
 static mi_export_t mi_cmds[] = {
 	{ "reload_blacklist", mi_reload_blacklist, MI_NO_INPUT_FLAG, 0, mi_child_init },
932a5e8a
 	{ "dump_blacklist", mi_dump_blacklist, MI_NO_INPUT_FLAG, 0, 0},
 	{ "check_blacklist", mi_check_blacklist, 0, 0, 0 },
 	{ "check_whitelist", mi_check_whitelist, 0, 0, 0 },
 	{ "check_userblacklist", mi_check_userblacklist, 0, 0, 0 },
 	{ "check_userwhitelist", mi_check_userwhitelist, 0, 0, 0 },
 	{ 0, 0, 0, 0, 0 }
c64dbbd1
 };
 
 
 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;
c28551cd
 static struct dtrie_node_t *dtrie_root = NULL;
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
 
cbeb136c
 		if(model->spec==NULL || model->spec->getf==NULL) {
c64dbbd1
 			if(param_no == 1) {
cbeb136c
 				if(str2int(&s, (unsigned int*)&model->spec->pvp.pvn.u.isname.name.n) != 0) {
0fe12cbb
 					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 */
cbeb136c
 	if(((pv_elem_p)str1)->spec!=NULL && ((pv_elem_p)str1)->spec->getf!=NULL) {
c64dbbd1
 		if(pv_printf_s(msg, (pv_elem_p)str1, &user) != 0) {
 			LM_ERR("cannot print user pseudo-variable\n");
 			return -1;
 		}
 	}
0fe12cbb
 	/* domain */
cbeb136c
 	if(((pv_elem_p)str2)->spec!=NULL && ((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 */
cbeb136c
 	if(str3 != NULL && ((pv_elem_p)str3)->spec!=NULL && ((pv_elem_p)str3)->spec->getf!=NULL) {
0fe12cbb
 		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.  */
d128ac96
 	while (match_mode == 10 && strlen(ptr) > 0 && !isdigit(*ptr)) {
0fe12cbb
 		ptr = ptr + 1;
 	}
 
2f8f8e58
 	nodeflags = dtrie_longest_match(dtrie_root, ptr, strlen(ptr), NULL, match_mode);
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);
 }
 
ae18879b
 static int check_user_whitelist2(struct sip_msg *msg, char* str1, char* str2)
 {
 	return check_user_list(msg, str1, str2, NULL, NULL, 1);
 }
 
 
 static int check_user_blacklist2(struct sip_msg *msg, char* str1, char* str2)
 {
 	return check_user_list(msg, str1, str2, NULL, NULL, 0);
 }
 
 static int check_user_whitelist3(struct sip_msg *msg, char* str1, char* str2, char* str3)
 {
 	return check_user_list(msg, str1, str2, str3, NULL, 1);
 }
 
 
 static int check_user_blacklist3(struct sip_msg *msg, char* str1, char* str2, char* str3)
 {
 	return check_user_list(msg, str1, str2, str3, NULL, 0);
 }
9a0b1825
 
 
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);
 
2f8f8e58
 	src->dtrie_root = dtrie_init(match_mode);
d72deb95
 
dc10e758
 	if (src->dtrie_root == NULL) {
 		LM_ERR("could not initialize data");
 		return -1;
 	}
 
 	return 0;
c64dbbd1
 }
 
 
9858546e
 static int check_globalblacklist_fixup(void** param, int param_no)
 {
 	char * table = globalblacklist_table.s;
 	if(param_no > 0){
 		LM_ERR("Wrong number of parameters\n");
 		return -1;
 	}
 
 	if (!table) {
 		LM_ERR("no table name\n");
 		return -1;
 	}
 	/* try to add the table */
 	if (add_source(table) != 0) {
 		LM_ERR("could not add table");
 		return -1;
 	}
 
 	gnode = table2dt(table);
 	if (!gnode) {
 		LM_ERR("invalid table '%s'\n", table);
 		return -1;
 	}
 
 	return 0;
 }
 
 static int check_globalblacklist(struct sip_msg* msg)
 {
 	static struct check_blacklist_fs_t* arg = NULL;
 	if(!arg){
 		arg = pkg_malloc(sizeof(struct check_blacklist_fs_t));
 		if (!arg) {
 			PKG_MEM_ERROR;
 			return -1;
 		}
 		memset(arg, 0, sizeof(struct check_blacklist_fs_t));
 		arg->dtrie_root = gnode;
 	}
 	return check_blacklist(msg, arg);
 }
 
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) || (!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.  */
2f8f8e58
 	while (match_mode == 10 && strlen(ptr) > 0 && !isdigit(*ptr)) {
 			ptr = ptr + 1;
0fe12cbb
 	}
 
c64dbbd1
 	LM_DBG("check entry %s\n", req_number);
8c2f0b88
 
 	/* avoids dirty reads when updating d-tree */
 	lock_get(lock);
2f8f8e58
 	nodeflags = dtrie_longest_match(arg1->dtrie_root, ptr, strlen(ptr), NULL, match_mode);
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
 }
 
4b335f64
 static int check_whitelist(struct sip_msg *msg, struct check_blacklist_fs_t *arg1)
 {
 	void **nodeflags;
 	char *ptr;
 	char req_number[MAXNUMBERLEN+1];
 	int ret = -1;
 
 	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) || (!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';
 
 	ptr = req_number;
 	/* Skip over non-digits.  */
 	while (strlen(ptr) > 0 && !isdigit(*ptr)) {
 		ptr = ptr + 1;
 	}
 
 	LM_DBG("check entry %s\n", req_number);
 
 	/* avoids dirty reads when updating d-tree */
 	lock_get(lock);
 	nodeflags = dtrie_longest_match(arg1->dtrie_root, ptr, strlen(ptr), NULL, 10);
 	if (nodeflags) {
 		if (*nodeflags == (void *)MARK_WHITELIST) {
 			/* LM_DBG("whitelisted"); */
 			ret = 1; /* found, but is whitelisted */
 		}
 	}
 	else {
 		/* LM_ERR("not found"); */
 		ret = -1; /* not found is ok */
 	}
 	lock_release(lock);
 
 	LM_DBG("entry %s is blacklisted\n", req_number);
 	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;
 	}
c28551cd
 	memset(sources, 0, sizeof(struct source_list_t));
c64dbbd1
 	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);
2f8f8e58
 			dtrie_destroy(&(src->dtrie_root), NULL, match_mode);
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;
 	}
 }
 
932a5e8a
 static void dump_dtrie_mi(const struct dtrie_node_t *root,
 	const unsigned int branches, char *prefix, int *length, struct mi_root *reply)
 {
 	struct mi_node *crt_node;
c7a8b95e
 	unsigned int i;
 	char digit, *val = NULL;
 	int val_len = 0;
932a5e8a
 
 	/* Sanity check - should not reach here anyway */
 	if (NULL == root) {
 		LM_ERR("root dtrie is NULL\n");
 		return ;
 	}
 
         /* If data found, add a new node to the reply tree */
         if (root->data) {
 		/* Create new node and add it to the roots's kids */
 		if(!(crt_node = add_mi_node_child(&reply->node, MI_DUP_NAME, prefix,
 				*length, 0, 0)) ) {
 			LM_ERR("cannot add the child node to the tree\n");
 			return ;
 		}
 
 		/* Resolve the value of the whitelist attribute */
 		if (root->data == (void *)MARK_BLACKLIST) {
 			val = int2str(0, &val_len);
 		} else if (root->data == (void *)MARK_WHITELIST) {
 			val = int2str(1, &val_len);
 		}
 
 		/* Add the attribute to the current node */
 		if((add_mi_attr(crt_node, MI_DUP_VALUE,
 				userblacklist_whitelist_col.s,
 				userblacklist_whitelist_col.len,
 				val, val_len)) == 0) {
 			LM_ERR("cannot add attributes to the node\n");
 			return ;
 		}
         }
 
 	/* Perform a DFS search */
         for (i = 0; i < branches; i++) {
                 /* If child branch found, traverse it */
                 if (root->child[i]) {
                         if (branches == 10) {
                                 digit = i + '0';
                         } else {
                                 digit = i;
                         }
 
                         /* Push digit in prefix stack */
 			if (*length >= MAXNUMBERLEN + 1) {
 				LM_ERR("prefix length exceeds %d\n", MAXNUMBERLEN + 1);
 				return ;
 			}
                         prefix[(*length)++] = digit;
 
                         /* Recursive DFS call */
 	                dump_dtrie_mi(root->child[i], branches, prefix, length, reply);
 
                         /* Pop digit from prefix stack */
                         (*length)--;
                 }
         }
 
         return ;
 }
 
 
 static struct mi_root * check_list_mi(struct mi_root* cmd, int list_type)
 {
 	struct mi_root *tmp = NULL;
 	struct mi_node *crt_node, *node;
 	struct mi_attr *crt_attr;
 	void **nodeflags;
 	int ret = -1;
 	char *ptr;
 	char req_prefix[MAXNUMBERLEN + 1];
 	str prefix, val, attr;
 
 	node = cmd->node.kids;
 
 	/* Get the prefix number */
 	if (NULL == node)
 		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
 
 	if (NULL == node->value.s || node->value.len == 0)
 		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
 	prefix = node->value;
 	strncpy(req_prefix, prefix.s, prefix.len);
 	req_prefix[prefix.len] = '\0';
 
 	/* Check that just 1 argument is given */
 	node = node->next;
 	if (node)
 		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
 
 	/* Check that global blacklist exists */
 	if (!gnode) {
 		LM_ERR("the global blacklist is NULL\n");
 		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	}
 
 	/* Check that reply tree is successfully initialized */
 	tmp = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
 	if (!tmp) {
 		LM_ERR("the MI tree cannot be initialized!\n");
 		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	}
 
 	/* Skip over non-digits. */
 	ptr = req_prefix;
 	while (match_mode == 10 && strlen(ptr) > 0 && !isdigit(*ptr)) {
 		ptr = ptr + 1;
 	}
 
 	/* Avoids dirty reads when updating d-tree */
 	lock_get(lock);
 	nodeflags = dtrie_longest_match(gnode, ptr, strlen(ptr), NULL, match_mode);
 	if (nodeflags) {
 		if (*nodeflags == (void *)MARK_WHITELIST) {
 			LM_DBG("prefix %.*s is whitelisted in table %.*s\n",
 				prefix.len, prefix.s, globalblacklist_table.len, globalblacklist_table.s);
 			ret = MARK_WHITELIST;
 		} else if (*nodeflags == (void *)MARK_BLACKLIST) {
 			LM_DBG("prefix %.*s is blacklisted in table %.*s\n",
 				prefix.len, prefix.s, globalblacklist_table.len, globalblacklist_table.s);
 			ret = MARK_BLACKLIST;
 		}
 	}
 	else {
 		LM_DBG("prefix %.*s not found in table %.*s\n",
 			prefix.len, prefix.s, globalblacklist_table.len, globalblacklist_table.s);
 	}
 	lock_release(lock);
 
 	/* Create new node and add it to the reply roots's kids */
 	if(!(crt_node = add_mi_node_child(&tmp->node, MI_DUP_NAME,
 			prefix.s, prefix.len, 0, 0)) ) {
 		LM_ERR("cannot add the child node to the tree\n");
 		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	}
 
 	/* Resolve the value of the attribute to be returned */
 	val.s = FALSE_S;
 	val.len = FALSE_LEN;
 
 	switch (list_type) {
 		case MARK_WHITELIST:
                         attr.s = WHITELISTED_S;
                         attr.len = WHITELISTED_LEN;
 
 			if (ret == MARK_WHITELIST) {
 				val.s = TRUE_S;
 				val.len = TRUE_LEN;
 			}
 
 			break;
 		case MARK_BLACKLIST:
                         attr.s = BLACKLISTED_S;
                         attr.len = BLACKLISTED_LEN;
 
 			if (ret == MARK_BLACKLIST) {
 				val.s = TRUE_S;
 				val.len = TRUE_LEN;
 			}
 
 			break;
 		default:
 			LM_ERR("list_type not found\n");
 			return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	}
 
 	/* Add the attribute to the current node */
 	if (!(crt_attr = add_mi_attr(crt_node, MI_DUP_VALUE,
 			attr.s, attr.len, val.s, val.len))) {
 		LM_ERR("cannot add attribute to the node\n");
 		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	}
 
 	return tmp;
 }
 
 
 static struct mi_root * check_userlist_mi(struct mi_root* cmd, int list_type)
 {
 	struct mi_root *tmp = NULL;
 	struct mi_node *crt_node, *node;
 	struct mi_attr *crt_attr;
 	void **nodeflags;
 	int ret = -1;
 	int local_use_domain = 0;
 	char *ptr;
 	char req_prefix[MAXNUMBERLEN + 1];
 	str user, prefix, table, val, attr, domain;
 
 	node = cmd->node.kids;
 
 	/* Get the user number */
 	if (NULL == node)
 		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
 
 	if (NULL == node->value.s || node->value.len == 0)
 		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
 	user = node->value;
 
 	/* Get the domain name */
 	node = node->next;
 	if (NULL == node)
 		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
 
 	if (NULL == node->value.s || node->value.len == 0)
 		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
 	domain = node->value;
 
 	/* Get the prefix number */
 	node = node->next;
 	if (node) {
 		/* Got 3 params, the third one is the prefix */
 		if (NULL == node->value.s || node->value.len == 0)
 			return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
 		prefix = node->value;
 		local_use_domain = 1;
 	} else {
 		/* Got 2 params, the second one is the prefix */
 		prefix = domain;
 		local_use_domain = 0;
 	}
 
 	strncpy(req_prefix, prefix.s, prefix.len);
 	req_prefix[prefix.len] = '\0';
 
 	/* Check that a maximum of 3 arguments are given */
 	if (node)
 		node = node->next;
 	if (node)
 		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
 
 	/* Build userblacklist dtrie */
 	table = userblacklist_table;
 	LM_DBG("check entry %s for user %.*s@%.*s in table %.*s, use domain=%d\n",
 		req_prefix, user.len, user.s, domain.len, domain.s,
 		table.len, table.s, local_use_domain);
 	if (db_build_userbl_tree(&user, &domain, &table, dtrie_root, local_use_domain) < 0) {
 		LM_ERR("cannot build d-tree\n");
 		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	}
 
 	/* Check that reply tree is successfully initialized */
 	tmp = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
 	if (!tmp) {
 		LM_ERR("the MI tree cannot be initialized!\n");
 		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	}
 
 	/* Skip over non-digits. */
 	ptr = req_prefix;
 	while (match_mode == 10 && strlen(ptr) > 0 && !isdigit(*ptr)) {
 		ptr = ptr + 1;
 	}
 
 	/* Search for a match in dtrie */
 	nodeflags = dtrie_longest_match(dtrie_root, ptr, strlen(ptr), NULL, match_mode);
 	if (nodeflags) {
 		if (*nodeflags == (void *)MARK_WHITELIST) {
 			LM_DBG("user %.*s is whitelisted for prefix %.*s in table %.*s\n",
 				user.len, user.s, prefix.len, prefix.s, table.len, table.s);
 			ret = MARK_WHITELIST;
 		} else if (*nodeflags == (void *)MARK_BLACKLIST) {
 			LM_DBG("user %.*s is blacklisted for prefix %.*s in table %.*s\n",
 				user.len, user.s, prefix.len, prefix.s, table.len, table.s);
 			ret = MARK_BLACKLIST;
 		}
 	} else {
 		LM_DBG("user %.*s, prefix %.*s not found in table %.*s\n",
 			user.len, user.s, prefix.len, prefix.s, table.len, table.s);
 	}
 
 
 	/* Create new node and add it to the reply roots's kids */
 	if(!(crt_node = add_mi_node_child(&tmp->node, MI_DUP_NAME,
 			prefix.s, prefix.len, 0, 0)) ) {
 		LM_ERR("cannot add the child node to the tree\n");
 		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	}
 
 	/* Resolve the value of the attribute to be returned */
 	val.s = FALSE_S;
 	val.len = FALSE_LEN;
 
 	switch (list_type) {
 		case MARK_WHITELIST:
                         attr.s = WHITELISTED_S;
                         attr.len = WHITELISTED_LEN;
 
 			if (ret == MARK_WHITELIST) {
 				val.s = TRUE_S;
 				val.len = TRUE_LEN;
 			}
 
 			break;
 		case MARK_BLACKLIST:
                         attr.s = BLACKLISTED_S;
                         attr.len = BLACKLISTED_LEN;
 
 			if (ret == MARK_BLACKLIST) {
 				val.s = TRUE_S;
 				val.len = TRUE_LEN;
 			}
 
 			break;
 		default:
 			LM_ERR("list_type not found\n");
 			return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	}
 
 	/* Add the attribute to the current node */
 	if (!(crt_attr = add_mi_attr(crt_node, MI_DUP_VALUE,
 			attr.s, attr.len, val.s, val.len))) {
 		LM_ERR("cannot add attribute to the node\n");
 		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	}
 
 	return tmp;
 }
 
c64dbbd1
 
 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;
 }
 
 
932a5e8a
 struct mi_root * mi_dump_blacklist(struct mi_root* cmd, void* param)
 {
 	char prefix_buff[MAXNUMBERLEN + 1];
 	int length = 0;
 	struct mi_root *tmp = NULL;
 
 	/* Check that global blacklist exists */
 	if (!gnode) {
 		LM_ERR("the global blacklist is NULL\n");
 		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	}
 
 	tmp = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
 	if (!tmp) {
 		LM_ERR("the MI tree cannot be initialized!\n");
 		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
 	}
 
 	dump_dtrie_mi(gnode, match_mode, prefix_buff, &length, tmp);
 
 	return tmp;
 }
 
 
 struct mi_root * mi_check_blacklist(struct mi_root* cmd, void* param)
 {
 	return check_list_mi(cmd, MARK_BLACKLIST);
 }
 
 
 struct mi_root * mi_check_whitelist(struct mi_root* cmd, void* param)
 {
 	return check_list_mi(cmd, MARK_WHITELIST);
 }
 
 
 struct mi_root * mi_check_userblacklist(struct mi_root* cmd, void* param)
 {
 	return check_userlist_mi(cmd, MARK_BLACKLIST);
 }
 
 
 struct mi_root * mi_check_userwhitelist(struct mi_root* cmd, void* param)
 {
 	return check_userlist_mi(cmd, MARK_WHITELIST);
 }
 
 
c64dbbd1
 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
 	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 */
 
3f2ca85f
 	return mi_child_init();
c64dbbd1
 }
 
3f2ca85f
 static int userblacklist_child_initialized = 0;
932a5e8a
 static int blacklist_child_initialized = 0;
3f2ca85f
 
c64dbbd1
 static int mi_child_init(void)
 {
932a5e8a
 	/* global blacklist init */
 	if (check_globalblacklist_fixup(NULL, 0) != 0) {
 		LM_ERR("could not add global table when init the module");
 	}
 
 	/* user blacklist init */
3f2ca85f
 	if(userblacklist_child_initialized)
 		return 0;
9230c04c
 	if (userblacklist_db_open() != 0) return -1;
2f8f8e58
 	dtrie_root=dtrie_init(match_mode);
dc10e758
 	if (dtrie_root == NULL) {
 		LM_ERR("could not initialize data");
 		return -1;
 	}
932a5e8a
 
c64dbbd1
 	/* because we've added new sources during the fixup */
 	if (reload_sources() != 0) return -1;
 
3f2ca85f
 	userblacklist_child_initialized = 1;
932a5e8a
 	blacklist_child_initialized = 1;
3f2ca85f
 
c64dbbd1
 	return 0;
 }
 
 
 static void mod_destroy(void)
 {
 	destroy_source_list();
 	destroy_shmlock();
9230c04c
 	userblacklist_db_close();
2f8f8e58
 	dtrie_destroy(&dtrie_root, NULL, match_mode);
c64dbbd1
 }