/*
* Hash functions for cached trusted and address tables
*
* Copyright (C) 2003-2006 Juha Heinanen
*
* This file is part of openser, a free SIP server.
*
* openser is free software; you can redistribute it and/or modify
* 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
*
* openser is distributed in the hope that it will be useful,
* 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"
#include "../../hash_func.h"
#include "../../usr_avp.h"
#include "../../ip_addr.h"
#include "hash.h"
#include "trusted.h"
#include "address.h"
#define perm_hash(_s) core_hash( &(_s), 0, PERM_HASH_SIZE)
/* tag AVP specs */
static int tag_avp_type;
static int_str tag_avp;
static str tag_str;
/*
* Parse and set tag AVP specs
*/
int init_tag_avp(char *tag_avp_param)
{
if (tag_avp_param && *tag_avp_param) {
tag_str.s = tag_avp_param;
tag_str.len = strlen(tag_str.s);
if (parse_avp_spec( &tag_str, &tag_avp_type, &tag_avp)<0) {
LOG(L_CRIT,"ERROR:permissions:init_tag_avp: "
"invalid tag AVP spec \"%s\"\n", tag_avp_param);
return -1;
}
} else {
tag_avp.n = 0;
tag_avp_type = 0;
}
return 0;
}
/*
* 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;
}
/*
* Create and initialize a hash table
*/
struct trusted_list** new_hash_table(void)
{
struct trusted_list** ptr;
/* Initializing hash tables and hash table variable */
ptr = (struct trusted_list **)shm_malloc
(sizeof(struct trusted_list*) * PERM_HASH_SIZE);
if (!ptr) {
LOG(L_ERR, "new_hash_table(): No memory for hash table\n");
return 0;
}
memset(ptr, 0, sizeof(struct trusted_list*) * PERM_HASH_SIZE);
return ptr;
}
/*
* Release all memory allocated for a hash table
*/
void free_hash_table(struct trusted_list** table)
{
if (!table)
return;
empty_hash_table(table);
shm_free(table);
}
/*
* Add <src_ip, proto, pattern, tag> into hash table, where proto is integer
* representation of string argument proto.
*/
int hash_table_insert(struct trusted_list** table, char* src_ip,
char* proto, char* pattern, char* tag)
{
struct trusted_list *np;
unsigned int hash_val;
np = (struct trusted_list *) shm_malloc(sizeof(*np));
if (np == NULL) {
LOG(L_CRIT, "hash_table_insert(): Cannot allocate shm memory "
"for table entry\n");
return -1;
}
if (strcmp(proto, "any") == 0) {
np->proto = PROTO_NONE;
} else if (strcmp(proto, "udp") == 0) {
np->proto = PROTO_UDP;
} else if (strcmp(proto, "tcp") == 0) {
np->proto = PROTO_TCP;
} else if (strcmp(proto, "tls") == 0) {
np->proto = PROTO_TLS;
} else if (strcmp(proto, "sctp") == 0) {
np->proto = PROTO_SCTP;
} else if (strcmp(proto, "none") == 0) {
shm_free(np);
return 1;
} else {
LOG(L_CRIT, "hash_table_insert(): Unknown protocol\n");
shm_free(np);
return -1;
}
np->src_ip.len = strlen(src_ip);
np->src_ip.s = (char *) shm_malloc(np->src_ip.len);
if (np->src_ip.s == NULL) {
LOG(L_CRIT, "hash_table_insert(): Cannot allocate memory for src_ip "
"string\n");
shm_free(np);
return -1;
}
(void) strncpy(np->src_ip.s, src_ip, np->src_ip.len);
if (pattern) {
np->pattern = (char *) shm_malloc(strlen(pattern)+1);
if (np->pattern == NULL) {
LOG(L_CRIT, "hash_table_insert(): Cannot allocate memory "
"for pattern string\n");
shm_free(np->src_ip.s);
shm_free(np);
return -1;
}
(void) strcpy(np->pattern, pattern);
} else {
np->pattern = 0;
}
if (tag) {
np->tag.len = strlen(tag);
np->tag.s = (char *) shm_malloc((np->tag.len) + 1);
if (np->tag.s == NULL) {
LOG(L_CRIT, "hash_table_insert(): Cannot allocate 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);
} else {
np->tag.len = 0;
np->tag.s = 0;
}
hash_val = perm_hash(np->src_ip);
np->next = table[hash_val];
table[hash_val] = np;
return 1;
}
/*
* Check if an entry exists in hash table that has given src_ip and protocol
* value and pattern that matches to From URI. If, assign
*/
int match_hash_table(struct trusted_list** table, struct sip_msg* msg)
{
str uri;
char uri_string[MAX_URI_SIZE + 1];
regex_t preg;
struct trusted_list *np;
str src_ip;
int_str val;
src_ip.s = ip_addr2a(&msg->rcv.src_ip);
src_ip.len = strlen(src_ip.s);
if (parse_from_header(msg) < 0) return -1;
uri = get_from(msg)->uri;
if (uri.len > MAX_URI_SIZE) {
LOG(L_ERR, "match_hash_table(): From URI too large\n");
return -1;
}
memcpy(uri_string, uri.s, uri.len);
uri_string[uri.len] = (char)0;
for (np = table[perm_hash(src_ip)]; np != NULL; np = np->next) {
if ((np->src_ip.len == src_ip.len) &&
(strncasecmp(np->src_ip.s, src_ip.s, src_ip.len) == 0) &&
((np->proto == PROTO_NONE) || (np->proto == msg->rcv.proto))) {
if (!(np->pattern)) goto found;
if (regcomp(&preg, np->pattern, REG_NOSUB)) {
LOG(L_ERR, "match_hash_table(): Error in regular expression\n");
return -1;
}
if (regexec(&preg, uri_string, 0, (regmatch_t *)0, 0)) {
regfree(&preg);
} else {
regfree(&preg);
goto found;
}
}
}
return -1;
found:
if (tag_avp.n && np->tag.s) {
val.s = np->tag;
if (add_avp(tag_avp_type|AVP_VAL_STR, tag_avp, val) != 0) {
LOG(L_ERR, "match_hash_table(): ERROR: setting of "
"tag_avp failed\n");
return -1;
}
}
return 1;
}
/*
* Print trusted entries stored in hash table
*/
int hash_table_mi_print(struct trusted_list** table, struct mi_node* rpl)
{
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;
}
}
return 0;
}
/*
* Free contents of hash table, it doesn't destroy the
* hash table itself
*/
void empty_hash_table(struct trusted_list **table)
{
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;
}
}
/*
* Create and initialize an address hash table
*/
struct addr_list** new_addr_hash_table(void)
{
struct addr_list** ptr;
/* Initializing hash tables and hash table variable */
ptr = (struct addr_list **)shm_malloc
(sizeof(struct addr_list*) * PERM_HASH_SIZE);
if (!ptr) {
LOG(L_ERR, "permissions:new_addr_hash_table(): "
"No memory for hash table\n");
return 0;
}
memset(ptr, 0, sizeof(struct addr_list*) * PERM_HASH_SIZE);
return ptr;
}
/*
* Release all memory allocated for a hash table
*/
void free_addr_hash_table(struct addr_list** table)
{
if (!table)
return;
empty_addr_hash_table(table);
shm_free(table);
}
/*
* Add <grp, ip_addr, port> into hash table
*/
int addr_hash_table_insert(struct addr_list** table, unsigned int grp,
unsigned int ip_addr, unsigned int port)
{
struct addr_list *np;
unsigned int hash_val;
str addr_str;
np = (struct addr_list *) shm_malloc(sizeof(*np));
if (np == NULL) {
LOG(L_CRIT, "permissions:addr_hash_table_insert(): "
"Cannot allocate shm memory for table entry\n");
return -1;
}
np->grp = grp;
np->ip_addr = ip_addr;
np->port = port;
addr_str.s = (char *)(&ip_addr);
addr_str.len = 4;
hash_val = perm_hash(addr_str);
np->next = table[hash_val];
table[hash_val] = np;
return 1;
}
/*
* 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,
unsigned int ip_addr, unsigned int port)
{
struct addr_list *np;
str addr_str;
addr_str.s = (char *)(&ip_addr);
addr_str.len = 4;
for (np = table[perm_hash(addr_str)]; np != NULL; np = np->next) {
if ((np->ip_addr == ip_addr) && (np->grp == group) &&
((np->port == 0) || (np->port == port))) {
return 1;
}
}
return -1;
}
/*
* Print addresses stored in hash table
*/
int addr_hash_table_mi_print(struct addr_list** table, struct mi_node* rpl)
{
int i;
struct addr_list *np;
struct ip_addr addr;
for (i = 0; i < PERM_HASH_SIZE; i++) {
np = table[i];
while (np) {
addr.af = AF_INET;
addr.len = 4;
addr.u.addr32[0] = np->ip_addr;
if (addf_mi_node_child(rpl, 0, 0, 0,
"%4d <%u, %s, %u>",
i, np->grp, ip_addr2a(&addr),
np->port) == 0)
return -1;
np = np->next;
}
}
return 0;
}
/*
* Free contents of hash table, it doesn't destroy the
* hash table itself
*/
void empty_addr_hash_table(struct addr_list **table)
{
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;
}
}
/*
* Create and initialize a subnet table
*/
struct subnet* new_subnet_table(void)
{
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) {
LOG(L_ERR, "permissions:new_subnet_table(): "
"No memory for subnet table\n");
return 0;
}
ptr[PERM_MAX_SUBNETS].grp = 0;
return ptr;
}
/*
* Add <grp, subnet, mask, port> into subnet table so that table is
* kept in increasing ordered according to grp.
*/
int subnet_table_insert(struct subnet* table, unsigned int grp,
unsigned int subnet, unsigned int mask,
unsigned int port)
{
int i;
unsigned int count;
count = table[PERM_MAX_SUBNETS].grp;
if (count == PERM_MAX_SUBNETS) {
LOG(L_CRIT, "permissions:subnet_table_insert(): "
"Subnet table is full\n");
return 0;
}
mask = 32 - mask;
subnet = subnet << mask;
i = count - 1;
while ((i >= 0) && (table[i].grp > grp)) {
table[i + 1] = table[i];
i--;
}
table[i + 1].grp = grp;
table[i + 1].subnet = subnet;
table[i + 1].port = port;
table[i + 1].mask = mask;
table[PERM_MAX_SUBNETS].grp = count + 1;
return 1;
}
/*
* 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,
unsigned int ip_addr, unsigned int port)
{
unsigned int count, i, subnet;
count = table[PERM_MAX_SUBNETS].grp;
i = 0;
while ((i < count) && (table[i].grp < grp))
i++;
if (i == count) return -1;
while ((i < count) && (table[i].grp == grp)) {
subnet = ip_addr << table[i].mask;
if ((table[i].subnet == subnet) &&
((table[i].port == port) || (table[i].port == 0)))
return 1;
i++;
}
return -1;
}
/*
* Print subnets stored in subnet table
*/
int subnet_table_mi_print(struct subnet* table, struct mi_node* rpl)
{
unsigned int count, i;
struct ip_addr addr;
count = table[PERM_MAX_SUBNETS].grp;
for (i = 0; i < count; i++) {
addr.af = AF_INET;
addr.len = 4;
addr.u.addr32[0] = table[i].subnet >> table[i].mask;
if (addf_mi_node_child(rpl, 0, 0, 0,
"%4d <%u, %s, %u, %u>",
i, table[i].grp, ip_addr2a(&addr),
32 - table[i].mask, table[i].port) == 0) {
return -1;
}
}
return 0;
}
/*
* Empty contents of subnet table
*/
void empty_subnet_table(struct subnet *table)
{
table[PERM_MAX_SUBNETS].grp = 0;
}
/*
* Release memory allocated for a subnet table
*/
void free_subnet_table(struct subnet* table)
{
if (!table)
return;
shm_free(table);
}
syntax highlighted by Code2HTML, v. 0.9.1