src/modules/topos/tps_msg.c
68717c2a
 /**
  * Copyright (C) 2016 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
  */
 
 /*!
  * \file
  * \brief SIP-router topoh ::
  * \ingroup topoh
  * Module: \ref topoh
  */
 
 #include <string.h>
 
cf83221d
 #include "../../core/dprint.h"
 #include "../../core/mem/mem.h"
 #include "../../core/data_lump.h"
 #include "../../core/forward.h"
 #include "../../core/trim.h"
 #include "../../core/dset.h"
 #include "../../core/msg_translator.h"
 #include "../../core/parser/parse_rr.h"
 #include "../../core/parser/parse_uri.h"
 #include "../../core/parser/parse_param.h"
 #include "../../core/parser/parse_from.h"
 #include "../../core/parser/parse_to.h"
 #include "../../core/parser/parse_via.h"
 #include "../../core/parser/contact/parse_contact.h"
 #include "../../core/parser/parse_refer_to.h"
68717c2a
 #include "tps_msg.h"
1a16f11f
 #include "tps_storage.h"
68717c2a
 
 extern int _tps_param_mask_callid;
2ffc26aa
 extern int _tps_contact_mode;
 extern str _tps_cparam_name;
7b27bb50
 extern int _tps_update_rr_dialog;
68717c2a
 
787c41c0
 str _sr_hname_xbranch = str_init("P-SR-XBranch");
95572016
 str _sr_hname_xuuid = str_init("P-SR-XUID");
787c41c0
 
1a16f11f
 /**
  *
  */
68717c2a
 int tps_skip_rw(char *s, int len)
 {
 	while(len>0)
 	{
 		if(s[len-1]==' ' || s[len-1]=='\t' || s[len-1]=='\n' || s[len-1]=='\r'
 				|| s[len-1]==',')
 			len--;
 		else return len;
 	}
 	return 0;
 }
 
1a16f11f
 /**
  *
  */
68717c2a
 struct via_param *tps_get_via_param(struct via_body *via, str *name)
 {
 	struct via_param *p;
 	for(p=via->param_lst; p; p=p->next)
 	{
 		if(p->name.len==name->len
 				&& strncasecmp(p->name.s, name->s, name->len)==0)
 			return p;
 	}
 	return NULL;
 }
 
1a16f11f
 /**
  *
  */
68717c2a
 int tps_get_param_value(str *in, str *name, str *value)
 {
 	param_t* params = NULL;
 	param_t* p = NULL;
 	param_hooks_t phooks;
 	if (parse_params(in, CLASS_ANY, &phooks, &params)<0)
 		return -1;
 	for (p = params; p; p=p->next)
 	{
 		if (p->name.len==name->len
 				&& strncasecmp(p->name.s, name->s, name->len)==0)
 		{
 			*value = p->body;
 			free_params(params);
 			return 0;
 		}
 	}
56540799
 
68717c2a
 	if(params) free_params(params);
 	return 1;
 
 }
 
1a16f11f
 /**
  *
  */
 int tps_remove_headers(sip_msg_t *msg, uint32_t hdr)
 {
 	struct hdr_field *hf;
 	struct lump* l;
 
35d2d50c
 	if(parse_headers(msg, HDR_EOH_F, 0)<0) {
 		LM_ERR("failed to parse headers\n");
 		return -1;
 	}
1a16f11f
 	for (hf=msg->headers; hf; hf=hf->next) {
 		if (hdr!=hf->type)
 			continue;
 		l=del_lump(msg, hf->name.s-msg->buf, hf->len, 0);
 		if (l==0) {
5f780a20
 			LM_ERR("failed to remove the header\n");
1a16f11f
 			return -1;
 		}
 	}
 	return 0;
 }
 
 /**
  *
  */
 int tps_add_headers(sip_msg_t *msg, str *hname, str *hbody, int hpos)
 {
 	struct lump* anchor;
 	str hs;
 
9b4c0c29
 	if(hname==NULL || hname->len<=0 || hbody==NULL || hbody->len<=0)
 		return 0;
 
35d2d50c
 	if(parse_headers(msg, HDR_EOH_F, 0)<0) {
 		LM_ERR("failed to parse headers\n");
 		return -1;
 	}
 
1a16f11f
 	if(hpos == 0) { /* append */
 		/* after last header */
 		anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0);
 	} else { /* insert */
 		/* before first header */
 		anchor = anchor_lump(msg, msg->headers->name.s - msg->buf, 0, 0);
 	}
 
 	if(anchor == 0) {
 		LM_ERR("can't get anchor\n");
 		return -1;
 	}
 
 	hs.len = hname->len + 2 + hbody->len;
 	hs.s  = (char*)pkg_malloc(hs.len + 3);
 	if (hs.s==NULL) {
b06268f0
 		PKG_MEM_ERROR_FMT("(%.*s - %d)\n", hname->len, hname->s, hs.len);
1a16f11f
 		return -1;
 	}
 	memcpy(hs.s, hname->s, hname->len);
 	hs.s[hname->len] = ':';
 	hs.s[hname->len+1] = ' ';
 	memcpy(hs.s + hname->len + 2, hbody->s, hbody->len);
 
 	/* add end of header if not present */
b933550a
 	if(hs.s[hname->len + 2 + hbody->len - 1]!='\n') {
1a16f11f
 		hs.s[hname->len + 2 + hbody->len] = '\r';
 		hs.s[hname->len + 2 + hbody->len+1] = '\n';
 		hs.len += 2;
 	}
 
bcff1c15
 	LM_DBG("adding to headers(%d) - [%.*s]\n", hpos, hs.len, hs.s);
 
1a16f11f
 	if (insert_new_lump_before(anchor, hs.s, hs.len, 0) == 0) {
 		LM_ERR("can't insert lump\n");
 		pkg_free(hs.s);
 		return -1;
 	}
 
 	return 0;
 }
 
 /**
  *
  */
68717c2a
 int tps_get_uri_param_value(str *uri, str *name, str *value)
 {
 	struct sip_uri puri;
 
 	memset(value, 0, sizeof(str));
 	if(parse_uri(uri->s, uri->len, &puri)<0)
 		return -1;
 	return tps_get_param_value(&puri.params, name, value);
 }
 
1a16f11f
 /**
  *
  */
68717c2a
 int tps_get_uri_type(str *uri, int *mode, str *value)
 {
 	struct sip_uri puri;
 	int ret;
 	str r2 = {"r2", 2};
 
 	memset(value, 0, sizeof(str));
 	*mode = 0;
 	if(parse_uri(uri->s, uri->len, &puri)<0)
 		return -1;
 
 	LM_DBG("PARAMS [%.*s]\n", puri.params.len, puri.params.s);
 
 	if(check_self(&puri.host, puri.port_no, 0)==1)
 	{
 		/* myself -- matched on all protos */
 		ret = tps_get_param_value(&puri.params, &r2, value);
 		if(ret<0)
 			return -1;
 		if(ret==1) /* not found */
 			return 0; /* skip */
 		LM_DBG("VALUE [%.*s]\n",
 				value->len, value->s);
 		if(value->len==2 && strncasecmp(value->s, "on", 2)==0)
 			*mode = 1;
 		memset(value, 0, sizeof(str));
 		return 0; /* skip */
 	}
 	/* not myself & not mask ip */
 	return 1; /* encode */
 }
 
1a16f11f
 /**
  *
  */
68717c2a
 char* tps_msg_update(sip_msg_t *msg, unsigned int *olen)
 {
 	struct dest_info dst;
 
 	init_dest_info(&dst);
 	dst.proto = PROTO_UDP;
 	return build_req_buf_from_sip_req(msg,
 			olen, &dst, BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE);
 }
 
1a16f11f
 /**
  *
  */
68717c2a
 int tps_skip_msg(sip_msg_t *msg)
 {
 	if (msg->cseq==NULL || get_cseq(msg)==NULL) {
 		LM_WARN("Invalid/Unparsed CSeq in message. Skipping.");
 		return 1;
 	}
 
 	if((get_cseq(msg)->method_id)&(METHOD_REGISTER|METHOD_PUBLISH))
 		return 1;
 
 	return 0;
 }
 
639013a1
 /**
  *
  */
 int tps_dlg_detect_direction(sip_msg_t *msg, tps_data_t *ptsd,
 		uint32_t *direction)
 {
 	str ftag = {0, 0};
 	/* detect direction - get from-tag */
 	if(parse_from_header(msg)<0 || msg->from==NULL) {
 		LM_ERR("failed getting 'from' header!\n");
 		goto error;
 	}
 	ftag = get_from(msg)->tag_value;
 
 	if(ptsd->a_tag.len!=ftag.len) {
 		*direction = TPS_DIR_UPSTREAM;
 	} else {
 		if(memcmp(ptsd->a_tag.s, ftag.s, ftag.len)==0) {
 			*direction = TPS_DIR_DOWNSTREAM;
 		} else {
 			*direction = TPS_DIR_UPSTREAM;
 		}
 	}
 	return 0;
 
 error:
 	return -1;
 }
 
2cb59f17
 /**
  *
  */
2ffc26aa
 int tps_dlg_message_update(sip_msg_t *msg, tps_data_t *ptsd, int ctmode)
2cb59f17
 {
2ffc26aa
 	str tmp;
b44b2452
 	int ret;
2ffc26aa
 
2cb59f17
 	if(parse_sip_msg_uri(msg)<0) {
 		LM_ERR("failed to parse r-uri\n");
 		return -1;
 	}
2ffc26aa
 
 	if (ctmode == 1 || ctmode == 2) {
 		if(msg->parsed_uri.sip_params.len<10) {
 			LM_DBG("not an expected param format\n");
 			return 1;
 		}
b44b2452
 		// find parameter, there might be others
 		ret = tps_get_param_value(&msg->parsed_uri.params,
 			&_tps_cparam_name, &tmp);
 		if (ret < 0) {
 			LM_ERR("failed to parse param\n");
 			return -1;
 		}
 		if (ret == 1) {
 			LM_DBG("prefix para not found\n");
 			return 1;
 		}
2ffc26aa
 		if(memcmp(tmp.s, "atpsh-", 6)==0) {
 			ptsd->a_uuid = tmp;
 			return 0;
 		}
 		if(memcmp(tmp.s, "btpsh-", 6)==0) {
 			ptsd->a_uuid = tmp;
 			ptsd->b_uuid = tmp;
 			return 0;
 		}
 
 	} else {
 		if(msg->parsed_uri.user.len<10) {
 			LM_DBG("not an expected user format\n");
 			return 1;
 		}
 		if(memcmp(msg->parsed_uri.user.s, "atpsh-", 6)==0) {
 			ptsd->a_uuid = msg->parsed_uri.user;
 			return 0;
 		}
 		if(memcmp(msg->parsed_uri.user.s, "btpsh-", 6)==0) {
 			ptsd->a_uuid = msg->parsed_uri.user;
 			ptsd->b_uuid = msg->parsed_uri.user;
 			return 0;
 		}
2cb59f17
 	}
2ffc26aa
 	LM_DBG("not an expected prefix\n");
2cb59f17
 
 	return 1;
 }
 
1a16f11f
 /**
  *
  */
787c41c0
 int tps_pack_message(sip_msg_t *msg, tps_data_t *ptsd)
1a16f11f
 {
 	hdr_field_t *hdr;
 	via_body_t *via;
 	rr_t *rr;
 	int i;
 	int vlen;
a215b926
 	int r2;
24a792d2
 	int isreq;
7b27bb50
 	int update_rr_dialog = _tps_update_rr_dialog;
1a16f11f
 
 	if(ptsd->cp==NULL) {
 		ptsd->cp = ptsd->cbuf;
 	}
787c41c0
 
7b27bb50
 	if(_tps_update_rr_dialog) {
 		if((msg->first_line.type==SIP_REQUEST) && !(get_to(msg)->tag_value.len>0)) {
 			update_rr_dialog = 0;
 		}
 	}
 
1a16f11f
 	i = 0;
 	for(hdr=msg->h_via1; hdr; hdr=next_sibling_hdr(hdr)) {
 		for(via=(struct via_body*)hdr->parsed; via; via=via->next) {
 			i++;
 			vlen = tps_skip_rw(via->name.s, via->bsize);
 			if(ptsd->cp + vlen + 2 >= ptsd->cbuf + TPS_DATA_SIZE) {
a21ae6ea
 				LM_ERR("no more space to pack via headers\n");
1a16f11f
 				return -1;
 			}
 			if(i>1) {
 				*ptsd->cp = ',';
 				ptsd->cp++;
 				if(i>2) {
 					ptsd->x_via2.len++;
 				}
 			}
 			memcpy(ptsd->cp, via->name.s, vlen);
 			if(i==1) {
 				ptsd->x_via1.s = ptsd->cp;
 				ptsd->x_via1.len = vlen;
 				if(via->branch!=NULL) {
 					ptsd->x_vbranch1.s = ptsd->x_via1.s + (via->branch->value.s - via->name.s);
 					ptsd->x_vbranch1.len = via->branch->value.len;
 				}
 			} else {
 				if(i==2) {
 					ptsd->x_via2.s = ptsd->cp;
 				}
 				ptsd->x_via2.len += vlen;
 			}
 			ptsd->cp += vlen;
 		}
 	}
 	LM_DBG("compacted headers - x_via1: [%.*s](%d) - x_via2: [%.*s](%d)"
 			" - x_vbranch1: [%.*s](%d)\n",
 			ptsd->x_via1.len, ZSW(ptsd->x_via1.s), ptsd->x_via1.len,
 			ptsd->x_via2.len, ZSW(ptsd->x_via2.s), ptsd->x_via2.len,
 			ptsd->x_vbranch1.len, ZSW(ptsd->x_vbranch1.s), ptsd->x_vbranch1.len);
 
 	ptsd->a_rr.len = 0;
a215b926
 	ptsd->s_rr.len = 0;
dc4306e4
 	i = 0;
a215b926
 	r2 = 0;
24a792d2
 	isreq = (msg->first_line.type==SIP_REQUEST)?1:0;
1a16f11f
 	for(hdr=msg->record_route; hdr; hdr=next_sibling_hdr(hdr)) {
 		if (parse_rr(hdr) < 0) {
 			LM_ERR("failed to parse RR\n");
 			return -1;
 		}
 
 		for(rr =(rr_t*)hdr->parsed; rr; rr=rr->next) {
 			i++;
 			if(ptsd->cp + rr->nameaddr.uri.len + 4 >= ptsd->cbuf + TPS_DATA_SIZE) {
a21ae6ea
 				LM_ERR("no more space to pack rr headers\n");
1a16f11f
 				return -1;
 			}
7b27bb50
 			if(isreq==1 || update_rr_dialog) {
24a792d2
 				/* sip request - get a+s-side record route */
 				if(i>1) {
 					if(i==2 &&r2==0) {
 						ptsd->s_rr.len = ptsd->a_rr.len;
 					}
 					if(i==3 &&r2==1) {
 						ptsd->s_rr.len = ptsd->a_rr.len;
 					}
 					*ptsd->cp = ',';
 					ptsd->cp++;
 					ptsd->a_rr.len++;
7b27bb50
 					if(update_rr_dialog) {
 						ptsd->b_rr.len++;
 					}
 				}
 				if(i==1 && update_rr_dialog) {
 					ptsd->b_rr.s = ptsd->cp;
a215b926
 				}
24a792d2
 				*ptsd->cp = '<';
 				if(i==1) {
 					ptsd->a_rr.s = ptsd->cp;
 					ptsd->s_rr.s = ptsd->cp;
a215b926
 				}
24a792d2
 				if(i==2 && r2==0) {
 					ptsd->a_rr.s = ptsd->cp;
 					ptsd->a_rr.len = 0;
 				}
 				if(i==3 && r2==1) {
 					ptsd->a_rr.s = ptsd->cp;
 					ptsd->a_rr.len = 0;
 				}
 
1a16f11f
 				ptsd->cp++;
 				ptsd->a_rr.len++;
7b27bb50
 				if(update_rr_dialog) {
 					ptsd->b_rr.len++;
 				}
a215b926
 
24a792d2
 				memcpy(ptsd->cp, rr->nameaddr.uri.s, rr->nameaddr.uri.len);
 				if(i==1) {
 					ptsd->bs_contact.s = ptsd->cp;
 					ptsd->bs_contact.len = rr->nameaddr.uri.len;
 					if(_strnstr(ptsd->bs_contact.s, ";r2=on",
 								ptsd->bs_contact.len)==0) {
 						LM_DBG("single record routing by proxy\n");
 						ptsd->as_contact.s = ptsd->cp;
 						ptsd->as_contact.len = rr->nameaddr.uri.len;
 					} else {
 						r2 = 1;
 					}
a215b926
 				} else {
24a792d2
 					if(i==2 && ptsd->as_contact.len==0) {
 						LM_DBG("double record routing by proxy\n");
 						ptsd->as_contact.s = ptsd->cp;
 						ptsd->as_contact.len = rr->nameaddr.uri.len;
 					}
1a16f11f
 				}
24a792d2
 				ptsd->a_rr.len += rr->nameaddr.uri.len;
 				ptsd->cp += rr->nameaddr.uri.len;
 				*ptsd->cp = '>';
 				ptsd->cp++;
 				ptsd->a_rr.len++;
7b27bb50
 				if(update_rr_dialog) {
 					ptsd->b_rr.len += rr->nameaddr.uri.len;
 					ptsd->b_rr.len++;
 				}
1a16f11f
 			} else {
24a792d2
 				/* sip response - get b-side record route */
fec80f51
 				if(i==1) {
 					ptsd->b_rr.s = ptsd->cp;
 				}
 				if(i>1) {
 					*ptsd->cp = ',';
 					ptsd->cp++;
 					ptsd->b_rr.len++;
 				}
24a792d2
 				*ptsd->cp = '<';
 				ptsd->cp++;
 				ptsd->b_rr.len++;
 				memcpy(ptsd->cp, rr->nameaddr.uri.s, rr->nameaddr.uri.len);
 				ptsd->cp += rr->nameaddr.uri.len;
 				ptsd->b_rr.len += rr->nameaddr.uri.len;
 				*ptsd->cp = '>';
 				ptsd->cp++;
 				ptsd->b_rr.len++;
1a16f11f
 			}
 		}
dc4306e4
 	}
24a792d2
 	if(isreq==1) {
 		if(i==1) {
 			ptsd->s_rr.len = ptsd->a_rr.len;
 			ptsd->a_rr.len = 0;
 		}
 		if(i==2 && r2==1) {
 			ptsd->s_rr.len = ptsd->a_rr.len;
 			ptsd->a_rr.len = 0;
 		}
1a16f11f
 	}
787c41c0
 	LM_DBG("compacted headers - a_rr: [%.*s](%d) - b_rr: [%.*s](%d)"
 			" - s_rr: [%.*s](%d)\n",
1a16f11f
 			ptsd->a_rr.len, ZSW(ptsd->a_rr.s), ptsd->a_rr.len,
787c41c0
 			ptsd->b_rr.len, ZSW(ptsd->b_rr.s), ptsd->b_rr.len,
 			ptsd->s_rr.len, ZSW(ptsd->s_rr.s), ptsd->s_rr.len);
1a16f11f
 	LM_DBG("compacted headers - as_contact: [%.*s](%d) - bs_contact: [%.*s](%d)\n",
 			ptsd->as_contact.len, ZSW(ptsd->as_contact.s), ptsd->as_contact.len,
 			ptsd->bs_contact.len, ZSW(ptsd->bs_contact.s), ptsd->bs_contact.len);
9b4c0c29
 	ptsd->x_rr = ptsd->a_rr;
1c7f3938
 	ptsd->s_method_id = get_cseq(msg)->method_id;
1a16f11f
 	return 0;
 }
 
9b4c0c29
 
1a16f11f
 /**
  *
  */
 int tps_reinsert_via(sip_msg_t *msg, tps_data_t *ptsd, str *hbody)
 {
 	str hname = str_init("Via");
56540799
 
1a16f11f
 	if(tps_add_headers(msg, &hname, hbody, 1)<0) {
 		return -1;
 	}
 
 	return 0;
 }
 
 /**
  *
  */
 int tps_reinsert_contact(sip_msg_t *msg, tps_data_t *ptsd, str *hbody)
 {
 	str hname = str_init("Contact");
56540799
 
1a16f11f
 	if(tps_add_headers(msg, &hname, hbody, 0)<0) {
 		return -1;
 	}
 
 	return 0;
 }
 
787c41c0
 /**
  *
  */
 int tps_remove_name_headers(sip_msg_t *msg, str *hname)
 {
 	hdr_field_t *hf;
 	struct lump* l;
 	for (hf=msg->headers; hf; hf=hf->next)
 	{
 		if (hf->name.len==hname->len
 				&& strncasecmp(hf->name.s, hname->s,
 					hname->len)==0)
 		{
 			l=del_lump(msg, hf->name.s-msg->buf, hf->len, 0);
 			if (l==0) {
 				LM_ERR("unable to delete header [%.*s]\n",
 						hname->len, hname->s);
 				return -1;
 			}
 			return 0;
 		}
 	}
 	return 0;
 }
 
9b4c0c29
 /**
  *
  */
 int tps_reappend_via(sip_msg_t *msg, tps_data_t *ptsd, str *hbody)
 {
 	str hname = str_init("Via");
 
 	if(tps_add_headers(msg, &hname, hbody, 0)<0) {
 		return -1;
 	}
 
 	return 0;
 }
 
787c41c0
 /**
  *
  */
 int tps_append_xbranch(sip_msg_t *msg, str *hbody)
 {
 	if(tps_add_headers(msg, &_sr_hname_xbranch, hbody, 0)<0) {
 		LM_ERR("failed to add xbranch header [%.*s]/%d\n",
 				hbody->len, hbody->s, hbody->len);
 		return -1;
 	}
 
 	return 0;
 }
 
 /**
  *
  */
 int tps_remove_xbranch(sip_msg_t *msg)
 {
 	return tps_remove_name_headers(msg, &_sr_hname_xbranch);
 }
 
 /**
  *
  */
 int tps_get_xbranch(sip_msg_t *msg, str *hbody)
 {
 	hdr_field_t *hf;
 	if(parse_headers(msg, HDR_EOH_F, 0)<0) {
 		return -1;
 	}
 
 	for (hf=msg->headers; hf; hf=hf->next)
 	{
 		if(_sr_hname_xbranch.len==hf->name.len
 				&& strncasecmp(_sr_hname_xbranch.s, hf->name.s,
 					hf->name.len)==0) {
 			break;
 		}
 	}
 	if(hf!=NULL) {
 		*hbody = hf->body;
 		return 0;
 	}
 	return -1;
 }
 
 
95572016
 /**
  *
  */
 int tps_append_xuuid(sip_msg_t *msg, str *hbody)
 {
 	if(tps_add_headers(msg, &_sr_hname_xuuid, hbody, 0)<0) {
 		LM_ERR("failed to add xuuid header [%.*s]/%d\n",
 				hbody->len, hbody->s, hbody->len);
 		return -1;
 	}
 
 	return 0;
 }
 
 /**
  *
  */
 int tps_remove_xuuid(sip_msg_t *msg)
 {
 	return tps_remove_name_headers(msg, &_sr_hname_xuuid);
 }
 
 /**
  *
  */
 int tps_get_xuuid(sip_msg_t *msg, str *hbody)
 {
 	hdr_field_t *hf;
 	if(parse_headers(msg, HDR_EOH_F, 0)<0) {
 		return -1;
 	}
 
 	for (hf=msg->headers; hf; hf=hf->next)
 	{
 		if(_sr_hname_xuuid.len==hf->name.len
 				&& strncasecmp(_sr_hname_xuuid.s, hf->name.s,
 					hf->name.len)==0) {
 			break;
 		}
 	}
 	if(hf!=NULL) {
 		*hbody = hf->body;
 		return 0;
 	}
 	return -1;
 }
 
9b4c0c29
 /**
  *
  */
 int tps_reappend_rr(sip_msg_t *msg, tps_data_t *ptsd, str *hbody)
 {
 	str hname = str_init("Record-Route");
 
 	if(tps_add_headers(msg, &hname, hbody, 0)<0) {
 		return -1;
 	}
 
 	return 0;
 }
 
2cb59f17
 /**
  *
  */
 int tps_reappend_route(sip_msg_t *msg, tps_data_t *ptsd, str *hbody, int rev)
 {
 	str hname = str_init("Route");
95572016
 	int i;
 	int c;
 	str sb;
 
5bc93940
 	if(hbody==NULL || hbody->s==NULL || hbody->len<=0 || hbody->s[0]=='\0')
95572016
 		return 0;
 
 	if(rev==1) {
 		c = 0;
 		sb.len = 1;
7c48b0fc
 		for(i=hbody->len-2; i>=0; i--) {
95572016
 			if(hbody->s[i]==',') {
 				c = 1;
 				if(sb.len>0) {
 					sb.s = hbody->s + i + 1;
65336276
 					if(sb.s[sb.len-1]==',') sb.len--;
95572016
 					if(tps_add_headers(msg, &hname, &sb, 0)<0) {
 						return -1;
 					}
 				}
 				sb.len = 0;
 			}
 			sb.len++;
 		}
 		if(c==1) {
 			if(sb.len>0) {
 				sb.s = hbody->s;
65336276
 				if(sb.s[sb.len-1]==',') sb.len--;
95572016
 				if(tps_add_headers(msg, &hname, &sb, 0)<0) {
 					return -1;
 				}
 			}
 			return 0;
 		}
 	}
2cb59f17
 
65336276
 	sb = *hbody;
5bc93940
 	if(sb.len>0 && sb.s[sb.len-1]==',') sb.len--;
 	trim_zeros_lr(&sb);
 	trim(&sb);
 	if(sb.len>0 && sb.s[sb.len-1]==',') sb.len--;
65336276
 	if(tps_add_headers(msg, &hname, &sb, 0)<0) {
2cb59f17
 		return -1;
 	}
 
 	return 0;
 }
 
a741f797
 /**
  *
  */
ca4494d4
 int tps_request_received(sip_msg_t *msg, int dialog)
a741f797
 {
2cb59f17
 	tps_data_t mtsd;
 	tps_data_t stsd;
 	str lkey;
 	str nuri;
 	uint32_t direction = TPS_DIR_DOWNSTREAM;
 	int ret;
37830f53
 	int use_branch = 0;
2cb59f17
 
ed262dfb
 	LM_DBG("handling incoming request\n");
 
a741f797
 	if(dialog==0) {
 		/* nothing to do for initial request */
 		return 0;
 	}
2cb59f17
 
 	memset(&mtsd, 0, sizeof(tps_data_t));
 	memset(&stsd, 0, sizeof(tps_data_t));
 
 	if(tps_pack_message(msg, &mtsd)<0) {
 		LM_ERR("failed to extract and pack the headers\n");
 		return -1;
 	}
 
2ffc26aa
 	ret = tps_dlg_message_update(msg, &mtsd, _tps_contact_mode);
2cb59f17
 	if(ret<0) {
 		LM_ERR("failed to update on dlg message\n");
 		return -1;
 	}
 
 	lkey = msg->callid->body;
 
 	tps_storage_lock_get(&lkey);
 
37830f53
 	if((get_cseq(msg)->method_id)&(METHOD_PRACK)) {
f4fa3104
 		if(tps_storage_link_msg(msg, &mtsd, TPS_DIR_DOWNSTREAM)<0) {
 			goto error;
 		}
4c5419d6
 		if(tps_storage_load_branch(msg, &mtsd, &stsd, 1)<0) {
37830f53
 			goto error;
 		}
 		use_branch = 1;
 	} else {
b3b95b5b
 		if(tps_storage_load_dialog(msg, &mtsd, &stsd) < 0) {
37830f53
 			goto error;
 		}
b3b95b5b
 		if(((get_cseq(msg)->method_id) & (METHOD_BYE))
 				&& stsd.b_contact.len <= 0) {
 			/* BYE but not B-side contact, look for INVITE transaction record */
 			memset(&stsd, 0, sizeof(tps_data_t));
 			if(tps_storage_link_msg(msg, &mtsd, TPS_DIR_DOWNSTREAM) < 0) {
 				goto error;
 			}
 			if(tps_storage_load_branch(msg, &mtsd, &stsd, 1) < 0) {
 				goto error;
 			}
 			use_branch = 1;
 		} else {
 			/* detect direction - via from-tag */
 			if(tps_dlg_detect_direction(msg, &stsd, &direction) < 0) {
 				goto error;
 			}
26c94b32
 		}
2cb59f17
 	}
 
 	mtsd.direction = direction;
 
 	tps_storage_lock_release(&lkey);
 
26c94b32
 	if(use_branch) {
7c48b0fc
 		nuri = stsd.b_contact;
26c94b32
 	} else {
 		if(direction == TPS_DIR_UPSTREAM) {
 			nuri = stsd.a_contact;
 		} else {
 			nuri = stsd.b_contact;
 		}
2cb59f17
 	}
 	if(nuri.len>0) {
 		if(rewrite_uri(msg, &nuri)<0) {
 			LM_ERR("failed to update r-uri\n");
 			return -1;
bcff1c15
 		} else {
 			LM_DBG("r-uri updated to: [%.*s]\n", nuri.len, nuri.s);
2cb59f17
 		}
 	}
 
26c94b32
 	if(use_branch) {
 		if(tps_reappend_route(msg, &stsd, &stsd.s_rr, 1) < 0) {
 			LM_ERR("failed to reappend s-route\n");
 			return -1;
 		}
 		if(tps_reappend_route(msg, &stsd, &stsd.y_rr, 1) < 0) {
 			LM_ERR("failed to reappend b-route\n");
 			return -1;
2cb59f17
 		}
 	} else {
26c94b32
 		if(tps_reappend_route(msg, &stsd, &stsd.s_rr,
 					(direction == TPS_DIR_UPSTREAM) ? 0 : 1) < 0) {
 			LM_ERR("failed to reappend s-route\n");
 			return -1;
 		}
 		if(direction == TPS_DIR_UPSTREAM) {
 			if(tps_reappend_route(msg, &stsd, &stsd.a_rr, 0) < 0) {
 				LM_ERR("failed to reappend a-route\n");
37830f53
 				return -1;
 			}
 		} else {
26c94b32
 			if(tps_reappend_route(msg, &stsd, &stsd.b_rr, 1) < 0) {
37830f53
 				LM_ERR("failed to reappend b-route\n");
 				return -1;
 			}
2cb59f17
 		}
 	}
95572016
 	if(dialog!=0) {
 		tps_append_xuuid(msg, &stsd.a_uuid);
7b27bb50
 		if(_tps_update_rr_dialog) {
 			if(tps_storage_update_dialog(msg, &mtsd, &stsd, TPS_DBU_RPLATTRS|TPS_DBU_BRR)<0) {
 				goto error;
 			}
 		}
95572016
 	}
a741f797
 	return 0;
2cb59f17
 
 error:
 	tps_storage_lock_release(&lkey);
 	return -1;
a741f797
 }
 
 /**
  *
  */
 int tps_response_received(sip_msg_t *msg)
 {
 	tps_data_t mtsd;
 	tps_data_t stsd;
9b4c0c29
 	tps_data_t btsd;
a741f797
 	str lkey;
639013a1
 	uint32_t direction = TPS_DIR_DOWNSTREAM;
a741f797
 
ed262dfb
 	LM_DBG("handling incoming response\n");
 
98f92714
 	if(msg->first_line.u.reply.statuscode==100) {
 		/* nothing to do - it should be absorbed */
 		return 0;
 	}
 
a741f797
 	memset(&mtsd, 0, sizeof(tps_data_t));
 	memset(&stsd, 0, sizeof(tps_data_t));
9b4c0c29
 	memset(&btsd, 0, sizeof(tps_data_t));
a741f797
 
9b4c0c29
 	lkey = msg->callid->body;
 
787c41c0
 	if(tps_pack_message(msg, &mtsd)<0) {
a741f797
 		LM_ERR("failed to extract and pack the headers\n");
 		return -1;
 	}
9b4c0c29
 	tps_storage_lock_get(&lkey);
4c5419d6
 	if(tps_storage_load_branch(msg, &mtsd, &btsd, 0)<0) {
a741f797
 		goto error;
 	}
9b4c0c29
 	LM_DBG("loaded dialog a_uuid [%.*s]\n",
 			btsd.a_uuid.len, ZSW(btsd.a_uuid.s));
 	if(tps_storage_load_dialog(msg, &btsd, &stsd)<0) {
 		goto error;
 	}
787c41c0
 
639013a1
 	/* detect direction - via from-tag */
 	if(tps_dlg_detect_direction(msg, &stsd, &direction)<0) {
787c41c0
 		goto error;
 	}
 	mtsd.direction = direction;
cec4393d
 	if(tps_storage_update_branch(msg, &mtsd, &btsd,
 				TPS_DBU_CONTACT|TPS_DBU_RPLATTRS)<0) {
787c41c0
 		goto error;
 	}
7b27bb50
 	if(tps_storage_update_dialog(msg, &mtsd, &stsd, _tps_update_rr_dialog?TPS_DBU_RPLATTRS|TPS_DBU_BRR|TPS_DBU_ARR:TPS_DBU_RPLATTRS)<0) {
dc4306e4
 		goto error;
 	}
9b4c0c29
 	tps_storage_lock_release(&lkey);
 
 	tps_reappend_via(msg, &btsd, &btsd.x_via);
a215b926
 	tps_reappend_rr(msg, &btsd, &btsd.s_rr);
9b4c0c29
 	tps_reappend_rr(msg, &btsd, &btsd.x_rr);
787c41c0
 	tps_append_xbranch(msg, &mtsd.x_vbranch1);
a741f797
 
 	return 0;
 
 error:
9b4c0c29
 	tps_storage_lock_release(&lkey);
a741f797
 	return -1;
 }
1a16f11f
 
68717c2a
 /**
  *
  */
ca4494d4
 int tps_request_sent(sip_msg_t *msg, int dialog, int local)
68717c2a
 {
1a16f11f
 	tps_data_t mtsd;
95572016
 	tps_data_t btsd;
1a16f11f
 	tps_data_t stsd;
 	tps_data_t *ptsd;
 	str lkey;
95572016
 	str xuuid;
639013a1
 	uint32_t direction = TPS_DIR_DOWNSTREAM;
1a16f11f
 
dd1872fb
 	LM_DBG("handling outgoing request (%d, %d)\n", dialog, local);
ed262dfb
 
1a16f11f
 	memset(&mtsd, 0, sizeof(tps_data_t));
95572016
 	memset(&btsd, 0, sizeof(tps_data_t));
1a16f11f
 	memset(&stsd, 0, sizeof(tps_data_t));
a8d4ca29
 	ptsd = NULL;
1a16f11f
 
787c41c0
 	if(tps_pack_message(msg, &mtsd)<0) {
1a16f11f
 		LM_ERR("failed to extract and pack the headers\n");
 		return -1;
 	}
 
95572016
 	if(dialog!=0) {
 		if(tps_get_xuuid(msg, &xuuid)<0) {
 			LM_DBG("no x-uuid header - nothing to do\n");
 			return 0;
 		}
 		mtsd.a_uuid = xuuid;
 		tps_remove_xuuid(msg);
 	}
 
6885748a
 	lkey = msg->callid->body;
 
1a16f11f
 	tps_storage_lock_get(&lkey);
95572016
 
 	if(dialog!=0) {
ed262dfb
 		if(tps_storage_load_dialog(msg, &mtsd, &stsd)==0) {
a4043c44
 			ptsd = &stsd;
1a16f11f
 		}
639013a1
 		/* detect direction - via from-tag */
 		if(tps_dlg_detect_direction(msg, &stsd, &direction)<0) {
a43d8460
 			goto error;
 		}
 		mtsd.direction = direction;
1a16f11f
 	}
 
a8d4ca29
 	if(tps_storage_load_branch(msg, &mtsd, &btsd, 0)!=0) {
 		if(tps_storage_record(msg, &mtsd, dialog, direction)<0) {
 			goto error;
 		}
 	} else {
 		if(ptsd==NULL) ptsd = &btsd;
 	}
 
 	if(ptsd==NULL) ptsd = &mtsd;
 
1a16f11f
 	/* local generated requests */
 	if(local) {
 		/* ACK and CANCEL go downstream */
 		if(get_cseq(msg)->method_id==METHOD_ACK
 				|| get_cseq(msg)->method_id==METHOD_CANCEL
 				|| local==2) {
56540799
 			// ts_mask_callid(&msg);
1a16f11f
 			goto done;
 		} else {
 			/* should be for upstream */
 			goto done;
 		}
 	}
 
 	tps_remove_headers(msg, HDR_RECORDROUTE_T);
 	tps_remove_headers(msg, HDR_CONTACT_T);
 	tps_remove_headers(msg, HDR_VIA_T);
 
a4043c44
 	tps_reinsert_via(msg, &mtsd, &mtsd.x_via1);
1a16f11f
 	if(direction==TPS_DIR_UPSTREAM) {
 		tps_reinsert_contact(msg, ptsd, &ptsd->as_contact);
 	} else {
 		tps_reinsert_contact(msg, ptsd, &ptsd->bs_contact);
 	}
 
ee67b5ee
 	if(dialog!=0) {
 		tps_storage_end_dialog(msg, &mtsd, ptsd);
7b27bb50
 		if(tps_storage_update_dialog(msg, &mtsd, &stsd, _tps_update_rr_dialog?TPS_DBU_CONTACT|TPS_DBU_ARR:TPS_DBU_CONTACT)<0) {
914e56b4
 			goto error;
 		}
639013a1
 	}
ee67b5ee
 
1a16f11f
 done:
 	tps_storage_lock_release(&lkey);
68717c2a
 	return 0;
1a16f11f
 
 error:
 	tps_storage_lock_release(&lkey);
 	return -1;
68717c2a
 }
 
 /**
  *
  */
 int tps_response_sent(sip_msg_t *msg)
 {
6885748a
 	tps_data_t mtsd;
 	tps_data_t stsd;
 	tps_data_t btsd;
 	str lkey;
639013a1
 	uint32_t direction = TPS_DIR_UPSTREAM;
95572016
 	str xvbranch = {0, 0};
dcf9ee88
 	int contact_keep = 0;
6885748a
 
ed262dfb
 	LM_DBG("handling outgoing response\n");
 
6885748a
 	memset(&mtsd, 0, sizeof(tps_data_t));
 	memset(&stsd, 0, sizeof(tps_data_t));
 	memset(&btsd, 0, sizeof(tps_data_t));
 
787c41c0
 	if(tps_get_xbranch(msg, &xvbranch)<0) {
 		LM_DBG("no x-branch header - nothing to do\n");
 		return 0;
 	}
 
 	if(tps_pack_message(msg, &mtsd)<0) {
6885748a
 		LM_ERR("failed to extract and pack the headers\n");
 		return -1;
 	}
787c41c0
 	mtsd.x_vbranch1 = xvbranch;
 	tps_remove_xbranch(msg);
6885748a
 
 	if(get_cseq(msg)->method_id==METHOD_MESSAGE) {
 		tps_remove_headers(msg, HDR_RECORDROUTE_T);
 		tps_remove_headers(msg, HDR_CONTACT_T);
 		return 0;
 	}
 
 	lkey = msg->callid->body;
 
 	tps_storage_lock_get(&lkey);
4c5419d6
 	if(tps_storage_load_branch(msg, &mtsd, &btsd, 0)<0) {
6885748a
 		goto error;
 	}
639013a1
 	LM_DBG("loaded branch a_uuid [%.*s]\n",
6885748a
 			btsd.a_uuid.len, ZSW(btsd.a_uuid.s));
 	if(tps_storage_load_dialog(msg, &btsd, &stsd)<0) {
 		goto error;
 	}
 	tps_storage_lock_release(&lkey);
 
639013a1
 	/* detect direction - via from-tag */
 	if(tps_dlg_detect_direction(msg, &stsd, &direction)<0) {
 		goto error1;
 	}
 	mtsd.direction = direction;
 
dc4306e4
 	tps_remove_headers(msg, HDR_RECORDROUTE_T);
 
fcf1d3d7
 	/* keep contact without updates for redirect responses sent out */
1f02ee62
 	if(msg->first_line.u.reply.statuscode>=300
 			&& msg->first_line.u.reply.statuscode<400) {
dcf9ee88
 		contact_keep = 1;
 	}
 	if(contact_keep==0 && msg->first_line.u.reply.statuscode>100
 				&& msg->first_line.u.reply.statuscode<200
 				&& msg->contact==NULL) {
 		contact_keep = 1;
 	}
 	if(contact_keep==0) {
fcf1d3d7
 		tps_remove_headers(msg, HDR_CONTACT_T);
 		if(direction==TPS_DIR_DOWNSTREAM) {
 			tps_reinsert_contact(msg, &stsd, &stsd.as_contact);
 		} else {
 			tps_reinsert_contact(msg, &stsd, &stsd.bs_contact);
 		}
dc4306e4
 	}
 
 	tps_reappend_rr(msg, &btsd, &btsd.x_rr);
ebf570f8
 	if(tps_storage_update_branch(msg, &mtsd, &btsd, TPS_DBU_CONTACT)<0) {
 		goto error;
 	}
639013a1
 	if(tps_storage_update_dialog(msg, &mtsd, &stsd, TPS_DBU_CONTACT)<0) {
 		goto error1;
 	}
68717c2a
 	return 0;
6885748a
 
 error:
 	tps_storage_lock_release(&lkey);
639013a1
 error1:
6885748a
 	return -1;
68717c2a
 }