/* * * 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 #include #include #include #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); }