mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1239 lines
30 KiB
1239 lines
30 KiB
/*
|
|
* Copyright (C) 2001-2004 FhG Fokus
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
/*!
|
|
* \file
|
|
* \brief Route & Record-Route module, loose routing support
|
|
* \ingroup rr
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
#include "../../ut.h"
|
|
#include "../../str.h"
|
|
#include "../../dprint.h"
|
|
#include "../../forward.h"
|
|
#include "../../data_lump.h"
|
|
#include "../../socket_info.h"
|
|
#include "../../parser/parse_rr.h"
|
|
#include "../../parser/parse_uri.h"
|
|
#include "../../parser/parse_from.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../dset.h"
|
|
#include "loose.h"
|
|
#include "rr_cb.h"
|
|
#include "rr_mod.h"
|
|
|
|
|
|
#define RR_ERROR -1 /*!< An error occurred while processing route set */
|
|
#define RR_DRIVEN 1 /*!< The next hop is determined from the route set */
|
|
#define RR_OB_DRIVEN 2 /*!< The next hop is determined from the route set based on flow-token */
|
|
#define NOT_RR_DRIVEN -1 /*!< The next hop is not determined from the route set */
|
|
#define FLOW_TOKEN_BROKEN -2 /*!< Outbound flow-token shows evidence of tampering */
|
|
|
|
#define RR_ROUTE_PREFIX ROUTE_PREFIX "<"
|
|
#define RR_ROUTE_PREFIX_LEN (sizeof(RR_ROUTE_PREFIX)-1)
|
|
|
|
#define ROUTE_SUFFIX ">\r\n" /*!< SIP header suffix */
|
|
#define ROUTE_SUFFIX_LEN (sizeof(ROUTE_SUFFIX)-1)
|
|
|
|
/*! variables used to hook the param part of the local route */
|
|
static unsigned int routed_msg_id = 0;
|
|
static int routed_msg_pid = 0;
|
|
static str routed_params = {0,0};
|
|
|
|
|
|
/*!
|
|
* \brief Test whether we are processing pre-loaded route set by looking at the To tag
|
|
* \param msg SIP message
|
|
* \return -1 on failure, 0 on success
|
|
*/
|
|
static int is_preloaded(struct sip_msg* msg)
|
|
{
|
|
str tag;
|
|
|
|
if (!msg->to && parse_headers(msg, HDR_TO_F, 0) == -1) {
|
|
LM_ERR("failed to parse To header field\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!msg->to) {
|
|
LM_ERR("To header field not found\n");
|
|
return -1;
|
|
}
|
|
|
|
tag = get_to(msg)->tag_value;
|
|
if (tag.s == 0 || tag.len == 0) {
|
|
LM_DBG("is_preloaded: Yes\n");
|
|
return 1;
|
|
}
|
|
|
|
LM_DBG("is_preloaded: No\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Parse the message and find first occurrence of Route header field.
|
|
* \param _m SIP message
|
|
* \return -1 or -2 on a parser error, 0 if there is a Route HF and 1 if there is no Route HF
|
|
*/
|
|
static inline int find_first_route(struct sip_msg* _m)
|
|
{
|
|
if (parse_headers(_m, HDR_ROUTE_F, 0) == -1) {
|
|
LM_ERR("failed to parse headers\n");
|
|
return -1;
|
|
} else {
|
|
if (_m->route) {
|
|
if (parse_rr(_m->route) < 0) {
|
|
LM_ERR("failed to parse Route HF\n");
|
|
return -2;
|
|
}
|
|
return 0;
|
|
} else {
|
|
LM_DBG("No Route headers found\n");
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Check if URI is myself
|
|
* \param _host host
|
|
* \param _port port
|
|
* \return 0 if the URI is not myself, 1 otherwise
|
|
*/
|
|
static inline int is_myself(sip_uri_t *_puri)
|
|
{
|
|
int ret;
|
|
|
|
ret = check_self(&_puri->host,
|
|
_puri->port_no?_puri->port_no:SIP_PORT, 0);/* match all protos*/
|
|
if (ret < 0) return 0;
|
|
|
|
#ifdef ENABLE_USER_CHECK
|
|
if(ret==1 && i_user.len && i_user.len==_puri->user.len
|
|
&& strncmp(i_user.s, _puri->user.s, _puri->user.len)==0)
|
|
{
|
|
LM_DBG("ignore user matched - URI is not to the server itself\n");
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if(ret==1) {
|
|
/* match on host:port, but if gruu, then fail */
|
|
if(_puri->gr.s!=NULL)
|
|
return 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Find and parse next Route header field
|
|
* \param _m SIP message
|
|
* \param _hdr SIP header
|
|
* \return negative on failure, 0 if the Route header was already parsed, 1 if no next
|
|
* Route header could be found
|
|
*/
|
|
static inline int find_next_route(struct sip_msg* _m, struct hdr_field** _hdr)
|
|
{
|
|
struct hdr_field* ptr;
|
|
|
|
ptr = (*_hdr)->next;
|
|
|
|
/* Try to find already parsed Route headers */
|
|
while(ptr) {
|
|
if (ptr->type == HDR_ROUTE_T) goto found;
|
|
ptr = ptr->next;
|
|
}
|
|
|
|
/* There are no already parsed Route headers, try to find next
|
|
* occurrence of Route header
|
|
*/
|
|
if (parse_headers(_m, HDR_ROUTE_F, 1) == -1) {
|
|
LM_ERR("failed to parse headers\n");
|
|
return -1;
|
|
}
|
|
|
|
if ((_m->last_header->type!=HDR_ROUTE_T) || (_m->last_header==*_hdr)) {
|
|
LM_DBG("No next Route HF found\n");
|
|
return 1;
|
|
}
|
|
|
|
ptr = _m->last_header;
|
|
|
|
found:
|
|
if (parse_rr(ptr) < 0) {
|
|
LM_ERR("failed to parse Route body\n");
|
|
return -2;
|
|
}
|
|
|
|
*_hdr = ptr;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Check if the given uri contains lr parameter which marks loose routers
|
|
* \param _params URI string
|
|
* \return 1 if URI contains no lr parameter, 0 if it contains a lr parameter
|
|
*/
|
|
static inline int is_strict(str* _params)
|
|
{
|
|
str s;
|
|
int i, state = 0;
|
|
|
|
if (_params->len == 0) return 1;
|
|
|
|
s.s = _params->s;
|
|
s.len = _params->len;
|
|
|
|
for(i = 0; i < s.len; i++) {
|
|
switch(state) {
|
|
case 0:
|
|
switch(s.s[i]) {
|
|
case ' ':
|
|
case '\r':
|
|
case '\n':
|
|
case '\t': break;
|
|
case 'l':
|
|
case 'L': state = 1; break;
|
|
default: state = 4; break;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
switch(s.s[i]) {
|
|
case 'r':
|
|
case 'R': state = 2; break;
|
|
default: state = 4; break;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
switch(s.s[i]) {
|
|
case ';': return 0;
|
|
case '=': return 0;
|
|
case ' ':
|
|
case '\r':
|
|
case '\n':
|
|
case '\t': state = 3; break;
|
|
default: state = 4; break;
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
switch(s.s[i]) {
|
|
case ';': return 0;
|
|
case '=': return 0;
|
|
case ' ':
|
|
case '\r':
|
|
case '\n':
|
|
case '\t': break;
|
|
default: state = 4; break;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
switch(s.s[i]) {
|
|
case '\"': state = 5; break;
|
|
case ';': state = 0; break;
|
|
default: break;
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
switch(s.s[i]) {
|
|
case '\\': state = 6; break;
|
|
case '\"': state = 4; break;
|
|
default: break;
|
|
}
|
|
break;
|
|
|
|
case 6: state = 5; break;
|
|
}
|
|
}
|
|
|
|
if ((state == 2) || (state == 3)) return 0;
|
|
else return 1;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Set Request-URI as last Route header of a SIP
|
|
*
|
|
* Set Request-URI as last Route header of a SIP message,
|
|
* this is necessary when forwarding to a strict router.
|
|
* Allocates memory for message lump in private memory.
|
|
* \param _m SIP message
|
|
* \return negative on failure, 0 on success
|
|
*/
|
|
static inline int save_ruri(struct sip_msg* _m)
|
|
{
|
|
struct lump* anchor;
|
|
char *s;
|
|
int len;
|
|
|
|
/* We must parse the whole message header here, because
|
|
* the Request-URI must be saved in last Route HF in the message
|
|
*/
|
|
if (parse_headers(_m, HDR_EOH_F, 0) == -1) {
|
|
LM_ERR("failed to parse message\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Create an anchor */
|
|
anchor = anchor_lump(_m, _m->unparsed - _m->buf, 0, 0);
|
|
if (anchor == 0) {
|
|
LM_ERR("failed to get anchor\n");
|
|
return -2;
|
|
}
|
|
|
|
/* Create buffer for new lump */
|
|
len = RR_ROUTE_PREFIX_LEN + _m->first_line.u.request.uri.len
|
|
+ ROUTE_SUFFIX_LEN;
|
|
s = (char*)pkg_malloc(len);
|
|
if (!s) {
|
|
LM_ERR("No memory pkg left\n");
|
|
return -3;
|
|
}
|
|
|
|
/* Create new header field */
|
|
memcpy(s, RR_ROUTE_PREFIX, RR_ROUTE_PREFIX_LEN);
|
|
memcpy(s + RR_ROUTE_PREFIX_LEN, _m->first_line.u.request.uri.s,
|
|
_m->first_line.u.request.uri.len);
|
|
memcpy(s + RR_ROUTE_PREFIX_LEN + _m->first_line.u.request.uri.len,
|
|
ROUTE_SUFFIX, ROUTE_SUFFIX_LEN);
|
|
|
|
LM_DBG("New header: '%.*s'\n", len, ZSW(s));
|
|
|
|
/* Insert it */
|
|
if (insert_new_lump_before(anchor, s, len, 0) == 0) {
|
|
pkg_free(s);
|
|
LM_ERR("failed to insert lump\n");
|
|
return -4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Parse URI and check if it has a maddr parameter
|
|
* \param uri URI to be checked if it has maddr, and also the output URI
|
|
* \param puri parsed URI
|
|
* \return -1 on failure, 0 on success
|
|
*/
|
|
static inline int get_maddr_uri(str *uri, struct sip_uri *puri)
|
|
{
|
|
/* The max length of the maddr parameter is 127 */
|
|
static char builturi[127+1];
|
|
struct sip_uri turi;
|
|
|
|
if(uri==NULL || uri->s==NULL)
|
|
return RR_ERROR;
|
|
if(puri==NULL)
|
|
{
|
|
if (parse_uri(uri->s, uri->len, &turi) < 0)
|
|
{
|
|
LM_ERR("failed to parse the URI\n");
|
|
return RR_ERROR;
|
|
}
|
|
puri = &turi;
|
|
}
|
|
|
|
if(puri->maddr.s==NULL)
|
|
return 0;
|
|
|
|
/* sip: + maddr + : + port */
|
|
if( (puri->maddr_val.len) > (127 - 10) )
|
|
{
|
|
LM_ERR( "Too long maddr parameter\n");
|
|
return RR_ERROR;
|
|
}
|
|
memcpy( builturi, "sip:", 4 );
|
|
memcpy( builturi+4, puri->maddr_val.s, puri->maddr_val.len );
|
|
|
|
if( puri->port.len > 0 )
|
|
{
|
|
builturi[4+puri->maddr_val.len] =':';
|
|
memcpy(builturi+5+puri->maddr_val.len, puri->port.s,
|
|
puri->port.len);
|
|
}
|
|
|
|
uri->len = 4+puri->maddr_val.len
|
|
+ ((puri->port.len>0)?(1+puri->port.len):0);
|
|
builturi[uri->len]='\0';
|
|
uri->s = builturi;
|
|
|
|
LM_DBG("uri is %s\n", builturi );
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Necessary logic to forward request to strict routers
|
|
* \param _m SIP message
|
|
* \param _hdr SIP header field
|
|
* \param _r Route & Record-Route header field body
|
|
* \return 0 on success, negative on an error
|
|
*/
|
|
static inline int handle_sr(struct sip_msg* _m, struct hdr_field* _hdr, rr_t* _r)
|
|
{
|
|
str uri;
|
|
char* rem_off;
|
|
int rem_len;
|
|
|
|
/* Next hop is strict router, save R-URI here */
|
|
if (save_ruri(_m) < 0) {
|
|
LM_ERR("failed to save Request-URI\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Put the first Route in Request-URI */
|
|
uri = _r->nameaddr.uri;
|
|
if(get_maddr_uri(&uri, 0)!=0) {
|
|
LM_ERR("failed to check maddr\n");
|
|
return RR_ERROR;
|
|
}
|
|
if (rewrite_uri(_m, &uri) < 0) {
|
|
LM_ERR("failed to rewrite request URI\n");
|
|
return -2;
|
|
}
|
|
|
|
if (!_r->next) {
|
|
rem_off = _hdr->name.s;
|
|
rem_len = _hdr->len;
|
|
} else {
|
|
rem_off = _hdr->body.s;
|
|
rem_len = _r->next->nameaddr.name.s - _hdr->body.s;
|
|
}
|
|
|
|
if (!del_lump(_m, rem_off - _m->buf, rem_len, 0)) {
|
|
LM_ERR("failed to remove Route HF\n");
|
|
return -9;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Find last route in the last Route header field
|
|
*
|
|
* Find last route in the last Route header field, if there was a previous
|
|
* route in the last Route header field, it will be saved in _p parameter
|
|
* \param _m SIP message
|
|
* \param _h SIP header field
|
|
* \param _l Route & Record-Route header field body
|
|
* \param _p Route & Record-Route header field body
|
|
* \return negative on failure, 0 on success
|
|
*/
|
|
static inline int find_rem_target(struct sip_msg* _m, struct hdr_field** _h, rr_t** _l, rr_t** _p)
|
|
{
|
|
struct hdr_field* ptr, *last;
|
|
|
|
if (parse_headers(_m, HDR_EOH_F, 0) == -1) {
|
|
LM_ERR("failed to parse message header\n");
|
|
return -1;
|
|
}
|
|
|
|
ptr = _m->route;
|
|
last = 0;
|
|
|
|
while(ptr) {
|
|
if (ptr->type == HDR_ROUTE_T) last = ptr;
|
|
ptr = ptr->next;
|
|
}
|
|
|
|
if (last) {
|
|
if (parse_rr(last) < 0) {
|
|
LM_ERR("failed to parse last Route HF\n");
|
|
return -2;
|
|
}
|
|
|
|
*_p = 0;
|
|
*_l = (rr_t*)last->parsed;
|
|
*_h = last;
|
|
while ((*_l)->next) {
|
|
*_p = *_l;
|
|
*_l = (*_l)->next;
|
|
}
|
|
return 0;
|
|
} else {
|
|
LM_ERR("search for last Route HF failed\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Largest route URI is of the form:
|
|
sip:[1234:5678:9012:3456:7890:1234:5678:9012]:12345;transport=sctp
|
|
this is 66 characters long */
|
|
#define MAX_ROUTE_URI_LEN 66
|
|
static char uri_buf[MAX_ROUTE_URI_LEN];
|
|
|
|
/*!
|
|
* \brief Perform outbound processing - force local socket and set destination URI
|
|
* \param _m SIP message
|
|
* \param flow_token string containing the flow-token extracted from the Route: header
|
|
* \param dst_uri string to write the destination URI to (extracted from flow-token)
|
|
* \return -1 on error, 0 when outbound not in use, 1 when outbound in use
|
|
*/
|
|
static inline int process_outbound(struct sip_msg *_m, str flow_token)
|
|
{
|
|
int ret;
|
|
struct receive_info *rcv = NULL;
|
|
struct socket_info *si;
|
|
str dst_uri;
|
|
|
|
if (!rr_obb.decode_flow_token)
|
|
return 0;
|
|
|
|
ret = rr_obb.decode_flow_token(_m, &rcv, flow_token);
|
|
|
|
if (ret == -2) {
|
|
LM_DBG("no flow token found - outbound not in use\n");
|
|
return 0;
|
|
} else if (ret == -1) {
|
|
LM_INFO("failed to decode flow token\n");
|
|
return -1;
|
|
} else if (!ip_addr_cmp(&rcv->src_ip, &_m->rcv.src_ip)
|
|
|| rcv->src_port != _m->rcv.src_port) {
|
|
LM_DBG("\"incoming\" request found. Using flow-token for"
|
|
"routing\n");
|
|
|
|
/* First, force the local socket */
|
|
si = find_si(&rcv->dst_ip, rcv->dst_port, rcv->proto);
|
|
if (si)
|
|
set_force_socket(_m, si);
|
|
else {
|
|
LM_INFO("cannot find socket from flow-token\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Second, override the destination URI */
|
|
dst_uri.s = uri_buf;
|
|
dst_uri.len = 0;
|
|
|
|
dst_uri.len += snprintf(dst_uri.s + dst_uri.len,
|
|
MAX_ROUTE_URI_LEN - dst_uri.len,
|
|
"sip:%s",
|
|
rcv->src_ip.af == AF_INET6 ? "[" : "");
|
|
dst_uri.len += ip_addr2sbuf(&rcv->src_ip,
|
|
dst_uri.s + dst_uri.len,
|
|
MAX_ROUTE_URI_LEN - dst_uri.len);
|
|
dst_uri.len += snprintf(dst_uri.s + dst_uri.len,
|
|
MAX_ROUTE_URI_LEN - dst_uri.len,
|
|
"%s:%d;transport=%s",
|
|
rcv->src_ip.af == AF_INET6 ? "]" : "",
|
|
rcv->src_port,
|
|
get_proto_name(rcv->proto));
|
|
|
|
if (set_dst_uri(_m, &dst_uri) < 0) {
|
|
LM_ERR("failed to set dst_uri\n");
|
|
return -1;
|
|
}
|
|
ruri_mark_new();
|
|
|
|
return 1;
|
|
}
|
|
|
|
LM_DBG("Not using flow-token for routing\n");
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
* \brief Previous hop was a strict router, handle this case
|
|
* \param _m SIP message
|
|
* \return -1 on error, 1 on success
|
|
*/
|
|
static inline int after_strict(struct sip_msg* _m)
|
|
{
|
|
int res, rem_len;
|
|
struct hdr_field* hdr;
|
|
struct sip_uri puri;
|
|
rr_t* rt, *prev;
|
|
char* rem_off;
|
|
str uri;
|
|
struct socket_info *si;
|
|
|
|
hdr = _m->route;
|
|
rt = (rr_t*)hdr->parsed;
|
|
uri = rt->nameaddr.uri;
|
|
|
|
/* reset rr handling static vars for safety in error case */
|
|
routed_msg_id = 0;
|
|
routed_msg_pid = 0;
|
|
routed_params.s = NULL;
|
|
routed_params.len = 0;
|
|
|
|
if (parse_uri(uri.s, uri.len, &puri) < 0) {
|
|
LM_ERR("failed to parse the first route URI\n");
|
|
return RR_ERROR;
|
|
}
|
|
|
|
if ( enable_double_rr && is_2rr(&puri.params) && is_myself(&puri)) {
|
|
/* double route may occure due different IP and port, so force as
|
|
* send interface the one advertise in second Route */
|
|
si = grep_sock_info( &puri.host, puri.port_no, puri.proto);
|
|
if (si) {
|
|
set_force_socket(_m, si);
|
|
} else {
|
|
if (enable_socket_mismatch_warning)
|
|
LM_WARN("no socket found for match second RR\n");
|
|
}
|
|
|
|
if (!rt->next) {
|
|
/* No next route in the same header, remove the whole header
|
|
* field immediately
|
|
*/
|
|
if (!del_lump(_m, hdr->name.s - _m->buf, hdr->len, 0)) {
|
|
LM_ERR("failed to remove Route HF\n");
|
|
return RR_ERROR;
|
|
}
|
|
res = find_next_route(_m, &hdr);
|
|
if (res < 0) {
|
|
LM_ERR("searching next route failed\n");
|
|
return RR_ERROR;
|
|
}
|
|
if (res > 0) { /* No next route found */
|
|
LM_DBG("after_strict: No next URI found\n");
|
|
return NOT_RR_DRIVEN;
|
|
}
|
|
rt = (rr_t*)hdr->parsed;
|
|
} else rt = rt->next;
|
|
|
|
/* parse the new found uri */
|
|
uri = rt->nameaddr.uri;
|
|
if (parse_uri(uri.s, uri.len, &puri) < 0) {
|
|
LM_ERR("failed to parse URI\n");
|
|
return RR_ERROR;
|
|
}
|
|
}
|
|
|
|
/* set the hooks for the param
|
|
* important note: RURI is already parsed by the above function, so
|
|
* we just used it without any checking */
|
|
routed_msg_id = _m->id;
|
|
routed_msg_pid = _m->pid;
|
|
routed_params = _m->parsed_uri.params;
|
|
|
|
if (is_strict(&puri.params)) {
|
|
LM_DBG("Next hop: '%.*s' is strict router\n", uri.len, ZSW(uri.s));
|
|
/* Previous hop was a strict router and the next hop is strict
|
|
* router too. There is no need to save R-URI again because it
|
|
* is saved already. In fact, in this case we will behave exactly
|
|
* like a strict router. */
|
|
|
|
/* Note: when there is only one Route URI left (endpoint), it will
|
|
* always be a strict router because endpoints don't use ;lr parameter
|
|
* In this case we will simply put the URI in R-URI and forward it,
|
|
* which will work perfectly */
|
|
if(get_maddr_uri(&uri, &puri)!=0) {
|
|
LM_ERR("failed to check maddr\n");
|
|
return RR_ERROR;
|
|
}
|
|
if (rewrite_uri(_m, &uri) < 0) {
|
|
LM_ERR("failed to rewrite request URI\n");
|
|
return RR_ERROR;
|
|
}
|
|
|
|
if (rt->next) {
|
|
rem_off = hdr->body.s;
|
|
rem_len = rt->next->nameaddr.name.s - hdr->body.s;
|
|
} else {
|
|
rem_off = hdr->name.s;
|
|
rem_len = hdr->len;
|
|
}
|
|
if (!del_lump(_m, rem_off - _m->buf, rem_len, 0)) {
|
|
LM_ERR("failed to remove Route HF\n");
|
|
return RR_ERROR;
|
|
}
|
|
} else {
|
|
LM_DBG("Next hop: '%.*s' is loose router\n",
|
|
uri.len, ZSW(uri.s));
|
|
|
|
if(get_maddr_uri(&uri, &puri)!=0) {
|
|
LM_ERR("failed to check maddr\n");
|
|
return RR_ERROR;
|
|
}
|
|
if (set_dst_uri(_m, &uri) < 0) {
|
|
LM_ERR("failed to set dst_uri\n");
|
|
return RR_ERROR;
|
|
}
|
|
|
|
/* Next hop is a loose router - Which means that is is not endpoint yet
|
|
* In This case we have to recover from previous strict routing, that
|
|
* means we have to find the last Route URI and put in in R-URI and
|
|
* remove the last Route URI. */
|
|
if (rt != hdr->parsed) {
|
|
/* There is a previous route uri which was 2nd uri of mine
|
|
* and must be removed here */
|
|
rem_off = hdr->body.s;
|
|
rem_len = rt->nameaddr.name.s - hdr->body.s;
|
|
if (!del_lump(_m, rem_off - _m->buf, rem_len, 0)) {
|
|
LM_ERR("failed to remove Route HF\n");
|
|
return RR_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
res = find_rem_target(_m, &hdr, &rt, &prev);
|
|
if (res < 0) {
|
|
LM_ERR("searching for last Route URI failed\n");
|
|
return RR_ERROR;
|
|
} else if (res > 0) {
|
|
/* No remote target is an error */
|
|
return RR_ERROR;
|
|
}
|
|
|
|
uri = rt->nameaddr.uri;
|
|
if(get_maddr_uri(&uri, 0)!=0) {
|
|
LM_ERR("checking maddr failed\n");
|
|
return RR_ERROR;
|
|
}
|
|
if (rewrite_uri(_m, &uri) < 0) {
|
|
LM_ERR("failed to rewrite R-URI\n");
|
|
return RR_ERROR;
|
|
}
|
|
|
|
/* The first character if uri will be either '<' when it is the
|
|
* only URI in a Route header field or ',' if there is more than
|
|
* one URI in the header field */
|
|
LM_DBG("The last route URI: '%.*s'\n", rt->nameaddr.uri.len,
|
|
ZSW(rt->nameaddr.uri.s));
|
|
|
|
if (prev) {
|
|
rem_off = prev->nameaddr.name.s + prev->len;
|
|
rem_len = rt->nameaddr.name.s + rt->len - rem_off;
|
|
} else {
|
|
rem_off = hdr->name.s;
|
|
rem_len = hdr->len;
|
|
}
|
|
if (!del_lump(_m, rem_off - _m->buf, rem_len, 0)) {
|
|
LM_ERR("failed to remove Route HF\n");
|
|
return RR_ERROR;
|
|
}
|
|
}
|
|
|
|
/* run RR callbacks only if we have Route URI parameters */
|
|
if(routed_params.len > 0)
|
|
run_rr_callbacks( _m, &routed_params );
|
|
|
|
return RR_DRIVEN;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Previous hop was a loose router, handle this case
|
|
* \param _m SIP message
|
|
* \param preloaded do we have a preloaded route set
|
|
* \return -1 on failure, 1 on success
|
|
*/
|
|
static inline int after_loose(struct sip_msg* _m, int preloaded)
|
|
{
|
|
struct hdr_field* hdr;
|
|
struct sip_uri puri;
|
|
rr_t* rt;
|
|
int res;
|
|
int status = RR_DRIVEN;
|
|
str uri;
|
|
struct socket_info *si;
|
|
int uri_is_myself;
|
|
int use_ob = 0;
|
|
str rparams;
|
|
|
|
hdr = _m->route;
|
|
rt = (rr_t*)hdr->parsed;
|
|
uri = rt->nameaddr.uri;
|
|
|
|
/* reset rr handling static vars for safety in error case */
|
|
routed_msg_id = 0;
|
|
routed_msg_pid = 0;
|
|
|
|
if (parse_uri(uri.s, uri.len, &puri) < 0) {
|
|
LM_ERR("failed to parse the first route URI (%.*s)\n",
|
|
uri.len, ZSW(uri.s));
|
|
return RR_ERROR;
|
|
}
|
|
|
|
routed_params = puri.params;
|
|
uri_is_myself = is_myself(&puri);
|
|
|
|
/* IF the URI was added by me, remove it */
|
|
if (uri_is_myself>0)
|
|
{
|
|
LM_DBG("Topmost route URI: '%.*s' is me\n",
|
|
uri.len, ZSW(uri.s));
|
|
/* set the hooks for the params */
|
|
routed_msg_id = _m->id;
|
|
routed_msg_pid = _m->pid;
|
|
|
|
if ((use_ob = process_outbound(_m, puri.user)) < 0) {
|
|
LM_INFO("failed to process outbound flow-token\n");
|
|
return FLOW_TOKEN_BROKEN;
|
|
}
|
|
|
|
if (!rt->next) {
|
|
/* No next route in the same header, remove the whole header
|
|
* field immediately
|
|
*/
|
|
if (!del_lump(_m, hdr->name.s - _m->buf, hdr->len, 0)) {
|
|
LM_ERR("failed to remove Route HF\n");
|
|
return RR_ERROR;
|
|
}
|
|
|
|
res = find_next_route(_m, &hdr);
|
|
if (res < 0) {
|
|
LM_ERR("failed to find next route\n");
|
|
return RR_ERROR;
|
|
}
|
|
if (res > 0) { /* No next route found */
|
|
LM_DBG("No next URI found\n");
|
|
status = (preloaded ? NOT_RR_DRIVEN : RR_DRIVEN);
|
|
goto done;
|
|
}
|
|
rt = (rr_t*)hdr->parsed;
|
|
} else rt = rt->next;
|
|
|
|
if (enable_double_rr && is_2rr(&puri.params)) {
|
|
/* double route may occure due different IP and port, so force as
|
|
* send interface the one advertise in second Route */
|
|
if (parse_uri(rt->nameaddr.uri.s,rt->nameaddr.uri.len,&puri)<0) {
|
|
LM_ERR("failed to parse the double route URI (%.*s)\n",
|
|
rt->nameaddr.uri.len, ZSW(rt->nameaddr.uri.s));
|
|
return RR_ERROR;
|
|
}
|
|
|
|
if (!use_ob) {
|
|
if ((si = grep_sock_info( &puri.host, puri.port_no?puri.port_no:proto_default_port(puri.proto), puri.proto)) != 0) {
|
|
set_force_socket(_m, si);
|
|
} else if ((si = grep_sock_info( &puri.host, puri.port_no, puri.proto)) != 0) {
|
|
set_force_socket(_m, si);
|
|
} else {
|
|
if (enable_socket_mismatch_warning) {
|
|
LM_WARN("no socket found to match second RR (%.*s)\n",
|
|
rt->nameaddr.uri.len, ZSW(rt->nameaddr.uri.s));
|
|
if(!is_myself(&puri)) {
|
|
LM_WARN("second RR uri is not myself (%.*s)\n",
|
|
rt->nameaddr.uri.len, ZSW(rt->nameaddr.uri.s));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rt->next) {
|
|
/* No next route in the same header, remove the whole header
|
|
* field immediately */
|
|
if (!del_lump(_m, hdr->name.s - _m->buf, hdr->len, 0)) {
|
|
LM_ERR("failed to remove Route HF\n");
|
|
return RR_ERROR;
|
|
}
|
|
res = find_next_route(_m, &hdr);
|
|
if (res < 0) {
|
|
LM_ERR("failed to find next route\n");
|
|
return RR_ERROR;
|
|
}
|
|
if (res > 0) { /* No next route found */
|
|
LM_DBG("no next URI found\n");
|
|
status = (preloaded ? NOT_RR_DRIVEN : RR_DRIVEN);
|
|
goto done;
|
|
}
|
|
rt = (rr_t*)hdr->parsed;
|
|
} else rt = rt->next;
|
|
}
|
|
|
|
uri = rt->nameaddr.uri;
|
|
if (parse_uri(uri.s, uri.len, &puri) < 0) {
|
|
LM_ERR("failed to parse the next route URI (%.*s)\n",
|
|
uri.len, ZSW(uri.s));
|
|
return RR_ERROR;
|
|
}
|
|
} else {
|
|
#ifdef ENABLE_USER_CHECK
|
|
/* check if it the ignored user */
|
|
if(uri_is_myself < 0)
|
|
return NOT_RR_DRIVEN;
|
|
#endif
|
|
LM_DBG("Topmost URI is NOT myself\n");
|
|
routed_params.s = NULL;
|
|
routed_params.len = 0;
|
|
}
|
|
|
|
LM_DBG("URI to be processed: '%.*s'\n", uri.len, ZSW(uri.s));
|
|
if (is_strict(&puri.params)) {
|
|
LM_DBG("Next URI is a strict router\n");
|
|
if (handle_sr(_m, hdr, rt) < 0) {
|
|
LM_ERR("failed to handle strict router\n");
|
|
return RR_ERROR;
|
|
}
|
|
} else {
|
|
/* Next hop is loose router */
|
|
LM_DBG("Next URI is a loose router\n");
|
|
|
|
if (!use_ob) {
|
|
if(get_maddr_uri(&uri, &puri)!=0) {
|
|
LM_ERR("checking maddr failed\n");
|
|
return RR_ERROR;
|
|
}
|
|
|
|
if (set_dst_uri(_m, &uri) < 0) {
|
|
LM_ERR("failed to set dst_uri\n");
|
|
return RR_ERROR;
|
|
}
|
|
/* dst_uri changed, so it makes sense to re-use the current uri for
|
|
forking */
|
|
ruri_mark_new(); /* re-use uri for serial forking */
|
|
}
|
|
|
|
/* There is a previous route uri which was 2nd uri of mine
|
|
* and must be removed here */
|
|
if (rt != hdr->parsed) {
|
|
if (!del_lump(_m, hdr->body.s - _m->buf,
|
|
rt->nameaddr.name.s - hdr->body.s, 0)) {
|
|
LM_ERR("failed to remove Route HF\n");
|
|
return RR_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (use_ob == 1)
|
|
status = RR_OB_DRIVEN;
|
|
|
|
/* run RR callbacks only if we have Route URI parameters */
|
|
if(routed_params.len > 0) {
|
|
rparams = routed_params;
|
|
run_rr_callbacks( _m, &rparams );
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Do loose routing as per RFC3261
|
|
* \param _m SIP message
|
|
* \return -1 on failure, 1 on success
|
|
*/
|
|
int loose_route(struct sip_msg* _m)
|
|
{
|
|
int ret;
|
|
|
|
if (find_first_route(_m) != 0) {
|
|
LM_DBG("There is no Route HF\n");
|
|
return -1;
|
|
}
|
|
|
|
if (parse_sip_msg_uri(_m)<0) {
|
|
LM_ERR("failed to parse Request URI\n");
|
|
return -1;
|
|
}
|
|
|
|
ret = is_preloaded(_m);
|
|
if (ret < 0) {
|
|
return -1;
|
|
} else if (ret == 1) {
|
|
return after_loose(_m, 1);
|
|
} else {
|
|
if (is_myself(&_m->parsed_uri)) {
|
|
return after_strict(_m);
|
|
} else {
|
|
return after_loose(_m, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int redo_route_params(sip_msg_t *msg)
|
|
{
|
|
hdr_field_t *hdr;
|
|
sip_uri_t puri;
|
|
rr_t* rt;
|
|
str uri;
|
|
int uri_is_myself;
|
|
|
|
int redo = 0;
|
|
|
|
if(msg->first_line.type != SIP_REQUEST) {
|
|
return -1;
|
|
}
|
|
|
|
if(msg->route==NULL || msg->route->parsed==NULL) {
|
|
return -1;
|
|
}
|
|
|
|
/* check if the hooked params belong to the same message */
|
|
if (routed_msg_id != msg->id || routed_msg_pid != msg->pid) {
|
|
redo = 1;
|
|
}
|
|
if((redo==0) && (routed_params.s==NULL || routed_params.len<=0)) {
|
|
redo = 1;
|
|
}
|
|
if((redo==0) && (routed_params.s<msg->buf
|
|
|| routed_params.s>msg->buf+msg->len)) {
|
|
redo = 1;
|
|
}
|
|
if(redo==1) {
|
|
hdr = msg->route;
|
|
rt = (rr_t*)hdr->parsed;
|
|
uri = rt->nameaddr.uri;
|
|
|
|
/* reset rr handling static vars for safety in error case */
|
|
routed_msg_id = 0;
|
|
routed_msg_pid = 0;
|
|
|
|
if (parse_uri(uri.s, uri.len, &puri) < 0) {
|
|
LM_ERR("failed to parse the first route URI (%.*s)\n",
|
|
uri.len, ZSW(uri.s));
|
|
return -1;
|
|
}
|
|
|
|
uri_is_myself = is_myself(&puri);
|
|
|
|
/* if the URI was added by me, remove it */
|
|
if (uri_is_myself>0) {
|
|
LM_DBG("Topmost route URI: '%.*s' is me\n",
|
|
uri.len, ZSW(uri.s));
|
|
/* set the hooks for the params */
|
|
routed_msg_id = msg->id;
|
|
routed_msg_pid = msg->pid;
|
|
routed_params = puri.params;
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
* \brief Check if the route hdr has the required parameter
|
|
*
|
|
* The function checks for the request "msg" if the URI parameters
|
|
* of the local Route header (corresponding to the local server)
|
|
* matches the given regular expression "re". It must be call
|
|
* after the loose_route was done.
|
|
*
|
|
* \param msg SIP message request that will has the Route header parameters checked
|
|
* \param re compiled regular expression to be checked against the Route header parameters
|
|
* \return -1 on failure, 1 on success
|
|
*/
|
|
int check_route_param(sip_msg_t * msg, regex_t* re)
|
|
{
|
|
regmatch_t pmatch;
|
|
char bk;
|
|
str params;
|
|
str rruri;
|
|
|
|
/* check if the hooked params belong to the same message */
|
|
if(redo_route_params(msg)<0) {
|
|
return -1;
|
|
}
|
|
|
|
/* check if params are present */
|
|
if ( !routed_params.s || routed_params.len<=0 ) {
|
|
return -1;
|
|
}
|
|
rruri = ((rr_t*)(msg->route->parsed))->nameaddr.uri;
|
|
|
|
/* include also the first ';' */
|
|
for( params=routed_params ;
|
|
params.s>rruri.s && params.s[0]!=';' ;
|
|
params.s--,params.len++ );
|
|
|
|
LM_DBG("route params checking against [%.*s] (orig: [%.*s])\n",
|
|
params.len, params.s, routed_params.len, routed_params.s);
|
|
|
|
/* do the well-known trick to convert to null terminted */
|
|
bk = params.s[params.len];
|
|
params.s[params.len] = 0;
|
|
LM_DBG("params are <%s>\n", params.s);
|
|
if (regexec( re, params.s, 1, &pmatch, 0)!=0) {
|
|
params.s[params.len] = bk;
|
|
return -1;
|
|
} else {
|
|
params.s[params.len] = bk;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Gets the value of a route parameter
|
|
*
|
|
* The function search in to the "msg"'s Route header parameters
|
|
* the parameter called "name" and returns its value into "val".
|
|
* It must be call only after the loose_route is done.
|
|
*
|
|
* \param msg - request that will have the Route header parameter searched
|
|
* \param name - contains the Route header parameter to be serached
|
|
* \param val returns the value of the searched Route header parameter if found.
|
|
* It might be an empty string if the parameter had no value.
|
|
* \return 0 if parameter was found (even if it has no value), -1 otherwise
|
|
*/
|
|
int get_route_param(sip_msg_t *msg, str *name, str *val)
|
|
{
|
|
char *p;
|
|
char *end;
|
|
char c;
|
|
int quoted;
|
|
|
|
/* check if the hooked params belong to the same message */
|
|
if(redo_route_params(msg)<0) {
|
|
goto notfound;
|
|
}
|
|
|
|
/* check if params are present */
|
|
if ( !routed_params.s || routed_params.len<=0 )
|
|
goto notfound;
|
|
|
|
end = routed_params.s + routed_params.len;
|
|
p = routed_params.s;
|
|
|
|
|
|
/* parse the parameters string and find the "name" param */
|
|
while ( end-p>name->len+2 ) {
|
|
if (p!=routed_params.s) {
|
|
/* go to first ';' char */
|
|
for( quoted=0 ; p<end && !(*p==';' && !quoted) ; p++ )
|
|
if ( (*p=='"' || *p=='\'') && *(p-1)!='\\' )
|
|
quoted ^= 0x1;
|
|
if (p==end)
|
|
goto notfound;
|
|
p++;
|
|
}
|
|
/* get first non space char */
|
|
while( p<end && (*p==' ' || *p=='\t') )
|
|
p++;
|
|
/* check the name - length first and content after */
|
|
if ( end-p<name->len+2 )
|
|
goto notfound;
|
|
if ( memcmp(p,name->s,name->len)!=0 ) {
|
|
p++;
|
|
continue;
|
|
}
|
|
p+=name->len;
|
|
while( p<end && (*p==' ' || *p=='\t') )
|
|
p++;
|
|
if (p==end|| *p==';') {
|
|
/* empty val */
|
|
val->len = 0;
|
|
val->s = 0;
|
|
goto found;
|
|
}
|
|
if (*(p++)!='=')
|
|
continue;
|
|
while( p<end && (*p==' ' || *p=='\t') )
|
|
p++;
|
|
if (p==end)
|
|
goto notfound;
|
|
/* get value */
|
|
if ( *p=='\'' || *p=='"') {
|
|
for( val->s = ++p ; p<end ; p++) {
|
|
if ((*p=='"' || *p=='\'') && *(p-1)!='\\' )
|
|
break;
|
|
}
|
|
} else {
|
|
for( val->s=p ; p<end ; p++) {
|
|
if ( (c=*p)==';' || c==' ' || c=='\t' )
|
|
break;
|
|
}
|
|
}
|
|
val->len = p-val->s;
|
|
if (val->len==0)
|
|
val->s = 0;
|
|
goto found;
|
|
}
|
|
|
|
notfound:
|
|
return -1;
|
|
found:
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Check the direction of the message
|
|
*
|
|
* The function checks the flow direction of the request "msg". As
|
|
* for checking it's used the "ftag" Route header parameter, the
|
|
* append_fromtag module parameter must be enables.
|
|
* Also this must be call only after the loose_route is done.
|
|
|
|
* \param msg SIP message request that will have the direction checked
|
|
* \param dir direction to be checked against. It may be RR_FLOW_UPSTREAM or RR_FLOW_DOWNSTREAM
|
|
* \return 0 if the request flow direction is the same as the given direction, -1 otherwise
|
|
*/
|
|
int is_direction(struct sip_msg * msg, int dir)
|
|
{
|
|
static str ftag_param = {"ftag",4};
|
|
static unsigned int last_id = (unsigned int)-1;
|
|
static unsigned int last_dir = 0;
|
|
str ftag_val;
|
|
str tag;
|
|
|
|
if ( last_id==msg->id && last_dir!=0) {
|
|
if (last_dir==RR_FLOW_UPSTREAM)
|
|
goto upstream;
|
|
else
|
|
goto downstream;
|
|
}
|
|
|
|
ftag_val.s = 0;
|
|
ftag_val.len = 0;
|
|
|
|
if (get_route_param( msg, &ftag_param, &ftag_val)!=0) {
|
|
LM_DBG("param ftag not found\n");
|
|
goto downstream;
|
|
}
|
|
|
|
if ( ftag_val.s==0 || ftag_val.len==0 ) {
|
|
LM_DBG("param ftag has empty val\n");
|
|
goto downstream;
|
|
}
|
|
|
|
/* get the tag value from FROM hdr */
|
|
if ( parse_from_header(msg)!=0 )
|
|
goto downstream;
|
|
|
|
tag = ((struct to_body*)msg->from->parsed)->tag_value;
|
|
if (tag.s==0 || tag.len==0)
|
|
goto downstream;
|
|
|
|
/* compare the 2 strings */
|
|
if (tag.len!=ftag_val.len || memcmp(tag.s,ftag_val.s,ftag_val.len))
|
|
goto upstream;
|
|
|
|
downstream:
|
|
last_id = msg->id;
|
|
last_dir = RR_FLOW_DOWNSTREAM;
|
|
return (dir==RR_FLOW_DOWNSTREAM)?0:-1;
|
|
upstream:
|
|
last_id = msg->id;
|
|
last_dir = RR_FLOW_UPSTREAM;
|
|
return (dir==RR_FLOW_UPSTREAM)?0:-1;
|
|
}
|