modules_k/permissions/hash.c
31ccf6a2
 /*
399f501d
  * Hash functions for cached trusted and address tables
31ccf6a2
  *
54d36ac5
  * Copyright (C) 2003-2012 Juha Heinanen
31ccf6a2
  *
27642a08
  * This file is part of Kamailio, a free SIP server.
31ccf6a2
  *
27642a08
  * Kamailio is free software; you can redistribute it and/or modify
31ccf6a2
  * 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,
31ccf6a2
  * 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
  */
 
 #include <sys/types.h>
 #include <regex.h>
 #include "../../mem/shm_mem.h"
 #include "../../parser/parse_from.h"
 #include "../../ut.h"
1aedf95f
 #include "../../hashes.h"
a7884e48
 #include "../../usr_avp.h"
399f501d
 #include "../../ip_addr.h"
b0a7f212
 #include "../../pvar.h"
31ccf6a2
 #include "hash.h"
a7884e48
 #include "trusted.h"
399f501d
 #include "address.h"
31ccf6a2
 
d5b0f656
 #define perm_hash(_s)  core_hash( &(_s), 0, PERM_HASH_SIZE)
af8ef260
 
5911754e
 
a7884e48
 /* tag AVP specs */
6b19ebd7
 static int     tag_avp_type;
 static int_str tag_avp;
a7884e48
 
bbb082d5
 extern int peer_tag_mode;
 
 
a7884e48
 
 /*
  * Parse and set tag AVP specs
  */
e2cf6343
 int init_tag_avp(str *tag_avp_param)
a7884e48
 {
7d4536e2
 	pv_spec_t avp_spec;
 	unsigned short avp_flags;
 
 	if (tag_avp_param->s && tag_avp_param->len > 0) {
 		if (pv_parse_spec(tag_avp_param, &avp_spec)==0
 				|| avp_spec.type != PVT_AVP) {
 			LM_ERR("malformed or non "
 					"AVP %.*s peer_tag_avp definition\n", tag_avp_param->len, tag_avp_param->s);
 			return -1;
 		}
 		if(pv_get_avp_name(0, &avp_spec.pvp, &tag_avp, &avp_flags)!=0) {
 			LM_ERR("[%.*s]- invalid "
 					"peer_tag_avp AVP definition\n", tag_avp_param->len, tag_avp_param->s);
 			return -1;
 		}
 		tag_avp_type = avp_flags;
 	} else {
 		tag_avp.n = 0;
a7884e48
 	}
7d4536e2
 	return 0;
a7884e48
 }
 
 
 /*
  * Gets tag avp specs
  */
 void get_tag_avp(int_str *tag_avp_p, int *tag_avp_type_p)
 {
 	*tag_avp_p = tag_avp;
 	*tag_avp_type_p = tag_avp_type;
 }
 
 
31ccf6a2
 /*
  * Create and initialize a hash table
  */
 struct trusted_list** new_hash_table(void)
 {
 	struct trusted_list** ptr;
 
af8ef260
 	/* Initializing hash tables and hash table variable */
 	ptr = (struct trusted_list **)shm_malloc
 		(sizeof(struct trusted_list*) * PERM_HASH_SIZE);
31ccf6a2
 	if (!ptr) {
387847d7
 		LM_ERR("no shm memory for hash table\n");
31ccf6a2
 		return 0;
 	}
 
af8ef260
 	memset(ptr, 0, sizeof(struct trusted_list*) * PERM_HASH_SIZE);
31ccf6a2
 	return ptr;
 }
 
 
 /*
  * Release all memory allocated for a hash table
  */
 void free_hash_table(struct trusted_list** table)
 {
b96c04f6
 	if (!table)
 		return;
31ccf6a2
 
b96c04f6
 	empty_hash_table(table);
31ccf6a2
 	shm_free(table);
 }
 
 
 /* 
a7884e48
  * Add <src_ip, proto, pattern, tag> into hash table, where proto is integer
31ccf6a2
  * representation of string argument proto.
  */
399f501d
 int hash_table_insert(struct trusted_list** table, char* src_ip, 
7d4536e2
 		char* proto, char* pattern, char* tag)
31ccf6a2
 {
 	struct trusted_list *np;
 	unsigned int hash_val;
 
 	np = (struct trusted_list *) shm_malloc(sizeof(*np));
 	if (np == NULL) {
387847d7
 		LM_ERR("cannot allocate shm memory for table entry\n");
31ccf6a2
 		return -1;
 	}
 
5733379f
 	if (strcasecmp(proto, "any") == 0) {
31ccf6a2
 		np->proto = PROTO_NONE;
5733379f
 	} else if (strcasecmp(proto, "udp") == 0) {
31ccf6a2
 		np->proto = PROTO_UDP;
5733379f
 	} else if (strcasecmp(proto, "tcp") == 0) {
31ccf6a2
 		np->proto = PROTO_TCP;
5733379f
 	} else if (strcasecmp(proto, "tls") == 0) {
31ccf6a2
 		np->proto = PROTO_TLS;
5733379f
 	} else if (strcasecmp(proto, "sctp") == 0) {
31ccf6a2
 		np->proto = PROTO_SCTP;
1e2f18da
 	} else if (strcasecmp(proto, "ws") == 0) {
 		np->proto = PROTO_WS;
 	} else if (strcasecmp(proto, "wss") == 0) {
 		np->proto = PROTO_WSS;
5733379f
 	} else if (strcasecmp(proto, "none") == 0) {
7d4536e2
 		shm_free(np);
5eb091bd
 		return 1;
31ccf6a2
 	} else {
387847d7
 		LM_CRIT("unknown protocol\n");
5733379f
 		shm_free(np);
31ccf6a2
 		return -1;
 	}
5eb091bd
 
 	np->src_ip.len = strlen(src_ip);
 	np->src_ip.s = (char *) shm_malloc(np->src_ip.len);
 
 	if (np->src_ip.s == NULL) {
387847d7
 		LM_CRIT("cannot allocate shm memory for src_ip string\n");
5eb091bd
 		shm_free(np);
 		return -1;
 	}
 
 	(void) strncpy(np->src_ip.s, src_ip, np->src_ip.len);
51dc5369
 
 	if (pattern) {
7d4536e2
 		np->pattern = (char *) shm_malloc(strlen(pattern)+1);
 		if (np->pattern == NULL) {
 			LM_CRIT("cannot allocate shm memory for pattern string\n");
 			shm_free(np->src_ip.s);
 			shm_free(np);
 			return -1;
 		}
 		(void) strcpy(np->pattern, pattern);
51dc5369
 	} else {
7d4536e2
 		np->pattern = 0;
31ccf6a2
 	}
 
a7884e48
 	if (tag) {
7d4536e2
 		np->tag.len = strlen(tag);
 		np->tag.s = (char *) shm_malloc((np->tag.len) + 1);
 		if (np->tag.s == NULL) {
 			LM_CRIT("cannot allocate shm memory for pattern string\n");
 			shm_free(np->src_ip.s);
 			shm_free(np->pattern);
 			shm_free(np);
 			return -1;
 		}
 		(void) strcpy(np->tag.s, tag);
a7884e48
 	} else {
7d4536e2
 		np->tag.len = 0;
 		np->tag.s = 0;
a7884e48
 	}
 
af8ef260
 	hash_val = perm_hash(np->src_ip);
399f501d
 	np->next = table[hash_val];
 	table[hash_val] = np;
31ccf6a2
 
 	return 1;
 }
 
 
 /* 
  * Check if an entry exists in hash table that has given src_ip and protocol
96da337a
  * value and pattern that matches to From URI.  If an entry exists and tag_avp
  * has been defined, tag of the entry is added as a value to tag_avp.
b93c8142
  * Returns number of matches or -1 if none matched.
31ccf6a2
  */
5733379f
 int match_hash_table(struct trusted_list** table, struct sip_msg* msg,
7d4536e2
 		char *src_ip_c_str, int proto)
31ccf6a2
 {
 	str uri;
 	char uri_string[MAX_URI_SIZE + 1];
 	regex_t preg;
 	struct trusted_list *np;
 	str src_ip;
a7884e48
 	int_str val;
b93c8142
 	int count = 0;
31ccf6a2
 
5733379f
 	src_ip.s = src_ip_c_str;
31ccf6a2
 	src_ip.len = strlen(src_ip.s);
 
462a7b4c
 	if (IS_SIP(msg))
 	{
 		if (parse_from_header(msg) < 0) return -1;
 		uri = get_from(msg)->uri;
 		if (uri.len > MAX_URI_SIZE) {
 			LM_ERR("from URI too large\n");
 			return -1;
 		}
 		memcpy(uri_string, uri.s, uri.len);
 		uri_string[uri.len] = (char)0;
31ccf6a2
 	}
 
af8ef260
 	for (np = table[perm_hash(src_ip)]; np != NULL; np = np->next) {
54d36ac5
 	    if ((np->src_ip.len == src_ip.len) && 
 		(strncmp(np->src_ip.s, src_ip.s, src_ip.len) == 0) &&
 		((np->proto == PROTO_NONE) || (proto == PROTO_NONE) ||
 		 (np->proto == proto))) {
462a7b4c
 		if (np->pattern && IS_SIP(msg)) {
54d36ac5
 		    if (regcomp(&preg, np->pattern, REG_NOSUB)) {
 			LM_ERR("invalid regular expression\n");
 			continue;
 		    }
 		    if (regexec(&preg, uri_string, 0, (regmatch_t *)0, 0)) {
 			regfree(&preg);
 			continue;
 		    }
 		    regfree(&preg);
31ccf6a2
 		}
54d36ac5
 		/* Found a match */
 		if (tag_avp.n && np->tag.s) {
 		    val.s = np->tag;
 		    if (add_avp(tag_avp_type|AVP_VAL_STR, tag_avp, val) != 0) {
 			LM_ERR("setting of tag_avp failed\n");
 			return -1;
 		    }
 		}
 		if (!peer_tag_mode)
 		    return 1;
 		count++;
 	    }
a7884e48
 	}
b93c8142
 	if (!count)
54d36ac5
 	    return -1;
b93c8142
 	else 
54d36ac5
 	    return count;
31ccf6a2
 }
 
 
ccf3893c
 /* 
  * Print trusted entries stored in hash table 
  */
 int hash_table_mi_print(struct trusted_list** table, struct mi_node* rpl)
 {
7d4536e2
 	int i;
 	struct trusted_list *np;
 
 	for (i = 0; i < PERM_HASH_SIZE; i++) {
 		np = table[i];
 		while (np) {
 			if (addf_mi_node_child(rpl, 0, 0, 0,
 						"%4d <%.*s, %d, %s, %s>",
 						i,
 						np->src_ip.len, ZSW(np->src_ip.s),
 						np->proto,
 						np->pattern?np->pattern:"NULL",
 						np->tag.len?np->tag.s:"NULL") == 0) {
 				return -1;
 			}
 			np = np->next;
 		}
ccf3893c
 	}
7d4536e2
 	return 0;
ccf3893c
 }
 
 
31ccf6a2
 /* 
  * Free contents of hash table, it doesn't destroy the
  * hash table itself
  */
399f501d
 void empty_hash_table(struct trusted_list **table)
31ccf6a2
 {
7d4536e2
 	int i;
 	struct trusted_list *np, *next;
 
 	for (i = 0; i < PERM_HASH_SIZE; i++) {
 		np = table[i];
 		while (np) {
 			if (np->src_ip.s) shm_free(np->src_ip.s);
 			if (np->pattern) shm_free(np->pattern);
 			if (np->tag.s) shm_free(np->tag.s);
 			next = np->next;
 			shm_free(np);
 			np = next;
 		}
 		table[i] = 0;
399f501d
 	}
 }
31ccf6a2
 
399f501d
 
 /*
  * Create and initialize an address hash table
  */
 struct addr_list** new_addr_hash_table(void)
 {
7d4536e2
 	struct addr_list** ptr;
399f501d
 
7d4536e2
 	/* Initializing hash tables and hash table variable */
 	ptr = (struct addr_list **)shm_malloc
 		(sizeof(struct addr_list*) * PERM_HASH_SIZE);
 	if (!ptr) {
 		LM_ERR("no shm memory for hash table\n");
 		return 0;
 	}
399f501d
 
7d4536e2
 	memset(ptr, 0, sizeof(struct addr_list*) * PERM_HASH_SIZE);
 	return ptr;
399f501d
 }
 
 
 /*
  * Release all memory allocated for a hash table
  */
 void free_addr_hash_table(struct addr_list** table)
 {
7d4536e2
 	if (!table)
 		return;
399f501d
 
7d4536e2
 	empty_addr_hash_table(table);
 	shm_free(table);
399f501d
 }
 
 
 /* 
  * Add <grp, ip_addr, port> into hash table
  */
 int addr_hash_table_insert(struct addr_list** table, unsigned int grp,
7d4536e2
 		ip_addr_t *addr, unsigned int port, char *tagv)
399f501d
 {
7d4536e2
 	struct addr_list *np;
 	unsigned int hash_val;
 	str addr_str;
cd93b6af
 	int len;
 
71a58b47
 	len = sizeof(struct addr_list);
 	if(tagv!=NULL)
 		len += strlen(tagv) + 1;
7d4536e2
 
 	np = (struct addr_list *) shm_malloc(len);
 	if (np == NULL) {
 		LM_ERR("no shm memory for table entry\n");
 		return -1;
 	}
399f501d
 
cd93b6af
 	memset(np, 0, len);
 
7d4536e2
 	np->grp = grp;
 	memcpy(&np->addr, addr, sizeof(ip_addr_t));
 	np->port = port;
cd93b6af
 	if(tagv!=NULL)
 	{
 		np->tag.s = (char*)np + sizeof(struct addr_list);
 		np->tag.len = strlen(tagv);
 		strcpy(np->tag.s, tagv);
 	}
7d4536e2
 
 	addr_str.s = (char*)addr->u.addr;
 	addr_str.len = 4;
 	hash_val = perm_hash(addr_str);
 	np->next = table[hash_val];
 	table[hash_val] = np;
 
 	return 1;
399f501d
 }
 
 
 /* 
  * Check if an entry exists in hash table that has given group, ip_addr, and
  * port.  Port 0 in hash table matches any port.
  */
 int match_addr_hash_table(struct addr_list** table, unsigned int group,
7d4536e2
 		ip_addr_t *addr, unsigned int port)
399f501d
 {
 	struct addr_list *np;
 	str addr_str;
cd93b6af
 	avp_value_t val;
399f501d
 
7d4536e2
 	addr_str.s = (char*)addr->u.addr;
399f501d
 	addr_str.len = 4;
 
 	for (np = table[perm_hash(addr_str)]; np != NULL; np = np->next) {
7d4536e2
 		if ( (np->grp == group)
 				&& ((np->port == 0) || (np->port == port))
 				&& ip_addr_cmp(&np->addr, addr)) {
cd93b6af
 
 			if (tag_avp.n && np->tag.s) {
 				val.s = np->tag;
 				if (add_avp(tag_avp_type|AVP_VAL_STR, tag_avp, val) != 0) {
 					LM_ERR("setting of tag_avp failed\n");
 					return -1;
 				}
 			}
 
7d4536e2
 			return 1;
 		}
399f501d
 	}
 
 	return -1;
 }
 
 
ef45c992
 /* 
  * Check if an ip_addr/port entry exists in hash table in any group.
  * Returns first group in which ip_addr/port is found.
  * Port 0 in hash table matches any port. 
  */
 int find_group_in_addr_hash_table(struct addr_list** table,
7d4536e2
 		ip_addr_t *addr, unsigned int port)
ef45c992
 {
 	struct addr_list *np;
 	str addr_str;
 
7d4536e2
 	addr_str.s = (char*)addr->u.addr;
ef45c992
 	addr_str.len = 4;
 
 	for (np = table[perm_hash(addr_str)]; np != NULL; np = np->next) {
7d4536e2
 		if (((np->port == 0) || (np->port == port))
 				&& ip_addr_cmp(&np->addr, addr)) {
 			return np->grp;
 		}
ef45c992
 	}
 
 	return -1;
 }
 
 
ccf3893c
 /* 
  * Print addresses stored in hash table 
  */
 int addr_hash_table_mi_print(struct addr_list** table, struct mi_node* rpl)
 {
7d4536e2
 	int i;
 	struct addr_list *np;
 
 	for (i = 0; i < PERM_HASH_SIZE; i++) {
 		np = table[i];
 		while (np) {
 			if (addf_mi_node_child(rpl, 0, 0, 0,
 						"%4d <%u, %s, %u> [%s]",
 						i, np->grp, ip_addr2a(&np->addr),
 						np->port, (np->tag.s==NULL)?"":np->tag.s) == 0)
 				return -1;
 			np = np->next;
 		}
ccf3893c
 	}
7d4536e2
 	return 0;
ccf3893c
 }
 
 
399f501d
 /* 
  * Free contents of hash table, it doesn't destroy the
  * hash table itself
  */
 void empty_addr_hash_table(struct addr_list **table)
 {
7d4536e2
 	int i;
 	struct addr_list *np, *next;
 
 	for (i = 0; i < PERM_HASH_SIZE; i++) {
 		np = table[i];
 		while (np) {
 			next = np->next;
 			shm_free(np);
 			np = next;
 		}
 		table[i] = 0;
31ccf6a2
 	}
 }
cf22215a
 
 
 /*
  * Create and initialize a subnet table
  */
 struct subnet* new_subnet_table(void)
 {
7d4536e2
 	struct subnet* ptr;
 
 	/* subnet record [PERM_MAX_SUBNETS] contains in its grp field 
 	   the number of subnet records in the subnet table */
 	ptr = (struct subnet *)shm_malloc
 		(sizeof(struct subnet) * (PERM_MAX_SUBNETS + 1));
 	if (!ptr) {
 		LM_ERR("no shm memory for subnet table\n");
 		return 0;
 	}
cd93b6af
 	memset(ptr, 0, sizeof(struct subnet) * (PERM_MAX_SUBNETS + 1));
7d4536e2
 	return ptr;
cf22215a
 }
 
7d4536e2
 
cf22215a
 /* 
cd93b6af
  * Add <grp, subnet, mask, port, tag> into subnet table so that table is
cf22215a
  * kept in increasing ordered according to grp.
  */
 int subnet_table_insert(struct subnet* table, unsigned int grp,
7d4536e2
 		ip_addr_t *subnet, unsigned int mask,
 		unsigned int port, char *tagv)
cf22215a
 {
7d4536e2
 	int i;
 	unsigned int count;
cd93b6af
 	str tags;
cf22215a
 
7d4536e2
 	count = table[PERM_MAX_SUBNETS].grp;
cf22215a
 
7d4536e2
 	if (count == PERM_MAX_SUBNETS) {
 		LM_CRIT("subnet table is full\n");
 		return 0;
 	}
cf22215a
 
cd93b6af
 	if(tagv==NULL)
 	{
 		tags.s = NULL;
 		tags.len = 0;
 	} else {
 		tags.len = strlen(tagv);
 		tags.s = (char*)shm_malloc(tags.len+1);
 		if(tags.s==NULL)
 		{
 			LM_ERR("No more shared memory\n");
 			return 0;
 		}
 		strcpy(tags.s, tagv);
 	}
 
7d4536e2
 	i = count - 1;
cf22215a
 
7d4536e2
 	while ((i >= 0) && (table[i].grp > grp)) {
 		table[i + 1] = table[i];
 		i--;
 	}
cf22215a
 
7d4536e2
 	table[i + 1].grp = grp;
 	memcpy(&table[i + 1].subnet, subnet, sizeof(ip_addr_t));
 	table[i + 1].port = port;
 	table[i + 1].mask = mask;
cd93b6af
 	table[i + 1].tag = tags;
cf22215a
 
7d4536e2
 	table[PERM_MAX_SUBNETS].grp = count + 1;
cf22215a
 
7d4536e2
 	return 1;
cf22215a
 }
 
 
 /* 
  * Check if an entry exists in subnet table that matches given group, ip_addr,
  * and port.  Port 0 in subnet table matches any port.
  */
 int match_subnet_table(struct subnet* table, unsigned int grp,
7d4536e2
 		ip_addr_t *addr, unsigned int port)
cf22215a
 {
7d4536e2
 	unsigned int count, i;
cd93b6af
 	avp_value_t val;
cf22215a
 
7d4536e2
 	count = table[PERM_MAX_SUBNETS].grp;
cf22215a
 
7d4536e2
 	i = 0;
 	while ((i < count) && (table[i].grp < grp))
 		i++;
cf22215a
 
7d4536e2
 	if (i == count) return -1;
 
 	while ((i < count) && (table[i].grp == grp)) {
 		if (((table[i].port == port) || (table[i].port == 0))
 			&& (ip_addr_match_net(addr, &table[i].subnet, table[i].mask)==0))
cd93b6af
 		{
 			if (tag_avp.n && table[i].tag.s) {
 				val.s = table[i].tag;
 				if (add_avp(tag_avp_type|AVP_VAL_STR, tag_avp, val) != 0) {
 					LM_ERR("setting of tag_avp failed\n");
 					return -1;
 				}
 			}
7d4536e2
 			return 1;
cd93b6af
 		}
7d4536e2
 		i++;
 	}
cf22215a
 
7d4536e2
 	return -1;
cf22215a
 }
 
 
ef45c992
 /* 
  * Check if an entry exists in subnet table that matches given ip_addr,
  * and port.  Port 0 in subnet table matches any port.  Return group of
  * first match or -1 if no match is found.
  */
 int find_group_in_subnet_table(struct subnet* table,
7d4536e2
 		ip_addr_t *addr, unsigned int port)
ef45c992
 {
7d4536e2
 	unsigned int count, i;
ef45c992
 
7d4536e2
 	count = table[PERM_MAX_SUBNETS].grp;
ef45c992
 
7d4536e2
 	i = 0;
 	while (i < count) {
 		if ( ((table[i].port == port) || (table[i].port == 0))
 			&& (ip_addr_match_net(addr, &table[i].subnet, table[i].mask)==0))
 			return table[i].grp;
 		i++;
 	}
ef45c992
 
7d4536e2
 	return -1;
ef45c992
 }
 
 
cf22215a
 /* 
  * Print subnets stored in subnet table 
  */
 int subnet_table_mi_print(struct subnet* table, struct mi_node* rpl)
 {
7d4536e2
 	unsigned int count, i;
 
 	count = table[PERM_MAX_SUBNETS].grp;
 
 	for (i = 0; i < count; i++) {
 		if (addf_mi_node_child(rpl, 0, 0, 0,
 					"%4d <%u, %s, %u, %u> [%s]",
 					i, table[i].grp, ip_addr2a(&table[i].subnet),
 					table[i].mask, table[i].port,
 					(table[i].tag.s==NULL)?"":table[i].tag.s) == 0) {
 			return -1;
 		}
cf22215a
 	}
7d4536e2
 	return 0;
cf22215a
 }
 
 
 /* 
  * Empty contents of subnet table
  */
 void empty_subnet_table(struct subnet *table)
 {
cd93b6af
 	int i;
7d4536e2
 	table[PERM_MAX_SUBNETS].grp = 0;
cd93b6af
 	for(i=0; i<PERM_MAX_SUBNETS; i++)
 	{
 		if(table[i].tag.s!=NULL)
 		{
 			shm_free(table[i].tag.s);
 			table[i].tag.s = NULL;
 			table[i].tag.len =0;
 		}
 	}
cf22215a
 }
 
 
 /*
  * Release memory allocated for a subnet table
  */
 void free_subnet_table(struct subnet* table)
 {
cd93b6af
 	int i;
7d4536e2
 	if (!table)
 		return;
cd93b6af
 	for(i=0; i<PERM_MAX_SUBNETS; i++)
 	{
 		if(table[i].tag.s!=NULL)
 		{
 			shm_free(table[i].tag.s);
 			table[i].tag.s = NULL;
 			table[i].tag.len =0;
 		}
 	}
cf22215a
 
7d4536e2
 	shm_free(table);
cf22215a
 }