You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/lcr/lcr_mod.c

2699 lines
75 KiB

/*
* Least Cost Routing module
*
* Copyright (C) 2005-2012 Juha Heinanen
* Copyright (C) 2006 Voice Sistem SRL
*
* This file is part of SIP Router, a free SIP server.
*
* SIP Router 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
*
* SIP Router 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:
* -------
* 2005-02-14: Introduced lcr module (jh)
* 2005-02-20: Added sequential forking functions (jh)
* 2005-02-25: Added support for int AVP names, combined addr and port
* AVPs (jh)
* 2005-07-28: Added support for gw URI scheme and transport,
* backport from ser (kd)
* 2005-08-20: Added support for gw prefixes (jh)
* 2005-09-03: Request-URI user part can be modified between load_gws()
* and first next_gw() calls.
* 2008-10-10: Database values are now checked and from/to_gw functions
* execute in O(logN) time.
* 2008-11-26: Added timer based check of gateways (shurik)
* 2009-05-12 added RPC support (andrei)
* 2009-06-21 Added support for more than one lcr instance and
gw defunct capability (jh)
*/
/*!
* \file
* \brief SIP-router LCR :: Module interface
* \ingroup lcr
* Module: \ref lcr
*/
/*! \defgroup lcr SIP-router Least Cost Routing Module
*
* The Least Cost Routing (LCR) module implements capability to serially
* forward a request to one or more gateways so that the order in which
* the gateways is tried is based on admin defined "least cost" rules.
* The LCR module supports many independent LCR instances (gateways and
* least cost rules). Each such instance has its own LCR identifier.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pcre.h>
#include "../../locking.h"
#include "../../sr_module.h"
#include "../../dprint.h"
#include "../../ut.h"
#include "../../error.h"
#include "../../mem/mem.h"
#include "../../mem/shm_mem.h"
#include "../../lib/srdb1/db.h"
#include "../../usr_avp.h"
#include "../../parser/parse_from.h"
#include "../../parser/msg_parser.h"
#include "../../action.h"
#include "../../qvalue.h"
#include "../../dset.h"
#include "../../ip_addr.h"
#include "../../resolve.h"
#include "../../mod_fix.h"
#include "../../socket_info.h"
#include "../../pvar.h"
#include "../../mod_fix.h"
#include "hash.h"
#include "lcr_rpc.h"
#include "../../rpc_lookup.h"
MODULE_VERSION
/*
* versions of database tables required by the module.
*/
#define LCR_RULE_TABLE_VERSION 2
#define LCR_RULE_TARGET_TABLE_VERSION 1
#define LCR_GW_TABLE_VERSION 2
/* database defaults */
#define LCR_RULE_TABLE "lcr_rule"
#define LCR_RULE_TARGET_TABLE "lcr_rule_target"
#define LCR_GW_TABLE "lcr_gw"
#define ID_COL "id"
#define LCR_ID_COL "lcr_id"
#define PREFIX_COL "prefix"
#define FROM_URI_COL "from_uri"
#define REQUEST_URI_COL "request_uri"
#define STOPPER_COL "stopper"
#define ENABLED_COL "enabled"
#define RULE_ID_COL "rule_id"
#define PRIORITY_COL "priority"
#define GW_ID_COL "gw_id"
#define WEIGHT_COL "weight"
#define GW_NAME_COL "gw_name"
#define IP_ADDR_COL "ip_addr"
#define PORT_COL "port"
#define URI_SCHEME_COL "uri_scheme"
#define TRANSPORT_COL "transport"
#define PARAMS_COL "params"
#define HOSTNAME_COL "hostname"
#define STRIP_COL "strip"
#define PREFIX_COL "prefix"
#define TAG_COL "tag"
#define FLAGS_COL "flags"
#define DEFUNCT_COL "defunct"
/* Default module parameter values */
#define DEF_LCR_COUNT 1
#define DEF_LCR_RULE_HASH_SIZE 128
#define DEF_LCR_GW_COUNT 128
#define DEF_FETCH_ROWS 1024
/*
* Type definitions
*/
struct matched_gw_info {
unsigned short gw_index;
unsigned short prefix_len;
unsigned short priority;
unsigned int weight;
unsigned short duplicate;
};
/*
* Database variables
*/
static db1_con_t* dbh = 0; /* Database connection handle */
static db_func_t lcr_dbf;
/*
* Locking variables
*/
gen_lock_t *reload_lock;
/*
* Module parameter variables
*/
/* database variables */
static str db_url = str_init(DEFAULT_RODB_URL);
static str lcr_rule_table = str_init(LCR_RULE_TABLE);
static str lcr_rule_target_table = str_init(LCR_RULE_TARGET_TABLE);
static str lcr_gw_table = str_init(LCR_GW_TABLE);
static str id_col = str_init(ID_COL);
static str lcr_id_col = str_init(LCR_ID_COL);
static str prefix_col = str_init(PREFIX_COL);
static str from_uri_col = str_init(FROM_URI_COL);
static str request_uri_col = str_init(REQUEST_URI_COL);
static str stopper_col = str_init(STOPPER_COL);
static str enabled_col = str_init(ENABLED_COL);
static str rule_id_col = str_init(RULE_ID_COL);
static str priority_col = str_init(PRIORITY_COL);
static str gw_id_col = str_init(GW_ID_COL);
static str weight_col = str_init(WEIGHT_COL);
static str gw_name_col = str_init(GW_NAME_COL);
static str ip_addr_col = str_init(IP_ADDR_COL);
static str port_col = str_init(PORT_COL);
static str uri_scheme_col = str_init(URI_SCHEME_COL);
static str transport_col = str_init(TRANSPORT_COL);
static str params_col = str_init(PARAMS_COL);
static str hostname_col = str_init(HOSTNAME_COL);
static str strip_col = str_init(STRIP_COL);
static str tag_col = str_init(TAG_COL);
static str flags_col = str_init(FLAGS_COL);
static str defunct_col = str_init(DEFUNCT_COL);
/* number of rows to fetch at a shot */
static int fetch_rows_param = DEF_FETCH_ROWS;
/* avps */
static char *gw_uri_avp_param = NULL;
static char *ruri_user_avp_param = NULL;
static char *tag_avp_param = NULL;
static char *flags_avp_param = NULL;
static char *defunct_gw_avp_param = NULL;
static char *lcr_id_avp_param = NULL;
/* max number of lcr instances */
unsigned int lcr_count_param = DEF_LCR_COUNT;
/* size of lcr rules hash table */
unsigned int lcr_rule_hash_size_param = DEF_LCR_RULE_HASH_SIZE;
/* max no of gws */
unsigned int lcr_gw_count_param = DEF_LCR_GW_COUNT;
/* can gws be defuncted */
static unsigned int defunct_capability_param = 0;
/* dont strip or tag param */
static int dont_strip_or_prefix_flag_param = -1;
/*
* Other module types and variables
*/
static int gw_uri_avp_type;
static int_str gw_uri_avp;
static int ruri_user_avp_type;
static int_str ruri_user_avp;
static int tag_avp_type;
static int_str tag_avp;
static int flags_avp_type;
static int_str flags_avp;
static int defunct_gw_avp_type;
static int_str defunct_gw_avp;
static int lcr_id_avp_type;
static int_str lcr_id_avp;
/* Pointer to rule hash table pointer table */
struct rule_info ***rule_pt = (struct rule_info ***)NULL;
/* Pointer to gw table pointer table */
struct gw_info **gw_pt = (struct gw_info **)NULL;
/* Pointer to rule_id info hash table */
struct rule_id_info **rule_id_hash_table = (struct rule_id_info **)NULL;
/*
* Functions that are defined later
*/
static void destroy(void);
static int mod_init(void);
static int child_init(int rank);
static void free_shared_memory(void);
static int load_gws(struct sip_msg* _m, int argc, action_u_t argv[]);
static int next_gw(struct sip_msg* _m, char* _s1, char* _s2);
static int defunct_gw(struct sip_msg* _m, char* _s1, char* _s2);
static int from_gw_1(struct sip_msg* _m, char* _s1, char* _s2);
static int from_gw_3(struct sip_msg* _m, char* _s1, char* _s2, char* _s3);
static int from_any_gw_0(struct sip_msg* _m, char* _s1, char* _s2);
static int from_any_gw_2(struct sip_msg* _m, char* _s1, char* _s2);
static int to_gw_1(struct sip_msg* _m, char* _s1, char* _s2);
static int to_gw_3(struct sip_msg* _m, char* _s1, char* _s2, char* _s3);
static int to_any_gw_0(struct sip_msg* _m, char* _s1, char* _s2);
static int to_any_gw_2(struct sip_msg* _m, char* _s1, char* _s2);
/*
* Exported functions
*/
static cmd_export_t cmds[] = {
{"load_gws", (cmd_function)load_gws, VAR_PARAM_NO, 0, 0,
REQUEST_ROUTE | FAILURE_ROUTE},
{"next_gw", (cmd_function)next_gw, 0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE},
{"defunct_gw", (cmd_function)defunct_gw, 1, 0, 0,
REQUEST_ROUTE | FAILURE_ROUTE},
{"from_gw", (cmd_function)from_gw_1, 1, 0, 0,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
{"from_gw", (cmd_function)from_gw_3, 3, 0, 0,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
{"from_any_gw", (cmd_function)from_any_gw_0, 0, 0, 0,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
{"from_any_gw", (cmd_function)from_any_gw_2, 2, 0, 0,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
{"to_gw", (cmd_function)to_gw_1, 1, 0, 0,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
{"to_gw", (cmd_function)to_gw_3, 3, 0, 0,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
{"to_any_gw", (cmd_function)to_any_gw_0, 0, 0, 0,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
{"to_any_gw", (cmd_function)to_any_gw_2, 2, 0, 0,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE},
{0, 0, 0, 0, 0, 0}
};
/*
* Exported parameters
*/
static param_export_t params[] = {
{"db_url", STR_PARAM, &db_url.s},
{"lcr_rule_table", STR_PARAM, &lcr_rule_table.s},
{"lcr_rule_target_table", STR_PARAM, &lcr_rule_target_table.s},
{"lcr_gw_table", STR_PARAM, &lcr_gw_table.s},
{"lcr_id_column", STR_PARAM, &lcr_id_col.s},
{"id_column", STR_PARAM, &id_col.s},
{"prefix_column", STR_PARAM, &prefix_col.s},
{"from_uri_column", STR_PARAM, &from_uri_col.s},
{"request_uri_column", STR_PARAM, &request_uri_col.s},
{"stopper_column", STR_PARAM, &stopper_col.s},
{"enabled_column", STR_PARAM, &enabled_col.s},
{"rule_id_column", STR_PARAM, &rule_id_col.s},
{"priority_column", STR_PARAM, &priority_col.s},
{"gw_id_column", STR_PARAM, &gw_id_col.s},
{"weight_column", STR_PARAM, &weight_col.s},
{"gw_name_column", STR_PARAM, &gw_name_col.s},
{"ip_addr_column", STR_PARAM, &ip_addr_col.s},
{"port_column", STR_PARAM, &port_col.s},
{"uri_scheme_column", STR_PARAM, &uri_scheme_col.s},
{"transport_column", STR_PARAM, &transport_col.s},
{"params_column", STR_PARAM, &params_col.s},
{"hostname_column", STR_PARAM, &hostname_col.s},
{"strip_column", STR_PARAM, &strip_col.s},
{"prefix_column", STR_PARAM, &prefix_col.s},
{"tag_column", STR_PARAM, &tag_col.s},
{"flags_column", STR_PARAM, &flags_col.s},
{"defunct_column", STR_PARAM, &defunct_col.s},
{"gw_uri_avp", STR_PARAM, &gw_uri_avp_param},
{"ruri_user_avp", STR_PARAM, &ruri_user_avp_param},
{"tag_avp", STR_PARAM, &tag_avp_param},
{"flags_avp", STR_PARAM, &flags_avp_param},
{"defunct_capability", INT_PARAM, &defunct_capability_param},
{"defunct_gw_avp", STR_PARAM, &defunct_gw_avp_param},
{"lcr_id_avp", STR_PARAM, &lcr_id_avp_param},
{"lcr_count", INT_PARAM, &lcr_count_param},
{"lcr_rule_hash_size", INT_PARAM, &lcr_rule_hash_size_param},
{"lcr_gw_count", INT_PARAM, &lcr_gw_count_param},
{"dont_strip_or_prefix_flag",INT_PARAM, &dont_strip_or_prefix_flag_param},
{"fetch_rows", INT_PARAM, &fetch_rows_param},
{0, 0, 0}
};
/*
* Module interface
*/
struct module_exports exports = {
"lcr",
DEFAULT_DLFLAGS, /* dlopen flags */
cmds, /* Exported functions */
params, /* Exported parameters */
0, /* exported statistics */
0, /* exported MI functions */
0, /* exported pseudo-variables */
0, /* extra processes */
mod_init, /* module initialization function */
0, /* response function */
destroy, /* destroy function */
child_init /* child initialization function */
};
static int lcr_db_init(const str* db_url)
{
if (lcr_dbf.init==0){
LM_CRIT("null lcr_dbf\n");
goto error;
}
if (dbh) {
LM_ERR("database is already connected\n");
goto error;
}
dbh=lcr_dbf.init(db_url);
if (dbh==0){
LM_ERR("unable to connect to the database\n");
goto error;
}
return 0;
error:
return -1;
}
static int lcr_db_bind(const str* db_url)
{
if (db_bind_mod(db_url, &lcr_dbf)<0){
LM_ERR("unable to bind to the database module\n");
return -1;
}
if (!DB_CAPABILITY(lcr_dbf, DB_CAP_QUERY)) {
LM_ERR("database module does not implement 'query' function\n");
return -1;
}
return 0;
}
static void lcr_db_close(void)
{
if (dbh && lcr_dbf.close){
lcr_dbf.close(dbh);
dbh=0;
}
}
/*
* Module initialization function that is called before the main process forks
*/
static int mod_init(void)
{
pv_spec_t avp_spec;
str s;
unsigned short avp_flags;
unsigned int i;
/* Register RPC commands */
if (rpc_register_array(lcr_rpc)!=0) {
LM_ERR("failed to register RPC commands\n");
return -1;
}
/* Update length of module variables */
db_url.len = strlen(db_url.s);
lcr_rule_table.len = strlen(lcr_rule_table.s);
lcr_rule_target_table.len = strlen(lcr_rule_target_table.s);
lcr_gw_table.len = strlen(lcr_gw_table.s);
id_col.len = strlen(id_col.s);
lcr_id_col.len = strlen(lcr_id_col.s);
prefix_col.len = strlen(prefix_col.s);
from_uri_col.len = strlen(from_uri_col.s);
request_uri_col.len = strlen(request_uri_col.s);
stopper_col.len = strlen(stopper_col.s);
enabled_col.len = strlen(enabled_col.s);
rule_id_col.len = strlen(rule_id_col.s);
priority_col.len = strlen(priority_col.s);
gw_id_col.len = strlen(gw_id_col.s);
weight_col.len = strlen(weight_col.s);
gw_name_col.len = strlen(gw_name_col.s);
ip_addr_col.len = strlen(ip_addr_col.s);
port_col.len = strlen(port_col.s);
uri_scheme_col.len = strlen(uri_scheme_col.s);
transport_col.len = strlen(transport_col.s);
params_col.len = strlen(params_col.s);
hostname_col.len = strlen(hostname_col.s);
strip_col.len = strlen(strip_col.s);
prefix_col.len = strlen(prefix_col.s);
tag_col.len = strlen(tag_col.s);
flags_col.len = strlen(flags_col.s);
defunct_col.len = strlen(defunct_col.s);
/* Bind database */
if (lcr_db_bind(&db_url)) {
LM_ERR("no database module found\n");
return -1;
}
/* Check values of some params */
if (lcr_count_param < 1) {
LM_ERR("invalid lcr_count module parameter value <%d>\n",
lcr_count_param);
return -1;
}
if (lcr_rule_hash_size_param < 1) {
LM_ERR("invalid lcr_rule_hash_size module parameter value <%d>\n",
lcr_rule_hash_size_param);
return -1;
}
if (lcr_gw_count_param < 1) {
LM_ERR("invalid lcr_gw_count module parameter value <%d>\n",
lcr_gw_count_param);
return -1;
}
if ((dont_strip_or_prefix_flag_param != -1) &&
!flag_in_range(dont_strip_or_prefix_flag_param)) {
LM_ERR("invalid dont_strip_or_prefix_flag value <%d>\n",
dont_strip_or_prefix_flag_param);
return -1;
}
/* Process AVP params */
if (gw_uri_avp_param && *gw_uri_avp_param) {
s.s = gw_uri_avp_param; s.len = strlen(s.s);
if (pv_parse_spec(&s, &avp_spec)==0
|| avp_spec.type!=PVT_AVP) {
LM_ERR("malformed or non AVP definition <%s>\n", gw_uri_avp_param);
return -1;
}
if (pv_get_avp_name(0, &(avp_spec.pvp), &gw_uri_avp, &avp_flags) != 0) {
LM_ERR("invalid AVP definition <%s>\n", gw_uri_avp_param);
return -1;
}
gw_uri_avp_type = avp_flags;
} else {
LM_ERR("AVP gw_uri_avp has not been defined\n");
return -1;
}
if (ruri_user_avp_param && *ruri_user_avp_param) {
s.s = ruri_user_avp_param; s.len = strlen(s.s);
if (pv_parse_spec(&s, &avp_spec)==0
|| avp_spec.type!=PVT_AVP) {
LM_ERR("malformed or non AVP definition <%s>\n",
ruri_user_avp_param);
return -1;
}
if (pv_get_avp_name(0, &(avp_spec.pvp), &ruri_user_avp, &avp_flags)
!= 0) {
LM_ERR("invalid AVP definition <%s>\n", ruri_user_avp_param);
return -1;
}
ruri_user_avp_type = avp_flags;
} else {
LM_ERR("AVP ruri_user_avp has not been defined\n");
return -1;
}
if (tag_avp_param) {
s.s = tag_avp_param; s.len = strlen(s.s);
if ((pv_parse_spec(&s, &avp_spec)==0) || (avp_spec.type!=PVT_AVP)) {
LM_ERR("malformed or non AVP definition <%s>\n", tag_avp_param);
return -1;
}
if (pv_get_avp_name(0, &(avp_spec.pvp), &tag_avp, &avp_flags) != 0) {
LM_ERR("invalid AVP definition <%s>\n", tag_avp_param);
return -1;
}
tag_avp_type = avp_flags | AVP_VAL_STR;
}
if (flags_avp_param) {
s.s = flags_avp_param; s.len = strlen(s.s);
if ((pv_parse_spec(&s, &avp_spec)==0) || (avp_spec.type != PVT_AVP)) {
LM_ERR("malformed or non AVP definition <%s>\n", flags_avp_param);
return -1;
}
if (pv_get_avp_name(0, &(avp_spec.pvp), &flags_avp, &avp_flags) != 0) {
LM_ERR("invalid AVP definition <%s>\n", flags_avp_param);
return -1;
}
flags_avp_type = avp_flags;
}
if (defunct_capability_param > 0) {
if (defunct_gw_avp_param && *defunct_gw_avp_param) {
s.s = defunct_gw_avp_param; s.len = strlen(s.s);
if ((pv_parse_spec(&s, &avp_spec) == 0) ||
(avp_spec.type != PVT_AVP)) {
LM_ERR("malformed or non AVP definition <%s>\n",
defunct_gw_avp_param);
return -1;
}
if (pv_get_avp_name(0, &(avp_spec.pvp), &defunct_gw_avp,
&avp_flags) != 0) {
LM_ERR("invalid AVP definition <%s>\n", defunct_gw_avp_param);
return -1;
}
defunct_gw_avp_type = avp_flags;
} else {
LM_ERR("AVP defunct_gw_avp has not been defined\n");
return -1;
}
if (lcr_id_avp_param && *lcr_id_avp_param) {
s.s = lcr_id_avp_param; s.len = strlen(s.s);
if ((pv_parse_spec(&s, &avp_spec) == 0) ||
(avp_spec.type != PVT_AVP)) {
LM_ERR("malformed or non AVP definition <%s>\n",
lcr_id_avp_param);
return -1;
}
if (pv_get_avp_name(0, &(avp_spec.pvp), &lcr_id_avp,
&avp_flags) != 0) {
LM_ERR("invalid AVP definition <%s>\n", lcr_id_avp_param);
return -1;
}
lcr_id_avp_type = avp_flags;
} else {
LM_ERR("AVP lcr_id_avp has not been defined\n");
return -1;
}
}
if (fetch_rows_param < 1) {
LM_ERR("invalid fetch_rows module parameter value <%d>\n",
fetch_rows_param);
return -1;
}
/* Check table version */
if (lcr_db_init(&db_url) < 0) {
LM_ERR("unable to open database connection\n");
return -1;
}
if ((db_check_table_version(&lcr_dbf, dbh, &lcr_rule_table,
LCR_RULE_TABLE_VERSION) < 0) ||
(db_check_table_version(&lcr_dbf, dbh, &lcr_rule_target_table,
LCR_RULE_TARGET_TABLE_VERSION) < 0) ||
(db_check_table_version(&lcr_dbf, dbh, &lcr_gw_table,
LCR_GW_TABLE_VERSION) < 0)) {
LM_ERR("error during table version check\n");
lcr_db_close();
goto err;
}
lcr_db_close();
/* rule shared memory */
/* rule hash table pointer table */
/* pointer at index 0 points to temp rule hash table */
rule_pt = (struct rule_info ***)shm_malloc(sizeof(struct rule_info **) *
(lcr_count_param + 1));
if (rule_pt == 0) {
LM_ERR("no memory for rule hash table pointer table\n");
goto err;
}
memset(rule_pt, 0, sizeof(struct rule_info **) * (lcr_count_param + 1));
/* rules hash tables */
/* last entry in hash table contains list of different prefix lengths */
for (i = 0; i <= lcr_count_param; i++) {
rule_pt[i] = (struct rule_info **)
shm_malloc(sizeof(struct rule_info *) *
(lcr_rule_hash_size_param + 1));
if (rule_pt[i] == 0) {
LM_ERR("no memory for rules hash table\n");
goto err;
}
memset(rule_pt[i], 0, sizeof(struct rule_info *) *
(lcr_rule_hash_size_param + 1));
}
/* gw shared memory */
/* gw table pointer table */
/* pointer at index 0 points to temp rule hash table */
gw_pt = (struct gw_info **)shm_malloc(sizeof(struct gw_info *) *
(lcr_count_param + 1));
if (gw_pt == 0) {
LM_ERR("no memory for gw table pointer table\n");
goto err;
}
memset(gw_pt, 0, sizeof(struct gw_info *) * (lcr_count_param + 1));
/* gw tables themselves */
/* ordered by ip_addr for from_gw/to_gw functions */
/* in each table i, (gw_pt[i])[0].ip_addr contains number of
gateways in the table and (gw_pt[i])[0].port as value 1
if some gateways in the table have null ip addr */
for (i = 0; i <= lcr_count_param; i++) {
gw_pt[i] = (struct gw_info *)shm_malloc(sizeof(struct gw_info) *
(lcr_gw_count_param + 1));
if (gw_pt[i] == 0) {
LM_ERR("no memory for gw table\n");
goto err;
}
memset(gw_pt[i], 0, sizeof(struct gw_info *) *
(lcr_gw_count_param + 1));
}
/* Allocate and initialize locks */
reload_lock = lock_alloc();
if (reload_lock == NULL) {
LM_ERR("cannot allocate reload_lock\n");
goto err;
}
if (lock_init(reload_lock) == NULL) {
LM_ERR("cannot init reload_lock\n");
goto err;
}
/* First reload */
lock_get(reload_lock);
if (reload_tables() < 0) {
lock_release(reload_lock);
LM_CRIT("failed to reload lcr tables\n");
goto err;
}
lock_release(reload_lock);
return 0;
err:
free_shared_memory();
return -1;
}
/* Module initialization function called in each child separately */
static int child_init(int rank)
{
return 0;
}
static void destroy(void)
{
lcr_db_close();
free_shared_memory();
}
/* Free shared memory */
static void free_shared_memory(void)
{
int i;
for (i = 0; i <= lcr_count_param; i++) {
if (rule_pt && rule_pt[i]) {
rule_hash_table_contents_free(rule_pt[i]);
shm_free(rule_pt[i]);
rule_pt[i] = 0;
}
}
if (rule_pt) {
shm_free(rule_pt);
rule_pt = 0;
}
for (i = 0; i <= lcr_count_param; i++) {
if (gw_pt && gw_pt[i]) {
shm_free(gw_pt[i]);
gw_pt[i] = 0;
}
}
if (gw_pt) {
shm_free(gw_pt);
gw_pt = 0;
}
if (reload_lock) {
lock_destroy(reload_lock);
lock_dealloc(reload_lock);
reload_lock=0;
}
}
/*
* Compare matched gateways based on prefix_len, priority, and randomized
* weight.
*/
static int comp_matched(const void *m1, const void *m2)
{
struct matched_gw_info *mi1 = (struct matched_gw_info *) m1;
struct matched_gw_info *mi2 = (struct matched_gw_info *) m2;
/* Sort by prefix_len */
if (mi1->prefix_len > mi2->prefix_len) return 1;
if (mi1->prefix_len == mi2->prefix_len) {
/* Sort by priority */
if (mi1->priority < mi2->priority) return 1;
if (mi1->priority == mi2->priority) {
/* Sort by randomized weigth */
if (mi1->weight > mi2->weight) return 1;
if (mi1->weight == mi2->weight) return 0;
return -1;
}
return -1;
}
return -1;
}
/* Compile pattern into shared memory and return pointer to it. */
static pcre *reg_ex_comp(const char *pattern)
{
pcre *re, *result;
const char *error;
int rc, err_offset;
size_t size;
re = pcre_compile(pattern, 0, &error, &err_offset, NULL);
if (re == NULL) {
LM_ERR("pcre compilation of '%s' failed at offset %d: %s\n",
pattern, err_offset, error);
return (pcre *)0;
}
rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &size);
if (rc != 0) {
LM_ERR("pcre_fullinfo on compiled pattern '%s' yielded error: %d\n",
pattern, rc);
return (pcre *)0;
}
result = (pcre *)shm_malloc(size);
if (result == NULL) {
pcre_free(re);
LM_ERR("not enough shared memory for compiled PCRE pattern\n");
return (pcre *)0;
}
memcpy(result, re, size);
pcre_free(re);
return result;
}
/*
* Compare gateways based on their IP address
*/
static int comp_gws(const void *_g1, const void *_g2)
{
struct gw_info *g1 = (struct gw_info *)_g1;
struct gw_info *g2 = (struct gw_info *)_g2;
if (g1->ip_addr.af < g2->ip_addr.af) return -1;
if (g1->ip_addr.af > g2->ip_addr.af) return 1;
if (g1->ip_addr.len < g2->ip_addr.len) return -1;
if (g1->ip_addr.len > g2->ip_addr.len) return 1;
return memcmp(g1->ip_addr.u.addr, g2->ip_addr.u.addr, g1->ip_addr.len);
}
/*
* Insert gw info into index i or gws table
*/
static int insert_gw(struct gw_info *gws, unsigned int i, unsigned int gw_id,
char *gw_name, unsigned int gw_name_len,
unsigned int scheme, struct ip_addr *ip_addr,
unsigned int port, unsigned int transport,
char *params, unsigned int params_len,
char *hostname, unsigned int hostname_len,
char *ip_string, unsigned int strip, char *prefix,
unsigned int prefix_len, char *tag, unsigned int tag_len,
unsigned int flags, unsigned int defunct_until)
{
gws[i].gw_id = gw_id;
if (gw_name_len) memcpy(&(gws[i].gw_name[0]), gw_name, gw_name_len);
gws[i].gw_name_len = gw_name_len;
gws[i].scheme = scheme;
gws[i].ip_addr = *ip_addr;
gws[i].port = port;
gws[i].transport = transport;
if (params_len) memcpy(&(gws[i].params[0]), params, params_len);
gws[i].params_len = params_len;
if (hostname_len) memcpy(&(gws[i].hostname[0]), hostname, hostname_len);
gws[i].hostname_len = hostname_len;
gws[i].strip = strip;
gws[i].prefix_len = prefix_len;
if (prefix_len) memcpy(&(gws[i].prefix[0]), prefix, prefix_len);
gws[i].tag_len = tag_len;
if (tag_len) memcpy(&(gws[i].tag[0]), tag, tag_len);
gws[i].flags = flags;
gws[i].defunct_until = defunct_until;
LM_DBG("inserted gw <%u, %.*s, %s, %u, %.*s> at index %u\n", gw_id,
gw_name_len, gw_name, ip_string, port, hostname_len, hostname, i);
return 1;
}
/*
* Insert prefix_len into list pointed by last rule hash table entry
* if not there already. Keep list in decending prefix_len order.
*/
static int prefix_len_insert(struct rule_info **table,
unsigned short prefix_len)
{
struct rule_info *lcr_rec, **previous, *this;
previous = &(table[lcr_rule_hash_size_param]);
this = table[lcr_rule_hash_size_param];
while (this) {
if (this->prefix_len == prefix_len)
return 1;
if (this->prefix_len < prefix_len) {
lcr_rec = shm_malloc(sizeof(struct rule_info));
if (lcr_rec == NULL) {
LM_ERR("no shared memory for rule_info\n");
return 0;
}
memset(lcr_rec, 0, sizeof(struct rule_info));
lcr_rec->prefix_len = prefix_len;
lcr_rec->next = this;
*previous = lcr_rec;
return 1;
}
previous = &(this->next);
this = this->next;
}
lcr_rec = shm_malloc(sizeof(struct rule_info));
if (lcr_rec == NULL) {
LM_ERR("no shared memory for rule_info\n");
return 0;
}
memset(lcr_rec, 0, sizeof(struct rule_info));
lcr_rec->prefix_len = prefix_len;
lcr_rec->next = NULL;
*previous = lcr_rec;
return 1;
}
static int insert_gws(db1_res_t *res, struct gw_info *gws,
unsigned int *null_gw_ip_addr,
unsigned int *gw_cnt)
{
unsigned int i, gw_id, defunct_until, gw_name_len, port, params_len,
hostname_len, strip, prefix_len, tag_len, flags;
char *gw_name, *params, *hostname, *prefix, *tag;
db_row_t* row;
struct in_addr in_addr;
struct ip_addr ip_addr, *ip_p;
str ip_string;
uri_type scheme;
uri_transport transport;
for (i = 0; i < RES_ROW_N(res); i++) {
row = RES_ROWS(res) + i;
if ((VAL_NULL(ROW_VALUES(row) + 12) == 1) ||
(VAL_TYPE(ROW_VALUES(row) + 12) != DB1_INT)) {
LM_ERR("lcr_gw id at row <%u> is null or not int\n", i);
return 0;
}
gw_id = (unsigned int)VAL_INT(ROW_VALUES(row) + 12);
if (VAL_NULL(ROW_VALUES(row) + 11)) {
defunct_until = 0;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 11) != DB1_INT) {
LM_ERR("lcr_gw defunct at row <%u> is not int\n", i);
return 0;
}
defunct_until = (unsigned int)VAL_INT(ROW_VALUES(row) + 11);
if (defunct_until > 4294967294UL) {
LM_DBG("skipping disabled gw <%u>\n", gw_id);
continue;
}
}
if (!VAL_NULL(ROW_VALUES(row)) &&
(VAL_TYPE(ROW_VALUES(row)) != DB1_STRING)) {
LM_ERR("lcr_gw gw_name at row <%u> is not null or string\n", i);
return 0;
}
if (VAL_NULL(ROW_VALUES(row))) {
gw_name_len = 0;
gw_name = (char *)0;
} else {
if (VAL_TYPE(ROW_VALUES(row)) != DB1_STRING) {
LM_ERR("lcr_gw gw_name at row <%u> is not string\n", i);
return 0;
}
gw_name = (char *)VAL_STRING(ROW_VALUES(row));
gw_name_len = strlen(gw_name);
}
if (gw_name_len > MAX_NAME_LEN) {
LM_ERR("lcr_gw gw_name <%u> at row <%u> it too long\n",
gw_name_len, i);
return 0;
}
if (!VAL_NULL(ROW_VALUES(row) + 1) &&
(VAL_TYPE(ROW_VALUES(row) + 1) != DB1_STRING)) {
LM_ERR("lcr_gw ip_addr at row <%u> is not null or string\n",
i);
return 0;
}
if (VAL_NULL(ROW_VALUES(row) + 1)) {
ip_string.s = (char *)0;
ip_addr.af = 0;
ip_addr.len = 0;
*null_gw_ip_addr = 1;
} else {
ip_string.s = (char *)VAL_STRING(ROW_VALUES(row) + 1);
ip_string.len = strlen(ip_string.s);
if ((ip_p = str2ip(&ip_string))) {
/* 123.123.123.123 */
ip_addr = *ip_p;
}
#ifdef USE_IPV6
else if ((ip_p = str2ip6(&ip_string))) {
/* fe80::123:4567:89ab:cdef and [fe80::123:4567:89ab:cdef] */
ip_addr = *ip_p;
}
#endif
else if (inet_aton(ip_string.s, &in_addr) == 0) {
/* backwards compatibility for integer or hex notations */
ip_addr.u.addr32[0] = in_addr.s_addr;
ip_addr.af = AF_INET;
ip_addr.len = 4;
}
else {
LM_ERR("lcr_gw ip_addr <%s> at row <%u> is invalid\n",
ip_string.s, i);
return 0;
}
}
if (VAL_NULL(ROW_VALUES(row) + 2)) {
port = 0;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 2) != DB1_INT) {
LM_ERR("lcr_gw port at row <%u> is not int\n", i);
return 0;
}
port = (unsigned int)VAL_INT(ROW_VALUES(row) + 2);
}
if (port > 65536) {
LM_ERR("lcr_gw port <%d> at row <%u> is too large\n", port, i);
return 0;
}
if (VAL_NULL(ROW_VALUES(row) + 3)) {
scheme = SIP_URI_T;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 3) != DB1_INT) {
LM_ERR("lcr_gw uri scheme at row <%u> is not int\n", i);
return 0;
}
scheme = (uri_type)VAL_INT(ROW_VALUES(row) + 3);
}
if ((scheme != SIP_URI_T) && (scheme != SIPS_URI_T)) {
LM_ERR("lcr_gw has unknown or unsupported URI scheme <%u> at "
"row <%u>\n", (unsigned int)scheme, i);
return 0;
}
if (VAL_NULL(ROW_VALUES(row) + 4)) {
transport = PROTO_NONE;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 4) != DB1_INT) {
LM_ERR("lcr_gw transport at row <%u> is not int\n", i);
return 0;
}
transport = (uri_transport)VAL_INT(ROW_VALUES(row) + 4);
}
if ((transport != PROTO_UDP) && (transport != PROTO_TCP) &&
(transport != PROTO_TLS) && (transport != PROTO_SCTP) &&
(transport != PROTO_NONE)) {
LM_ERR("lcr_gw has unknown or unsupported transport <%u> at "
" row <%u>\n", (unsigned int)transport, i);
return 0;
}
if ((scheme == SIPS_URI_T) && (transport == PROTO_UDP)) {
LM_ERR("lcr_gw has wrong transport <%u> for SIPS URI "
"scheme at row <%u>\n", transport, i);
return 0;
}
if (VAL_NULL(ROW_VALUES(row) + 5)) {
params_len = 0;
params = (char *)0;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 5) != DB1_STRING) {
LM_ERR("lcr_gw params at row <%u> is not string\n", i);
return 0;
}
params = (char *)VAL_STRING(ROW_VALUES(row) + 5);
params_len = strlen(params);
if ((params_len > 0) && (params[0] != ';')) {
LM_ERR("lcr_gw params at row <%u> does not start "
"with ';'\n", i);
return 0;
}
}
if (params_len > MAX_PARAMS_LEN) {
LM_ERR("lcr_gw params length <%u> at row <%u> it too large\n",
params_len, i);
return 0;
}
if (VAL_NULL(ROW_VALUES(row) + 6)) {
if (ip_string.s == 0) {
LM_ERR("lcr_gw gw ip_addr and hostname are both null "
"at row <%u>\n", i);
return 0;
}
hostname_len = 0;
hostname = (char *)0;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 6) != DB1_STRING) {
LM_ERR("hostname at row <%u> is not string\n", i);
return 0;
}
hostname = (char *)VAL_STRING(ROW_VALUES(row) + 6);
hostname_len = strlen(hostname);
}
if (hostname_len > MAX_HOST_LEN) {
LM_ERR("lcr_gw hostname at row <%u> it too long\n", i);
return 0;
}
if (VAL_NULL(ROW_VALUES(row) + 7)) {
strip = 0;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 7) != DB1_INT) {
LM_ERR("lcr_gw strip count at row <%u> is not int\n", i);
return 0;
}
strip = (unsigned int)VAL_INT(ROW_VALUES(row) + 7);
}
if (strip > MAX_USER_LEN) {
LM_ERR("lcr_gw strip count <%u> at row <%u> it too large\n",
strip, i);
return 0;
}
if (VAL_NULL(ROW_VALUES(row) + 8)) {
prefix_len = 0;
prefix = (char *)0;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 8) != DB1_STRING) {
LM_ERR("lcr_gw prefix at row <%u> is not string\n", i);
return 0;
}
prefix = (char *)VAL_STRING(ROW_VALUES(row) + 8);
prefix_len = strlen(prefix);
}
if (prefix_len > MAX_PREFIX_LEN) {
LM_ERR("lcr_gw prefix at row <%u> it too long\n", i);
return 0;
}
if (VAL_NULL(ROW_VALUES(row) + 9)) {
tag_len = 0;
tag = (char *)0;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 9) != DB1_STRING) {
LM_ERR("lcr_gw tag at row <%u> is not string\n", i);
return 0;
}
tag = (char *)VAL_STRING(ROW_VALUES(row) + 9);
tag_len = strlen(tag);
}
if (tag_len > MAX_TAG_LEN) {
LM_ERR("lcr_gw tag at row <%u> it too long\n", i);
return 0;
}
if (VAL_NULL(ROW_VALUES(row) + 10)) {
flags = 0;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 10) != DB1_INT) {
LM_ERR("lcr_gw flags at row <%u> is not int\n", i);
return 0;
}
flags = (unsigned int)VAL_INT(ROW_VALUES(row) + 10);
}
(*gw_cnt)++;
if (!insert_gw(gws, *gw_cnt, gw_id, gw_name, gw_name_len,
scheme, &ip_addr, port,
transport, params, params_len, hostname,
hostname_len, ip_string.s, strip, prefix, prefix_len,
tag, tag_len, flags, defunct_until)) {
return 0;
}
}
return 1;
}
/*
* Reload gws to unused gw table, rules to unused lcr hash table, and
* prefix lens to a new prefix_len list. When done, make these tables
* and list the current ones.
*/
int reload_tables()
{
unsigned int i, n, lcr_id, rule_id, gw_id, from_uri_len, request_uri_len,
stopper, prefix_len, enabled, gw_cnt, null_gw_ip_addr, priority,
weight, tmp;
char *prefix, *from_uri, *request_uri;
db1_res_t* res = NULL;
db_row_t* row;
db_key_t key_cols[1];
db_op_t op[1];
db_val_t vals[1];
db_key_t gw_cols[13];
db_key_t rule_cols[6];
db_key_t target_cols[4];
pcre *from_uri_re, *request_uri_re;
struct gw_info *gws, *gw_pt_tmp;
struct rule_info **rules, **rule_pt_tmp;
key_cols[0] = &lcr_id_col;
op[0] = OP_EQ;
VAL_TYPE(vals) = DB1_INT;
VAL_NULL(vals) = 0;
rule_cols[0] = &id_col;
rule_cols[1] = &prefix_col;
rule_cols[2] = &from_uri_col;
rule_cols[3] = &stopper_col;
rule_cols[4] = &enabled_col;
rule_cols[5] = &request_uri_col;
gw_cols[0] = &gw_name_col;
gw_cols[1] = &ip_addr_col;
gw_cols[2] = &port_col;
gw_cols[3] = &uri_scheme_col;
gw_cols[4] = &transport_col;
gw_cols[5] = &params_col;
gw_cols[6] = &hostname_col;
gw_cols[7] = &strip_col;
gw_cols[8] = &prefix_col;
gw_cols[9] = &tag_col;
gw_cols[10] = &flags_col;
gw_cols[11] = &defunct_col;
gw_cols[12] = &id_col;
target_cols[0] = &rule_id_col;
target_cols[1] = &gw_id_col;
target_cols[2] = &priority_col;
target_cols[3] = &weight_col;
request_uri_re = from_uri_re = 0;
if (lcr_db_init(&db_url) < 0) {
LM_ERR("unable to open database connection\n");
return -1;
}
rule_id_hash_table = pkg_malloc(sizeof(struct rule_id_info *) *
lcr_rule_hash_size_param);
if (!rule_id_hash_table) {
LM_ERR("no pkg memory for rule_id hash table\n");
goto err;
}
memset(rule_id_hash_table, 0, sizeof(struct rule_id_info *) *
lcr_rule_hash_size_param);
for (lcr_id = 1; lcr_id <= lcr_count_param; lcr_id++) {
/* Reload rules */
rules = rule_pt[0];
rule_hash_table_contents_free(rules);
rule_id_hash_table_contents_free();
if (lcr_dbf.use_table(dbh, &lcr_rule_table) < 0) {
LM_ERR("error while trying to use lcr_rule table\n");
goto err;
}
VAL_INT(vals) = lcr_id;
if (DB_CAPABILITY(lcr_dbf, DB_CAP_FETCH)) {
if (lcr_dbf.query(dbh, key_cols, op, vals, rule_cols, 1, 6, 0, 0)
< 0) {
LM_ERR("db query on lcr_rule table failed\n");
goto err;
}
if (lcr_dbf.fetch_result(dbh, &res, fetch_rows_param) < 0) {
LM_ERR("failed to fetch rows from lcr_rule table\n");
goto err;
}
} else {
if (lcr_dbf.query(dbh, key_cols, op, vals, rule_cols, 1, 6, 0, &res)
< 0) {
LM_ERR("db query on lcr_rule table failed\n");
goto err;
}
}
n = 0;
request_uri_re = from_uri_re = 0;
do {
LM_DBG("loading, cycle %d with <%d> rows", n++, RES_ROW_N(res));
for (i = 0; i < RES_ROW_N(res); i++) {
request_uri_re = from_uri_re = 0;
row = RES_ROWS(res) + i;
if ((VAL_NULL(ROW_VALUES(row)) == 1) ||
(VAL_TYPE(ROW_VALUES(row)) != DB1_INT)) {
LM_ERR("lcr rule id at row <%u> is null or not int\n", i);
goto err;
}
rule_id = (unsigned int)VAL_INT(ROW_VALUES(row));
enabled = (unsigned int)VAL_INT(ROW_VALUES(row) + 4);
if ((enabled != 0) && (enabled != 1)) {
LM_ERR("lcr rule <%u> enabled is not 0 or 1\n", rule_id);
goto err;
}
if (enabled == 0) {
LM_DBG("skipping disabled lcr rule <%u>\n", rule_id);
continue;
}
if (VAL_NULL(ROW_VALUES(row) + 1) == 1) {
prefix_len = 0;
prefix = 0;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 1) != DB1_STRING) {
LM_ERR("lcr rule <%u> prefix is not string\n", rule_id);
goto err;
}
prefix = (char *)VAL_STRING(ROW_VALUES(row) + 1);
prefix_len = strlen(prefix);
}
if (prefix_len > MAX_PREFIX_LEN) {
LM_ERR("lcr rule <%u> prefix is too long\n", rule_id);
goto err;
}
if ((VAL_NULL(ROW_VALUES(row) + 3) == 1) ||
(VAL_TYPE(ROW_VALUES(row) + 3) != DB1_INT)) {
LM_ERR("lcr rule <%u> stopper is NULL or not int\n",
rule_id);
goto err;
}
stopper = (unsigned int)VAL_INT(ROW_VALUES(row) + 3);
if ((stopper != 0) && (stopper != 1)) {
LM_ERR("lcr rule <%u> stopper is not 0 or 1\n", rule_id);
goto err;
}
if ((VAL_NULL(ROW_VALUES(row) + 4) == 1) ||
(VAL_TYPE(ROW_VALUES(row) + 4) != DB1_INT)) {
LM_ERR("lcr rule <%u> enabled is NULL or not int\n",
rule_id);
goto err;
}
if (VAL_NULL(ROW_VALUES(row) + 2) == 1) {
from_uri_len = 0;
from_uri = 0;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 2) != DB1_STRING) {
LM_ERR("lcr rule <%u> from_uri is not string\n",
rule_id);
goto err;
}
from_uri = (char *)VAL_STRING(ROW_VALUES(row) + 2);
from_uri_len = strlen(from_uri);
}
if (from_uri_len > MAX_URI_LEN) {
LM_ERR("lcr rule <%u> from_uri is too long\n", rule_id);
goto err;
}
if (from_uri_len > 0) {
from_uri_re = reg_ex_comp(from_uri);
if (from_uri_re == 0) {
LM_ERR("failed to compile lcr rule <%u> from_uri "
"<%s>\n", rule_id, from_uri);
goto err;
}
} else {
from_uri_re = 0;
}
if (VAL_NULL(ROW_VALUES(row) + 5) == 1) {
request_uri_len = 0;
request_uri = 0;
} else {
if (VAL_TYPE(ROW_VALUES(row) + 5) != DB1_STRING) {
LM_ERR("lcr rule <%u> request_uri is not string\n",
rule_id);
goto err;
}
request_uri = (char *)VAL_STRING(ROW_VALUES(row) + 5);
request_uri_len = strlen(request_uri);
}
if (request_uri_len > MAX_URI_LEN) {
LM_ERR("lcr rule <%u> request_uri is too long\n", rule_id);
goto err;
}
if (request_uri_len > 0) {
request_uri_re = reg_ex_comp(request_uri);
if (request_uri_re == 0) {
LM_ERR("failed to compile lcr rule <%u> request_uri "
"<%s>\n", rule_id, request_uri);
goto err;
}
} else {
request_uri_re = 0;
}
if (!rule_hash_table_insert(rules, lcr_id, rule_id, prefix_len,
prefix, from_uri_len, from_uri,
from_uri_re, request_uri_len,
request_uri, request_uri_re, stopper) ||
!prefix_len_insert(rules, prefix_len)) {
goto err;
}
}
if (DB_CAPABILITY(lcr_dbf, DB_CAP_FETCH)) {
if (lcr_dbf.fetch_result(dbh, &res, fetch_rows_param) < 0) {
LM_ERR("fetching of rows from lcr_rule table failed\n");
goto err;
}
} else {
break;
}
} while (RES_ROW_N(res) > 0);
lcr_dbf.free_result(dbh, res);
res = NULL;
/* Reload gws */
gws = gw_pt[0];
if (lcr_dbf.use_table(dbh, &lcr_gw_table) < 0) {
LM_ERR("error while trying to use lcr_gw table\n");
goto err;
}
VAL_INT(vals) = lcr_id;
if (lcr_dbf.query(dbh, key_cols, op, vals, gw_cols, 1, 13, 0, &res)
< 0) {
LM_ERR("failed to query gw data\n");
goto err;
}
if (RES_ROW_N(res) + 1 > lcr_gw_count_param) {
LM_ERR("too many gateways\n");
goto err;
}
null_gw_ip_addr = gw_cnt = 0;
if (!insert_gws(res, gws, &null_gw_ip_addr, &gw_cnt)) goto err;
lcr_dbf.free_result(dbh, res);
res = NULL;
VAL_INT(vals) = 0;
if (lcr_dbf.query(dbh, key_cols, op, vals, gw_cols, 1, 13, 0, &res)
< 0) {
LM_ERR("failed to query gw data\n");
goto err;
}
if (RES_ROW_N(res) + 1 + gw_cnt > lcr_gw_count_param) {
LM_ERR("too many gateways\n");
goto err;
}
if (!insert_gws(res, gws, &null_gw_ip_addr, &gw_cnt)) goto err;
lcr_dbf.free_result(dbh, res);
res = NULL;
qsort(&(gws[1]), gw_cnt, sizeof(struct gw_info), comp_gws);
gws[0].ip_addr.u.addr32[0] = gw_cnt;
gws[0].port = null_gw_ip_addr;
/* Reload targets */
if (lcr_dbf.use_table(dbh, &lcr_rule_target_table) < 0) {
LM_ERR("error while trying to use lcr_rule_target table\n");
goto err;
}
VAL_INT(vals) = lcr_id;
if (DB_CAPABILITY(lcr_dbf, DB_CAP_FETCH)) {
if (lcr_dbf.query(dbh, key_cols, op, vals, target_cols, 1, 4, 0, 0)
< 0) {
LM_ERR("db query on lcr_rule_target table failed\n");
goto err;
}
if (lcr_dbf.fetch_result(dbh, &res, fetch_rows_param) < 0) {
LM_ERR("failed to fetch rows from lcr_rule_target table\n");
goto err;
}
} else {
if (lcr_dbf.query(dbh, key_cols, op, vals, target_cols, 1, 4, 0,
&res) < 0) {
LM_ERR("db query on lcr_rule_target table failed\n");
goto err;
}
}
n = 0;
do {
LM_DBG("loading, cycle %d with <%d> rows", n++, RES_ROW_N(res));
for (i = 0; i < RES_ROW_N(res); i++) {
row = RES_ROWS(res) + i;
if ((VAL_NULL(ROW_VALUES(row)) == 1) ||
(VAL_TYPE(ROW_VALUES(row)) != DB1_INT)) {
LM_ERR("lcr_rule_target rule_id at row <%u> is null "
"or not int\n", i);
goto err;
}
rule_id = (unsigned int)VAL_INT(ROW_VALUES(row));
if ((VAL_NULL(ROW_VALUES(row) + 1) == 1) ||
(VAL_TYPE(ROW_VALUES(row) + 1) != DB1_INT)) {
LM_ERR("lcr_rule_target gw_id at row <%u> is null "
"or not int\n", i);
goto err;
}
gw_id = (unsigned int)VAL_INT(ROW_VALUES(row) + 1);
if ((VAL_NULL(ROW_VALUES(row) + 2) == 1) ||
(VAL_TYPE(ROW_VALUES(row) + 2) != DB1_INT)) {
LM_ERR("lcr_rule_target priority at row <%u> is null "
"or not int\n", i);
goto err;
}
priority = (unsigned int)VAL_INT(ROW_VALUES(row) + 2);
if (priority > 255) {
LM_ERR("lcr_rule_target priority value at row <%u> is "
"not 0-255\n", i);
goto err;
}
if ((VAL_NULL(ROW_VALUES(row) + 3) == 1) ||
(VAL_TYPE(ROW_VALUES(row) + 3) != DB1_INT)) {
LM_ERR("lcr_rule_target weight at row <%u> is null "
"or not int\n", i);
goto err;
}
weight = (unsigned int)VAL_INT(ROW_VALUES(row) + 3);
if ((weight < 1) || (weight > 254)) {
LM_ERR("lcr_rule_target weight value at row <%u> is "
"not 1-254\n", i);
goto err;
}
tmp = rule_hash_table_insert_target(rules, gws, rule_id, gw_id,
priority, weight);
if (tmp == 2) {
LM_INFO("skipping disabled <gw/rule> = <%u/%u>\n",
gw_id, rule_id);
} else if (tmp != 1) {
LM_ERR("could not insert target to rule <%u>\n", rule_id);
goto err;
}
}
if (DB_CAPABILITY(lcr_dbf, DB_CAP_FETCH)) {
if (lcr_dbf.fetch_result(dbh, &res, fetch_rows_param) < 0) {
LM_ERR("fetching of rows from lcr_rule_target table "
"failed\n");
goto err;
}
} else {
break;
}
} while (RES_ROW_N(res) > 0);
lcr_dbf.free_result(dbh, res);
res = NULL;
/* swap tables */
rule_pt_tmp = rule_pt[lcr_id];
gw_pt_tmp = gw_pt[lcr_id];
rule_pt[lcr_id] = rules;
gw_pt[lcr_id] = gws;
rule_pt[0] = rule_pt_tmp;
gw_pt[0] = gw_pt_tmp;
}
lcr_db_close();
rule_id_hash_table_contents_free();
if (rule_id_hash_table) pkg_free(rule_id_hash_table);
return 1;
err:
lcr_dbf.free_result(dbh, res);
lcr_db_close();
rule_id_hash_table_contents_free();
if (rule_id_hash_table) pkg_free(rule_id_hash_table);
return -1;
}
inline int encode_avp_value(char *value, unsigned int gw_index, uri_type scheme,
unsigned int strip,
char *prefix, unsigned int prefix_len,
char *tag, unsigned int tag_len,
struct ip_addr *ip_addr, char *hostname,
unsigned int hostname_len, unsigned int port,
char *params, unsigned int params_len,
uri_transport transport, unsigned int flags)
{
char *at, *string;
int len;
at = value;
/* gw index */
string = int2str(gw_index, &len);
append_str(at, string, len);
append_chr(at, '|');
/* scheme */
string = int2str(scheme, &len);
append_str(at, string, len);
append_chr(at, '|');
/* strip */
string = int2str(strip, &len);
append_str(at, string, len);
append_chr(at, '|');
/* prefix */
append_str(at, prefix, prefix_len);
append_chr(at, '|');
/* tag */
append_str(at, tag, tag_len);
append_chr(at, '|');
/* ip_addr */
if (ip_addr->af == AF_INET && ip_addr->u.addr32[0] > 0) {
string = int2str(ip_addr->u.addr32[0], &len);
append_str(at, string, len);
}
#ifdef USE_IPV6
else if (ip_addr->af == AF_INET6 && !ip_addr_any(ip_addr)) {
append_chr(at, '[');
at += ip6tosbuf(ip_addr->u.addr, at, MAX_URI_LEN - (at - value));
append_chr(at, ']');
}
#endif
append_chr(at, '|');
/* hostname */
append_str(at, hostname, hostname_len);
append_chr(at, '|');
/* port */
if (port > 0) {
string = int2str(port, &len);
append_str(at, string, len);
}
append_chr(at, '|');
/* params */
append_str(at, params, params_len);
append_chr(at, '|');
/* transport */
string = int2str(transport, &len);
append_str(at, string, len);
append_chr(at, '|');
/* flags */
string = int2str(flags, &len);
append_str(at, string, len);
return at - value;
}
inline int decode_avp_value(char *value, unsigned int *gw_index, str *scheme,
unsigned int *strip, str *prefix, str *tag,
struct ip_addr *addr, str *hostname, str *port,
str *params, str *transport, unsigned int *flags)
{
unsigned int u;
str s;
char *sep;
struct ip_addr *ip;
/* gw index */
s.s = value;
sep = index(s.s, '|');
if (sep == NULL) {
LM_ERR("index was not found in AVP value\n");
return 0;
}
s.len = sep - s.s;
str2int(&s, gw_index);
/* scheme */
s.s = sep + 1;
sep = index(s.s, '|');
if (sep == NULL) {
LM_ERR("scheme was not found in AVP value\n");
return 0;
}
s.len = sep - s.s;
str2int(&s, &u);
if (u == SIP_URI_T) {
scheme->s = "sip:";
scheme->len = 4;
} else {
scheme->s = "sips:";
scheme->len = 5;
}
/* strip */
s.s = sep + 1;
sep = index(s.s, '|');
if (sep == NULL) {
LM_ERR("strip was not found in AVP value\n");
return 0;
}
s.len = sep - s.s;
str2int(&s, strip);
/* prefix */
prefix->s = sep + 1;
sep = index(prefix->s, '|');
if (sep == NULL) {
LM_ERR("prefix was not found in AVP value\n");
return 0;
}
prefix->len = sep - prefix->s;
/* tag */
tag->s = sep + 1;
sep = index(tag->s, '|');
if (sep == NULL) {
LM_ERR("tag was not found in AVP value\n");
return 0;
}
tag->len = sep - tag->s;
/* addr */
s.s = sep + 1;
sep = index(s.s, '|');
if (sep == NULL) {
LM_ERR("ip_addr was not found in AVP value\n");
return 0;
}
s.len = sep - s.s;
if (s.len > 0) {
if ((ip = str2ip(&s)) != NULL)
*addr = *ip;
#ifdef USE_IPV6
else if ((ip = str2ip6(&s)) != NULL)
*addr = *ip;
#endif
else {
str2int(&s, &u);
addr->af = AF_INET;
addr->len = 4;
addr->u.addr32[0] = u;
}
} else {
addr->af = 0;
}
/* hostname */
hostname->s = sep + 1;
sep = index(hostname->s, '|');
if (sep == NULL) {
LM_ERR("hostname was not found in AVP value\n");
return 0;
}
hostname->len = sep - hostname->s;
/* port */
port->s = sep + 1;
sep = index(port->s, '|');
if (sep == NULL) {
LM_ERR("scheme was not found in AVP value\n");
return 0;
}
port->len = sep - port->s;
/* params */
params->s = sep + 1;
sep = index(params->s, '|');
if (sep == NULL) {
LM_ERR("params was not found in AVP value\n");
return 0;
}
params->len = sep - params->s;
/* transport */
s.s = sep + 1;
sep = index(s.s, '|');
if (sep == NULL) {
LM_ERR("transport was not found in AVP value\n");
return 0;
}
s.len = sep - s.s;
str2int(&s, &u);
switch (u) {
case PROTO_NONE:
transport->s = (char *)0;
transport->len = 0;
break;
case PROTO_UDP:
transport->s = ";transport=udp";
transport->len = 14;
break;
case PROTO_TCP:
transport->s = ";transport=tcp";
transport->len = 14;
break;
case PROTO_TLS:
transport->s = ";transport=tls";
transport->len = 14;
break;
case PROTO_SCTP:
transport->s = ";transport=sctp";
transport->len = 15;
break;
default:
LM_ERR("unknown transport '%d'\n", u);
return 0;
}
/* flags */
s.s = sep + 1;
s.len = strlen(s.s);
str2int(&s, flags);
return 1;
}
/* Add gateways in matched_gws array into gw_uri_avps */
void add_gws_into_avps(struct gw_info *gws, struct matched_gw_info *matched_gws,
unsigned int gw_cnt, str *ruri_user)
{
unsigned int i, index, strip, hostname_len, params_len;
int prefix_len, tag_len;
str value;
char encoded_value[MAX_URI_LEN];
int_str val;
delete_avp(gw_uri_avp_type|AVP_VAL_STR, gw_uri_avp);
for (i = 0; i < gw_cnt; i++) {
if (matched_gws[i].duplicate == 1) continue;
index = matched_gws[i].gw_index;
hostname_len = gws[index].hostname_len;
params_len = gws[index].params_len;
strip = gws[index].strip;
if (strip > ruri_user->len) {
LM_ERR("strip count of gw is too large <%u>\n", strip);
goto skip;
}
prefix_len = gws[index].prefix_len;
tag_len = gws[index].tag_len;
if (5 /* gw_index */ + 5 /* scheme */ + 4 /* strip */ + prefix_len +
tag_len + 1 /* @ */ +
((hostname_len > IP6_MAX_STR_SIZE+2)?hostname_len:IP6_MAX_STR_SIZE+2) + 6 /* port */ +
params_len /* params */ + 15 /* transport */ + 10 /* flags */ +
7 /* separators */ > MAX_URI_LEN) {
LM_ERR("too long AVP value\n");
goto skip;
}
value.len =
encode_avp_value(encoded_value, index, gws[index].scheme,
strip, gws[index].prefix, prefix_len,
gws[index].tag, tag_len,
&gws[index].ip_addr,
gws[index].hostname, hostname_len,
gws[index].port, gws[index].params, params_len,
gws[index].transport, gws[index].flags);
value.s = (char *)&(encoded_value[0]);
val.s = value;
add_avp(gw_uri_avp_type|AVP_VAL_STR, gw_uri_avp, val);
LM_DBG("added gw_uri_avp <%.*s> with weight <%u>\n",
value.len, value.s, matched_gws[i].weight);
skip:
continue;
}
}
/*
* Load info of matching GWs into gw_uri_avps
*/
static int load_gws(struct sip_msg* _m, int argc, action_u_t argv[])
{
str ruri_user, from_uri, *request_uri;
int i, j, lcr_id;
unsigned int gw_index, now, dex;
int_str val;
struct matched_gw_info matched_gws[MAX_NO_OF_GWS + 1];
struct rule_info **rules, *rule, *pl;
struct gw_info *gws;
struct target *t;
char* tmp;
/* Get and check parameter values */
if (argc < 1) {
LM_ERR("lcr_id parameter is missing\n");
return -1;
}
lcr_id = strtol(argv[0].u.string, &tmp, 10);
if ((tmp == 0) || (*tmp) || (tmp == argv[0].u.string)) {
LM_ERR("invalid lcr_id parameter %s\n", argv[0].u.string);
return -1;
}
if ((lcr_id < 1) || (lcr_id > lcr_count_param)) {
LM_ERR("invalid lcr_id parameter value %d\n", lcr_id);
return -1;
}
if (argc > 1) {
ruri_user = argv[1].u.str;
} else {
if ((parse_sip_msg_uri(_m) < 0) || (!_m->parsed_uri.user.s)) {
LM_ERR("error while parsing R-URI\n");
return -1;
}
ruri_user = _m->parsed_uri.user;
}
if (argc > 2) {
from_uri = argv[2].u.str;
} else {
from_uri.len = 0;
from_uri.s = (char *)0;
}
if (argc > 3) {
LM_ERR("too many parameters\n");
return -1;
}
LM_DBG("load_gws(%u, %.*s, %.*s)\n", lcr_id, ruri_user.len, ruri_user.s,
from_uri.len, from_uri.s);
request_uri = GET_RURI(_m);
/* Use rules and gws with index lcr_id */
rules = rule_pt[lcr_id];
gws = gw_pt[lcr_id];
/*
* Find lcr entries that match based on prefix and from_uri and collect
* gateways of matching entries into matched_gws array so that each
* gateway appears in the array only once.
*/
pl = rules[lcr_rule_hash_size_param];
gw_index = 0;
if (defunct_capability_param > 0) {
delete_avp(defunct_gw_avp_type, defunct_gw_avp);
}
now = time((time_t *)NULL);
/* check prefixes in from longest to shortest */
while (pl) {
if (ruri_user.len < pl->prefix_len) {
pl = pl->next;
continue;
}
rule = rule_hash_table_lookup(rules, pl->prefix_len, ruri_user.s);
while (rule) {
/* Match prefix */
if ((rule->prefix_len != pl->prefix_len) ||
(strncmp(rule->prefix, ruri_user.s, pl->prefix_len)))
goto next;
/* Match from uri */
if ((rule->from_uri_len != 0) &&
(pcre_exec(rule->from_uri_re, NULL, from_uri.s,
from_uri.len, 0, 0, NULL, 0) < 0)) {
LM_DBG("from uri <%.*s> did not match to from regex <%.*s>",
from_uri.len, from_uri.s, rule->from_uri_len,
rule->from_uri);
goto next;
}
/* Match request uri */
if ((rule->request_uri_len != 0) &&
(pcre_exec(rule->request_uri_re, NULL, request_uri->s,
request_uri->len, 0, 0, NULL, 0) < 0)) {
LM_DBG("request uri <%.*s> did not match to request regex <%.*s>",
request_uri->len, request_uri->s, rule->request_uri_len,
rule->request_uri);
goto next;
}
/* Load gws associated with this rule */
t = rule->targets;
while (t) {
/* If this gw is defunct, skip it */
if (gws[t->gw_index].defunct_until > now) goto skip_gw;
matched_gws[gw_index].gw_index = t->gw_index;
matched_gws[gw_index].prefix_len = pl->prefix_len;
matched_gws[gw_index].priority = t->priority;
matched_gws[gw_index].weight = t->weight *
(rand() >> 8);
matched_gws[gw_index].duplicate = 0;
LM_DBG("added matched_gws[%d]=[%u, %u, %u, %u]\n",
gw_index, t->gw_index, pl->prefix_len,
t->priority, matched_gws[gw_index].weight);
gw_index++;
skip_gw:
t = t->next;
}
/* Do not look further if this matching rule was stopper */
if (rule->stopper == 1) goto done;
next:
rule = rule->next;
}
pl = pl->next;
}
done:
/* Sort gateways in reverse order based on prefix_len, priority,
and randomized weight */
qsort(matched_gws, gw_index, sizeof(struct matched_gw_info), comp_matched);
/* Remove duplicate gws */
for (i = gw_index - 1; i >= 0; i--) {
if (matched_gws[i].duplicate == 1) continue;
dex = matched_gws[i].gw_index;
for (j = i - 1; j >= 0; j--) {
if (matched_gws[j].gw_index == dex) {
matched_gws[j].duplicate = 1;
}
}
}
/* Add gateways into gw_uris_avp */
add_gws_into_avps(gws, matched_gws, gw_index, &ruri_user);
/* Add lcr_id into AVP */
if (defunct_capability_param > 0) {
delete_avp(lcr_id_avp_type, lcr_id_avp);
val.n = lcr_id;
add_avp(lcr_id_avp_type, lcr_id_avp, val);
}
if (gw_index > 0) {
return 1;
} else {
return 2;
}
}
/* Generate Request-URI and Destination URI */
static int generate_uris(struct sip_msg* _m, char *r_uri, str *r_uri_user,
unsigned int *r_uri_len, char *dst_uri,
unsigned int *dst_uri_len, struct ip_addr *addr,
unsigned int *gw_index, unsigned int *flags,
str *tag)
{
int_str gw_uri_val;
struct usr_avp *gu_avp;
str scheme, prefix, hostname, port, params, transport, addr_str,
tmp_tag;
char *at;
unsigned int strip;
gu_avp = search_first_avp(gw_uri_avp_type, gw_uri_avp, &gw_uri_val, 0);
if (!gu_avp) return 0; /* No more gateways left */
decode_avp_value(gw_uri_val.s.s, gw_index, &scheme, &strip, &prefix,
&tmp_tag, addr, &hostname, &port, &params, &transport,
flags);
if (addr->af != 0) {
addr_str.s = ip_addr2a(addr);
addr_str.len = strlen(addr_str.s);
} else {
addr_str.len = 0;
}
if (scheme.len + r_uri_user->len - strip + prefix.len + 1 /* @ */ +
((hostname.len > IP6_MAX_STR_SIZE+2)?hostname.len:IP6_MAX_STR_SIZE+2) + 1 /* : */ +
port.len + params.len + transport.len + 1 /* null */ > MAX_URI_LEN) {
LM_ERR("too long Request URI or DST URI\n");
return -1;
}
if ((dont_strip_or_prefix_flag_param != -1) &&
isflagset(_m, dont_strip_or_prefix_flag_param)) {
strip = 0;
prefix.len = 0;
}
at = r_uri;
append_str(at, scheme.s, scheme.len);
append_str(at, prefix.s, prefix.len);
if (strip > r_uri_user->len) {
LM_ERR("strip count <%u> is larger than R-URI user <%.*s>\n",
strip, r_uri_user->len, r_uri_user->s);
return -1;
}
append_str(at, r_uri_user->s + strip, r_uri_user->len - strip);
append_chr(at, '@');
if ((addr_str.len > 0) && (hostname.len > 0)) {
/* both ip_addr and hostname specified:
place hostname in r-uri and ip_addr in dst-uri */
append_str(at, hostname.s, hostname.len);
if (params.len > 0) {
append_str(at, params.s, params.len);
}
*at = '\0';
*r_uri_len = at - r_uri;
at = dst_uri;
append_str(at, scheme.s, scheme.len);
if (addr->af == AF_INET6)
append_chr(at, '[');
append_str(at, addr_str.s, addr_str.len);
if (addr->af == AF_INET6)
append_chr(at, ']');
if (port.len > 0) {
append_chr(at, ':');
append_str(at, port.s, port.len);
}
if (transport.len > 0) {
append_str(at, transport.s, transport.len);
}
*at = '\0';
*dst_uri_len = at - dst_uri;
} else {
/* either ip_addr or hostname specified:
place the given one in r-uri and leave dst-uri empty */
if (addr_str.len > 0) {
if (addr->af == AF_INET6)
append_chr(at, '[');
append_str(at, addr_str.s, addr_str.len);
if (addr->af == AF_INET6)
append_chr(at, ']');
} else {
append_str(at, hostname.s, hostname.len);
}
if (port.len > 0) {
append_chr(at, ':');
append_str(at, port.s, port.len);
}
if (transport.len > 0) {
append_str(at, transport.s, transport.len);
}
if (params.len > 0) {
append_str(at, params.s, params.len);
}
*at = '\0';
*r_uri_len = at - r_uri;
*dst_uri_len = 0;
}
memcpy(tag->s, tmp_tag.s, tmp_tag.len);
tag->len = tmp_tag.len;
destroy_avp(gu_avp);
LM_DBG("r_uri <%.*s>, dst_uri <%.*s>\n",
(int)*r_uri_len, r_uri, (int)*dst_uri_len, dst_uri);
return 1;
}
/*
* Defunct current gw until time given as argument has passed.
*/
static int defunct_gw(struct sip_msg* _m, char *_defunct_period, char *_s2)
{
int_str lcr_id_val, index_val;
struct gw_info *gws;
char *tmp;
unsigned int gw_index, defunct_until;
int defunct_period;
/* Check defunct gw capability */
if (defunct_capability_param == 0) {
LM_ERR("no defunct gw capability, activate by setting "
"defunct_capability_param module param\n");
return -1;
}
/* Get parameter value */
/* Get and check parameter value */
defunct_period = strtol(_defunct_period, &tmp, 10);
if ((tmp == 0) || (*tmp) || (tmp == _defunct_period)) {
LM_ERR("invalid defunct_period parameter %s\n", _defunct_period);
return -1;
}
if (defunct_period < 1) {
LM_ERR("invalid defunct_period param value %d\n", defunct_period);
return -1;
}
/* Get AVP values */
if (search_first_avp(lcr_id_avp_type, lcr_id_avp, &lcr_id_val, 0)
== NULL) {
LM_ERR("lcr_id_avp was not found\n");
return -1;
}
gws = gw_pt[lcr_id_val.n];
if (search_first_avp(defunct_gw_avp_type, defunct_gw_avp,
&index_val, 0) == NULL) {
LM_ERR("defucnt_gw_avp was not found\n");
return -1;
}
gw_index = index_val.n;
if ((gw_index < 1) || (gw_index > gws[0].ip_addr.u.addr32[0])) {
LM_ERR("gw index <%u> is out of bounds\n", gw_index);
return -1;
}
/* Defunct gw */
defunct_until = time((time_t *)NULL) + defunct_period;
LM_DBG("defuncting gw with name <%.*s> until <%u>\n",
gws[gw_index].gw_name_len, gws[gw_index].gw_name, defunct_until);
gws[gw_index].defunct_until = defunct_until;
return 1;
}
/*
* Defunct given gw in given lcr until time period given as argument has passed.
*/
int rpc_defunct_gw(unsigned int lcr_id, unsigned int gw_id, unsigned int period)
{
struct gw_info *gws;
unsigned int until, i;
if ((lcr_id < 1) || (lcr_id > lcr_count_param)) {
LM_ERR("invalid lcr_id value <%u>\n", lcr_id);
return 0;
}
until = time((time_t *)NULL) + period;
LM_DBG("defuncting gw <lcr_id/gw_id>=<%u/%u> for %u seconds until %d\n",
lcr_id, gw_id, period, until);
gws = gw_pt[lcr_id];
for (i = 1; i <= gws[0].ip_addr.u.addr32[0]; i++) {
if (gws[i].gw_id == gw_id) {
gws[i].defunct_until = until;
return 1;
}
}
LM_ERR("gateway with id <%u> not found\n", gw_id);
return 0;
}
/*
* When called first time, rewrites scheme, host, port, and
* transport parts of R-URI based on first gw_uri_avp value, which is then
* destroyed. Saves R-URI user to ruri_user_avp for later use.
*
* On other calls, rewrites R-URI, where scheme, host, port,
* and transport of URI are taken from the first gw_uri_avp value,
* which is then destroyed. URI user is taken either from ruri_user_avp
* value saved earlier.
*
* Returns 1 upon success and -1 upon failure.
*/
static int next_gw(struct sip_msg* _m, char* _s1, char* _s2)
{
int_str ruri_user_val, val;
struct usr_avp *ru_avp;
int rval;
str uri_str, tag_str;
char tag[MAX_TAG_LEN];
unsigned int flags, r_uri_len, dst_uri_len, gw_index;
char r_uri[MAX_URI_LEN], dst_uri[MAX_URI_LEN];
struct ip_addr addr;
tag_str.s = &(tag[0]);
ru_avp = search_first_avp(ruri_user_avp_type, ruri_user_avp,
&ruri_user_val, 0);
if (ru_avp == NULL) {
/* First invocation either in route or failure route block.
* Take Request-URI user from Request-URI and generate Request
* and Destination URIs. */
if (parse_sip_msg_uri(_m) < 0) {
LM_ERR("parsing of R-URI failed\n");
return -1;
}
if (!generate_uris(_m, r_uri, &(_m->parsed_uri.user), &r_uri_len,
dst_uri, &dst_uri_len, &addr, &gw_index, &flags,
&tag_str)) {
return -1;
}
/* Save Request-URI user into uri_user_avp for use in subsequent
* invocations. */
val.s = _m->parsed_uri.user;
add_avp(ruri_user_avp_type|AVP_VAL_STR, ruri_user_avp, val);
LM_DBG("added ruri_user_avp <%.*s>\n", val.s.len, val.s.s);
} else {
/* Subsequent invocation either in route or failure route block.
* Take Request-URI user from ruri_user_avp and generate Request
* and Destination URIs. */
if (!generate_uris(_m, r_uri, &(ruri_user_val.s), &r_uri_len, dst_uri,
&dst_uri_len, &addr, &gw_index, &flags, &tag_str)) {
return -1;
}
}
/* Rewrite Request URI */
uri_str.s = r_uri;
uri_str.len = r_uri_len;
rewrite_uri(_m, &uri_str);
/* Set Destination URI if not empty */
if (dst_uri_len > 0) {
uri_str.s = dst_uri;
uri_str.len = dst_uri_len;
LM_DBG("setting du to <%.*s>\n", uri_str.len, uri_str.s);
rval = set_dst_uri(_m, &uri_str);
if (rval != 0) {
LM_ERR("calling do_action failed with return value <%d>\n", rval);
return -1;
}
}
/* Set flags_avp */
if (flags_avp_param) {
val.n = flags;
add_avp(flags_avp_type, flags_avp, val);
LM_DBG("added flags_avp <%u>\n", (unsigned int)val.n);
}
/* Set tag_avp */
if (tag_avp_param) {
val.s = tag_str;
add_avp(tag_avp_type, tag_avp, val);
LM_DBG("added tag_avp <%.*s>\n", val.s.len, val.s.s);
}
/* Add index of selected gw to defunct gw AVP */
if (defunct_capability_param > 0) {
delete_avp(defunct_gw_avp_type, defunct_gw_avp);
val.n = gw_index;
add_avp(defunct_gw_avp_type, defunct_gw_avp, val);
LM_DBG("added defunct_gw_avp <%u>", addr.u.addr32[0]);
}
return 1;
}
/*
* Checks if request comes from ip address of a gateway
*/
static int do_from_gw(struct sip_msg* _m, unsigned int lcr_id,
struct ip_addr *src_addr, uri_transport transport)
{
struct gw_info *res, gw, *gws;
int_str val;
gws = gw_pt[lcr_id];
/* Skip lcr instance if some of its gws do not have ip_addr */
if (gws[0].port != 0) {
LM_DBG("lcr instance <%u> has gw(s) without ip_addr\n", lcr_id);
return -1;
}
/* Search for gw ip address */
gw.ip_addr = *src_addr;
res = (struct gw_info *)bsearch(&gw, &(gws[1]), gws[0].ip_addr.u.addr32[0],
sizeof(struct gw_info), comp_gws);
/* Store tag and flags and return result */
if ((res != NULL) &&
((transport == PROTO_NONE) || (res->transport == transport))) {
LM_DBG("request game from gw\n");
if (tag_avp_param) {
val.s.s = res->tag;
val.s.len = res->tag_len;
add_avp(tag_avp_type, tag_avp, val);
LM_DBG("added tag_avp <%.*s>\n", val.s.len, val.s.s);
}
if (flags_avp_param) {
val.n = res->flags;
add_avp(flags_avp_type, flags_avp, val);
LM_DBG("added flags_avp <%u>\n", (unsigned int)val.n);
}
return 1;
} else {
LM_DBG("request did not come from gw\n");
return -1;
}
}
/*
* Checks if request comes from ip address of a gateway taking source
* address and transport protocol from request.
*/
static int from_gw_1(struct sip_msg* _m, char* _lcr_id, char* _s2)
{
int lcr_id;
char *tmp;
uri_transport transport;
/* Get and check parameter value */
lcr_id = strtol(_lcr_id, &tmp, 10);
if ((tmp == 0) || (*tmp) || (tmp == _lcr_id)) {
LM_ERR("invalid lcr_id parameter %s\n", _lcr_id);
return -1;
}
if ((lcr_id < 1) || (lcr_id > lcr_count_param)) {
LM_ERR("invalid lcr_id parameter value %d\n", lcr_id);
return -1;
}
/* Get transport protocol */
transport = _m->rcv.proto;
/* Do test */
return do_from_gw(_m, lcr_id, &_m->rcv.src_ip, transport);
}
/*
* Checks if request comes from ip address of a gateway taking source
* address and transport protocol from parameters
*/
static int from_gw_3(struct sip_msg* _m, char* _lcr_id, char* _addr,
char* _transport)
{
struct ip_addr src_addr;
int lcr_id;
char *tmp;
struct ip_addr *ip;
str addr_str;
uri_transport transport;
/* Get and check parameter values */
lcr_id = strtol(_lcr_id, &tmp, 10);
if ((tmp == 0) || (*tmp) || (tmp == _lcr_id)) {
LM_ERR("invalid lcr_id parameter %s\n", _lcr_id);
return -1;
}
if ((lcr_id < 1) || (lcr_id > lcr_count_param)) {
LM_ERR("invalid lcr_id parameter value %d\n", lcr_id);
return -1;
}
addr_str.s = _addr;
addr_str.len = strlen(_addr);
if ((ip = str2ip(&addr_str)) != NULL)
src_addr = *ip;
#ifdef USE_IPV6
else if ((ip = str2ip6(&addr_str)) != NULL)
src_addr = *ip;
#endif
else {
LM_ERR("addr param value %s is not an IP address\n", _addr);
return -1;
}
transport = strtol(_transport, &tmp, 10);
if ((tmp == 0) || (*tmp) || (tmp == _transport)) {
LM_ERR("invalid transport parameter %s\n", _lcr_id);
return -1;
}
if ((transport < PROTO_NONE) || (transport > PROTO_SCTP)) {
LM_ERR("invalid transport parameter value %d\n", transport);
return -1;
}
/* Do test */
return do_from_gw(_m, lcr_id, &src_addr, transport);
}
/*
* Checks if request comes from ip address of any gateway taking source
* address from request.
*/
static int from_any_gw_0(struct sip_msg* _m, char* _s1, char* _s2)
{
unsigned int i;
uri_transport transport;
transport = _m->rcv.proto;
for (i = 1; i <= lcr_count_param; i++) {
if (do_from_gw(_m, i, &_m->rcv.src_ip, transport) == 1) {
return i;
}
}
return -1;
}
/*
* Checks if request comes from ip address of a a gateway taking source
* IP address and transport protocol from parameters.
*/
static int from_any_gw_2(struct sip_msg* _m, char* _addr, char* _transport)
{
unsigned int i;
char *tmp;
struct ip_addr *ip, src_addr;
str addr_str;
uri_transport transport;
/* Get and check parameter values */
addr_str.s = _addr;
addr_str.len = strlen(_addr);
if ((ip = str2ip(&addr_str)) != NULL)
src_addr = *ip;
#ifdef USE_IPV6
else if ((ip = str2ip6(&addr_str)) != NULL)
src_addr = *ip;
#endif
else {
LM_ERR("addr param value %s is not an IP address\n", _addr);
return -1;
}
transport = strtol(_transport, &tmp, 10);
if ((tmp == 0) || (*tmp) || (tmp == _transport)) {
LM_ERR("invalid transport parameter %s\n", _transport);
return -1;
}
if ((transport < PROTO_NONE) || (transport > PROTO_SCTP)) {
LM_ERR("invalid transport parameter value %d\n", transport);
return -1;
}
/* Do test */
for (i = 1; i <= lcr_count_param; i++) {
if (do_from_gw(_m, i, &src_addr, transport) == 1) {
return i;
}
}
return -1;
}
/*
* Checks if in-dialog request goes to ip address of a gateway.
*/
static int do_to_gw(struct sip_msg* _m, unsigned int lcr_id,
struct ip_addr *dst_addr, uri_transport transport)
{
struct gw_info *res, gw, *gws;
gws = gw_pt[lcr_id];
/* Skip lcr instance if some of its gws do not have ip addr */
if (gws[0].port != 0) {
LM_DBG("lcr instance <%u> has gw(s) without ip_addr\n", lcr_id);
return -1;
}
/* Search for gw ip address */
gw.ip_addr = *dst_addr;
res = (struct gw_info *)bsearch(&gw, &(gws[1]), gws[0].ip_addr.u.addr32[0],
sizeof(struct gw_info), comp_gws);
/* Return result */
if ((res != NULL) &&
((transport == PROTO_NONE) || (res->transport == transport))) {
LM_DBG("request goes to gw\n");
return 1;
} else {
LM_DBG("request is not going to gw\n");
return -1;
}
}
/*
* Checks if request goes to ip address and transport protocol of a gateway
* taking lcr_id from parameter and destination address and transport protocol
* from Request URI.
*/
static int to_gw_1(struct sip_msg* _m, char* _lcr_id, char* _s2)
{
int lcr_id;
char *tmp;
struct ip_addr *ip, dst_addr;
uri_transport transport;
/* Get and check parameter value */
lcr_id = strtol(_lcr_id, &tmp, 10);
if ((tmp == 0) || (*tmp) || (tmp == _lcr_id)) {
LM_ERR("invalid lcr_id parameter %s\n", _lcr_id);
return -1;
}
if ((lcr_id < 1) || (lcr_id > lcr_count_param)) {
LM_ERR("invalid lcr_id parameter value %d\n", lcr_id);
return -1;
}
/* Get destination address and transport protocol from R-URI */
if ((_m->parsed_uri_ok == 0) && (parse_sip_msg_uri(_m) < 0)) {
LM_ERR("while parsing Request-URI\n");
return -1;
}
if (_m->parsed_uri.host.len > IP6_MAX_STR_SIZE+2) {
LM_DBG("request is not going to gw "
"(Request-URI host is not an IP address)\n");
return -1;
}
if ((ip = str2ip(&(_m->parsed_uri.host))) != NULL)
dst_addr = *ip;
#ifdef USE_IPV6
else if ((ip = str2ip6(&(_m->parsed_uri.host))) != NULL)
dst_addr = *ip;
#endif
else {
LM_DBG("request is not going to gw "
"(Request-URI host is not an IP address)\n");
return -1;
}
transport = _m->parsed_uri.proto;
/* Do test */
return do_to_gw(_m, lcr_id, &dst_addr, transport);
}
/*
* Checks if request goes to ip address of a gateway taking lcr_id,
* destination address and transport protocol from parameters.
*/
static int to_gw_3(struct sip_msg* _m, char* _lcr_id, char* _addr,
char* _transport)
{
int lcr_id;
char *tmp;
struct ip_addr *ip, dst_addr;
str addr_str;
uri_transport transport;
/* Get and check parameter values */
lcr_id = strtol(_lcr_id, &tmp, 10);
if ((tmp == 0) || (*tmp) || (tmp == _lcr_id)) {
LM_ERR("invalid lcr_id parameter %s\n", _lcr_id);
return -1;
}
if ((lcr_id < 1) || (lcr_id > lcr_count_param)) {
LM_ERR("invalid lcr_id parameter value %d\n", lcr_id);
return -1;
}
addr_str.s = _addr;
addr_str.len = strlen(_addr);
if ((ip = str2ip(&addr_str)) != NULL)
dst_addr = *ip;
#ifdef USE_IPV6
else if ((ip = str2ip(&addr_str)) != NULL)
dst_addr = *ip;
#endif
else {
LM_ERR("addr param value %s is not an IP address\n", _addr);
return -1;
}
transport = strtol(_transport, &tmp, 10);
if ((tmp == 0) || (*tmp) || (tmp == _transport)) {
LM_ERR("invalid transport parameter %s\n", _transport);
return -1;
}
if ((transport < PROTO_NONE) || (transport > PROTO_SCTP)) {
LM_ERR("invalid transport parameter value %d\n", transport);
return -1;
}
/* Do test */
return do_to_gw(_m, lcr_id, &dst_addr, transport);
}
/*
* Checks if request goes to ip address of any gateway taking destination
* address and transport protocol from Request-URI.
*/
static int to_any_gw_0(struct sip_msg* _m, char* _s1, char* _s2)
{
unsigned int i;
struct ip_addr *ip, dst_addr;
uri_transport transport;
/* Get destination address and transport protocol from R-URI */
if ((_m->parsed_uri_ok == 0) && (parse_sip_msg_uri(_m) < 0)) {
LM_ERR("while parsing Request-URI\n");
return -1;
}
if (_m->parsed_uri.host.len > IP6_MAX_STR_SIZE+2) {
LM_DBG("request is not going to gw "
"(Request-URI host is not an IP address)\n");
return -1;
}
if ((ip = str2ip(&(_m->parsed_uri.host))) != NULL)
dst_addr = *ip;
#ifdef USE_IPV6
else if ((ip = str2ip6(&(_m->parsed_uri.host))) != NULL)
dst_addr = *ip;
#endif
else {
LM_DBG("request is not going to gw "
"(Request-URI host is not an IP address)\n");
return -1;
}
transport = _m->parsed_uri.proto;
/* Do test */
for (i = 1; i <= lcr_count_param; i++) {
if (do_to_gw(_m, i, &dst_addr, transport) == 1) {
return i;
}
}
return -1;
}
/*
* Checks if request goes to ip address of any gateway taking destination
* address and transport protocol from parameters.
*/
static int to_any_gw_2(struct sip_msg* _m, char* _addr, char* _transport)
{
unsigned int i;
char *tmp;
struct ip_addr *ip, dst_addr;
uri_transport transport;
str addr_str;
/* Get and check parameter values */
addr_str.s = _addr;
addr_str.len = strlen(_addr);
if ((ip = str2ip(&addr_str)) != NULL)
dst_addr = *ip;
#ifdef USE_IPV6
if ((ip = str2ip6(&addr_str)) != NULL)
dst_addr = *ip;
#endif
else {
LM_ERR("addr param value %s is not an IP address\n", _addr);
return -1;
}
transport = strtol(_transport, &tmp, 10);
if ((tmp == 0) || (*tmp) || (tmp == _transport)) {
LM_ERR("invalid transport parameter %s\n", _transport);
return -1;
}
if ((transport < PROTO_NONE) || (transport > PROTO_SCTP)) {
LM_ERR("invalid transport parameter value %d\n", transport);
return -1;
}
/* Do test */
for (i = 1; i <= lcr_count_param; i++) {
if (do_to_gw(_m, i, &dst_addr, transport) == 1) {
return i;
}
}
return -1;
}