michael@16: /* michael@16: * $Id: lcr_mod.c 6015 2009-08-22 21:45:06Z bogdan_iancu $ michael@16: * michael@16: * Least Cost Routing module (also implements sequential forking) michael@16: * michael@16: * Copyright (C) 2005 Juha Heinanen michael@16: * Copyright (C) 2006 Voice Sistem SRL michael@16: * michael@16: * This file is part of opensips, a free SIP server. michael@16: * michael@16: * opensips is free software; you can redistribute it and/or modify michael@16: * it under the terms of the GNU General Public License as published by michael@16: * the Free Software Foundation; either version 2 of the License, or michael@16: * (at your option) any later version michael@16: * michael@16: * opensips is distributed in the hope that it will be useful, michael@16: * but WITHOUT ANY WARRANTY; without even the implied warranty of michael@16: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the michael@16: * GNU General Public License for more details. michael@16: * michael@16: * You should have received a copy of the GNU General Public License michael@16: * along with this program; if not, write to the Free Software michael@16: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA michael@16: * michael@16: * History: michael@16: * ------- michael@16: * 2005-02-14: Introduced lcr module (jh) michael@16: * 2005-02-20: Added sequential forking functions (jh) michael@16: * 2005-02-25: Added support for int AVP names, combined addr and port michael@16: * AVPs (jh) michael@16: * 2005-07-28: Added support for gw URI scheme and transport, michael@16: * backport from ser (kd) michael@16: * 2005-08-20: Added support for gw prefixes (jh) michael@16: * 2005-09-03: Request-URI user part can be modified between load_gws() michael@16: * and first next_gw() calls. michael@16: */ michael@16: michael@16: #include michael@16: #include michael@16: #include michael@16: #include michael@16: #include michael@16: #include michael@16: #include michael@16: #include michael@16: #include "../../sr_module.h" michael@16: #include "../../dprint.h" michael@16: #include "../../ut.h" michael@16: #include "../../error.h" michael@16: #include "../../mem/mem.h" michael@16: #include "../../mem/shm_mem.h" michael@16: #include "../../db/db.h" michael@16: #include "../../usr_avp.h" michael@16: #include "../../parser/parse_uri.h" michael@16: #include "../../parser/parse_from.h" michael@16: #include "../../parser/msg_parser.h" michael@16: #include "../../action.h" michael@16: #include "../../qvalue.h" michael@16: #include "../../dset.h" michael@16: #include "../../ip_addr.h" michael@16: #include "../../resolve.h" michael@16: #include "../../mi/mi.h" michael@16: #include "../../mod_fix.h" michael@16: #include "../../socket_info.h" michael@16: #include "../../pvar.h" michael@16: #include "../../mod_fix.h" michael@16: #include "mi.h" michael@16: michael@16: michael@16: michael@16: /* michael@16: * Version of gw and lcr tables required by the module, michael@16: * increment this value if you change the table in michael@16: * an backwards incompatible way michael@16: */ michael@16: #define GW_TABLE_VERSION 8 michael@16: #define LCR_TABLE_VERSION 3 michael@16: michael@16: /* usr_avp flag for sequential forking */ michael@16: #define Q_FLAG (1<<2) michael@16: michael@16: static void destroy(void); /* Module destroy function */ michael@16: static int mi_child_init(void); michael@16: static int mod_init(void); /* Module initialization function */ michael@16: static int fixstringloadgws(void **param, int param_count); michael@16: michael@16: int reload_gws ( void ); michael@16: michael@16: #define GW_TABLE "gw" michael@16: michael@16: #define GW_NAME_COL "gw_name" michael@16: michael@16: #define GRP_ID_COL "grp_id" michael@16: michael@16: #define IP_ADDR_COL "ip_addr" michael@16: michael@16: #define PORT_COL "port" michael@16: michael@16: #define URI_SCHEME_COL "uri_scheme" michael@16: michael@16: #define TRANSPORT_COL "transport" michael@16: michael@16: #define STRIP_COL "strip" michael@16: michael@16: #define TAG_COL "tag" michael@16: michael@16: #define FLAGS_COL "flags" michael@16: michael@16: #define LCR_TABLE "lcr" michael@16: michael@16: #define PREFIX_COL "prefix" michael@16: michael@16: #define FROM_URI_COL "from_uri" michael@16: michael@16: #define PRIORITY_COL "priority" michael@16: michael@16: #define MAX_NO_OF_GWS 32 michael@16: #define MAX_NO_OF_LCRS 256 michael@16: #define MAX_PREFIX_LEN 256 michael@16: #define MAX_TAG_LEN 16 michael@16: #define MAX_FROM_URI_LEN 256 michael@16: michael@16: /* Default module parameter values */ michael@16: #define DEF_FR_INV_TIMER 90 michael@16: #define DEF_FR_INV_TIMER_NEXT 30 michael@16: #define DEF_PREFIX_MODE 0 michael@16: michael@16: /* michael@16: * Type definitions michael@16: */ michael@16: michael@16: typedef enum sip_protos uri_transport; michael@16: michael@16: struct gw_info { michael@16: unsigned int ip_addr; michael@16: unsigned int port; michael@16: unsigned int grp_id; michael@16: uri_type scheme; michael@16: uri_transport transport; michael@16: unsigned int strip; michael@16: char tag[MAX_TAG_LEN + 1]; michael@16: unsigned short tag_len; michael@16: unsigned int flags; michael@16: }; michael@16: michael@16: struct lcr_info { michael@16: char prefix[MAX_PREFIX_LEN + 1]; michael@16: unsigned short prefix_len; michael@16: char from_uri[MAX_FROM_URI_LEN + 1]; michael@16: unsigned short from_uri_len; michael@16: unsigned int grp_id; michael@16: unsigned short priority; michael@16: unsigned short end_record; michael@16: }; michael@16: michael@16: struct prefix_regex { michael@16: regex_t re; michael@16: short int valid; michael@16: }; michael@16: michael@16: struct from_uri_regex { michael@16: regex_t re; michael@16: short int valid; michael@16: }; michael@16: michael@16: struct mi { michael@16: int gw_index; michael@16: int route_index; michael@16: int randomizer; michael@16: }; michael@16: michael@16: michael@16: /* michael@16: * Database variables michael@16: */ michael@16: static db_con_t* db_handle = 0; /* Database connection handle */ michael@16: static db_func_t lcr_dbf; michael@16: michael@16: /* michael@16: * Module parameter variables michael@16: */ michael@16: michael@16: /* database */ michael@16: static str db_url = str_init(DEFAULT_RODB_URL); michael@16: static str gw_table = str_init(GW_TABLE); michael@16: static str gw_name_col = str_init(GW_NAME_COL); michael@16: static str grp_id_col = str_init(GRP_ID_COL); michael@16: static str ip_addr_col = str_init(IP_ADDR_COL); michael@16: static str port_col = str_init(PORT_COL); michael@16: static str uri_scheme_col = str_init(URI_SCHEME_COL); michael@16: static str transport_col = str_init(TRANSPORT_COL); michael@16: static str strip_col = str_init(STRIP_COL); michael@16: static str tag_col = str_init(TAG_COL); michael@16: static str flags_col = str_init(FLAGS_COL); michael@16: static str lcr_table = str_init(LCR_TABLE); michael@16: static str prefix_col = str_init(PREFIX_COL); michael@16: static str from_uri_col = str_init(FROM_URI_COL); michael@16: static str priority_col = str_init(PRIORITY_COL); michael@16: michael@16: /* timer */ michael@16: int fr_inv_timer = DEF_FR_INV_TIMER; michael@16: int fr_inv_timer_next = DEF_FR_INV_TIMER_NEXT; michael@16: michael@16: /* avps */ michael@16: static char *fr_inv_timer_avp_param = NULL; michael@16: static char *gw_uri_avp_param = NULL; michael@16: static char *ruri_user_avp_param = NULL; michael@16: static char *contact_avp_param = NULL; michael@16: static char *rpid_avp_param = NULL; michael@16: static char *flags_avp_param = NULL; michael@16: michael@16: /* prefix mode */ michael@16: int prefix_mode_param = DEF_PREFIX_MODE; michael@16: michael@16: /* michael@16: * Other module types and variables michael@16: */ michael@16: michael@16: struct contact { michael@16: str uri; michael@16: qvalue_t q; michael@16: str dst_uri; michael@16: str path; michael@16: unsigned int flags; michael@16: struct socket_info* sock; michael@16: unsigned short q_flag; michael@16: struct contact *next; michael@16: }; michael@16: michael@16: static int fr_inv_timer_avp_type; michael@16: static int_str fr_inv_timer_avp; michael@16: static int gw_uri_avp_type; michael@16: static int_str gw_uri_avp; michael@16: static int ruri_user_avp_type; michael@16: static int_str ruri_user_avp; michael@16: static int contact_avp_type; michael@16: static int_str contact_avp; michael@16: static int rpid_avp_type; michael@16: static int_str rpid_avp; michael@16: static int flags_avp_type; michael@16: static int_str flags_avp; michael@16: michael@16: struct gw_info **gws; /* Pointer to current gw table pointer */ michael@16: struct gw_info *gws_1; /* Pointer to gw table 1 */ michael@16: struct gw_info *gws_2; /* Pointer to gw table 2 */ michael@16: michael@16: struct lcr_info **lcrs; /* Pointer to current lcr table pointer */ michael@16: struct lcr_info *lcrs_1; /* Pointer to lcr table 1 */ michael@16: struct lcr_info *lcrs_2; /* Pointer to lcr table 2 */ michael@16: michael@16: unsigned int *lcrs_ws_reload_counter; michael@16: unsigned int reload_counter; michael@16: michael@16: struct prefix_regex prefix_reg[MAX_NO_OF_LCRS]; michael@16: struct from_uri_regex from_uri_reg[MAX_NO_OF_LCRS]; michael@16: michael@16: /* michael@16: * Module functions that are defined later michael@16: */ michael@16: static int load_gws_0(struct sip_msg* _m, char* _s1, char* _s2); michael@16: static int load_gws_1(struct sip_msg* _m, char* _s1, char* _s2); michael@16: static int load_gws_from_grp(struct sip_msg* _m, char* _s1, char* _s2); michael@16: static int next_gw(struct sip_msg* _m, char* _s1, char* _s2); michael@16: static int from_gw_0(struct sip_msg* _m, char* _s1, char* _s2); michael@16: static int from_gw_1(struct sip_msg* _m, char* _s1, char* _s2); michael@16: static int from_gw_grp(struct sip_msg* _m, char* _s1, char* _s2); michael@16: static int to_gw(struct sip_msg* _m, char* _s1, char* _s2); michael@16: static int to_gw_grp(struct sip_msg* _m, char* _s1, char* _s2); michael@16: static int load_contacts (struct sip_msg*, char*, char*); michael@16: static int next_contacts (struct sip_msg*, char*, char*); michael@16: michael@16: michael@16: /* michael@16: * Exported functions michael@16: */ michael@16: static cmd_export_t cmds[] = { michael@16: {"load_gws", (cmd_function)load_gws_0, 0, 0, 0, REQUEST_ROUTE}, michael@16: {"load_gws", (cmd_function)load_gws_1, 1, fixup_pvar_null, michael@16: fixup_free_pvar_null, REQUEST_ROUTE}, michael@16: {"load_gws_from_grp", (cmd_function)load_gws_from_grp, 1, michael@16: fixstringloadgws, 0, REQUEST_ROUTE}, michael@16: {"next_gw", (cmd_function)next_gw, 0, 0, 0, michael@16: REQUEST_ROUTE | FAILURE_ROUTE}, michael@16: {"from_gw", (cmd_function)from_gw_0, 0, 0, 0, michael@16: REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE}, michael@16: {"from_gw", (cmd_function)from_gw_1, 1, fixup_pvar_null, michael@16: fixup_free_pvar_null, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE}, michael@16: {"from_gw_grp", (cmd_function)from_gw_grp, 1, fixup_uint_null, 0, michael@16: REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE}, michael@16: {"to_gw", (cmd_function)to_gw, 0, 0, 0, michael@16: REQUEST_ROUTE | FAILURE_ROUTE}, michael@16: {"to_gw", (cmd_function)to_gw_grp, 1, fixup_uint_null, 0, michael@16: REQUEST_ROUTE | FAILURE_ROUTE}, michael@16: {"load_contacts", (cmd_function)load_contacts, 0, 0, 0, michael@16: REQUEST_ROUTE}, michael@16: {"next_contacts", (cmd_function)next_contacts, 0, 0, 0, michael@16: REQUEST_ROUTE | FAILURE_ROUTE}, michael@16: {0, 0, 0, 0, 0, 0} michael@16: }; michael@16: michael@16: michael@16: /* michael@16: * Exported parameters michael@16: */ michael@16: static param_export_t params[] = { michael@16: {"db_url", STR_PARAM, &db_url.s }, michael@16: {"gw_table", STR_PARAM, &gw_table.s }, michael@16: {"gw_name_column", STR_PARAM, &gw_name_col.s }, michael@16: {"grp_id_column", STR_PARAM, &grp_id_col.s }, michael@16: {"ip_addr_column", STR_PARAM, &ip_addr_col.s }, michael@16: {"port_column", STR_PARAM, &port_col.s }, michael@16: {"uri_scheme_column", STR_PARAM, &uri_scheme_col.s }, michael@16: {"transport_column", STR_PARAM, &transport_col.s }, michael@16: {"strip_column", STR_PARAM, &strip_col.s }, michael@16: {"tag_column", STR_PARAM, &tag_col.s }, michael@16: {"flags_column", STR_PARAM, &flags_col.s }, michael@16: {"lcr_table", STR_PARAM, &lcr_table.s }, michael@16: {"prefix_column", STR_PARAM, &prefix_col.s }, michael@16: {"from_uri_column", STR_PARAM, &from_uri_col.s }, michael@16: {"priority_column", STR_PARAM, &priority_col.s }, michael@16: {"fr_inv_timer_avp", STR_PARAM, &fr_inv_timer_avp_param }, michael@16: {"gw_uri_avp", STR_PARAM, &gw_uri_avp_param }, michael@16: {"ruri_user_avp", STR_PARAM, &ruri_user_avp_param }, michael@16: {"contact_avp", STR_PARAM, &contact_avp_param }, michael@16: {"rpid_avp", STR_PARAM, &rpid_avp_param }, michael@16: {"flags_avp", STR_PARAM, &flags_avp_param }, michael@16: {"fr_inv_timer", INT_PARAM, &fr_inv_timer }, michael@16: {"fr_inv_timer_next", INT_PARAM, &fr_inv_timer_next }, michael@16: {"prefix_mode", INT_PARAM, &prefix_mode_param }, michael@16: {0, 0, 0} michael@16: }; michael@16: michael@16: michael@16: /* michael@16: * Exported MI functions michael@16: */ michael@16: static mi_export_t mi_cmds[] = { michael@16: { MI_LCR_RELOAD, mi_lcr_reload, MI_NO_INPUT_FLAG, 0, mi_child_init }, michael@16: { MI_LCR_DUMP, mi_lcr_dump, MI_NO_INPUT_FLAG, 0, 0 }, michael@16: { 0, 0, 0, 0 ,0} michael@16: }; michael@16: michael@16: michael@16: /* michael@16: * Module interface michael@16: */ michael@16: struct module_exports exports = { michael@16: "lcr", michael@16: MODULE_VERSION, michael@16: DEFAULT_DLFLAGS, /* dlopen flags */ michael@16: cmds, /* Exported functions */ michael@16: params, /* Exported parameters */ michael@16: 0, /* exported statistics */ michael@16: mi_cmds, /* exported MI functions */ michael@16: 0, /* exported pseudo-variables */ michael@16: 0, /* extra processes */ michael@16: mod_init, /* module initialization function */ michael@16: 0, /* response function */ michael@16: destroy, /* destroy function */ michael@16: 0 /* child initialization function */ michael@16: }; michael@16: michael@16: michael@16: static int lcr_db_init(const str* db_url) michael@16: { michael@16: if (lcr_dbf.init==0){ michael@16: LM_CRIT("Null lcr_dbf\n"); michael@16: goto error; michael@16: } michael@16: db_handle=lcr_dbf.init(db_url); michael@16: if (db_handle==0){ michael@16: LM_ERR("Unable to connect to the database\n"); michael@16: goto error; michael@16: } michael@16: return 0; michael@16: error: michael@16: return -1; michael@16: } michael@16: michael@16: michael@16: michael@16: static int lcr_db_bind(const str* db_url) michael@16: { michael@16: if (db_bind_mod(db_url, &lcr_dbf)<0){ michael@16: LM_ERR("Unable to bind to the database module\n"); michael@16: return -1; michael@16: } michael@16: michael@16: if (!DB_CAPABILITY(lcr_dbf, DB_CAP_QUERY)) { michael@16: LM_ERR("Database module does not implement 'query' function\n"); michael@16: return -1; michael@16: } michael@16: michael@16: return 0; michael@16: } michael@16: michael@16: michael@16: static void lcr_db_close(void) michael@16: { michael@16: if (db_handle && lcr_dbf.close){ michael@16: lcr_dbf.close(db_handle); michael@16: db_handle=0; michael@16: } michael@16: } michael@16: michael@16: michael@16: static int mi_child_init(void) michael@16: { michael@16: return lcr_db_init(&db_url); michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Module initialization function that is called before the main process forks michael@16: */ michael@16: static int mod_init(void) michael@16: { michael@16: int i; michael@16: pv_spec_t avp_spec; michael@16: str s; michael@16: unsigned short avp_flags; michael@16: michael@16: LM_DBG("Initializing\n"); michael@16: michael@16: /* Update length of module variables */ michael@16: db_url.len = strlen(db_url.s); michael@16: gw_table.len = strlen(gw_table.s); michael@16: gw_name_col.len = strlen(gw_name_col.s); michael@16: grp_id_col.len = strlen(grp_id_col.s); michael@16: ip_addr_col.len = strlen(ip_addr_col.s); michael@16: port_col.len = strlen(port_col.s); michael@16: uri_scheme_col.len = strlen(uri_scheme_col.s); michael@16: transport_col.len = strlen(transport_col.s); michael@16: strip_col.len = strlen(strip_col.s); michael@16: tag_col.len = strlen(tag_col.s); michael@16: flags_col.len = strlen(flags_col.s); michael@16: lcr_table.len = strlen(lcr_table.s); michael@16: prefix_col.len = strlen(prefix_col.s); michael@16: from_uri_col.len = strlen(from_uri_col.s); michael@16: priority_col.len = strlen(priority_col.s); michael@16: michael@16: /* Bind database */ michael@16: if (lcr_db_bind(&db_url)) { michael@16: LM_ERR("No database module found\n"); michael@16: return -1; michael@16: } michael@16: michael@16: /* Check value of prefix_mode */ michael@16: if ((prefix_mode_param != 0) && (prefix_mode_param != 1)) { michael@16: LM_ERR("Invalid prefix_mode value <%d>\n", prefix_mode_param); michael@16: return -1; michael@16: } michael@16: michael@16: /* Process AVP params */ michael@16: if (fr_inv_timer_avp_param && *fr_inv_timer_avp_param) { michael@16: s.s = fr_inv_timer_avp_param; s.len = strlen(s.s); michael@16: if (pv_parse_spec(&s, &avp_spec)==0 michael@16: || avp_spec.type!=PVT_AVP) { michael@16: LM_ERR("Malformed or non AVP definition <%s>\n", michael@16: fr_inv_timer_avp_param); michael@16: return -1; michael@16: } michael@16: michael@16: if(pv_get_avp_name(0, &(avp_spec.pvp), &fr_inv_timer_avp, &avp_flags)!=0) { michael@16: LM_ERR("Invalid AVP definition <%s>\n", fr_inv_timer_avp_param); michael@16: return -1; michael@16: } michael@16: fr_inv_timer_avp_type = avp_flags; michael@16: } else { michael@16: LM_ERR("AVP fr_inv_timer_avp has not been defined\n"); michael@16: return -1; michael@16: } michael@16: michael@16: if (gw_uri_avp_param && *gw_uri_avp_param) { michael@16: s.s = gw_uri_avp_param; s.len = strlen(s.s); michael@16: if (pv_parse_spec(&s, &avp_spec)==0 michael@16: || avp_spec.type!=PVT_AVP) { michael@16: LM_ERR("Malformed or non AVP definition <%s>\n", gw_uri_avp_param); michael@16: return -1; michael@16: } michael@16: michael@16: if(pv_get_avp_name(0, &(avp_spec.pvp), &gw_uri_avp, &avp_flags)!=0) { michael@16: LM_ERR("Invalid AVP definition <%s>\n", gw_uri_avp_param); michael@16: return -1; michael@16: } michael@16: gw_uri_avp_type = avp_flags; michael@16: } else { michael@16: LM_ERR("AVP gw_uri_avp has not been defined\n"); michael@16: return -1; michael@16: } michael@16: michael@16: if (ruri_user_avp_param && *ruri_user_avp_param) { michael@16: s.s = ruri_user_avp_param; s.len = strlen(s.s); michael@16: if (pv_parse_spec(&s, &avp_spec)==0 michael@16: || avp_spec.type!=PVT_AVP) { michael@16: LM_ERR("Malformed or non AVP definition <%s>\n", michael@16: ruri_user_avp_param); michael@16: return -1; michael@16: } michael@16: michael@16: if(pv_get_avp_name(0, &(avp_spec.pvp), &ruri_user_avp, &avp_flags)!=0) { michael@16: LM_ERR("Invalid AVP definition <%s>\n", ruri_user_avp_param); michael@16: return -1; michael@16: } michael@16: ruri_user_avp_type = avp_flags; michael@16: } else { michael@16: LM_ERR("AVP ruri_user_avp has not been defined\n"); michael@16: return -1; michael@16: } michael@16: michael@16: if (contact_avp_param && *contact_avp_param) { michael@16: s.s = contact_avp_param; s.len = strlen(s.s); michael@16: if (pv_parse_spec(&s, &avp_spec)==0 michael@16: || avp_spec.type!=PVT_AVP) { michael@16: LM_ERR("Malformed or non AVP definition <%s>\n", michael@16: contact_avp_param); michael@16: return -1; michael@16: } michael@16: michael@16: if(pv_get_avp_name(0, &(avp_spec.pvp), &contact_avp, &avp_flags)!=0) { michael@16: LM_ERR("Invalid AVP definition <%s>\n", contact_avp_param); michael@16: return -1; michael@16: } michael@16: contact_avp_type = avp_flags; michael@16: } else { michael@16: LM_ERR("AVP contact_avp has not been defined\n"); michael@16: return -1; michael@16: } michael@16: michael@16: if (rpid_avp_param && *rpid_avp_param) { michael@16: s.s = rpid_avp_param; s.len = strlen(s.s); michael@16: if (pv_parse_spec(&s, &avp_spec)==0 michael@16: || avp_spec.type!=PVT_AVP) { michael@16: LM_ERR("Malformed or non AVP definition <%s>\n", rpid_avp_param); michael@16: return -1; michael@16: } michael@16: michael@16: if(pv_get_avp_name(0, &(avp_spec.pvp), &rpid_avp, &avp_flags)!=0) { michael@16: LM_ERR("Invalid AVP definition <%s>\n", rpid_avp_param); michael@16: return -1; michael@16: } michael@16: rpid_avp_type = avp_flags; michael@16: } else { michael@16: LM_ERR("AVP rpid_avp has not been defined\n"); michael@16: return -1; michael@16: } michael@16: michael@16: if (flags_avp_param && *flags_avp_param) { michael@16: s.s = flags_avp_param; s.len = strlen(s.s); michael@16: if (pv_parse_spec(&s, &avp_spec)==0 michael@16: || avp_spec.type!=PVT_AVP) { michael@16: LM_ERR("Malformed or non AVP definition <%s>\n", flags_avp_param); michael@16: return -1; michael@16: } michael@16: michael@16: if(pv_get_avp_name(0, &(avp_spec.pvp), &flags_avp, &avp_flags)!=0) { michael@16: LM_ERR("Invalid AVP definition <%s>\n", flags_avp_param); michael@16: return -1; michael@16: } michael@16: flags_avp_type = avp_flags; michael@16: } else { michael@16: LM_ERR("AVP flags_avp has not been defined\n"); michael@16: return -1; michael@16: } michael@16: michael@16: /* Check table version */ michael@16: db_con_t* dbh; michael@16: if (lcr_dbf.init==0){ michael@16: LM_CRIT("Unbound database\n"); michael@16: return -1; michael@16: } michael@16: dbh=lcr_dbf.init(&db_url); michael@16: if (dbh==0){ michael@16: LM_ERR("Unable to open database connection\n"); michael@16: return -1; michael@16: } michael@16: if((db_check_table_version(&lcr_dbf, dbh, &gw_table, GW_TABLE_VERSION) < 0) || michael@16: (db_check_table_version(&lcr_dbf, dbh, &lcr_table, LCR_TABLE_VERSION) < 0)) { michael@16: LM_ERR("error during table version check.\n"); michael@16: lcr_dbf.close(dbh); michael@16: goto err; michael@16: } michael@16: lcr_dbf.close(dbh); michael@16: michael@16: /* Initializing gw tables and gw table pointer variable */ michael@16: gws_1 = (struct gw_info *)shm_malloc(sizeof(struct gw_info) * michael@16: (MAX_NO_OF_GWS + 1)); michael@16: if (gws_1 == 0) { michael@16: LM_ERR("No memory for gw table\n"); michael@16: goto err; michael@16: } michael@16: gws_2 = (struct gw_info *)shm_malloc(sizeof(struct gw_info) * michael@16: (MAX_NO_OF_GWS + 1)); michael@16: if (gws_2 == 0) { michael@16: LM_ERR("No memory for gw table\n"); michael@16: goto err; michael@16: } michael@16: for (i = 0; i < MAX_NO_OF_GWS + 1; i++) { michael@16: gws_1[i].ip_addr = gws_2[i].ip_addr = 0; michael@16: } michael@16: gws = (struct gw_info **)shm_malloc(sizeof(struct gw_info *)); michael@16: if (gws == 0) { michael@16: LM_ERR("No memory for gw table pointer\n"); michael@16: } michael@16: *gws = gws_1; michael@16: michael@16: /* Initializing lcr tables and lcr table pointer variable */ michael@16: lcrs_1 = (struct lcr_info *)shm_malloc(sizeof(struct lcr_info) * michael@16: (MAX_NO_OF_LCRS + 1)); michael@16: if (lcrs_1 == 0) { michael@16: LM_ERR("No memory for lcr table\n"); michael@16: goto err; michael@16: } michael@16: lcrs_2 = (struct lcr_info *)shm_malloc(sizeof(struct lcr_info) * michael@16: (MAX_NO_OF_LCRS + 1)); michael@16: if (lcrs_2 == 0) { michael@16: LM_ERR("No memory for lcr table\n"); michael@16: goto err; michael@16: } michael@16: for (i = 0; i < MAX_NO_OF_LCRS + 1; i++) { michael@16: lcrs_1[i].end_record = lcrs_2[i].end_record = 0; michael@16: } michael@16: lcrs = (struct lcr_info **)shm_malloc(sizeof(struct lcr_info *)); michael@16: if (lcrs == 0) { michael@16: LM_ERR("No memory for lcr table pointer\n"); michael@16: goto err; michael@16: } michael@16: *lcrs = lcrs_1; michael@16: michael@16: lcrs_ws_reload_counter = (unsigned int *)shm_malloc(sizeof(unsigned int)); michael@16: if (lcrs_ws_reload_counter == 0) { michael@16: LM_ERR("No memory for reload counter\n"); michael@16: goto err; michael@16: } michael@16: *lcrs_ws_reload_counter = reload_counter = 0; michael@16: michael@16: memset(prefix_reg, 0, sizeof(struct prefix_regex) * MAX_NO_OF_LCRS); michael@16: memset(from_uri_reg, 0, sizeof(struct from_uri_regex) * MAX_NO_OF_LCRS); michael@16: michael@16: /* First reload */ michael@16: if (reload_gws() == -1) { michael@16: LM_CRIT("Failed to reload gateways and routes\n"); michael@16: goto err; michael@16: } michael@16: michael@16: return 0; michael@16: michael@16: err: michael@16: return -1; michael@16: } michael@16: michael@16: michael@16: static void destroy(void) michael@16: { michael@16: lcr_db_close(); michael@16: } michael@16: michael@16: /* michael@16: * Sort lcr records by prefix_len and priority. michael@16: */ michael@16: static int comp_lcrs(const void *m1, const void *m2) michael@16: { michael@16: int result = -1; michael@16: michael@16: struct mi *mi1 = (struct mi *) m1; michael@16: struct mi *mi2 = (struct mi *) m2; michael@16: michael@16: struct lcr_info lcr_record1 = (*lcrs)[mi1->route_index]; michael@16: struct lcr_info lcr_record2 = (*lcrs)[mi2->route_index]; michael@16: michael@16: if (prefix_mode_param == 0) { michael@16: /* Sort by prefix. */ michael@16: if (lcr_record1.prefix_len > lcr_record2.prefix_len) { michael@16: result = 1; michael@16: } else if (lcr_record1.prefix_len == lcr_record2.prefix_len) { michael@16: /* Sort by priority. */ michael@16: if (lcr_record1.priority < lcr_record2.priority) { michael@16: result = 1; michael@16: } else if (lcr_record1.priority == lcr_record2.priority) { michael@16: /* Nothing to do. */ michael@16: result = 0; michael@16: } michael@16: } michael@16: } else { michael@16: if (lcr_record1.priority < lcr_record2.priority) { michael@16: result = 1; michael@16: } else if (lcr_record1.priority == lcr_record2.priority) { michael@16: /* Nothing to do. */ michael@16: result = 0; michael@16: } michael@16: } michael@16: michael@16: return result; michael@16: } michael@16: michael@16: /* michael@16: * Sort lcr records by rand table. michael@16: */ michael@16: static int rand_lcrs(const void *m1, const void *m2) michael@16: { michael@16: int result = -1; michael@16: michael@16: struct mi mi1 = *((struct mi *) m1); michael@16: struct mi mi2 = *((struct mi *) m2); michael@16: michael@16: if (mi1.randomizer > mi2.randomizer) { michael@16: result = 1; michael@16: } else if (mi1.randomizer == mi2.randomizer) { michael@16: result = 0; michael@16: } michael@16: michael@16: return result; michael@16: } michael@16: michael@16: /* michael@16: * regcomp each prefix. michael@16: */ michael@16: static int load_prefix_regex(void) michael@16: { michael@16: int i, status, result = 0; michael@16: michael@16: for (i = 0; i < MAX_NO_OF_LCRS; i++) { michael@16: if ((*lcrs)[i].end_record != 0) { michael@16: break; michael@16: } michael@16: if (prefix_reg[i].valid) { michael@16: regfree(&(prefix_reg[i].re)); michael@16: prefix_reg[i].valid = 0; michael@16: } michael@16: memset(&(prefix_reg[i].re), 0, sizeof(regex_t)); michael@16: if ((status=regcomp(&(prefix_reg[i].re),(*lcrs)[i].prefix,0))!=0){ michael@16: LM_ERR("bad prefix re <%s>, regcomp returned %d (check regex.h)\n", michael@16: (*lcrs)[i].prefix, status); michael@16: result = -1; michael@16: break; michael@16: } michael@16: prefix_reg[i].valid = 1; michael@16: } michael@16: michael@16: return result; michael@16: } michael@16: michael@16: /* michael@16: * regcomp each from_uri. michael@16: */ michael@16: static int load_from_uri_regex(void) michael@16: { michael@16: int i, status, result = 0; michael@16: michael@16: for (i = 0; i < MAX_NO_OF_LCRS; i++) { michael@16: if ((*lcrs)[i].end_record != 0) { michael@16: break; michael@16: } michael@16: if (from_uri_reg[i].valid) { michael@16: regfree(&(from_uri_reg[i].re)); michael@16: from_uri_reg[i].valid = 0; michael@16: } michael@16: memset(&(from_uri_reg[i].re), 0, sizeof(regex_t)); michael@16: if ((status=regcomp(&(from_uri_reg[i].re),(*lcrs)[i].from_uri,0))!=0){ michael@16: LM_ERR("Bad from_uri re <%s>, regcomp returned %d (check regex.h)\n", michael@16: (*lcrs)[i].from_uri, status); michael@16: result = -1; michael@16: break; michael@16: } michael@16: from_uri_reg[i].valid = 1; michael@16: } michael@16: michael@16: if (result != -1) { michael@16: reload_counter = *lcrs_ws_reload_counter; michael@16: } michael@16: return result; michael@16: } michael@16: michael@16: static int load_all_regex(void) michael@16: { michael@16: int result =0; michael@16: michael@16: if (prefix_mode_param != 0) { michael@16: result = load_prefix_regex(); michael@16: } michael@16: michael@16: if (result == 0) { michael@16: result = load_from_uri_regex(); michael@16: } else { michael@16: LM_ERR("Unable to load prefix regex\n"); michael@16: } michael@16: michael@16: if (result == 0) { michael@16: reload_counter = *lcrs_ws_reload_counter; michael@16: } else { michael@16: LM_ERR("Unable to load from_uri regex\n"); michael@16: } michael@16: michael@16: return result; michael@16: } michael@16: michael@16: /* michael@16: * Reload gws to unused gw table and lcrs to unused lcr table, and, when done michael@16: * make unused gw and lcr table the one in use. michael@16: */ michael@16: int reload_gws(void) michael@16: { michael@16: unsigned int i, port, strip, tag_len, prefix_len, from_uri_len, michael@16: grp_id, priority; michael@16: struct in_addr ip_addr; michael@16: unsigned int flags; michael@16: uri_type scheme; michael@16: uri_transport transport; michael@16: db_con_t* dbh; michael@16: char *tag, *prefix, *from_uri; michael@16: db_res_t* res = NULL; michael@16: db_row_t* row; michael@16: db_key_t gw_cols[8]; michael@16: db_key_t lcr_cols[4]; michael@16: michael@16: gw_cols[0] = &ip_addr_col; michael@16: gw_cols[1] = &port_col; michael@16: gw_cols[2] = &uri_scheme_col; michael@16: gw_cols[3] = &transport_col; michael@16: gw_cols[4] = &strip_col; michael@16: gw_cols[5] = &tag_col; michael@16: /* FIXME: is this ok if we have different names for grp_id michael@16: in the two tables? (ge vw lcr) */ michael@16: gw_cols[6] = &grp_id_col; michael@16: gw_cols[7] = &flags_col; michael@16: michael@16: lcr_cols[0] = &prefix_col; michael@16: lcr_cols[1] = &from_uri_col; michael@16: /* FIXME: is this ok if we have different names for grp_id michael@16: in the two tables? (ge vw lcr) */ michael@16: lcr_cols[2] = &grp_id_col; michael@16: lcr_cols[3] = &priority_col; michael@16: michael@16: if (lcr_dbf.init==0){ michael@16: LM_CRIT("Unbound database\n"); michael@16: return -1; michael@16: } michael@16: dbh=lcr_dbf.init(&db_url); michael@16: if (dbh==0){ michael@16: LM_ERR("Unable to open database connection\n"); michael@16: return -1; michael@16: } michael@16: michael@16: if (lcr_dbf.use_table(dbh, &gw_table) < 0) { michael@16: LM_ERR("Error while trying to use gw table\n"); michael@16: return -1; michael@16: } michael@16: michael@16: if (lcr_dbf.query(dbh, NULL, 0, NULL, gw_cols, 0, 8, 0, &res) < 0) { michael@16: LM_ERR("Failed to query gw data\n"); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: michael@16: if (RES_ROW_N(res) + 1 > MAX_NO_OF_GWS) { michael@16: LM_ERR("Too many gateways\n"); michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: michael@16: for (i = 0; i < RES_ROW_N(res); i++) { michael@16: row = RES_ROWS(res) + i; michael@16: if (!((VAL_TYPE(ROW_VALUES(row)) == DB_STRING) && michael@16: !VAL_NULL(ROW_VALUES(row)) && michael@16: inet_aton((char *)VAL_STRING(ROW_VALUES(row)), &ip_addr) != 0)) { michael@16: LM_ERR("Invalid IP address of gw <%s>\n", michael@16: (char *)VAL_STRING(ROW_VALUES(row))); michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: if (VAL_NULL(ROW_VALUES(row) + 1) == 1) { michael@16: port = 0; michael@16: } else { michael@16: port = (unsigned int)VAL_INT(ROW_VALUES(row) + 1); michael@16: } michael@16: if (port > 65536) { michael@16: LM_ERR("Port of gw is too large <%u>\n", port); michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: if (VAL_NULL(ROW_VALUES(row) + 2) == 1) { michael@16: scheme = SIP_URI_T; michael@16: } else { michael@16: scheme = (uri_type)VAL_INT(ROW_VALUES(row) + 2); michael@16: if ((scheme != SIP_URI_T) && (scheme != SIPS_URI_T)) { michael@16: LM_ERR("Unknown or unsupported URI scheme <%u>\n", michael@16: (unsigned int)scheme); michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: } michael@16: if (VAL_NULL(ROW_VALUES(row) + 3) == 1) { michael@16: transport = PROTO_NONE; michael@16: } else { michael@16: transport = (uri_transport)VAL_INT(ROW_VALUES(row) + 3); michael@16: if ((transport != PROTO_UDP) && (transport != PROTO_TCP) && michael@16: (transport != PROTO_TLS)) { michael@16: LM_ERR("Unknown or unsupported transport <%u>\n", michael@16: (unsigned int)transport); michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: } michael@16: if (VAL_NULL(ROW_VALUES(row) + 4) == 1) { michael@16: strip = 0; michael@16: } else { michael@16: strip = (unsigned int)VAL_INT(ROW_VALUES(row) + 4); michael@16: } michael@16: if (VAL_NULL(ROW_VALUES(row) + 5) == 1) { michael@16: tag_len = 0; michael@16: tag = (char *)0; michael@16: } else { michael@16: tag = (char *)VAL_STRING(ROW_VALUES(row) + 5); michael@16: tag_len = strlen(tag); michael@16: if (tag_len > MAX_TAG_LEN) { michael@16: LM_ERR("Too long gw tag <%u>\n", tag_len); michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: } michael@16: if (VAL_NULL(ROW_VALUES(row) + 6) == 1) { michael@16: grp_id = 0; michael@16: } else { michael@16: grp_id = VAL_INT(ROW_VALUES(row) + 6); michael@16: } michael@16: if (!VAL_NULL(ROW_VALUES(row) + 7) && michael@16: (VAL_TYPE(ROW_VALUES(row) + 7) == DB_INT)) { michael@16: flags = (unsigned int)VAL_INT(ROW_VALUES(row) + 7); michael@16: } else { michael@16: LM_ERR("Attribute flags is NULL or non-int\n"); michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: if (*gws == gws_1) { michael@16: gws_2[i].ip_addr = (unsigned int)ip_addr.s_addr; michael@16: gws_2[i].port = port; michael@16: gws_2[i].grp_id = grp_id; michael@16: gws_2[i].scheme = scheme; michael@16: gws_2[i].transport = transport; michael@16: gws_2[i].flags = flags; michael@16: gws_2[i].strip = strip; michael@16: gws_2[i].tag_len = tag_len; michael@16: if (tag_len) michael@16: memcpy(&(gws_2[i].tag[0]), tag, tag_len); michael@16: } else { michael@16: gws_1[i].ip_addr = (unsigned int)ip_addr.s_addr; michael@16: gws_1[i].port = port; michael@16: gws_1[i].grp_id = grp_id; michael@16: gws_1[i].scheme = scheme; michael@16: gws_1[i].transport = transport; michael@16: gws_1[i].flags = flags; michael@16: gws_1[i].strip = strip; michael@16: gws_1[i].tag_len = tag_len; michael@16: if (tag_len) michael@16: memcpy(&(gws_1[i].tag[0]), tag, tag_len); michael@16: } michael@16: } michael@16: michael@16: lcr_dbf.free_result(dbh, res); michael@16: michael@16: if (*gws == gws_1) { michael@16: gws_2[i].ip_addr = 0; michael@16: *gws = gws_2; michael@16: } else { michael@16: gws_1[i].ip_addr = 0; michael@16: *gws = gws_1; michael@16: } michael@16: michael@16: michael@16: if (lcr_dbf.use_table(dbh, &lcr_table) < 0) { michael@16: LM_ERR("Error while trying to use lcr table\n"); michael@16: return -1; michael@16: } michael@16: michael@16: if (lcr_dbf.query(dbh, NULL, 0, NULL, lcr_cols, 0, 4, 0, &res) < 0) { michael@16: LM_ERR("Failed to query lcr data\n"); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: michael@16: if (RES_ROW_N(res) + 1 > MAX_NO_OF_LCRS) { michael@16: LM_ERR("Too many lcr entries <%d>\n", RES_ROW_N(res)); michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: for (i = 0; i < RES_ROW_N(res); i++) { michael@16: row = RES_ROWS(res) + i; michael@16: if (VAL_NULL(ROW_VALUES(row)) == 1) { michael@16: prefix_len = 0; michael@16: prefix = 0; michael@16: } else { michael@16: prefix = (char *)VAL_STRING(ROW_VALUES(row)); michael@16: prefix_len = strlen(prefix); michael@16: if (prefix_len > MAX_PREFIX_LEN) { michael@16: LM_ERR("Too long lcr prefix <%u>\n", prefix_len); michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: } michael@16: if (VAL_NULL(ROW_VALUES(row) + 1) == 1) { michael@16: from_uri_len = 0; michael@16: from_uri = 0; michael@16: } else { michael@16: from_uri = (char *)VAL_STRING(ROW_VALUES(row) + 1); michael@16: from_uri_len = strlen(from_uri); michael@16: if (from_uri_len > MAX_FROM_URI_LEN) { michael@16: LM_ERR("Too long from_uri <%u>\n", from_uri_len); michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: } michael@16: if (VAL_NULL(ROW_VALUES(row) + 2) == 1) { michael@16: LM_ERR("Route grp_id is NULL\n"); michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: grp_id = (unsigned int)VAL_INT(ROW_VALUES(row) + 2); michael@16: if (VAL_NULL(ROW_VALUES(row) + 3) == 1) { michael@16: LM_ERR("Route priority is NULL\n"); michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: return -1; michael@16: } michael@16: priority = (unsigned int)VAL_INT(ROW_VALUES(row) + 3); michael@16: michael@16: if (*lcrs == lcrs_1) { michael@16: lcrs_2[i].prefix_len = prefix_len; michael@16: if (prefix_len) michael@16: memcpy(&(lcrs_2[i].prefix[0]), prefix, prefix_len); michael@16: lcrs_2[i].from_uri_len = from_uri_len; michael@16: if (from_uri_len) { michael@16: memcpy(&(lcrs_2[i].from_uri[0]), from_uri, from_uri_len); michael@16: lcrs_2[i].from_uri[from_uri_len] = '\0'; michael@16: } michael@16: lcrs_2[i].grp_id = grp_id; michael@16: lcrs_2[i].priority = priority; michael@16: lcrs_2[i].end_record = 0; michael@16: } else { michael@16: lcrs_1[i].prefix_len = prefix_len; michael@16: if (prefix_len) michael@16: memcpy(&(lcrs_1[i].prefix[0]), prefix, prefix_len); michael@16: lcrs_1[i].from_uri_len = from_uri_len; michael@16: if (from_uri_len) { michael@16: memcpy(&(lcrs_1[i].from_uri[0]), from_uri, from_uri_len); michael@16: lcrs_1[i].from_uri[from_uri_len] = '\0'; michael@16: } michael@16: lcrs_1[i].grp_id = grp_id; michael@16: lcrs_1[i].priority = priority; michael@16: lcrs_1[i].end_record = 0; michael@16: } michael@16: } michael@16: michael@16: lcr_dbf.free_result(dbh, res); michael@16: lcr_dbf.close(dbh); michael@16: michael@16: if (*lcrs == lcrs_1) { michael@16: lcrs_2[i].end_record = 1; michael@16: *lcrs = lcrs_2; michael@16: } else { michael@16: lcrs_1[i].end_record = 1; michael@16: *lcrs = lcrs_1; michael@16: } michael@16: michael@16: (*lcrs_ws_reload_counter)++; michael@16: if (0 != load_all_regex()) { michael@16: michael@16: return -1; michael@16: } michael@16: michael@16: return 1; michael@16: } michael@16: michael@16: michael@16: int mi_print_gws(struct mi_node* rpl) michael@16: { michael@16: unsigned int i; michael@16: struct mi_attr* attr; michael@16: uri_transport transport; michael@16: char *transp; michael@16: struct mi_node* node; michael@16: struct ip_addr address; michael@16: char* p; michael@16: int len; michael@16: michael@16: for (i = 0; i < MAX_NO_OF_GWS; i++) { michael@16: michael@16: if ((*gws)[i].ip_addr == 0) michael@16: break; michael@16: michael@16: node= add_mi_node_child(rpl,0 ,"GW", 2, 0, 0); michael@16: if(node == NULL) michael@16: return -1; michael@16: michael@16: p = int2str((unsigned long)(*gws)[i].grp_id, &len ); michael@16: attr = add_mi_attr(node, MI_DUP_VALUE, "GRP_ID", 6, p, len ); michael@16: if(attr == NULL) michael@16: return -1; michael@16: michael@16: transport = (*gws)[i].transport; michael@16: if (transport == PROTO_UDP) michael@16: transp= ";transport=udp"; michael@16: else if (transport == PROTO_TCP) michael@16: transp= ";transport=tcp"; michael@16: else if (transport == PROTO_TLS) michael@16: transp= ";transport=tls"; michael@16: else michael@16: transp= ""; michael@16: michael@16: address.af = AF_INET; michael@16: address.len = 4; michael@16: address.u.addr32[0] = (*gws)[i].ip_addr; michael@16: attr= addf_mi_attr(node,0 ,"URI", 3,"%s:%s:%d%s", michael@16: ((*gws)[i].scheme == SIP_URI_T)?"sip":"sips", michael@16: ip_addr2a(&address), michael@16: ((*gws)[i].port == 0)?5060:(*gws)[i].port,transp); michael@16: if(attr == NULL) michael@16: return -1; michael@16: michael@16: p = int2str((unsigned long)(*gws)[i].strip, &len ); michael@16: attr = add_mi_attr(node, MI_DUP_VALUE, "STRIP", 5, p, len); michael@16: if(attr == NULL) michael@16: return -1; michael@16: michael@16: attr = add_mi_attr(node, MI_DUP_VALUE, "TAG", 3, michael@16: (*gws)[i].tag, (*gws)[i].tag_len ); michael@16: if(attr == NULL) michael@16: return -1; michael@16: michael@16: p = int2str((unsigned long)(*gws)[i].flags, &len ); michael@16: attr = add_mi_attr(node, MI_DUP_VALUE, "FLAGS", 5, p, len); michael@16: if(attr == NULL) michael@16: return -1; michael@16: } michael@16: michael@16: for (i = 0; i < MAX_NO_OF_LCRS; i++) { michael@16: if ((*lcrs)[i].end_record != 0) michael@16: break; michael@16: michael@16: node= add_mi_node_child(rpl, 0, "RULE", 4, 0, 0); michael@16: attr = add_mi_attr(node, 0, "PREFIX", 6, (*lcrs)[i].prefix, michael@16: (*lcrs)[i].prefix_len ); michael@16: if(attr== 0) michael@16: return -1; michael@16: michael@16: attr = add_mi_attr(node, 0, "FROM_URI", 8, (*lcrs)[i].from_uri, michael@16: (*lcrs)[i].from_uri_len ); michael@16: if(attr== 0) michael@16: return -1; michael@16: michael@16: p = int2str((unsigned long)(*lcrs)[i].grp_id, &len ); michael@16: attr = add_mi_attr(node, MI_DUP_VALUE, "GRP_ID", 6, p, len ); michael@16: if(attr == NULL) michael@16: return -1; michael@16: michael@16: p = int2str((unsigned long)(*lcrs)[i].priority, &len ); michael@16: attr = add_mi_attr(node, MI_DUP_VALUE, "PRIORITY", 8, p, len ); michael@16: if(attr == NULL) michael@16: return -1; michael@16: michael@16: } michael@16: michael@16: return 0; michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Load info of matching GWs from database to gw_uri AVPs michael@16: */ michael@16: static int do_load_gws(struct sip_msg* _m, str *_from_uri, int _grp_id) michael@16: { michael@16: str ruri_user, from_uri, value; michael@16: char from_uri_str[MAX_FROM_URI_LEN + 1]; michael@16: char ruri[MAX_URI_SIZE]; michael@16: unsigned int i, j, k, index, addr, port, strip, gw_index, michael@16: duplicated_gw, flags, have_rpid_avp; michael@16: uri_type scheme; michael@16: uri_transport transport; michael@16: struct ip_addr address; michael@16: str addr_str, port_str; michael@16: char *at, *tag, *strip_string, *flags_string; michael@16: struct usr_avp *avp; michael@16: int_str val; michael@16: struct mi matched_gws[MAX_NO_OF_GWS + 1]; michael@16: unsigned short tag_len, prefix_len, priority; michael@16: int randomizer_start, randomizer_end, randomizer_flag, michael@16: strip_len, flags_len; michael@16: struct lcr_info lcr_rec; michael@16: michael@16: /* Find Request-URI user */ michael@16: if ((parse_sip_msg_uri(_m) < 0) || (!_m->parsed_uri.user.s)) { michael@16: LM_ERR("Error while parsing R-URI\n"); michael@16: return -1; michael@16: } michael@16: ruri_user = _m->parsed_uri.user; michael@16: michael@16: if (_from_uri) { michael@16: /* take caller uri from _from_uri argument */ michael@16: from_uri = *_from_uri; michael@16: } else { michael@16: /* take caller uri from RPID or From URI */ michael@16: have_rpid_avp = 0; michael@16: avp = search_first_avp(rpid_avp_type, rpid_avp, &val, 0); michael@16: if (avp != NULL) { michael@16: /* Get URI user from RPID if not empty */ michael@16: if (avp->flags & AVP_VAL_STR) { michael@16: if (val.s.s && val.s.len) { michael@16: from_uri = val.s; michael@16: have_rpid_avp = 1; michael@16: } michael@16: } else { michael@16: from_uri.s = int2str(val.n, &from_uri.len); michael@16: have_rpid_avp = 1; michael@16: } michael@16: } michael@16: if (!have_rpid_avp) { michael@16: /* Get URI from From URI */ michael@16: if ((!_m->from) && (parse_headers(_m, HDR_FROM_F, 0) == -1)) { michael@16: LM_ERR("Error while parsing headers\n"); michael@16: return -1; michael@16: } michael@16: if (!_m->from) { michael@16: LM_ERR("From header field not found\n"); michael@16: return -1; michael@16: } michael@16: if ((!(_m->from)->parsed) && (parse_from_header(_m) < 0)) { michael@16: LM_ERR("Error while parsing From header\n"); michael@16: return -1; michael@16: } michael@16: from_uri = get_from(_m)->uri; michael@16: } michael@16: } michael@16: if (from_uri.len <= MAX_FROM_URI_LEN) { michael@16: strncpy(from_uri_str, from_uri.s, from_uri.len); michael@16: from_uri_str[from_uri.len] = '\0'; michael@16: } else { michael@16: LM_ERR("From URI is too long <%u>\n", from_uri.len); michael@16: return -1; michael@16: } michael@16: michael@16: /* michael@16: * Check if the gws and lcrs were reloaded michael@16: */ michael@16: if (reload_counter != *lcrs_ws_reload_counter) { michael@16: if (load_all_regex() != 0) { michael@16: return -1; michael@16: } michael@16: } michael@16: michael@16: /* michael@16: * Let's match the gws: michael@16: * 1. prefix matching michael@16: * 2. from_uri matching michael@16: * 3. _grp_id matching michael@16: * michael@16: * Note: A gateway must be in the list _only_ once. michael@16: */ michael@16: gw_index = 0; michael@16: duplicated_gw = 0; michael@16: for (i = 0; i < MAX_NO_OF_LCRS; i++) { michael@16: lcr_rec = (*lcrs)[i]; michael@16: if (lcr_rec.end_record != 0) { michael@16: break; michael@16: } michael@16: if ( ((prefix_mode_param == 0) && (lcr_rec.prefix_len <= ruri_user.len) && michael@16: (strncmp(lcr_rec.prefix, ruri_user.s, lcr_rec.prefix_len)==0)) || michael@16: ( (prefix_mode_param != 0) && ( (lcr_rec.prefix_len == 0) || michael@16: (prefix_reg[i].valid && michael@16: (regexec(&(prefix_reg[i].re), ruri_user.s, 0, michael@16: (regmatch_t *)NULL, 0) == 0)) ) ) ) { michael@16: /* 1. Prefix matching is done */ michael@16: if ((lcr_rec.from_uri_len == 0) || michael@16: (from_uri_reg[i].valid && michael@16: (regexec(&(from_uri_reg[i].re), from_uri_str, 0, michael@16: (regmatch_t *)NULL, 0) == 0))) { michael@16: /* 2. from_uri matching is done */ michael@16: for (j = 0; j < MAX_NO_OF_GWS; j++) { michael@16: if ((*gws)[j].ip_addr == 0) { michael@16: break; michael@16: } michael@16: if (lcr_rec.grp_id == (*gws)[j].grp_id && michael@16: (_grp_id < 0 || (*gws)[j].grp_id == _grp_id)) { michael@16: /* 3. _grp_id matching is done */ michael@16: for (k = 0; k < gw_index; k++) { michael@16: if ((*gws)[j].ip_addr == michael@16: (*gws)[matched_gws[k].gw_index].ip_addr) { michael@16: /* Found the same gw in the list */ michael@16: /* Let's keep the one with higher */ michael@16: /* match on prefix len */ michael@16: LM_DBG("Duplicate gw for index" michael@16: " %d [%d,%d] and current [%d,%d] \n", michael@16: k, matched_gws[k].route_index, michael@16: matched_gws[k].route_index, i, j); michael@16: duplicated_gw = 1; michael@16: if (lcr_rec.prefix_len > michael@16: (*lcrs)[matched_gws[k].route_index].prefix_len) { michael@16: /* Replace the old entry with the new one */ michael@16: LM_DBG("Replace [%d,%d]" michael@16: " with [%d,%d] on index %d:" michael@16: " prefix reason %d>%d\n", michael@16: matched_gws[k].route_index, michael@16: matched_gws[k].gw_index, i, j, k, michael@16: lcr_rec.prefix_len, michael@16: (*lcrs)[matched_gws[k].route_index].prefix_len); michael@16: matched_gws[k].route_index = i; michael@16: matched_gws[k].gw_index = j; michael@16: /* Stop searching in the matched_gws list */ michael@16: break; michael@16: } else if (lcr_rec.prefix_len == michael@16: (*lcrs)[matched_gws[k].route_index].prefix_len) { michael@16: if (lcr_rec.priority > michael@16: (*lcrs)[matched_gws[k].route_index].priority) { michael@16: /* Replace the old entry with the new one */ michael@16: LM_DBG("Replace [%d,%d] with" michael@16: " [%d,%d] on index %d:" michael@16: " priority reason %d>%d\n", michael@16: matched_gws[k].route_index, michael@16: matched_gws[k].gw_index, michael@16: i, j, k, lcr_rec.priority, michael@16: (*lcrs)[matched_gws[k].route_index].priority); michael@16: matched_gws[k].route_index = i; michael@16: matched_gws[k].gw_index = j; michael@16: /* Stop searching in the matched_gws list */ michael@16: break; michael@16: } michael@16: } michael@16: } michael@16: } michael@16: if (duplicated_gw == 0) { michael@16: /* This is a new gw */ michael@16: matched_gws[gw_index].route_index = i; michael@16: matched_gws[gw_index].gw_index = j; michael@16: LM_DBG("Added matched_gws[%d]=[%d,%d]\n", michael@16: gw_index, i, j); michael@16: gw_index++; michael@16: } else { michael@16: duplicated_gw = 0; michael@16: } michael@16: } michael@16: } michael@16: } michael@16: } michael@16: } michael@16: matched_gws[gw_index].route_index = -1; michael@16: matched_gws[gw_index].gw_index = -1; michael@16: michael@16: /* michael@16: * Sort the gateways based on: michael@16: * 1. prefix len michael@16: * 2. priority michael@16: */ michael@16: qsort(matched_gws, gw_index, sizeof(struct mi), comp_lcrs); michael@16: randomizer_start = 0; michael@16: michael@16: /* Randomizing the gateways with same prefix_len and same priority */ michael@16: randomizer_flag = 0; michael@16: prefix_len = (*lcrs)[matched_gws[0].route_index].prefix_len; michael@16: priority = (*lcrs)[matched_gws[0].route_index].priority; michael@16: for (i = 1; i < gw_index; i++) { michael@16: if ( prefix_len == (*lcrs)[matched_gws[i].route_index].prefix_len && michael@16: priority == (*lcrs)[matched_gws[i].route_index].priority) { michael@16: /* we have a match */ michael@16: if (randomizer_flag == 0) { michael@16: randomizer_flag = 1; michael@16: randomizer_start = i - 1; michael@16: } michael@16: matched_gws[i - 1].randomizer = rand(); michael@16: } michael@16: else { michael@16: if (randomizer_flag == 1) { michael@16: randomizer_end = i - 1; michael@16: randomizer_flag = 0; michael@16: qsort(&matched_gws[randomizer_start], michael@16: randomizer_end - randomizer_start + 1, michael@16: sizeof(struct mi), rand_lcrs); michael@16: } michael@16: prefix_len = (*lcrs)[matched_gws[i].route_index].prefix_len; michael@16: priority = (*lcrs)[matched_gws[i].route_index].priority; michael@16: } michael@16: } michael@16: if (randomizer_flag == 1) { michael@16: randomizer_end = gw_index - 1; michael@16: matched_gws[i - 1].randomizer = rand(); michael@16: qsort(&matched_gws[randomizer_start], michael@16: randomizer_end - randomizer_start + 1, michael@16: sizeof(struct mi), rand_lcrs); michael@16: } michael@16: michael@16: for (i = 0; i < MAX_NO_OF_GWS; i++) { michael@16: index = matched_gws[i].gw_index; michael@16: if (index == -1) { michael@16: break; michael@16: } michael@16: addr = (*gws)[index].ip_addr; michael@16: port = (*gws)[index].port; michael@16: scheme = (*gws)[index].scheme; michael@16: transport = (*gws)[index].transport; michael@16: flags = (*gws)[index].flags; michael@16: strip = (*gws)[index].strip; michael@16: if (strip > ruri_user.len) { michael@16: LM_ERR("Strip count of gw is too large <%u>\n", strip); michael@16: goto skip; michael@16: } michael@16: tag_len = (*gws)[index].tag_len; michael@16: tag = (*gws)[index].tag; michael@16: if (6 + tag_len + 40 /* flags + strip */ + 1 + 15 + 1 + 5 + 1 + 14 > michael@16: MAX_URI_SIZE) { michael@16: LM_ERR("Request URI would be too long\n"); michael@16: goto skip; michael@16: } michael@16: at = (char *)&(ruri[0]); michael@16: flags_string = int2str(flags, &flags_len); michael@16: memcpy(at, flags_string, flags_len); michael@16: at = at + flags_len; michael@16: if (scheme == SIP_URI_T) { michael@16: memcpy(at, "sip:", 4); at = at + 4; michael@16: } else if (scheme == SIPS_URI_T) { michael@16: memcpy(at, "sips:", 5); at = at + 5; michael@16: } else { michael@16: LM_ERR("Unknown or unsupported URI scheme <%u>\n", michael@16: (unsigned int)scheme); michael@16: goto skip; michael@16: } michael@16: if (tag_len) { michael@16: memcpy(at, tag, tag_len); at = at + tag_len; michael@16: } michael@16: /* Add strip in this form |number. michael@16: * For example: |3 means strip first 3 characters. michael@16: */ michael@16: *at = '|'; at = at + 1; michael@16: strip_string = int2str(strip, &strip_len); michael@16: memcpy(at, strip_string, strip_len); michael@16: at = at + strip_len; michael@16: *at = '@'; at = at + 1; michael@16: address.af = AF_INET; michael@16: address.len = 4; michael@16: address.u.addr32[0] = addr; michael@16: addr_str.s = ip_addr2a(&address); michael@16: addr_str.len = strlen(addr_str.s); michael@16: memcpy(at, addr_str.s, addr_str.len); at = at + addr_str.len; michael@16: if (port != 0) { michael@16: if (port > 65536) { michael@16: LM_ERR("Port of GW is too large <%u>\n", port); michael@16: goto skip; michael@16: } michael@16: *at = ':'; at = at + 1; michael@16: port_str.s = int2str(port, &port_str.len); michael@16: memcpy(at, port_str.s, port_str.len); at = at + port_str.len; michael@16: } michael@16: if (transport != PROTO_NONE) { michael@16: memcpy(at, ";transport=", 11); at = at + 11; michael@16: if (transport == PROTO_UDP) { michael@16: memcpy(at, "udp", 3); at = at + 3; michael@16: } else if (transport == PROTO_TCP) { michael@16: memcpy(at, "tcp", 3); at = at + 3; michael@16: } else if (transport == PROTO_TLS) { michael@16: memcpy(at, "tls", 3); at = at + 3; michael@16: } else { michael@16: LM_ERR("Unknown or unsupported transport <%u>\n", michael@16: (unsigned int)transport); michael@16: goto skip; michael@16: } michael@16: } michael@16: value.s = (char *)&(ruri[0]); michael@16: value.len = at - value.s; michael@16: val.s = value; michael@16: add_avp(gw_uri_avp_type|AVP_VAL_STR, gw_uri_avp, val); michael@16: LM_DBG("Added gw_uri_avp <%.*s>\n", value.len, value.s); michael@16: skip: michael@16: continue; michael@16: } michael@16: michael@16: return 1; michael@16: } michael@16: michael@16: /* michael@16: * Load info of matching GWs from database to gw_uri AVPs michael@16: * taking into account the given group id. Caller URI is taken michael@16: * from request. michael@16: */ michael@16: static int load_gws_from_grp(struct sip_msg* _m, char* _s1, char* _s2) michael@16: { michael@16: str grp_s; michael@16: unsigned int grp_id; michael@16: michael@16: if(((pv_elem_p)_s1)->spec.getf!=NULL) michael@16: { michael@16: if(pv_printf_s(_m, (pv_elem_p)_s1, &grp_s)!=0) michael@16: return -1; michael@16: if(str2int(&grp_s, &grp_id)!=0) michael@16: return -1; michael@16: } else { michael@16: grp_id = ((pv_elem_p)_s1)->spec.pvp.pvn.u.isname.name.n; michael@16: } michael@16: if (grp_id > 0) return do_load_gws(_m, (str *)0, (int)grp_id); michael@16: else return -1; michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Load info of matching GWs from database to gw_uri AVPs. michael@16: * Caller URI is taken from request. michael@16: */ michael@16: static int load_gws_0(struct sip_msg* _m, char* _s1, char* _s2) michael@16: { michael@16: return do_load_gws(_m, (str *)0, -1); michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Load info of matching GWs from database to gw_uri AVPs. michael@16: * Caller URI is taken from pseudo variable argument. michael@16: */ michael@16: static int load_gws_1(struct sip_msg* _m, char* _sp, char* _s2) michael@16: { michael@16: pv_spec_t *sp; michael@16: pv_value_t pv_val; michael@16: sp = (pv_spec_t *)_sp; michael@16: michael@16: if (sp && (pv_get_spec_value(_m, sp, &pv_val) == 0)) { michael@16: if (pv_val.flags & PV_VAL_STR) { michael@16: if (pv_val.rs.len == 0 || pv_val.rs.s == NULL) { michael@16: LM_DBG("missing from uri\n"); michael@16: return -1; michael@16: } michael@16: return do_load_gws(_m, &(pv_val.rs), -1); michael@16: } else { michael@16: LM_DBG("pseudo variable value is not string\n"); michael@16: return -1; michael@16: } michael@16: } else { michael@16: LM_DBG("cannot get pseudo variable value\n"); michael@16: return -1; michael@16: } michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Rewrites scheme, host, port, and transport parts of R-URI based on first michael@16: * gw_uri AVP value, which is then destroyed. Also saves R-URI user to michael@16: * ruri_user AVP for later use in failure route block. michael@16: * If called from failure route block, appends a new branch to request michael@16: * where scheme, host, port, and transport of URI are taken from the first michael@16: * gw_uri AVP value, which is then destroyed. URI user is taken from michael@16: * ruri_user AVP value saved earlier. michael@16: * Returns 1 upon success and -1 upon failure. michael@16: */ michael@16: static int next_gw(struct sip_msg* _m, char* _s1, char* _s2) michael@16: { michael@16: int_str gw_uri_val, ruri_user_val, val; michael@16: struct usr_avp *gu_avp, *ru_avp; michael@16: int rval; michael@16: str new_ruri; michael@16: char *at, *at_char, *strip_char, *endptr; michael@16: unsigned int strip; michael@16: michael@16: gu_avp = search_first_avp(gw_uri_avp_type, gw_uri_avp, &gw_uri_val, 0); michael@16: if (!gu_avp) return -1; michael@16: michael@16: /* Set flags_avp from integer at the beginning of of gw_uri */ michael@16: val.n = (int)strtoul(gw_uri_val.s.s, &at, 0); michael@16: add_avp(flags_avp_type, flags_avp, val); michael@16: LM_DBG("Added flags_avp <%u>\n", (unsigned int)val.n); michael@16: michael@16: gw_uri_val.s.len = gw_uri_val.s.len - (at - gw_uri_val.s.s); michael@16: gw_uri_val.s.s = at; michael@16: michael@16: /* Create new Request-URI taking URI user from ruri_user AVP michael@16: and other parts of from gateway URI AVP. */ michael@16: ru_avp = search_first_avp(ruri_user_avp_type, ruri_user_avp, michael@16: &ruri_user_val, 0); michael@16: if (!ru_avp) { michael@16: LM_DBG("ruri_user AVP no yet set -> use RURI\n"); michael@16: /* parse RURI and ger username */ michael@16: if (parse_sip_msg_uri(_m) < 0) { michael@16: LM_ERR("Parsing of R-URI failed\n"); michael@16: return -1; michael@16: } michael@16: ruri_user_val.s = _m->parsed_uri.user; michael@16: /* Save Request-URI user for use in FAILURE_ROUTE */ michael@16: val.s = _m->parsed_uri.user; michael@16: add_avp(ruri_user_avp_type|AVP_VAL_STR, ruri_user_avp, val); michael@16: LM_DBG("Added ruri_user_avp <%.*s>\n", val.s.len, val.s.s); michael@16: } michael@16: michael@16: new_ruri.s = pkg_malloc(gw_uri_val.s.len + ruri_user_val.s.len); michael@16: if (!new_ruri.s) { michael@16: LM_ERR("No memory for new R-URI.\n"); michael@16: return -1; michael@16: } michael@16: at_char = memchr(gw_uri_val.s.s, '@', gw_uri_val.s.len); michael@16: if (!at_char) { michael@16: pkg_free(new_ruri.s); michael@16: LM_ERR("No @ in gateway URI <%.*s>\n", michael@16: gw_uri_val.s.len, gw_uri_val.s.s); michael@16: return -1; michael@16: } michael@16: strip_char = memchr(gw_uri_val.s.s, '|', gw_uri_val.s.len); michael@16: if (!strip_char || strip_char + 1 >= at_char) { michael@16: pkg_free(new_ruri.s); michael@16: LM_ERR("No strip char | and at least one " michael@16: "char before @ in gateway URI <%.*s>\n", michael@16: gw_uri_val.s.len, gw_uri_val.s.s); michael@16: return -1; michael@16: } michael@16: at = new_ruri.s; michael@16: memcpy(at, gw_uri_val.s.s, strip_char - gw_uri_val.s.s); michael@16: at = at + (strip_char - gw_uri_val.s.s); michael@16: strip = strtol(strip_char + 1, &endptr, 10); michael@16: if (endptr != at_char) { michael@16: pkg_free(new_ruri.s); michael@16: LM_ERR("Non-digit char between | and @ chars in gw URI <%.*s>\n", michael@16: gw_uri_val.s.len, gw_uri_val.s.s); michael@16: return -1; michael@16: } michael@16: if (ruri_user_val.s.len - strip > 0) { michael@16: memcpy(at, ruri_user_val.s.s + strip, michael@16: ruri_user_val.s.len - strip); michael@16: at = at + ruri_user_val.s.len - strip; michael@16: } michael@16: if (*(at - 1) != ':') { michael@16: memcpy(at, at_char, gw_uri_val.s.len - (at_char - gw_uri_val.s.s)); michael@16: at = at + gw_uri_val.s.len - (at_char - gw_uri_val.s.s); michael@16: } else { michael@16: memcpy(at, at_char + 1, gw_uri_val.s.len - michael@16: (at_char + 1 - gw_uri_val.s.s)); michael@16: at = at + gw_uri_val.s.len - (at_char + 1 - gw_uri_val.s.s); michael@16: } michael@16: new_ruri.len = at - new_ruri.s; michael@16: michael@16: /* set new RURI */ michael@16: rval = set_ruri( _m, &new_ruri); michael@16: pkg_free(new_ruri.s); michael@16: destroy_avp(gu_avp); michael@16: if (rval!=0) { michael@16: LM_ERR("failed to set new RURI\n"); michael@16: return -1; michael@16: } michael@16: michael@16: return 1; michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Checks if request comes from a gateway michael@16: */ michael@16: static int do_from_gw(struct sip_msg* _m, pv_spec_t *addr_sp, int grp_id) michael@16: { michael@16: int i; michael@16: unsigned int src_addr; michael@16: pv_value_t pv_val; michael@16: struct ip_addr *ip; michael@16: int_str val; michael@16: michael@16: if (addr_sp && (pv_get_spec_value(_m, addr_sp, &pv_val) == 0)) { michael@16: if (pv_val.flags & PV_VAL_INT) { michael@16: src_addr = pv_val.ri; michael@16: } else if (pv_val.flags & PV_VAL_STR) { michael@16: if ( (ip=str2ip( &pv_val.rs)) == NULL) { michael@16: LM_ERR("failed to convert IP address string to in_addr\n"); michael@16: return -1; michael@16: } else { michael@16: src_addr = ip->u.addr32[0]; michael@16: } michael@16: } else { michael@16: LM_ERR("IP address PV empty value\n"); michael@16: return -1; michael@16: } michael@16: } else { michael@16: src_addr = _m->rcv.src_ip.u.addr32[0]; michael@16: } michael@16: michael@16: for (i = 0; i < MAX_NO_OF_GWS; i++) { michael@16: if ((*gws)[i].ip_addr == 0) { michael@16: return -1; michael@16: } michael@16: if ((*gws)[i].ip_addr == src_addr && michael@16: (grp_id < 0 || (*gws)[i].grp_id == grp_id)) { michael@16: LM_DBG("Request came from gw\n"); michael@16: val.n = (int)(*gws)[i].flags; michael@16: add_avp(flags_avp_type, flags_avp, val); michael@16: LM_DBG("Added flags_avp <%u>\n", (unsigned int)val.n); michael@16: return 1; michael@16: } michael@16: } michael@16: michael@16: LM_DBG("Request did not come from gw\n"); michael@16: return -1; michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Checks if request comes from a gateway, taking source address from request michael@16: * and taking into account the group id. michael@16: */ michael@16: static int from_gw_grp(struct sip_msg* _m, char* _grp_id, char* _s2) michael@16: { michael@16: return do_from_gw(_m, (pv_spec_t *)0, (int)(long)_grp_id); michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Checks if request comes from a gateway, taking src_address from request michael@16: * and ignoring group id. michael@16: */ michael@16: static int from_gw_0(struct sip_msg* _m, char* _s1, char* _s2) michael@16: { michael@16: return do_from_gw(_m, (pv_spec_t *)0, -1); michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Checks if request comes from a gateway, taking source address from pw michael@16: * and ignoring group id. michael@16: */ michael@16: static int from_gw_1(struct sip_msg* _m, char* _addr_sp, char* _s2) michael@16: { michael@16: return do_from_gw(_m, (pv_spec_t *)_addr_sp, -1); michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Checks if in-dialog request goes to gateway michael@16: */ michael@16: static int do_to_gw(struct sip_msg* _m, int grp_id) michael@16: { michael@16: char host[16]; michael@16: struct in_addr addr; michael@16: unsigned int i; michael@16: michael@16: if((_m->parsed_uri_ok == 0) && (parse_sip_msg_uri(_m) < 0)) { michael@16: LM_ERR("Error while parsing the R-URI\n"); michael@16: return -1; michael@16: } michael@16: michael@16: if (_m->parsed_uri.host.len > 15) { michael@16: return -1; michael@16: } michael@16: memcpy(host, _m->parsed_uri.host.s, _m->parsed_uri.host.len); michael@16: host[_m->parsed_uri.host.len] = 0; michael@16: michael@16: if (!inet_aton(host, &addr)) { michael@16: return -1; michael@16: } michael@16: michael@16: for (i = 0; i < MAX_NO_OF_GWS; i++) { michael@16: if ((*gws)[i].ip_addr == 0) { michael@16: return -1; michael@16: } michael@16: if ((*gws)[i].ip_addr == addr.s_addr && michael@16: (grp_id < 0 || (*gws)[i].grp_id == grp_id)) { michael@16: return 1; michael@16: } michael@16: } michael@16: michael@16: return -1; michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Checks if in-dialog request goes to gateway, taking michael@16: * into account the group id. michael@16: */ michael@16: static int to_gw_grp(struct sip_msg* _m, char* _s1, char* _s2) michael@16: { michael@16: int grp_id; michael@16: michael@16: grp_id = (int)(long)_s1; michael@16: return do_to_gw(_m, grp_id); michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Checks if in-dialog request goes to gateway, ignoring michael@16: * the group id. michael@16: */ michael@16: static int to_gw(struct sip_msg* _m, char* _s1, char* _s2) michael@16: { michael@16: return do_to_gw(_m, -1); michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Frees contact list used by load_contacts function michael@16: */ michael@16: static inline void free_contact_list(struct contact *curr) { michael@16: struct contact *prev; michael@16: while (curr) { michael@16: prev = curr; michael@16: curr = curr->next; michael@16: pkg_free(prev); michael@16: } michael@16: } michael@16: michael@16: /* Encode branch info from contact struct to str */ michael@16: static inline int encode_branch_info(str *info, struct contact *con) michael@16: { michael@16: char *at, *s; michael@16: int len; michael@16: michael@16: info->len = con->uri.len + con->dst_uri.len + michael@16: con->path.len + MAX_SOCKET_STR + INT2STR_MAX_LEN + 5; michael@16: info->s = pkg_malloc(info->len); michael@16: if (!info->s) { michael@16: LM_ERR("No memory left for branch info\n"); michael@16: return 0; michael@16: } michael@16: at = info->s; michael@16: memcpy(at, con->uri.s, con->uri.len); michael@16: at = at + con->uri.len; michael@16: *at = '\n'; michael@16: at++; michael@16: memcpy(at, con->dst_uri.s, con->dst_uri.len); michael@16: at = at + con->dst_uri.len; michael@16: *at = '\n'; michael@16: at++; michael@16: memcpy(at, con->path.s, con->path.len); michael@16: at = at + con->path.len; michael@16: *at = '\n'; michael@16: at++; michael@16: if (con->sock) { michael@16: len = MAX_SOCKET_STR; michael@16: if (!socket2str(con->sock, at, &len, 1)) { michael@16: LM_ERR("Failed to convert socket to str\n"); michael@16: return 0; michael@16: } michael@16: } else { michael@16: len = 0; michael@16: } michael@16: at = at + len; michael@16: *at = '\n'; michael@16: at++; michael@16: s = int2str(con->flags, &len); michael@16: memcpy(at, s, len); michael@16: at = at + len; michael@16: *at = '\n'; michael@16: info->len = at - info->s + 1; michael@16: michael@16: return 1; michael@16: } michael@16: michael@16: michael@16: /* Encode branch info from str */ michael@16: static inline int decode_branch_info(char *info, str *uri, str *dst, str *path, michael@16: struct socket_info **sock, unsigned int *flags) michael@16: { michael@16: str s, host; michael@16: int port, proto; michael@16: char *pos, *at; michael@16: michael@16: pos = strchr(info, '\n'); michael@16: uri->len = pos - info; michael@16: if (uri->len) { michael@16: uri->s = info; michael@16: } else { michael@16: uri->s = 0; michael@16: } michael@16: at = pos + 1; michael@16: michael@16: pos = strchr(at, '\n'); michael@16: dst->len = pos - at; michael@16: if (dst->len) { michael@16: dst->s = at; michael@16: } else { michael@16: dst->s = 0; michael@16: } michael@16: at = pos + 1; michael@16: michael@16: pos = strchr(at, '\n'); michael@16: path->len = pos - at; michael@16: if (path->len) { michael@16: path->s = at; michael@16: } else { michael@16: path->s = 0; michael@16: } michael@16: at = pos + 1; michael@16: michael@16: pos = strchr(at, '\n'); michael@16: s.len = pos - at; michael@16: if (s.len) { michael@16: s.s = at; michael@16: if (parse_phostport(s.s, s.len, &host.s, &host.len, michael@16: &port, &proto) != 0) { michael@16: LM_ERR("Parsing of socket info <%.*s> failed\n", s.len, s.s); michael@16: return 0; michael@16: } michael@16: *sock = grep_sock_info(&host, (unsigned short)port, michael@16: (unsigned short)proto); michael@16: if (*sock == 0) { michael@16: LM_ERR("Invalid socket <%.*s>\n", s.len, s.s); michael@16: return 0; michael@16: } michael@16: } else { michael@16: *sock = 0; michael@16: } michael@16: at = pos + 1; michael@16: michael@16: pos = strchr(at, '\n'); michael@16: s.len = pos - at; michael@16: if (s.len) { michael@16: s.s = at; michael@16: if (str2int(&s, flags) != 0) { michael@16: LM_ERR("Failed to decode flags <%.*s>\n", s.len, s.s); michael@16: return 0; michael@16: } michael@16: } else { michael@16: *flags = 0; michael@16: } michael@16: michael@16: return 1; michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Loads contacts in destination set into "lcr_contact" AVP in reverse michael@16: * priority order and associated each contact with Q_FLAG telling if michael@16: * contact is the last one in its priority class. Finally, removes michael@16: * all branches from destination set. michael@16: */ michael@16: static int load_contacts(struct sip_msg* msg, char* key, char* value) michael@16: { michael@16: str uri, dst_uri, path, branch_info, *ruri; michael@16: qvalue_t q, ruri_q; michael@16: struct contact *contacts, *next, *prev, *curr; michael@16: int_str val; michael@16: int idx; michael@16: struct socket_info* sock; michael@16: unsigned int flags; michael@16: michael@16: /* Check if anything needs to be done */ michael@16: if (nr_branches == 0) { michael@16: LM_DBG("Nothing to do - no branches!\n"); michael@16: return 1; michael@16: } michael@16: michael@16: ruri = GET_RURI(msg); michael@16: if (!ruri) { michael@16: LM_ERR("No Request-URI found\n"); michael@16: return -1; michael@16: } michael@16: ruri_q = get_ruri_q(); michael@16: michael@16: for(idx = 0; (uri.s = get_branch(idx, &uri.len, &q, 0, 0, 0, 0)) != 0; michael@16: idx++) { michael@16: if (q != ruri_q) { michael@16: goto rest; michael@16: } michael@16: } michael@16: LM_DBG("Nothing to do - all contacts have same q!\n"); michael@16: return 1; michael@16: michael@16: rest: michael@16: /* Insert Request-URI branch to contact list */ michael@16: contacts = (struct contact *)pkg_malloc(sizeof(struct contact)); michael@16: if (!contacts) { michael@16: LM_ERR("No memory for contact info\n"); michael@16: return -1; michael@16: } michael@16: contacts->uri.s = ruri->s; michael@16: contacts->uri.len = ruri->len; michael@16: contacts->q = ruri_q; michael@16: contacts->dst_uri = msg->dst_uri; michael@16: contacts->sock = msg->force_send_socket; michael@16: contacts->flags = getb0flags(); michael@16: contacts->path = msg->path_vec; michael@16: contacts->next = (struct contact *)0; michael@16: michael@16: /* Insert branches to contact list in increasing q order */ michael@16: for(idx = 0; michael@16: (uri.s = get_branch(idx,&uri.len,&q,&dst_uri,&path,&flags,&sock)) michael@16: != 0; michael@16: idx++ ) { michael@16: next = (struct contact *)pkg_malloc(sizeof(struct contact)); michael@16: if (!next) { michael@16: LM_ERR("No memory for contact info\n"); michael@16: free_contact_list(contacts); michael@16: return -1; michael@16: } michael@16: next->uri = uri; michael@16: next->q = q; michael@16: next->dst_uri = dst_uri; michael@16: next->path = path; michael@16: next->flags = flags; michael@16: next->sock = sock; michael@16: next->next = (struct contact *)0; michael@16: prev = (struct contact *)0; michael@16: curr = contacts; michael@16: while (curr && (curr->q < q)) { michael@16: prev = curr; michael@16: curr = curr->next; michael@16: } michael@16: if (!curr) { michael@16: next->next = (struct contact *)0; michael@16: prev->next = next; michael@16: } else { michael@16: next->next = curr; michael@16: if (prev) { michael@16: prev->next = next; michael@16: } else { michael@16: contacts = next; michael@16: } michael@16: } michael@16: } michael@16: michael@16: /* Assign values for q_flags */ michael@16: curr = contacts; michael@16: curr->q_flag = 0; michael@16: while (curr->next) { michael@16: if (curr->q < curr->next->q) { michael@16: curr->next->q_flag = Q_FLAG; michael@16: } else { michael@16: curr->next->q_flag = 0; michael@16: } michael@16: curr = curr->next; michael@16: } michael@16: michael@16: /* Add contacts to "contacts" AVP */ michael@16: curr = contacts; michael@16: while (curr) { michael@16: if (encode_branch_info(&branch_info, curr) == 0) { michael@16: LM_ERR("Encoding of branch info failed\n"); michael@16: free_contact_list(contacts); michael@16: if (branch_info.s) pkg_free(branch_info.s); michael@16: return -1; michael@16: } michael@16: val.s = branch_info; michael@16: add_avp(contact_avp_type|AVP_VAL_STR|(curr->q_flag), michael@16: contact_avp, val); michael@16: pkg_free(branch_info.s); michael@16: LM_DBG("Loaded contact <%.*s> with q_flag <%d>\n", michael@16: val.s.len, val.s.s, curr->q_flag); michael@16: curr = curr->next; michael@16: } michael@16: michael@16: /* Clear all branches */ michael@16: clear_branches(); michael@16: michael@16: /* Free contact list */ michael@16: free_contact_list(contacts); michael@16: michael@16: return 1; michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Adds to request a destination set that includes all highest priority michael@16: * class contacts in "lcr_contact" AVP. If called from a route block, michael@16: * rewrites the request uri with first contact and adds the remaining michael@16: * contacts as branches. If called from failure route block, adds all michael@16: * contacts as branches. Removes added contacts from "lcr_contact" AVP. michael@16: */ michael@16: static int next_contacts(struct sip_msg* msg, char* key, char* value) michael@16: { michael@16: struct usr_avp *avp, *prev; michael@16: int_str val; michael@16: str uri, dst, path; michael@16: struct socket_info *sock; michael@16: unsigned int flags; michael@16: michael@16: /* Find first lcr_contact_avp value */ michael@16: avp = search_first_avp(contact_avp_type, contact_avp, &val, 0); michael@16: if (!avp) { michael@16: LM_DBG("No AVPs -- we are done!\n"); michael@16: return -1; michael@16: } michael@16: michael@16: LM_DBG("Next contact is <%s>\n", val.s.s); michael@16: michael@16: if (decode_branch_info(val.s.s, &uri, &dst, &path, &sock, &flags)== 0) { michael@16: LM_ERR("Decoding of branch info <%.*s> failed\n", michael@16: val.s.len, val.s.s); michael@16: destroy_avp(avp); michael@16: return -1; michael@16: } michael@16: michael@16: set_ruri(msg, &uri); michael@16: set_dst_uri(msg, &dst); michael@16: set_path_vector(msg, &path); michael@16: msg->force_send_socket = sock; michael@16: setb0flags(flags); michael@16: michael@16: if (avp->flags & Q_FLAG) { michael@16: destroy_avp(avp); michael@16: if (route_type == REQUEST_ROUTE) { michael@16: /* Set fr_inv_timer */ michael@16: val.n = fr_inv_timer_next; michael@16: if (add_avp(fr_inv_timer_avp_type, fr_inv_timer_avp, val) != 0) { michael@16: LM_ERR("Setting of fr_inv_timer_avp failed\n"); michael@16: return -1; michael@16: } michael@16: } michael@16: return 1; michael@16: } michael@16: michael@16: /* Append branches until out of branches or Q_FLAG is set */ michael@16: prev = avp; michael@16: while ((avp = search_next_avp(avp, &val))) { michael@16: destroy_avp(prev); michael@16: michael@16: LM_DBG("Next contact is <%s>\n", val.s.s); michael@16: michael@16: if (decode_branch_info(val.s.s, &uri, &dst, &path, &sock, &flags)== 0){ michael@16: LM_ERR("Decoding of branch info <%.*s> failed\n", michael@16: val.s.len, val.s.s); michael@16: destroy_avp(avp); michael@16: return -1; michael@16: } michael@16: michael@16: if (append_branch(msg, &uri, &dst, &path, 0, flags, sock) != 1) { michael@16: LM_ERR("Appending branch failed\n"); michael@16: destroy_avp(avp); michael@16: return -1; michael@16: } michael@16: michael@16: if (avp->flags & Q_FLAG) { michael@16: destroy_avp(avp); michael@16: if (route_type == REQUEST_ROUTE) { michael@16: val.n = fr_inv_timer_next; michael@16: if (add_avp(fr_inv_timer_avp_type, fr_inv_timer_avp, val)!= 0){ michael@16: LM_ERR("Setting of fr_inv_timer_avp failed\n"); michael@16: return -1; michael@16: } michael@16: } michael@16: return 1; michael@16: } michael@16: prev = avp; michael@16: } michael@16: michael@16: /* Restore fr_inv_timer */ michael@16: val.n = fr_inv_timer; michael@16: if (add_avp(fr_inv_timer_avp_type, fr_inv_timer_avp, val) != 0) { michael@16: LM_ERR("Setting of fr_inv_timer_avp failed\n"); michael@16: return -1; michael@16: } michael@16: michael@16: return 1; michael@16: } michael@16: michael@16: michael@16: /* michael@16: * Convert string parameter to integer for functions that expect an integer. michael@16: * Taken from sl module. michael@16: */ michael@16: static int fixstringloadgws(void **param, int param_count) michael@16: { michael@16: pv_elem_t *model=NULL; michael@16: str s; michael@16: michael@16: /* convert to str */ michael@16: s.s = (char*)*param; michael@16: s.len = strlen(s.s); michael@16: michael@16: model=NULL; michael@16: if (param_count==1) { michael@16: if(s.len==0) { michael@16: LM_ERR("No param <%d>!\n", param_count); michael@16: return -1; michael@16: } michael@16: michael@16: if(pv_parse_format(&s,&model)<0 || model==NULL) { michael@16: LM_ERR("Wrong format <%s> for param <%d>!\n", s.s, param_count); michael@16: return -1; michael@16: } michael@16: if(model->spec.getf==NULL) { michael@16: if(param_count==1) { michael@16: if(str2int(&s, (unsigned int*)&model->spec.pvp.pvn.u.isname.name.n)!=0) { michael@16: LM_ERR("Wrong value <%s> for param <%d>!\n", michael@16: s.s, param_count); michael@16: return -1; michael@16: } michael@16: } michael@16: } michael@16: *param = (void*)model; michael@16: } michael@16: michael@16: return 0; michael@16: }