/*
*
* allow_address related functions
*
* Copyright (C) 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
*
* History:
* --------
* 2006-09-01 Introduced allow_address function
*/
#include <sys/types.h>
#include <regex.h>
#include <string.h>
#include <arpa/inet.h>
#include "permissions.h"
#include "hash.h"
#include "unixsock.h"
#include "../../config.h"
#include "../../db/db.h"
#include "../../ip_addr.h"
#include "../../mem/shm_mem.h"
#include "../../parser/msg_parser.h"
#include "../../parser/parse_from.h"
#include "../../usr_avp.h"
#include "../../items.h"
#include "../../ut.h"
#define TABLE_VERSION 3
struct addr_list ***addr_hash_table; /* Ptr to current hash table ptr */
struct addr_list **addr_hash_table_1; /* Pointer to hash table 1 */
struct addr_list **addr_hash_table_2; /* Pointer to hash table 2 */
struct subnet **subnet_table; /* Ptr to current subnet table */
struct subnet *subnet_table_1; /* Ptr to subnet table 1 */
struct subnet *subnet_table_2; /* Ptr to subnet table 2 */
/* Address group of allow_address queries */
static unsigned int addr_group = 0;
static db_con_t* db_handle = 0;
static db_func_t perm_dbf;
/*
* Reload addr table to new hash table and when done, make new hash table
* current one.
*/
int reload_address_table(void)
{
db_key_t cols[4];
db_res_t* res = NULL;
db_row_t* row;
db_val_t* val;
struct addr_list **new_hash_table;
struct subnet *new_subnet_table;
int i;
struct in_addr ip_addr;
cols[0] = grp_col;
cols[1] = ip_addr_col;
cols[2] = mask_col;
cols[3] = port_col;
if (perm_dbf.use_table(db_handle, address_table) < 0) {
LOG(L_ERR, "ERROR: permissions: reload_address_table():"
" Error while trying to use address table\n");
return -1;
}
if (perm_dbf.query(db_handle, NULL, 0, NULL, cols, 0, 4, 0, &res) < 0) {
LOG(L_ERR, "ERROR: permissions: reload_address_table():"
" Error while querying database\n");
return -1;
}
/* Choose new hash table and free its old contents */
if (*addr_hash_table == addr_hash_table_1) {
empty_addr_hash_table(addr_hash_table_2);
new_hash_table = addr_hash_table_2;
} else {
empty_addr_hash_table(addr_hash_table_1);
new_hash_table = addr_hash_table_1;
}
/* Choose new subnet table */
if (*subnet_table == subnet_table_1) {
empty_subnet_table(subnet_table_2);
new_subnet_table = subnet_table_2;
} else {
empty_subnet_table(subnet_table_1);
new_subnet_table = subnet_table_1;
}
row = RES_ROWS(res);
DBG("Number of rows in address table: %d\n", RES_ROW_N(res));
for (i = 0; i < RES_ROW_N(res); i++) {
val = ROW_VALUES(row + i);
if ((ROW_N(row + i) == 4) &&
(VAL_TYPE(val) == DB_INT) && !VAL_NULL(val) &&
(VAL_TYPE(val + 1) == DB_STRING) && !VAL_NULL(val + 1) &&
inet_aton((char *)VAL_STRING(val + 1), &ip_addr) != 0 &&
(VAL_TYPE(val + 2) == DB_INT) && !VAL_NULL(val + 2) &&
((unsigned int)VAL_INT(val + 2) > 0) &&
((unsigned int)VAL_INT(val + 2) <= 32) &&
(VAL_TYPE(val + 3) == DB_INT) && !VAL_NULL(val + 3)) {
if ((unsigned int)VAL_INT(val + 2) == 32) {
if (addr_hash_table_insert(new_hash_table,
(unsigned int)VAL_INT(val),
(unsigned int)ip_addr.s_addr,
(unsigned int)VAL_INT(val + 3))
== -1) {
LOG(L_ERR, "ERROR: permissions: "
"address_reload(): Hash table problem\n");
perm_dbf.free_result(db_handle, res);
return -1;
}
DBG("Tuple <%u, %s, %u> inserted into address hash "
"table\n", (unsigned int)VAL_INT(val),
(char *)VAL_STRING(val + 1),
(unsigned int)VAL_INT(val + 2));
} else {
if (subnet_table_insert(new_subnet_table,
(unsigned int)VAL_INT(val),
(unsigned int)ip_addr.s_addr,
(unsigned int)VAL_INT(val + 2),
(unsigned int)VAL_INT(val + 3))
== -1) {
LOG(L_ERR, "ERROR: permissions: "
"address_reload(): subnet table problem\n");
perm_dbf.free_result(db_handle, res);
return -1;
}
DBG("Tuple <%u, %s, %u, %u> inserted into subnet "
"table\n", (unsigned int)VAL_INT(val),
(char *)VAL_STRING(val + 1),
(unsigned int)VAL_INT(val + 2),
(unsigned int)VAL_INT(val + 3));
}
} else {
LOG(L_ERR, "ERROR: permissions: address_reload():"
" Database problem\n");
perm_dbf.free_result(db_handle, res);
return -1;
}
}
perm_dbf.free_result(db_handle, res);
*addr_hash_table = new_hash_table;
*subnet_table = new_subnet_table;
DBG("Address table reloaded successfully.\n");
return 1;
}
/*
* Initialize data structures
*/
int init_addresses(void)
{
int ver;
str name;
if (!db_url) {
LOG(L_INFO, "db_url parameter of permissions module not set, "
"disabling allow_addr\n");
return 0;
} else {
if (bind_dbmod(db_url, &perm_dbf) < 0) {
LOG(L_ERR, "ERROR: permissions: init_addresses: "
"load a database support module\n");
return -1;
}
if (!DB_CAPABILITY(perm_dbf, DB_CAP_QUERY)) {
LOG(L_ERR, "ERROR: permissions: init_addresses: "
"Database module does not implement 'query' function\n");
return -1;
}
}
addr_hash_table_1 = addr_hash_table_2 = 0;
addr_hash_table = 0;
db_handle = perm_dbf.init(db_url);
if (!db_handle) {
LOG(L_ERR, "ERROR: permissions: init_addresses():"
" Unable to connect database\n");
return -1;
}
name.s = address_table;
name.len = strlen(address_table);
ver = table_version(&perm_dbf, db_handle, &name);
if (ver < 0) {
LOG(L_ERR, "permissions:init_addresses(): Error while querying "
"table version\n");
perm_dbf.close(db_handle);
return -1;
} else if (ver < TABLE_VERSION) {
LOG(L_ERR, "permissions:init_addresses(): "
"Invalid table version %d "
"- expected %d\n", ver,TABLE_VERSION);
perm_dbf.close(db_handle);
return -1;
}
if (init_address_unixsock() < 0) {
LOG(L_ERR, "permissions:init_addr(): Error while initializing "
"unixsock interface\n");
perm_dbf.close(db_handle);
return -1;
}
addr_hash_table_1 = new_addr_hash_table();
if (!addr_hash_table_1) return -1;
addr_hash_table_2 = new_addr_hash_table();
if (!addr_hash_table_2) goto error;
addr_hash_table = (struct addr_list ***)shm_malloc
(sizeof(struct addr_list **));
if (!addr_hash_table) goto error;
*addr_hash_table = addr_hash_table_1;
subnet_table_1 = new_subnet_table();
if (!subnet_table_1) goto error;
subnet_table_2 = new_subnet_table();
if (!subnet_table_2) goto error;
subnet_table = (struct subnet **)shm_malloc(sizeof(struct subnet *));
if (!subnet_table) goto error;
*subnet_table = subnet_table_1;
if (reload_address_table() == -1) {
LOG(L_CRIT, "permissions:init_addresses(): "
"Reload of address table failed\n");
goto error;
}
perm_dbf.close(db_handle);
db_handle = 0;
return 0;
error:
if (addr_hash_table_1) {
free_addr_hash_table(addr_hash_table_1);
addr_hash_table_1 = 0;
}
if (addr_hash_table_2) {
free_addr_hash_table(addr_hash_table_2);
addr_hash_table_2 = 0;
}
if (addr_hash_table) {
shm_free(addr_hash_table);
addr_hash_table = 0;
}
if (subnet_table_1) {
free_subnet_table(subnet_table_1);
subnet_table_1 = 0;
}
if (subnet_table_2) {
free_subnet_table(subnet_table_2);
subnet_table_2 = 0;
}
if (subnet_table) {
shm_free(subnet_table);
subnet_table = 0;
}
perm_dbf.close(db_handle);
db_handle = 0;
return -1;
}
/*
* Open database connection if necessary
*/
int mi_init_addresses()
{
if (!db_url || db_handle) return 0;
db_handle = perm_dbf.init(db_url);
if (!db_handle) {
LOG(L_ERR, "ERROR: permissions: init_mi_addresses():"
" Unable to connect database\n");
return -1;
}
return 0;
}
/*
* Close connections and release memory
*/
void clean_addresses(void)
{
if (addr_hash_table_1) free_addr_hash_table(addr_hash_table_1);
if (addr_hash_table_2) free_addr_hash_table(addr_hash_table_2);
if (addr_hash_table) shm_free(addr_hash_table);
if (subnet_table_1) free_subnet_table(subnet_table_1);
if (subnet_table_2) free_subnet_table(subnet_table_2);
if (subnet_table) shm_free(subnet_table);
}
/*
* Sets address group to be used by subsequent allow_address() tests.
*/
int set_address_group(struct sip_msg* _msg, char* _addr_group, char* _str2)
{
int_or_pvar_t *i_or_p;
xl_value_t xl_val;
i_or_p = (int_or_pvar_t *)_addr_group;
if (i_or_p->pvar) {
if (xl_get_spec_value(_msg, i_or_p->pvar, &xl_val, 0) == 0) {
if (xl_val.flags & XL_VAL_INT) {
addr_group = xl_val.ri;
} else if (xl_val.flags & XL_VAL_STR) {
if (str2int(&(xl_val.rs), &addr_group) == -1) {
LOG(L_ERR, "set_address_group(): Error while "
"converting group string to int\n");
return -1;
}
} else {
LOG(L_ERR, "set_address_group(): Error while converting "
"group string to int\n");
return -1;
}
} else {
LOG(L_ERR, "set_address_group(): cannot get pseudo variable "
"value\n");
return -1;
}
} else {
addr_group = i_or_p->i;
}
DBG("Set addr_group to <%u>\n", addr_group);
return 1;
}
/*
* Checks if an entry exists in cached address table that belongs to
* pre-assigned group and has ip address and port given in pseudo
* variable parameters. Port value 0 in cached address table matches
* any port.
*/
int allow_address(struct sip_msg* _msg, char* _addr_sp, char* _port_sp)
{
xl_spec_t *addr_sp, *port_sp;
xl_value_t xl_val;
unsigned int addr, port;
struct in_addr addr_struct;
addr_sp = (xl_spec_t *)_addr_sp;
port_sp = (xl_spec_t *)_port_sp;
if (addr_sp && (xl_get_spec_value(_msg, addr_sp, &xl_val, 0) == 0)) {
if (xl_val.flags & XL_VAL_INT) {
addr = xl_val.ri;
} else if (xl_val.flags & XL_VAL_STR) {
if (inet_aton(xl_val.rs.s, &addr_struct) == 0) {
LOG(L_ERR, "allow_address(): Error while converting "
"IP address string to in_addr\n");
return -1;
} else {
addr = addr_struct.s_addr;
}
} else {
LOG(L_ERR, "allow_address(): Error while converting "
"IP address string to in_addr\n");
return -1;
}
} else {
LOG(L_ERR, "allow_address(): cannot get pseudo variable value\n");
return -1;
}
if (port_sp && (xl_get_spec_value(_msg, port_sp, &xl_val, 0) == 0)) {
if (xl_val.flags & XL_VAL_INT) {
port = xl_val.ri;
} else if (xl_val.flags & XL_VAL_STR) {
if (str2int(&(xl_val.rs), &port) == -1) {
LOG(L_ERR, "allow_address(): Error while converting "
"port string to int\n");
return -1;
}
} else {
LOG(L_ERR, "allow_address(): Error while converting "
"port string to int\n");
return -1;
}
} else {
LOG(L_ERR, "allow_address(): cannot get pseudo variable value\n");
return -1;
}
if (match_addr_hash_table(*addr_hash_table, addr_group, addr, port) == 1)
return 1;
else
return match_subnet_table(*subnet_table, addr_group, addr, port);
}
/*
* allow_source_address(group) equals to
* set_address_group(group); allow_address("$si", "$sp");
* but is faster. group can be an integer string or pseudo variable.
*/
int allow_source_address(struct sip_msg* _msg, char* _addr_group, char* _str2)
{
int_or_pvar_t *i_or_p;
xl_value_t xl_val;
unsigned int group;
i_or_p = (int_or_pvar_t *)_addr_group;
if (i_or_p->pvar) {
if (xl_get_spec_value(_msg, i_or_p->pvar, &xl_val, 0) == 0) {
if (xl_val.flags & XL_VAL_INT) {
group = xl_val.ri;
} else if (xl_val.flags & XL_VAL_STR) {
if (str2int(&(xl_val.rs), &group) == -1) {
LOG(L_ERR, "allow_source_address(): Error while "
"converting group string to int\n");
return -1;
}
} else {
LOG(L_ERR, "allow_source_address(): Error while converting "
"group string to int\n");
return -1;
}
} else {
LOG(L_ERR, "allow_source_address(): cannot get pseudo variable "
"value\n");
return -1;
}
} else {
group = i_or_p->i;
}
DBG("allow_source_address(): looking for <%u, %x, %u>\n",
group, _msg->rcv.src_ip.u.addr32[0], _msg->rcv.src_port);
if (match_addr_hash_table(*addr_hash_table, group,
_msg->rcv.src_ip.u.addr32[0],
_msg->rcv.src_port) == 1)
return 1;
else
return match_subnet_table(*subnet_table, group,
_msg->rcv.src_ip.u.addr32[0],
_msg->rcv.src_port);
}
syntax highlighted by Code2HTML, v. 0.9.1