Browse Source

userblacklist: adding new fifo commands

- adding dump_blacklist, check_blacklist, check_whitelist, check_userblacklist,
  check_userwhitelist commands
Stefan Mititelu 10 years ago
parent
commit
932a5e8a45
2 changed files with 535 additions and 22 deletions
  1. 130 17
      modules/userblacklist/doc/userblacklist_admin.xml
  2. 405 5
      modules/userblacklist/userblacklist.c

+ 130 - 17
modules/userblacklist/doc/userblacklist_admin.xml

@@ -383,25 +383,138 @@ if (!check_whitelist("globalblacklist")) {
 </section>
 
 <section>
-	    <title><acronym>MI</acronym> Commands</title>
+	<title><acronym>MI</acronym> Commands</title>
 		<section>
-	    <title>
-		<function moreinfo="none">reload_blacklist</function>
-	    </title>
-	    <para>
-		Reload the internal global blacklist cache. This is necessary after
-		the database tables for the global blacklist have been changed.
-	    </para>
-	<example>
-		<title><function>reload_blacklist</function> usage</title>
-		<programlisting format="linespecific">
-...
-&ctltool; fifo reload_blacklist
-...
-		</programlisting>
-	    </example>
-	</section>
+			<title>
+				<function moreinfo="none">reload_blacklist</function>
+			</title>
+			<para>
+				Reload the internal global blacklist cache. This is necessary after
+				the database tables for the global blacklist have been changed.
+			</para>
+			<example>
+				<title><function>reload_blacklist</function> usage</title>
+				<programlisting format="linespecific">
+					...
+					&ctltool; fifo reload_blacklist
+					...
+				</programlisting>
+	    		</example>
+		</section>
+
+		<section>
+			<title>
+				<function moreinfo="none">dump_blacklist</function>
+			</title>
+			<para>
+				Dumps the default, in memory, global_blacklist content to stdout.
+				Note that a reload_blacklist should be issued before, 
+				in order to see the latest content of the database.
+			</para>
+			<example>
+				<title><function>dump_blacklist</function> usage</title>
+				<programlisting format="linespecific">
+					...
+					&ctltool; fifo reload_blacklist
+					&ctltool; fifo dump_blacklist
+					...
+				</programlisting>
+	    		</example>
+		</section>
+
+		<section>
+			<title>
+				<function moreinfo="none">check_blacklist prefix</function>
+			</title>
+			<para>
+				Searches in the default, in memory, global list. 
+				Finds the longest prefix that matches the given prefix parameter. 
+				Returns true if the prefix is found and the whitelist is <emphasis>not</emphasis> set. 
+				Returns false otherwise - either prefix found and whitelist set or prefix not found. 
+				Note that a reload_blacklist should be issued before, 
+				in order to check through the latest content of the database.
+			</para>
+			<example>
+				<title><function>check_blacklist</function> usage</title>
+				<programlisting format="linespecific">
+					...
+					&ctltool; fifo reload_blacklist
+					&ctltool; fifo check_blacklist prefix
+					...
+				</programlisting>
+	    		</example>
+		</section>
+
+		<section>
+			<title>
+				<function moreinfo="none">check_whitelist prefix</function>
+			</title>
+			<para>
+				Searches in the default, in memory, global list. 
+				Finds the longest prefix that matches the given prefix parameter. 
+				Returns true if the prefix is found and the whitelist is set. 
+				Returns false otherwise - either prefix found and whitelist 
+				<emphasis>not</emphasis> set or prefix not found. 
+				Note that a reload_blacklist should be issued before, 
+				in order to check through the latest content of the database.
+			</para>
+			<example>
+				<title><function>check_whitelist</function> usage</title>
+				<programlisting format="linespecific">
+					...
+					&ctltool; fifo reload_blacklist
+					&ctltool; fifo check_whitelist prefix
+					...
+				</programlisting>
+	    		</example>
+		</section>
+
+		<section>
+			<title>
+				<function moreinfo="none">check_userblacklist user [domain] prefix</function>
+			</title>
+			<para>
+				Searches in the default user list table.
+				Finds the longest prefix for the given user@domain that matches the given prefix parameter. 
+				Returns true if the prefix is found and the whitelist is <emphasis>not</emphasis> set. 
+				Returns false otherwise - either prefix found and whitelist set or prefix not found. 
+				Note that the domain parameter is optional. 
+				If not given, the second parameter is the considered to be the prefix. 
+			</para>
+			<example>
+				<title><function>check_userblacklist</function> usage</title>
+				<programlisting format="linespecific">
+					...
+					&ctltool; fifo check_userblacklist user [domain] prefix
+					...
+				</programlisting>
+	    		</example>
+		</section>
+
+		<section>
+			<title>
+				<function moreinfo="none">check_userwhitelist user [domain] prefix</function>
+			</title>
+			<para>
+				Searches in the default user list table.
+				Finds the longest prefix for the given user@domain that matches the given prefix parameter. 
+				Returns true if the prefix is found and the whitelist is set. 
+				Returns false otherwise - either prefix found and whitelist 
+				<emphasis>not</emphasis> set or prefix not found. 
+				Note that the domain parameter is optional. 
+				If not given, the second parameter is the considered to be the prefix. 
+			</para>
+			<example>
+				<title><function>check_userwhitelist</function> usage</title>
+				<programlisting format="linespecific">
+					...
+					&ctltool; fifo check_userwhitelist user [domain] prefix
+					...
+				</programlisting>
+	    		</example>
+		</section>
 </section>
+
     <section>
 	<title>Installation and Running</title>
 	<section>

+ 405 - 5
modules/userblacklist/userblacklist.c

@@ -58,6 +58,14 @@ MODULE_VERSION
 
 #define MAXNUMBERLEN 31
 
+#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)
 
 typedef struct _avp_check
 {
@@ -71,7 +79,7 @@ struct check_blacklist_fs_t {
 };
 
 str userblacklist_db_url = str_init(DEFAULT_RODB_URL);
-int use_domain   = 0;
+int use_domain = 0;
 int match_mode = 10; /* numeric */
 static struct dtrie_node_t *gnode = NULL;
 
@@ -100,6 +108,11 @@ static void mod_destroy(void);
 
 /* --- fifo functions */
 struct mi_root * mi_reload_blacklist(struct mi_root* cmd, void* param);  /* usage: kamctl fifo reload_blacklist */
+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 */
 
 
 static cmd_export_t cmds[]={
@@ -123,15 +136,20 @@ static param_export_t params[] = {
 	userblacklist_DB_COLS
 	globalblacklist_DB_COLS
 	{ "use_domain",      INT_PARAM, &use_domain },
-	{ "match_mode",	     INT_PARAM, &match_mode},
-	{ 0, 0, 0}
+	{ "match_mode",	     INT_PARAM, &match_mode },
+	{ 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}
+	{ "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 }
 };
 
 
@@ -673,6 +691,332 @@ static void destroy_shmlock(void)
 	}
 }
 
+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;
+        unsigned int i;
+        char digit, *val;
+	int val_len;
+
+	/* 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;
+}
+
 
 struct mi_root * mi_reload_blacklist(struct mi_root* cmd, void* param)
 {
@@ -687,6 +1031,54 @@ struct mi_root * mi_reload_blacklist(struct mi_root* cmd, void* param)
 }
 
 
+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);
+}
+
+
 static int mod_init(void)
 {
 	if(register_mi_mod(exports.name, mi_cmds)!=0)
@@ -710,11 +1102,17 @@ static int child_init(int rank)
 	return mi_child_init();
 }
 
-
 static int userblacklist_child_initialized = 0;
+static int blacklist_child_initialized = 0;
 
 static int mi_child_init(void)
 {
+	/* global blacklist init */
+	if (check_globalblacklist_fixup(NULL, 0) != 0) {
+		LM_ERR("could not add global table when init the module");
+	}
+
+	/* user blacklist init */
 	if(userblacklist_child_initialized)
 		return 0;
 	if (userblacklist_db_open() != 0) return -1;
@@ -723,10 +1121,12 @@ static int mi_child_init(void)
 		LM_ERR("could not initialize data");
 		return -1;
 	}
+
 	/* because we've added new sources during the fixup */
 	if (reload_sources() != 0) return -1;
 
 	userblacklist_child_initialized = 1;
+	blacklist_child_initialized = 1;
 
 	return 0;
 }