diff --git a/modules/routing.c b/modules/routing.c new file mode 100644 index 00000000000..8498acb2179 --- /dev/null +++ b/modules/routing.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com + * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com + * + * Jason Penton (jason(dot)penton(at)smilecoms.com and + * Richard Good (richard(dot)good(at)smilecoms.com) as part of an + * effort to add full IMS support to Kamailio/SR using a new and + * improved architecture + * + * NB: Alot of this code was originally part of OpenIMSCore, + * FhG Fokus. + * Copyright (C) 2004-2006 FhG Fokus + * Thanks for great work! This is an effort to + * break apart the various CSCF functions into logically separate + * components. We hope this will drive wider use. We also feel + * that in this way the architecture is more complete and thereby easier + * to manage in the Kamailio/SR environment + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio 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 + * + * Kamailio 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef __OS_darwin +#include +#include +#endif + +#include "routing.h" +#include "config.h" +#include "peermanager.h" +#include "diameter_api.h" +#include "math.h" + +#define LB_MAX_PEERS 20 /**< maximum peers that can be loadbalanced accross i.e. same metric */ + +extern dp_config *config; /**< Configuration for this diameter peer */ +int gcount = 0; + +/** + * portable implementation for clock_gettime(CLOCK_REALTIME, ts) + */ +int ser_clock_gettime(struct timespec *ts) +{ +#ifdef __OS_darwin + clock_serv_t cclock; + mach_timespec_t mts; + + /* OS X does not have clock_gettime, use clock_get_time */ + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts->tv_sec = mts.tv_sec; + ts->tv_nsec = mts.tv_nsec; + return 0; +#else + return clock_gettime(CLOCK_REALTIME, ts); +#endif +} + +/** + * Returns if the peer advertised support for an Application ID + * @param p - the peer to check + * @param app_id - the application id to look for + * @param vendor_id - the vendor id to look for, 0 if not vendor specific + * @returns 0 if not found, 1 if found + */ +int peer_handles_application(peer *p, int app_id, int vendor_id) { + int i; + LM_DBG("Checking if peer %.*s handles application %d for vendord %d\n", p->fqdn.len, p->fqdn.s, app_id, vendor_id); + if (!p || !p->applications || !p->applications_cnt) return 0; + for (i = 0; i < p->applications_cnt; i++) + if (p->applications[i].id == app_id && p->applications[i].vendor == vendor_id) return 1; + return 0; +} + +/** + * Get the first peer that is connected from the list of routing entries. + * @param r - the list of routing entries to look into + * @returns - the peer or null if none connected + */ +peer* get_first_connected_route(cdp_session_t* cdp_session, routing_entry *r, int app_id, int vendor_id) { + peer * peers[LB_MAX_PEERS]; + int peer_count = 0; + int prev_metric = 0; + routing_entry *i; + peer *p; + int j; + unsigned char isCdpSession = 0; /* V1.2 */ + time_t least_recent_time; + struct timespec time_spec; + + if (cdp_session) + { + /*try and find an already used peer for this session - sticky*/ + if ((cdp_session->sticky_peer_fqdn.len > 0) && cdp_session->sticky_peer_fqdn.s) + { + //we have an old sticky peer. let's make sure it's up and connected before we use it. + AAASessionsUnlock(cdp_session->hash); /*V1.1 - Don't attempt to hold two locks at same time */ + p = get_peer_by_fqdn(&cdp_session->sticky_peer_fqdn); /*V1.1 NB!!! get_peer_by_fqdn does lock_get(peer_list_lock) AND lock_release;*/ + AAASessionsLock(cdp_session->hash); /*V1.1 - As we were...no call seems to pass cdp_session unlocked */ + if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && + peer_handles_application(p, app_id, vendor_id) + ) + { + p->last_selected = time(NULL); + LM_DBG("Found a sticky peer [%.*s] for this session - re-using\n", p->fqdn.len, p->fqdn.s); + return p; + } + } + isCdpSession = 1; + } + + for (i = r; i; i = i->next) + { + if (peer_count >= LB_MAX_PEERS) + break; + if (isCdpSession) /*V1.2--we dont want to hold two locks again in below code that calls get_peer_by_fqdn*/ + { + AAASessionsUnlock(cdp_session->hash); + } + p = get_peer_by_fqdn(&(i->fqdn)); + + if (isCdpSession) /*V1.2--we dont want to hold two locks again in below code that calls get_peer_by_fqdn*/ + { + AAASessionsLock(cdp_session->hash); + } + + if (!p) + LM_DBG("The peer %.*s does not seem to be connected or configured\n", + i->fqdn.len, i->fqdn.s); + else + LM_DBG("The peer %.*s state is %s\n", i->fqdn.len, i->fqdn.s, + (p->state == I_Open || p->state == R_Open) ? "opened" : "closed"); + if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) + { + LM_DBG("The peer %.*s matches - will forward there\n", i->fqdn.len, i->fqdn.s); + if (peer_count != 0) //check the metric + { + if (i->metric != prev_metric) + break; + //metric must be the same + peers[peer_count++] = p; + } + else //we're first + { prev_metric = i->metric; + peers[peer_count++] = p; + } + } + } + + if (peer_count == 0) { + return 0; + } + + least_recent_time = peers[0]->last_selected; + LM_DBG("peer [%.*s] was last used @ %ld\n", peers[0]->fqdn.len, peers[0]->fqdn.s, peers[0]->last_selected); + p = peers[0]; + for (j = 1; j < peer_count; j++) { + LM_DBG("Peer [%.*s] was last used at [%ld]\n", peers[j]->fqdn.len, peers[j]->fqdn.s, peers[j]->last_selected); + if (peers[j]->last_selected < least_recent_time) { + least_recent_time = peers[j]->last_selected; + p = peers[j]; + } + } + + ser_clock_gettime(&time_spec); + + p->last_selected = (time_spec.tv_sec*1000000) + round(time_spec.tv_nsec / 1.0e3); // Convert nanoseconds to microseconds + LM_DBG("chosen peer [%.*s]\n", p->fqdn.len, p->fqdn.s); + + if (isCdpSession) { /*V1.2 just to conform to test mechanism*/ + if (cdp_session->sticky_peer_fqdn_buflen <= p->fqdn.len) { + LM_DBG("not enough storage for sticky peer - allocating more\n"); + if (cdp_session->sticky_peer_fqdn.s) + shm_free(cdp_session->sticky_peer_fqdn.s); + + cdp_session->sticky_peer_fqdn.s = (char*) shm_malloc(p->fqdn.len + 1); + if (!cdp_session->sticky_peer_fqdn.s) { + LM_ERR("no more shm memory\n"); + return 0; + } + cdp_session->sticky_peer_fqdn_buflen = p->fqdn.len + 1; + memset(cdp_session->sticky_peer_fqdn.s, 0, p->fqdn.len + 1); + } + cdp_session->sticky_peer_fqdn.len = p->fqdn.len; + memcpy(cdp_session->sticky_peer_fqdn.s, p->fqdn.s, p->fqdn.len); + } + + return p; +} + +/** + * Get the first connect peer that matches the routing mechanisms. + * - First the Destination-Host AVP value is tried if connected (the peer does not have to + * be in the routing table at all). + * - Then we look for a connected peer in the specific realm for the Destination-Realm AVP + * - Then we look for the first connected peer in the default routes + * @param m - the Diameter message to find the destination peer for + * @returns - the connected peer or null if none connected found + */ +peer* get_routing_peer(cdp_session_t* cdp_session, AAAMessage *m) { + str destination_realm = {0, 0}, destination_host = {0, 0}; + AAA_AVP *avp, *avp_vendor, *avp2; + AAA_AVP_LIST group; + peer *p; + routing_realm *rr; + int app_id = 0, vendor_id = 0; + + LM_DBG("getting diameter routing peer for realm: [%.*s]\n", m->dest_realm->data.len, m->dest_realm->data.s); + + app_id = m->applicationId; + avp = AAAFindMatchingAVP(m, 0, AVP_Vendor_Specific_Application_Id, 0, AAA_FORWARD_SEARCH); + if (avp) { + group = AAAUngroupAVPS(avp->data); + avp_vendor = AAAFindMatchingAVPList(group, group.head, AVP_Vendor_Id, 0, 0); + avp2 = AAAFindMatchingAVPList(group, group.head, AVP_Auth_Application_Id, 0, 0); + if (avp_vendor && avp2) { + vendor_id = get_4bytes(avp_vendor->data.s); + app_id = get_4bytes(avp2->data.s); + } + avp2 = AAAFindMatchingAVPList(group, group.head, AVP_Acct_Application_Id, 0, 0); + if (avp_vendor && avp2) { + vendor_id = get_4bytes(avp_vendor->data.s); + app_id = get_4bytes(avp2->data.s); + } + AAAFreeAVPList(&group); + } + + avp_vendor = AAAFindMatchingAVP(m, 0, AVP_Vendor_Id, 0, AAA_FORWARD_SEARCH); + avp = AAAFindMatchingAVP(m, 0, AVP_Auth_Application_Id, 0, AAA_FORWARD_SEARCH); + if (avp && avp_vendor) { + vendor_id = get_4bytes(avp_vendor->data.s); + app_id = get_4bytes(avp->data.s); + } + + avp = AAAFindMatchingAVP(m, 0, AVP_Acct_Application_Id, 0, AAA_FORWARD_SEARCH); + if (avp && avp_vendor) { + vendor_id = get_4bytes(avp_vendor->data.s); + app_id = get_4bytes(avp->data.s); + } + + avp = AAAFindMatchingAVP(m, 0, AVP_Destination_Host, 0, AAA_FORWARD_SEARCH); + if (avp) destination_host = avp->data; + + if (destination_host.len) { + /* There is a destination host present in the message try and route directly there */ + p = get_peer_by_fqdn(&destination_host); + if (p && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) { + p->last_selected = time(NULL); + return p; + } + /* the destination host peer is not connected at the moment, try a normal route then */ + } + + avp = AAAFindMatchingAVP(m, 0, AVP_Destination_Realm, 0, AAA_FORWARD_SEARCH); + if (avp) destination_realm = avp->data; + + if (!config->r_table) { + LM_ERR("get_routing_peer(): Empty routing table.\n"); + return 0; + } + + if (destination_realm.len) { + /* first search for the destination realm */ + for (rr = config->r_table->realms; rr; rr = rr->next) + if (rr->realm.len == destination_realm.len && + strncasecmp(rr->realm.s, destination_realm.s, destination_realm.len) == 0) + break; + if (rr) { + p = get_first_connected_route(cdp_session, rr->routes, app_id, vendor_id); + if (p) return p; + else LM_ERR("get_routing_peer(): No connected Route peer found for Realm <%.*s>. Trying DefaultRoutes next...\n", + destination_realm.len, destination_realm.s); + } + } + /* if not found in the realms or no destination_realm, + * get the first connected host in default routes */ + LM_DBG("no routing peer found, trying default route\n"); + p = get_first_connected_route(cdp_session, config->r_table->routes, app_id, vendor_id); + if (!p) { + LM_ERR("get_routing_peer(): No connected DefaultRoute peer found for app_id %d and vendor id %d.\n", + app_id, vendor_id); + } + return p; +} diff --git a/msrp_env.c b/msrp_env.c new file mode 100644 index 00000000000..f599714f36c --- /dev/null +++ b/msrp_env.c @@ -0,0 +1,226 @@ +/** + * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com) + * + * This file is part of Kamailio, a free SIP server. + * + * This file 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 + * + * + * This file 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include + +#include "../../dprint.h" +#include "../../globals.h" +#include "../../ut.h" +#include "../../dset.h" + +#include "msrp_parser.h" +#include "msrp_netio.h" +#include "msrp_env.h" + +extern int msrp_param_sipmsg; + +static msrp_env_t _msrp_env = {0}; + +/** + * + */ +msrp_env_t *msrp_get_env(void) +{ + return &_msrp_env; +} +static int init_fake_sipmsg(); /* once-off method to initialize internal buffer */ +/** + * + */ +void msrp_reset_env(void) +{ + memset(&_msrp_env, 0, sizeof(struct msrp_env)); +} + +/** + * + */ +msrp_frame_t *msrp_get_current_frame(void) +{ + return _msrp_env.msrp; +} + +/** + * + */ +int msrp_set_current_frame(msrp_frame_t *mf) +{ + _msrp_env.msrp = mf; + init_dst_from_rcv(&_msrp_env.srcinfo, mf->tcpinfo->rcv); + _msrp_env.envflags |= MSRP_ENV_SRCINFO; + return 0; +} + + +/** + * + */ +int msrp_env_set_dstinfo(msrp_frame_t *mf, str *addr, str *fsock, int flags) +{ + struct socket_info *si = NULL; + snd_flags_t sflags = {0}; + + if(fsock!=NULL && fsock->len>0) + { + si = msrp_get_local_socket(fsock); + if(si==NULL) + { + LM_DBG("local socket not found [%.*s] - trying to continue\n", + fsock->len, fsock->s); + } + } + sflags.f = flags; + if(si==NULL) + { + sflags.f &= ~SND_F_FORCE_SOCKET; + } else { + sflags.f |= SND_F_FORCE_SOCKET; + } + + sflags.f |= _msrp_env.sndflags; + memset(&_msrp_env.dstinfo, 0, sizeof(struct dest_info)); + if(msrp_uri_to_dstinfo(NULL, &_msrp_env.dstinfo, si, sflags, addr)==NULL) + { + LM_ERR("failed to set destination address [%.*s]\n", + addr->len, addr->s); + return -1; + } + _msrp_env.envflags |= MSRP_ENV_DSTINFO; + return 0; +} + +/** + * + */ +int msrp_env_set_sndflags(msrp_frame_t *mf, int flags) +{ + _msrp_env.sndflags |= (flags & (~SND_F_FORCE_SOCKET)); + if(_msrp_env.envflags & MSRP_ENV_DSTINFO) + { + _msrp_env.dstinfo.send_flags.f |= _msrp_env.sndflags; + } + return 0; +} + +/** + * + */ +int msrp_env_set_rplflags(msrp_frame_t *mf, int flags) +{ + _msrp_env.rplflags |= (flags & (~SND_F_FORCE_SOCKET)); + if(_msrp_env.envflags & MSRP_ENV_SRCINFO) + { + _msrp_env.srcinfo.send_flags.f |= _msrp_env.rplflags; + } + return 0; +} + + +/** + * + */ +#define MSRP_FAKED_SIPMSG_START "MSRP sip:a@127.0.0.1 SIP/2.0\r\nVia: SIP/2.0/UDP 127.0.0.1:9;branch=z9hG4bKa\r\nFrom: ;tag=a\r\nTo: \r\nCall-ID: a\r\nCSeq: 1 MSRP\r\nContent-Length: 0\r\nMSRP-First-Line: " + +static msrp_options_t opts = MSRP_OPTIONS_DEFAULT; + +static char * _msrp_faked_sipmsg_buf = NULL; /* changed to ptr, from global []*/ +static sip_msg_t _msrp_faked_sipmsg; +static unsigned int _msrp_faked_sipmsg_no = 0; +sip_msg_t *msrp_fake_sipmsg(msrp_frame_t *mf) +{ + int len; + + if (unlikely(!_msrp_faked_sipmsg_buf)) + { + if (unlikely(init_fake_sipmsg() != 0)) + { + LM_CRIT("Failed initializing faked sip buffer!!"); + return NULL; + } + } + + if(msrp_param_sipmsg==0) + { + LM_WARN("msrp_param_sipmsg is NULL...msg will be dropped!!"); + return NULL; + } + + if (mf->buf.len >= GET_MSRP_MAX_FRAME_SIZE(&opts) - sizeof(MSRP_FAKED_SIPMSG_START)) + { + LM_WARN("msg size [%lu] > than internal max buffer size[%u] ...msg will be dropped!!", mf->buf.len + sizeof(MSRP_FAKED_SIPMSG_START), GET_MSRP_MAX_FRAME_SIZE(&opts) ); + return NULL; + } + + + len = sizeof(MSRP_FAKED_SIPMSG_START)-1; + memcpy(_msrp_faked_sipmsg_buf, MSRP_FAKED_SIPMSG_START, len); + memcpy(_msrp_faked_sipmsg_buf + len, mf->buf.s, + mf->fline.buf.len + mf->hbody.len); + len += mf->fline.buf.len + mf->hbody.len; + memcpy(_msrp_faked_sipmsg_buf + len, "\r\n", 2); + len += 2; + _msrp_faked_sipmsg_buf[len] = '\0'; + + memset(&_msrp_faked_sipmsg, 0, sizeof(sip_msg_t)); + + _msrp_faked_sipmsg.buf=_msrp_faked_sipmsg_buf; + _msrp_faked_sipmsg.len=len; + + _msrp_faked_sipmsg.set_global_address=default_global_address; + _msrp_faked_sipmsg.set_global_port=default_global_port; + + if (parse_msg(_msrp_faked_sipmsg.buf, _msrp_faked_sipmsg.len, + &_msrp_faked_sipmsg)!=0) + { + LM_ERR("parse_msg failed\n"); + return NULL; + } + + _msrp_faked_sipmsg.id = 1 + _msrp_faked_sipmsg_no++; + _msrp_faked_sipmsg.pid = my_pid(); + clear_branches(); + return &_msrp_faked_sipmsg; +} + + +/** + * + */ +static int init_fake_sipmsg() +{ + + if (_msrp_faked_sipmsg_buf) + { + LM_WARN("Cannot initialize faked sip buffer more than once!!"); + return -1; + } + if (set_msrp_options_on_buffer(&_msrp_faked_sipmsg_buf)!=0) + { + LM_CRIT(" memory alloc failure!! "); + return -1; + } + get_current_msrp_options(&opts); + return 0; +} diff --git a/msrp_mod.c b/msrp_mod.c new file mode 100644 index 00000000000..ed1476f9b5a --- /dev/null +++ b/msrp_mod.c @@ -0,0 +1,505 @@ +/** + * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com) + * + * This file is part of Kamailio, a free SIP server. + * + * This file 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 + * + * + * This file 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include + +#include "../../sr_module.h" +#include "../../dprint.h" +#include "../../ut.h" +#include "../../dset.h" +#include "../../action.h" +#include "../../mod_fix.h" +#include "../../events.h" +#include "../../tcp_conn.h" +#include "../../pvar.h" +#include "../../timer_proc.h" /* register_sync_timer */ + +#include "msrp_parser.h" +#include "msrp_netio.h" +#include "msrp_vars.h" +#include "msrp_env.h" +#include "msrp_cmap.h" + +MODULE_VERSION + +static int mod_init(void); +static int child_init(int); +static void mod_destroy(void); + +static int w_msrp_relay(sip_msg_t* msg, char* str1, char* str2); +static int w_msrp_reply2(sip_msg_t* msg, char* code, char* text); +static int w_msrp_reply3(sip_msg_t* msg, char* code, char* text, char* hdrs); +static int w_msrp_is_request(sip_msg_t* msg, char* str1, char* str2); +static int w_msrp_is_reply(sip_msg_t* msg, char* str1, char* str2); +static int w_msrp_set_dst(sip_msg_t* msg, char* taddr, char* fsock); +static int w_msrp_relay_flags(sip_msg_t* msg, char *tflags, char* str2); +static int w_msrp_reply_flags(sip_msg_t* msg, char *tflags, char* str2); +static int w_msrp_cmap_save(sip_msg_t* msg, char* str1, char* str2); +static int w_msrp_cmap_lookup(sip_msg_t* msg, char* str1, char* str2); + +static void msrp_local_timer(unsigned int ticks, void* param); /*!< Local timer handler */ + +int msrp_param_sipmsg = 1; +int msrp_cmap_size = 0; +int msrp_auth_min_expires = 60; +int msrp_auth_max_expires = 3600; +int msrp_timer_interval = 60; +str msrp_use_path_addr = { 0 }; +int msrp_tls_module_loaded = 0; + +msrp_options_t msrp_opt = MSRP_OPTIONS_DEFAULT; + +static int msrp_frame_received(void *data); +sip_msg_t *msrp_fake_sipmsg(msrp_frame_t *mf); + +static tr_export_t mod_trans[] = { + { {"msrpuri", sizeof("msrpuri")-1}, /* string class */ + tr_parse_msrpuri }, + + { { 0, 0 }, 0 } +}; + +static pv_export_t mod_pvs[] = { + { {"msrp", (sizeof("msrp")-1)}, PVT_OTHER, pv_get_msrp, + pv_set_msrp, pv_parse_msrp_name, 0, 0, 0}, + + { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } +}; + +static cmd_export_t cmds[]={ + {"msrp_relay", (cmd_function)w_msrp_relay, 0, 0, + 0, ANY_ROUTE}, + {"msrp_reply", (cmd_function)w_msrp_reply2, 2, fixup_spve_spve, + 0, ANY_ROUTE}, + {"msrp_reply", (cmd_function)w_msrp_reply3, 3, fixup_spve_all, + 0, ANY_ROUTE}, + {"msrp_is_request", (cmd_function)w_msrp_is_request, 0, 0, + 0, ANY_ROUTE}, + {"msrp_is_reply", (cmd_function)w_msrp_is_reply, 0, 0, + 0, ANY_ROUTE}, + {"msrp_set_dst", (cmd_function)w_msrp_set_dst, 2, fixup_spve_all, + 0, ANY_ROUTE}, + {"msrp_relay_flags", (cmd_function)w_msrp_relay_flags, 1, fixup_igp_null, + 0, ANY_ROUTE}, + {"msrp_reply_flags", (cmd_function)w_msrp_reply_flags, 1, fixup_igp_null, + 0, ANY_ROUTE}, + {"msrp_cmap_save", (cmd_function)w_msrp_cmap_save, 0, 0, + 0, ANY_ROUTE}, + {"msrp_cmap_lookup", (cmd_function)w_msrp_cmap_lookup, 0, 0, + 0, ANY_ROUTE}, + {0, 0, 0, 0, 0, 0} +}; + +static param_export_t params[]={ + {"sipmsg", PARAM_INT, &msrp_param_sipmsg}, + {"cmap_size", PARAM_INT, &msrp_cmap_size}, + {"auth_min_expires", PARAM_INT, &msrp_auth_min_expires}, + {"auth_max_expires", PARAM_INT, &msrp_auth_max_expires}, + {"timer_interval", PARAM_INT, &msrp_timer_interval}, + {"use_path_addr", PARAM_STR, &msrp_use_path_addr}, + {"msrp_hdr_size", PARAM_INT, &msrp_opt.msrp_frame_hdrs_max_size}, + {"msrp_body_size", PARAM_INT, &msrp_opt.msrp_frame_body_max_size}, + {"msrp_use_sys_mem", PARAM_INT, &msrp_opt.msrp_frame_use_sys_mem}, + {0, 0, 0} +}; + +struct module_exports exports = { + "msrp", + DEFAULT_DLFLAGS, /* dlopen flags */ + cmds, + params, + 0, + 0, /* exported MI functions */ + mod_pvs, /* exported pseudo-variables */ + 0, /* extra processes */ + mod_init, /* module initialization function */ + 0, /* response function */ + mod_destroy, /* destroy function */ + child_init /* per child init function */ +}; + + + +/** + * init module function + */ +static int mod_init(void) +{ + if(msrp_sruid_init()<0) { + LM_ERR("cannot init msrp uid\n"); + return -1; + } + + if(msrp_cmap_init_rpc()<0) + { + LM_ERR("failed to register cmap RPC commands\n"); + return -1; + } + + if(msrp_cmap_size>0) { + if(msrp_cmap_size>16) + msrp_cmap_size = 16; + if(msrp_cmap_init(1< 0) + { + msrp_opt.msrp_frame_use_sys_mem=1; + } + init_msrp_options(&msrp_opt); + msrp_print_options(); + return 0; +} + +/** + * @brief Initialize async module children + */ +static int child_init(int rank) +{ + if(msrp_sruid_init()<0) { + LM_ERR("cannot init msrp uid\n"); + return -1; + } + + if (rank!=PROC_MAIN) + return 0; + if(msrp_cmap_size>0) { + if(fork_sync_timer(PROC_TIMER, "MSRP Timer", 1 /*socks flag*/, + msrp_local_timer, NULL, msrp_timer_interval /*sec*/)<0) { + LM_ERR("failed to start timer routine as process\n"); + return -1; /* error */ + } + } + + return 0; +} +/** + * destroy module function + */ +static void mod_destroy(void) +{ + return; +} + +/** + * + */ +int mod_register(char *path, int *dlflags, void *p1, void *p2) +{ + return register_trans_mod(path, mod_trans); +} + +/** + * + */ +static int w_msrp_relay(sip_msg_t* msg, char* str1, char* str2) +{ + msrp_frame_t *mf; + int ret; + + mf = msrp_get_current_frame(); + if(mf==NULL) + return -1; + + ret = msrp_relay(mf); + if(ret==0) ret = 1; + return ret; +} + + +/** + * + */ +static int w_msrp_reply(struct sip_msg* msg, char* code, char* text, + char *hdrs) +{ + str rcode; + str rtext; + str rhdrs; + msrp_frame_t *mf; + int ret; + + if(fixup_get_svalue(msg, (gparam_t*)code, &rcode)!=0) + { + LM_ERR("no reply status code\n"); + return -1; + } + + if(fixup_get_svalue(msg, (gparam_t*)text, &rtext)!=0) + { + LM_ERR("no reply status phrase\n"); + return -1; + } + + if(hdrs!=NULL && fixup_get_svalue(msg, (gparam_t*)hdrs, &rhdrs)!=0) + { + LM_ERR("invalid extra headers\n"); + return -1; + } + + mf = msrp_get_current_frame(); + if(mf==NULL) + return -1; + + ret = msrp_reply(mf, &rcode, &rtext, (hdrs!=NULL)?&rhdrs:NULL); + if(ret==0) ret = 1; + return ret; +} + +/** + * + */ +static int w_msrp_reply2(sip_msg_t* msg, char* code, char* text) +{ + return w_msrp_reply(msg, code, text, NULL); +} + +/** + * + */ +static int w_msrp_reply3(sip_msg_t* msg, char* code, char* text, + char *hdrs) +{ + return w_msrp_reply(msg, code, text, hdrs); +} + +/** + * + */ +static int w_msrp_is_request(sip_msg_t* msg, char* str1, char* str2) +{ + msrp_frame_t *mf; + mf = msrp_get_current_frame(); + if(mf==NULL) + return -1; + if(mf->fline.msgtypeid==MSRP_REQUEST) + return 1; + return -1; +} + +/** + * + */ +static int w_msrp_is_reply(sip_msg_t* msg, char* str1, char* str2) +{ + msrp_frame_t *mf; + mf = msrp_get_current_frame(); + if(mf==NULL) + return -1; + if(mf->fline.msgtypeid==MSRP_REPLY) + return 1; + return -1; +} + +/** + * + */ +static int w_msrp_set_dst(sip_msg_t* msg, char* taddr, char* fsock) +{ + str rtaddr = {0}; + str rfsock = {0}; + msrp_frame_t *mf; + int ret; + + if(fixup_get_svalue(msg, (gparam_t*)taddr, &rtaddr)!=0) + { + LM_ERR("invalid target address parameter\n"); + return -1; + } + + if(fixup_get_svalue(msg, (gparam_t*)fsock, &rfsock)!=0) + { + LM_ERR("invalid local socket parameter\n"); + return -1; + } + + + mf = msrp_get_current_frame(); + if(mf==NULL) + return -1; + + ret = msrp_env_set_dstinfo(mf, &rtaddr, &rfsock, 0); + if(ret==0) ret = 1; + return ret; +} + +/** + * + */ +static int w_msrp_relay_flags(sip_msg_t* msg, char *tflags, char* str2) +{ + int rtflags = 0; + msrp_frame_t *mf; + int ret; + if(fixup_get_ivalue(msg, (gparam_t*)tflags, &rtflags)!=0) + { + LM_ERR("invalid send flags parameter\n"); + return -1; + } + + mf = msrp_get_current_frame(); + if(mf==NULL) + return -1; + + ret = msrp_env_set_sndflags(mf, rtflags); + if(ret==0) ret = 1; + return ret; +} + +/** + * + */ +static int w_msrp_reply_flags(sip_msg_t* msg, char *tflags, char* str2) +{ + int rtflags = 0; + msrp_frame_t *mf; + int ret; + if(fixup_get_ivalue(msg, (gparam_t*)tflags, &rtflags)!=0) + { + LM_ERR("invalid send flags parameter\n"); + return -1; + } + + mf = msrp_get_current_frame(); + if(mf==NULL) + return -1; + + ret = msrp_env_set_rplflags(mf, rtflags); + if(ret==0) ret = 1; + return ret; +} + + +/** + * + */ +static int w_msrp_cmap_save(sip_msg_t* msg, char* str1, char* str2) +{ + msrp_frame_t *mf; + int ret; + + mf = msrp_get_current_frame(); + if(mf==NULL) + return -1; + + ret = msrp_cmap_save(mf); + if(ret==0) ret = 1; + return ret; +} + + +/** + * + */ +static int w_msrp_cmap_lookup(sip_msg_t* msg, char* str1, char* str2) +{ + msrp_frame_t *mf; + int ret; + + mf = msrp_get_current_frame(); + if(mf==NULL) + return -1; + + ret = msrp_cmap_lookup(mf); + if(ret==0) ret = 1; + return ret; +} + +/** + * + */ +static int msrp_frame_received(void *data) +{ + tcp_event_info_t *tev; + static msrp_frame_t mf; + sip_msg_t *fmsg; + struct run_act_ctx ctx; + int rtb, rt; + + + tev = (tcp_event_info_t*)data; + + if(tev==NULL || tev->buf==NULL || tev->len<=0) + { + LM_DBG("invalid parameters\n"); + return -1; + } + + memset(&mf, 0, sizeof(msrp_frame_t)); + mf.buf.s = tev->buf; + mf.buf.len = tev->len; + mf.tcpinfo = tev; + if(msrp_parse_frame(&mf)<0) + { + LM_ERR("error parsing msrp frame\n"); + return -1; + } + msrp_reset_env(); + msrp_set_current_frame(&mf); + rt = route_get(&event_rt, "msrp:frame-in"); + if(rt>=0 && event_rt.rlist[rt]!=NULL) { + LM_DBG("executing event_route[msrp:frame-in] (%d)\n", rt); + fmsg = msrp_fake_sipmsg(&mf); + if(fmsg!=NULL) + fmsg->rcv = *tev->rcv; + rtb = get_route_type(); + set_route_type(REQUEST_ROUTE); + init_run_actions_ctx(&ctx); + run_top_route(event_rt.rlist[rt], fmsg, &ctx); + if(ctx.run_flags&DROP_R_F) + { + LM_DBG("exit due to 'drop' in event route\n"); + } + set_route_type(rtb); + if(fmsg!=NULL) + free_sip_msg(fmsg); + } + msrp_reset_env(); + msrp_destroy_frame(&mf); + return 0; +} + +/** + * + */ +static void msrp_local_timer(unsigned int ticks, void* param) +{ + msrp_cmap_clean(); +} diff --git a/msrp_netio.c b/msrp_netio.c new file mode 100644 index 00000000000..22768663fc9 --- /dev/null +++ b/msrp_netio.c @@ -0,0 +1,453 @@ +/** + * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com) + * + * This file is part of Kamailio, a free SIP server. + * + * This file 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 + * + * + * This file 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include + +#include "../../dprint.h" +#include "../../ut.h" +#include "../../cfg_core.h" +#include "../../tcp_server.h" +#include "../../forward.h" + +#include "msrp_env.h" +#include "msrp_netio.h" +#include "msrp_options.h" + +/** + * + */ + +static char * G_BUFF = NULL; /* wired buffer*/ +static msrp_options_t opts = MSRP_OPTIONS_DEFAULT; + +short init_frame_rw_buffer() +{ + if (G_BUFF) + { + LM_WARN("Cannot initialize msrp frame buffer more than once!!"); + return -1; + } + if (set_msrp_options_on_buffer(&G_BUFF)!=0) + { + LM_CRIT(" memory alloc failure!! "); + return -1; + } + get_current_msrp_options(&opts); + LM_DBG(" Initialized!! "); + return 0; +} + +int msrp_forward_frame(msrp_frame_t *mf, int flags) +{ +#if 0 + if ((msrp_uri_to_dstinfo(0, &dst, uac_r->dialog->send_sock, snd_flags, + uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) || + (dst.send_sock==0)){ + LOG(L_ERR, "no send socket found\n"); + return -1; + } +#endif + return 0; +} + +/** + * + */ +int msrp_send_buffer(str *buf, str *addr, int flags) +{ + return 0; +} + +/** + * + */ +int msrp_relay(msrp_frame_t *mf) +{ + struct dest_info *dst; + struct tcp_connection *con = NULL; + msrp_hdr_t *tpath; + msrp_hdr_t *fpath; + msrp_env_t *env; + str_array_t *sar; + char *p; + char *l; + int port; + + if (unlikely(!G_BUFF)) + { + if (unlikely(init_frame_rw_buffer() != 0)) + { + LM_CRIT("Failed initializing msrp_frame!!"); + return -1; + } + } + + if(mf->buf.len>=GET_MSRP_MAX_FRAME_SIZE(&opts)-3) + { + LM_ERR("Incoming buffer length[%u] exceeds internal capacity[%u]!!", mf->buf.len, + GET_MSRP_MAX_FRAME_SIZE(&opts)-3); + return -1; + } + + + tpath = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH); + if(tpath==NULL) + { + LM_ERR("To-Path header not found\n"); + return -1; + } + fpath = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH); + if(fpath==NULL) + { + LM_ERR("From-Path header not found\n"); + return -1; + } + + l = q_memchr(tpath->body.s, ' ', tpath->body.len); + if(l==NULL) + { + LM_DBG("To-Path has only one URI -- nowehere to forward\n"); + return -1; + } + + p = G_BUFF; + + memcpy(p, mf->buf.s, tpath->body.s - mf->buf.s); + p += tpath->body.s - mf->buf.s; + + memcpy(p, l + 1, fpath->body.s - l - 1); + p += fpath->body.s - l - 1; + + memcpy(p, tpath->body.s, l + 1 - tpath->body.s); + p += l + 1 - tpath->body.s; + + memcpy(p, fpath->name.s + 11, mf->buf.s + mf->buf.len - fpath->name.s - 11); + p += mf->buf.s + mf->buf.len - fpath->name.s - 11; + + env = msrp_get_env(); + if(env->envflags&MSRP_ENV_DSTINFO) + { + dst = &env->dstinfo; + goto done; + } + if(msrp_parse_hdr_to_path(mf)<0) + { + LM_ERR("error parsing To-Path header\n"); + return -1; + } + sar = (str_array_t*)tpath->parsed.data; + if(sar==NULL || sar->size<2) + { + LM_DBG("To-Path has no next hop URI -- nowehere to forward\n"); + return -1; + } + if(msrp_env_set_dstinfo(mf, &sar->list[1], NULL, 0)<0) + { + LM_ERR("unable to set destination address\n"); + return -1; + } + dst = &env->dstinfo; +done: + + LM_DBG("Relay: Sending frame on wire : size [%d]...", (int)(p - G_BUFF)); + if (dst->send_flags.f & SND_F_FORCE_CON_REUSE) + { + port = su_getport(&dst->to); + if (likely(port)) + { + ticks_t con_lifetime; + struct ip_addr ip; + + con_lifetime = cfg_get(tcp, tcp_cfg, con_lifetime); + su2ip_addr(&ip, &dst->to); + con = tcpconn_get(dst->id, &ip, port, NULL, con_lifetime); + } + else if (likely(dst->id)) + { + con = tcpconn_get(dst->id, 0, 0, 0, 0); + } + + if (con == NULL) + { + LM_WARN("TCP/TLS connection not found\n"); + return -1; + } + + if (unlikely((con->rcv.proto == PROTO_WS || con->rcv.proto == PROTO_WSS) + && sr_event_enabled(SREV_TCP_WS_FRAME_OUT))) { + ws_event_info_t wsev; + + memset(&wsev, 0, sizeof(ws_event_info_t)); + wsev.type = SREV_TCP_WS_FRAME_OUT; + wsev.buf = G_BUFF; + wsev.len = p - G_BUFF; + wsev.id = con->id; + return sr_event_exec(SREV_TCP_WS_FRAME_OUT, (void *) &wsev); + } + else if (tcp_send(dst, 0, G_BUFF, p - G_BUFF) < 0) { + LM_ERR("forwarding frame failed\n"); + return -1; + } + } + else if (tcp_send(dst, 0, G_BUFF, p - G_BUFF) < 0) { + LM_ERR("forwarding frame failed\n"); + return -1; + } + + LM_DBG("Relay: Sent frame OK : [%d]!!!", (int)(p - G_BUFF)); + return 0; +} + +/** + * + */ +int msrp_reply(msrp_frame_t *mf, str *code, str *text, str *xhdrs) +{ + msrp_hdr_t *hdr; + msrp_env_t *env; + char *p; + char *l; + + /* no reply for a reply */ + if(mf->fline.msgtypeid==MSRP_REPLY) + return 0; + + if(mf->fline.msgtypeid==MSRP_REQ_REPORT) + { + /* it does not take replies */ + return 0; + } + + if (unlikely(!G_BUFF)) + { + if (unlikely(init_frame_rw_buffer() != 0) ) + { + LM_CRIT("Failed initializing msrp_frame!!"); + return -1; + } + } + + p = G_BUFF; + memcpy(p, mf->fline.protocol.s, mf->fline.protocol.len); + p += mf->fline.protocol.len; + *p = ' '; p++; + memcpy(p, mf->fline.transaction.s, mf->fline.transaction.len); + p += mf->fline.transaction.len; + *p = ' '; p++; + memcpy(p, code->s, code->len); + p += code->len; + *p = ' '; p++; + memcpy(p, text->s, text->len); + p += text->len; + memcpy(p, "\r\n", 2); + p += 2; + memcpy(p, "To-Path: ", 9); + p += 9; + hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH); + if(hdr==NULL) + { + LM_ERR("From-Path header not found\n"); + return -1; + } + if(mf->fline.msgtypeid==MSRP_REQ_SEND) + { + l = q_memchr(hdr->body.s, ' ', hdr->body.len); + if(l==NULL) { + memcpy(p, hdr->body.s, hdr->body.len + 2); + p += hdr->body.len + 2; + } else { + memcpy(p, hdr->body.s, l - hdr->body.s); + p += l - hdr->body.s; + memcpy(p, "\r\n", 2); + p += 2; + } + } else { + memcpy(p, hdr->body.s, hdr->body.len + 2); + p += hdr->body.len + 2; + } + hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH); + if(hdr==NULL) + { + LM_ERR("To-Path header not found\n"); + return -1; + } + memcpy(p, "From-Path: ", 11); + p += 11; + l = q_memchr(hdr->body.s, ' ', hdr->body.len); + if(l==NULL) { + memcpy(p, hdr->body.s, hdr->body.len + 2); + p += hdr->body.len + 2; + } else { + memcpy(p, hdr->body.s, l - hdr->body.s); + p += l - hdr->body.s; + memcpy(p, "\r\n", 2); + p += 2; + } + hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_MESSAGE_ID); + if(hdr!=NULL) + { + memcpy(p, hdr->buf.s, hdr->buf.len); + p += hdr->buf.len; + } + + if(xhdrs!=NULL && xhdrs->s!=NULL) + { + memcpy(p, xhdrs->s, xhdrs->len); + p += xhdrs->len; + } + + memcpy(p, mf->endline.s, mf->endline.len); + p += mf->endline.len; + *(p-3) = '$'; + + env = msrp_get_env(); + + if (unlikely((env->srcinfo.proto == PROTO_WS + || env->srcinfo.proto == PROTO_WSS) + && sr_event_enabled(SREV_TCP_WS_FRAME_OUT))) { + struct tcp_connection *con = tcpconn_get(env->srcinfo.id, 0, 0, + 0, 0); + ws_event_info_t wsev; + + if (con == NULL) + { + LM_WARN("TCP/TLS connection for WebSocket could not be" + "found\n"); + return -1; + } + + memset(&wsev, 0, sizeof(ws_event_info_t)); + wsev.type = SREV_TCP_WS_FRAME_OUT; + wsev.buf = G_BUFF; + wsev.len = p - G_BUFF; + wsev.id = con->id; + return sr_event_exec(SREV_TCP_WS_FRAME_OUT, (void *) &wsev); + } + else + if (tcp_send(&env->srcinfo, 0, G_BUFF, p - G_BUFF) < 0) { + LM_ERR("sending reply failed\n"); + return -1; + } + + return 0; +} + + +/** + * + */ +struct dest_info *msrp_uri_to_dstinfo(struct dns_srv_handle* dns_h, + struct dest_info* dst, struct socket_info *force_send_socket, + snd_flags_t sflags, str *uri) +{ + msrp_uri_t parsed_uri; + str* host; + int port; + int ip_found; + union sockaddr_union to; + int err; + + init_dest_info(dst); + + if (msrp_parse_uri(uri->s, uri->len, &parsed_uri) < 0) { + LM_ERR("bad msrp uri: %.*s\n", uri->len, uri->s ); + return 0; + } + + if (parsed_uri.scheme_no==MSRP_SCHEME_MSRPS){ + dst->proto = PROTO_TLS; + } else { + dst->proto = PROTO_TCP; + } + + dst->send_flags=sflags; + host=&parsed_uri.host; + port = parsed_uri.port_no; + + if (dns_h && cfg_get(core, core_cfg, use_dns_failover)){ + ip_found=0; + do{ + /* try all the ips until we find a good send socket */ + err=dns_sip_resolve2su(dns_h, &to, host, + port, &dst->proto, dns_flags); + if (err!=0){ + if (ip_found==0){ + if (err!=-E_DNS_EOR) + LM_ERR("failed to resolve \"%.*s\" :" + "%s (%d)\n", host->len, ZSW(host->s), + dns_strerror(err), err); + return 0; /* error, no ip found */ + } + break; + } + if (ip_found==0){ + dst->to=to; + ip_found=1; + } + dst->send_sock = get_send_socket2(force_send_socket, &to, + dst->proto, 0); + if (dst->send_sock){ + dst->to=to; + return dst; /* found a good one */ + } + } while(dns_srv_handle_next(dns_h, err)); + ERR("no corresponding socket for \"%.*s\" af %d\n", host->len, + ZSW(host->s), dst->to.s.sa_family); + /* try to continue */ + return dst; + } + + if (sip_hostport2su(&dst->to, host, port, &dst->proto)!=0){ + ERR("failed to resolve \"%.*s\"\n", host->len, ZSW(host->s)); + return 0; + } + dst->send_sock = get_send_socket2(force_send_socket, &dst->to, + dst->proto, 0); + if (dst->send_sock==0) { + ERR("no corresponding socket for af %d\n", dst->to.s.sa_family); + /* try to continue */ + } + return dst; +} + +struct socket_info *msrp_get_local_socket(str *sockaddr) +{ + int port, proto; + str host; + char backup; + struct socket_info *si; + + backup = sockaddr->s[sockaddr->len]; + sockaddr->s[sockaddr->len] = '\0'; + if (parse_phostport(sockaddr->s, &host.s, &host.len, &port, &proto) < 0) + { + LM_ERR("invalid socket specification\n"); + sockaddr->s[sockaddr->len] = backup; + return NULL; + } + sockaddr->s[sockaddr->len] = backup; + si = grep_sock_info(&host, (unsigned short)port, (unsigned short)proto); + return si; +} diff --git a/msrp_netio.h b/msrp_netio.h new file mode 100644 index 00000000000..2748cccbd3b --- /dev/null +++ b/msrp_netio.h @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com) + * + * This file is part of Kamailio, a free SIP server. + * + * This file 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 + * + * + * This file 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _MSRP_NETIO_H_ +#define _MSRP_NETIO_H_ + +#include "../../dns_cache.h" + +#include "msrp_parser.h" +#include "msrp_options.h" + +int msrp_forward_frame(msrp_frame_t *mf, int flags); +int msrp_send_buffer(str *buf, str *addr, int flags); + +int msrp_relay(msrp_frame_t *mf); +int msrp_reply(msrp_frame_t *mf, str *code, str *text, str *xhdrs); + +struct dest_info *msrp_uri_to_dstinfo(struct dns_srv_handle* dns_h, + struct dest_info* dst, struct socket_info *force_send_socket, + snd_flags_t sflags, str *uri); +struct socket_info *msrp_get_local_socket(str *sockaddr); +short init_frame_rw_buffer(); +#endif diff --git a/msrp_options.c b/msrp_options.c new file mode 100644 index 00000000000..d24a7c83fdd --- /dev/null +++ b/msrp_options.c @@ -0,0 +1,75 @@ +/* V1.1 T.Ntamane controls file xfer size*/ +#include +#include "../../mem/mem.h" +#include "msrp_options.h" + + +static msrp_options_t current_msrp_options = MSRP_OPTIONS_DEFAULT; +static int optionsSet = 0; + +int init_msrp_options(const msrp_options_t *const opts) +{ + + if (optionsSet) + { + LM_ERR("MSRP options already set peviously!!"); + return -1; + } + + if (opts) + { + memcpy(¤t_msrp_options, opts, sizeof(msrp_options_t)); + ++optionsSet; + } + return 0; +} + +void get_current_msrp_options(msrp_options_t *const opts) +{ + + if (opts) memcpy(opts, ¤t_msrp_options, sizeof(msrp_options_t)); +} + +void msrp_print_options() +{ + LM_INFO("CURRENT msrp_options: max hdr_size[%u]: max_body_size[%u] uses_sys_mem[%c]", + current_msrp_options.msrp_frame_hdrs_max_size, + current_msrp_options.msrp_frame_body_max_size, + current_msrp_options.msrp_frame_use_sys_mem ? 'Y' : 'N'); +} + +int set_msrp_options_on_buffer(char **buffer) +{ + + unsigned int max_frame_size; + if (!buffer ) + { + LM_CRIT("null buffer received!!"); + return -1; + } + + if (!optionsSet) + { + LM_WARN("Running on default options :max hdr [%u] max body [%u] use_sys_mem[%c]", + current_msrp_options.msrp_frame_hdrs_max_size, + current_msrp_options.msrp_frame_body_max_size, + current_msrp_options.msrp_frame_use_sys_mem ? 'Y' : 'N' + ); + } + max_frame_size = GET_MSRP_MAX_FRAME_SIZE(¤t_msrp_options); + + if (current_msrp_options.msrp_frame_use_sys_mem) + { + *buffer = calloc(1, max_frame_size); + } + else + { + *buffer = pkg_malloc(max_frame_size); + } + if (!*buffer) + { + LM_CRIT("Failed to allocate %s memory for buffer!!", current_msrp_options.msrp_frame_use_sys_mem ? "SYSTEM" : "PKG"); + return -1; + } + return 0; +} diff --git a/msrp_options.h b/msrp_options.h new file mode 100644 index 00000000000..303214bddef --- /dev/null +++ b/msrp_options.h @@ -0,0 +1,21 @@ +/* V1.1 T.Ntamane controls file xfer size*/ +#ifndef MSRP_OPTIONS +#define MSRP_OPTIONS + +#include "msrp_parser.h" + +typedef struct +{ + unsigned int msrp_frame_hdrs_max_size; + unsigned int msrp_frame_body_max_size; + unsigned short msrp_frame_use_sys_mem; /*1 = SYSTEM 0 = PKG*/ +}msrp_options_t; + +#define MSRP_OPTIONS_DEFAULT {MSRP_MAX_HDRS_SIZE, MSRP_MAX_BODY_SIZE, 0}; +#define GET_MSRP_MAX_FRAME_SIZE(msrp_opt) ((msrp_opt)->msrp_frame_hdrs_max_size + (msrp_opt)->msrp_frame_body_max_size) + +int init_msrp_options(const msrp_options_t *const opts); +void get_current_msrp_options(msrp_options_t *const opts); +int set_msrp_options_on_buffer(char **buffer); +void msrp_print_options(); +#endif diff --git a/ro_session_hash.h b/ro_session_hash.h new file mode 100644 index 00000000000..705b8b26137 --- /dev/null +++ b/ro_session_hash.h @@ -0,0 +1,244 @@ +/* + * File: ro_session_hash.h + * Author: Jason Penton + * + * Created on 07 April 2011, 4:12 PM + */ + +#ifndef RO_SESSION_HASH_H +#define RO_SESSION_HASH_H + +#include "ro_timer.h" +#include "../../mem/shm_mem.h" +#include "../../rand/kam_rand.h" +#include "ims_charging_stats.h" +#include + + +/* ro session flags */ +#define RO_SESSION_FLAG_NEW (1<<0) /*!< new ro session */ +#define RO_SESSION_FLAG_INSERTED (1<<1) /*!< session has been written to DB */ +#define RO_SESSION_FLAG_CHANGED (1<<2) /*!< ro session has been updated */ +#define RO_SESSION_FLAG_DELETED (1<<3) /*!< ro session has been deleted */ + +#define MAX_PANI_LEN 100 + +extern struct ims_charging_counters_h ims_charging_cnts_h; + +enum ro_session_event_type { + pending, + answered, + no_more_credit, + delayed_delete, + unknown_error, +}; + +struct diameter_avp_value { + str mac; +}; + +//used to pass data into dialog callbacks + +struct impu_data { + str identity; + str contact; +} impu_data_t; + +struct ro_session { + volatile int ref; + int direction; + struct ro_session* next; + struct ro_session* prev; + str ro_session_id; + str callid; + str asserted_identity; + str called_asserted_identity; + str incoming_trunk_id; + str outgoing_trunk_id; + str pani; + unsigned int hop_by_hop; + struct ro_tl ro_tl; + unsigned int reserved_secs; + unsigned int valid_for; + unsigned int dlg_h_entry; + unsigned int dlg_h_id; + unsigned int h_entry; + unsigned int h_id; + time_t start_time; + time_t last_event_timestamp; + time_t last_event_timestamp_backup; + enum ro_session_event_type event_type; + int auth_appid; + int auth_session_type; + int active; + unsigned int flags; + str mac; + int rating_group; + int service_identifier; + unsigned int is_final_allocation; + long billed; + unsigned int ccr_sent; +}; + +/*! entries in the main ro_session table */ +struct ro_session_entry { + struct ro_session *first; /*!< dialog list */ + struct ro_session *last; /*!< optimisation, end of the dialog list */ + unsigned int next_id; /*!< next id */ + unsigned int lock_idx; /*!< lock index */ +}; + +/*! main ro_sesion table */ +struct ro_session_table { + unsigned int size; /*!< size of the dialog table */ + struct ro_session_entry *entries; /*!< dialog hash table */ + unsigned int locks_no; /*!< number of locks */ + gen_lock_set_t *locks; /*!< lock table */ +}; + + +/*! global ro_session table */ +extern struct ro_session_table *ro_session_table; + + +/*! + * \brief Set a ro_session lock + * \param _table ro_session table + * \param _entry locked entry + */ +#define ro_session_lock(_table, _entry) \ + { LM_DBG("LOCKING %d", (_entry)->lock_idx); lock_set_get( (_table)->locks, (_entry)->lock_idx); LM_DBG("LOCKED %d", (_entry)->lock_idx);} + + +/*! + * \brief Release a ro_session lock + * \param _table ro_session table + * \param _entry locked entry + */ +#define ro_session_unlock(_table, _entry) \ + { LM_DBG("UNLOCKING %d", (_entry)->lock_idx); lock_set_release( (_table)->locks, (_entry)->lock_idx); LM_DBG("UNLOCKED %d", (_entry)->lock_idx); } + +/*! + * \brief Reference an ro_session without locking + * \param _ro_session Ro Session + * \param _cnt increment for the reference counter + */ +#define ref_ro_session_unsafe(_session,_cnt) \ + do { \ + (_session)->ref += (_cnt); \ + LM_DBG("ref ro_session %p with %d -> %d (tl=%p)\n", \ + (_session),(_cnt),(_session)->ref,&(_session)->ro_tl); \ + }while(0) + + +/*! + * \brief Unreference an ro_session without locking + * \param _ro_session Ro Session + * \param _cnt decrement for the reference counter + */ +#define unref_ro_session_unsafe(_ro_session,_cnt,_ro_session_entry) \ + do { \ + (_ro_session)->ref -= (_cnt); \ + LM_DBG("unref ro_session %p with %d -> %d (tl=%p)\n",\ + (_ro_session),(_cnt),(_ro_session)->ref,&(_ro_session)->ro_tl);\ + if ((_ro_session)->ref<0) {\ + LM_CRIT("bogus ref for session id < 0 [%d]\n",(_ro_session)->ref);\ + }\ + if ((_ro_session)->ref<=0) { \ + unlink_unsafe_ro_session( _ro_session_entry, _ro_session);\ + LM_DBG("ref <=0 for ro_session %p\n",_ro_session);\ + put_ro_session_on_wait(_ro_session);\ + }\ + }while(0) + +/*! + * \brief Unlink a ro_session from the list without locking + * \see unref_ro_session_unsafe + * \param ro_session_entry unlinked entry + * \param ro_session unlinked ro_session + */ + +static inline void unlink_unsafe_ro_session(struct ro_session_entry *ro_session_entry, struct ro_session *ro_session) { + /*---------------------------------------------------------------\ + |This entry is NOT linked...do not delink as it corrupts the list| + \---------------------------------------------------------------*/ + if ((ro_session->next == 0x00) && + (ro_session->prev == 0x00) && + (ro_session != ro_session_entry->first) ) + { + LM_DBG("Skipping possible corrupter: ro_session %p ro_tl %p, CallID %*.*s", + ro_session, &(ro_session->ro_tl), + ro_session->callid.len, + ro_session->callid.len, + ro_session->callid.s); + return; + } + if (ro_session->next) + ro_session->next->prev = ro_session->prev; + else + ro_session_entry->last = ro_session->prev; + if (ro_session->prev) + ro_session->prev->next = ro_session->next; + else + ro_session_entry->first = ro_session->next; + + ro_session->next = ro_session->prev = 0; + + counter_add(ims_charging_cnts_h.active_ro_sessions, -1); + + return; +} + +/*! + * \brief Initialize the global ro_session table + * \param size size of the table + * \return 0 on success, -1 on failure + */ +int init_ro_session_table(unsigned int size); + +/*! + * \brief Destroy the ro_session dialog table + */ +void destroy_ro_session(struct ro_session *ro_session); + + +/*! + * \brief Link a ro_session structure + * \param ro_session Ro Session + * \param n extra increments for the reference counter + */ +void link_ro_session(struct ro_session *ro_session, int n); + +void remove_aaa_session(str *session_id); + +struct ro_session* build_new_ro_session(int direction, int auth_appid, int auth_session_type, str *session_id, str *callid, str *asserted_identity, str* called_asserted_identity, + str* mac, unsigned int dlg_h_entry, unsigned int dlg_h_id, unsigned int requested_secs, unsigned int validity_timeout, + int active_rating_group, int active_service_identifier, str *incoming_trunk_id, str *outgoing_trunk_id, str *pani); + +/*! + * \brief Refefence a ro_session with locking + * \see ref_ro_session_unsafe + * \param ro_session Ro Session + * \param cnt increment for the reference counter + */ +void ref_ro_session_helper(struct ro_session *ro_session, unsigned int cnt, unsigned int mustlock, char *fname, int fline); +#define ref_ro_session(ro_session, cnt, mustlock) ref_ro_session_helper(ro_session, cnt, mustlock, __FILE__, __LINE__) + +/*! + * \brief Unreference a ro_session with locking + * \see unref_ro_session_unsafe + * \param ro_session Ro Session + * \param cnt decrement for the reference counter + */ +void unref_ro_session_helper(struct ro_session *ro_session, unsigned int cnt, unsigned int mustlock, char *fname, int fline); +#define unref_ro_session(ro_session, cnt, mustlock) unref_ro_session_helper(ro_session, cnt, mustlock, __FILE__, __LINE__) + +struct ro_session* lookup_ro_session(unsigned int h_entry, str *callid, int direction, unsigned int *del); + +void free_impu_data(struct impu_data *impu_data); + +int put_ro_session_on_wait(struct ro_session* session); + + +#endif /* RO_SESSION_HASH_H */ +