/*
* $Id: t_lookup.c,v 1.91.2.5 2005/11/29 19:39:45 andrei Exp $
*
* This C-file takes care of matching requests and replies with
* existing transactions. Note that we do not do SIP-compliant
* request matching as asked by SIP spec. We do bitwise matching of
* all header fields in requests which form a transaction key.
* It is much faster and it works pretty well -- we haven't
* had any interop issue neither in lab nor in bake-offs. The reason
* is that retransmissions do look same as original requests
* (it would be really silly if they would be mangled). The only
* exception is we parse To as To in ACK is compared to To in
* reply and both of them are constructed by different software.
*
* As for reply matching, we match based on branch value -- that is
* faster too. There are two versions .. with SYNONYMs #define
* enabled, the branch includes ordinal number of a transaction
* in a synonym list in hash table and is somewhat faster but
* not reboot-resilient. SYNONYMs turned off are little slower
* but work across reboots as well.
*
* The branch parameter is formed as follows:
* SYNONYMS on: hash.synonym.branch
* SYNONYMS off: hash.md5.branch
*
* -jiri
*
*
* Copyright (C) 2001-2003 FhG Fokus
*
* This file is part of ser, a free SIP server.
*
* ser 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
*
* For a license to use the ser software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* ser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* History:
* ----------
* 2003-01-23 options for disabling r-uri matching introduced (jiri)
* nameser_compat.h (andrei)
* 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri)
* 2003-01-28 scratchpad removed (jiri)
* 2003-02-13 init_rb() is proto indep. & it uses struct dest_info (andrei)
* 2003-02-24 s/T_NULL/T_NULL_CELL/ to avoid redefinition conflict w/
* 2003-02-27 3261 ACK/200 consumption bug removed (jiri)
* 2003-02-28 scratchpad compatibility abandoned (jiri)
* 2003-03-01 kr set through a function now (jiri)
* 2003-03-06 dialog matching introduced for ACKs -- that's important for
* INVITE UAS (like INVITE) and 200/ACK proxy matching (jiri)
* 2003-03-29 optimization: e2e ACK matching only if callback installed
* (jiri)
* 2003-03-30 set_kr for requests only (jiri)
* 2003-04-04 bug_fix: RESPONSE_IN callback not called for local
* UAC transactions (jiri)
* 2003-04-07 new transactions inherit on_negative and on_relpy from script
* variables on instantiation (jiri)
* 2003-04-30 t_newtran clean up (jiri)
* 2003-08-21 request lookups fixed to skip UAC transactions,
* thanks Ed (jiri)
* 2003-12-04 global TM callbacks switched to per transaction callbacks
* (bogdan)
* 2004-02-11 FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri)
* 2004-02-13: t->is_invite and t->local replaced with flags (bogdan)
* 2004-10-10: use of mhomed disabled for replies (jiri)
* 2005-02-01: use the incoming request interface for sending the replies
* - changes in init_rb() (bogdan)
*/
#include "defs.h"
#include "../../comp_defs.h"
#include "../../dprint.h"
#include "../../config.h"
#include "../../parser/parser_f.h"
#include "../../parser/parse_from.h"
#include "../../ut.h"
#include "../../timer.h"
#include "../../hash_func.h"
#include "../../globals.h"
#include "../../forward.h"
#include "t_funcs.h"
#include "config.h"
#include "sip_msg.h"
#include "t_hooks.h"
#include "t_lookup.h"
#include "dlg.h" /* for t_lookup_callid */
#include "t_msgbuilder.h" /* for t_lookup_callid */
#define EQ_VIA_LEN(_via)\
( (p_msg->_via->port_str.len > 0 ? (p_msg->_via->port_str.s + p_msg->_via->port_str.len - p_msg->_via->name.s) : p_msg->_via->name.len) ==\
(t_msg->_via->port_str.len > 0 ? (t_msg->_via->port_str.s + t_msg->_via->port_str.len - t_msg->_via->name.s) : t_msg->_via->name.len) )
#define EQ_LEN(_hf) (t_msg->_hf->body.len==p_msg->_hf->body.len)
#define EQ_REQ_URI_LEN\
(p_msg->first_line.u.request.uri.len==t_msg->first_line.u.request.uri.len)
#define EQ_STR(_hf) (memcmp(t_msg->_hf->body.s,\
p_msg->_hf->body.s, \
p_msg->_hf->body.len)==0)
#define EQ_REQ_URI_STR\
( memcmp( t_msg->first_line.u.request.uri.s,\
p_msg->first_line.u.request.uri.s,\
p_msg->first_line.u.request.uri.len)==0)
#define EQ_VIA_STR(_via)\
( memcmp( t_msg->_via->name.s,\
p_msg->_via->name.s,\
(t_msg->_via->port_str.len > 0 ? (t_msg->_via->port_str.s+t_msg->_via->port_str.len - t_msg->_via->name.s) : t_msg->_via->name.len) \
)==0 )
#define HF_LEN(_hf) ((_hf)->len)
/* should be request-uri matching used as a part of pre-3261
* transaction matching, as the standard wants us to do so
* (and is reasonable to do so, to be able to distinguish
* spirals)? turn only off for better interaction with
* devices that are broken and send different r-uri in
* CANCEL/ACK than in original INVITE
*/
int ruri_matching=1;
int via1_matching=1;
/* presumably matching transaction for an e2e ACK */
static struct cell *t_ack;
/* this is a global variable which keeps pointer to
transaction currently processed by a process; it it
set by t_lookup_request or t_reply_matching; don't
dare to change it anywhere else as it would
break ref_counting
*/
static struct cell *T;
/* number of currently processed message; good to know
to be able to doublecheck whether we are still working
on a current transaction or a new message arrived;
don't even think of changing it
*/
unsigned int global_msg_id;
struct cell *get_t() { return T; }
void set_t(struct cell *t) { T=t; }
void init_t() {global_msg_id=0; set_t(T_UNDEFINED);}
static inline int parse_dlg( struct sip_msg *msg )
{
if (parse_headers(msg, HDR_FROM | HDR_CSEQ | HDR_TO, 0)==-1) {
LOG(L_ERR, "ERROR: parse_dlg: From or Cseq or To invalid\n");
return 0;
}
if ((msg->from==0)||(msg->cseq==0)||(msg->to==0)) {
LOG(L_ERR, "ERROR: parse_dlg: missing From or Cseq or To\n");
return 0;
}
if (parse_from_header(msg)==-1) {
LOG(L_ERR, "ERROR: parse_dlg: From broken\n");
return 0;
}
/* To is automatically parsed through HDR_TO in parse bitmap,
* we don't need to worry about it now
if (parse_to_header(msg)==-1) {
LOG(L_ERR, "ERROR: tid_matching: To broken\n");
return 0;
}
*/
return 1;
}
/* is the ACK (p_msg) in p_msg dialog-wise equal to the INVITE (t_msg)
* except to-tags? */
static inline int partial_dlg_matching(struct sip_msg *t_msg, struct sip_msg *p_msg)
{
struct to_body *inv_from;
if (!EQ_LEN(callid)) return 0;
if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len)
return 0;
inv_from=get_from(t_msg);
if (!inv_from) {
LOG(L_ERR, "ERROR: partial_dlg_matching: INV/From not parsed\n");
return 0;
}
if (inv_from->tag_value.len!=get_from(p_msg)->tag_value.len)
return 0;
if (!EQ_STR(callid))
return 0;
if (memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s,
get_cseq(p_msg)->number.len)!=0)
return 0;
if (memcmp(inv_from->tag_value.s, get_from(p_msg)->tag_value.s,
get_from(p_msg)->tag_value.len)!=0)
return 0;
return 1;
}
/* are to-tags in ACK/200 same as those we sent out? */
static inline int dlg_matching(struct cell *p_cell, struct sip_msg *ack )
{
if (get_to(ack)->tag_value.len!=p_cell->uas.local_totag.len)
return 0;
if (memcmp(get_to(ack)->tag_value.s,p_cell->uas.local_totag.s,
p_cell->uas.local_totag.len)!=0)
return 0;
return 1;
}
static inline int ack_matching(struct cell *p_cell, struct sip_msg *p_msg)
{
/* partial dialog matching -- no to-tag, only from-tag,
* callid, cseq number ; */
if (!partial_dlg_matching(p_cell->uas.request, p_msg))
return 0;
/* if this transaction is proxied (as opposed to UAS) we're
* done now -- we ignore to-tags; the ACK simply belongs to
* this UAS part of dialog, whatever to-tag it gained
*/
if (p_cell->relaied_reply_branch!=-2) {
return 2; /* e2e proxied ACK */
}
/* it's a local dialog -- we wish to verify to-tags too */
if (dlg_matching(p_cell, p_msg)) {
return 1;
}
return 0;
}
/* branch-based transaction matching */
static inline int via_matching( struct via_body *inv_via,
struct via_body *ack_via )
{
if (inv_via->tid.len!=ack_via->tid.len)
return 0;
if (memcmp(inv_via->tid.s, ack_via->tid.s,
ack_via->tid.len)!=0)
return 0;
/* ok, tid matches -- now make sure that the
* originator matches too to avoid confusion with
* different senders generating the same tid
*/
if (inv_via->host.len!=ack_via->host.len)
return 0;;
if (memcmp(inv_via->host.s, ack_via->host.s,
ack_via->host.len)!=0)
return 0;
#if 0
if (inv_via->port!=ack_via->port)
return 0;
#endif
if (inv_via->transport.len!=ack_via->transport.len)
return 0;
if (memcmp(inv_via->transport.s, ack_via->transport.s,
ack_via->transport.len)!=0)
return 0;
/* everything matched -- we found it */
return 1;
}
/* transaction matching a-la RFC-3261 using transaction ID in branch
(the function assumes there is magic cookie in branch)
It returns:
2 if e2e ACK for a proxied transaction found
1 if found (covers ACK for local UAS)
0 if not found (trans undefined)
*/
static int matching_3261( struct sip_msg *p_msg, struct cell **trans,
enum request_method skip_method)
{
struct cell *p_cell;
struct sip_msg *t_msg;
struct via_body *via1;
int is_ack;
int dlg_parsed;
int ret = 0;
struct cell *e2e_ack_trans;
e2e_ack_trans=0;
via1=p_msg->via1;
is_ack=p_msg->REQ_METHOD==METHOD_ACK;
dlg_parsed=0;
/* update parsed tid */
via1->tid.s=via1->branch->value.s+MCOOKIE_LEN;
via1->tid.len=via1->branch->value.len-MCOOKIE_LEN;
for ( p_cell = get_tm_table()->entrys[p_msg->hash_index].first_cell;
p_cell; p_cell = p_cell->next_cell )
{
t_msg=p_cell->uas.request;
if (!t_msg) continue; /* don't try matching UAC transactions */
if (skip_method & t_msg->REQ_METHOD) continue;
/* here we do an exercise which will be removed from future code
* versions: we try to match end-2-end ACKs if they appear at our
* server. This allows some applications bound to TM via callbacks
* to correlate the e2e ACKs with transaction context, e.g., for
* purpose of accounting. We think it is a bad place here, among
* other things because it is not reliable. If a transaction loops
* via SER the ACK can't be matched to proper INVITE transaction
* (it is a separate transactino with its own branch ID) and it
* matches all transaction instances in the loop dialog-wise.
* Eventually, regardless to which transaction in the loop the
* ACK belongs, only the first one will match.
*/
/* dialog matching needs to be applied for ACK/200s */
if (is_ack && p_cell->uas.status<300 && e2e_ack_trans==0) {
/* make sure we have parsed all things we need for dialog
* matching */
if (!dlg_parsed) {
dlg_parsed=1;
if (!parse_dlg(p_msg)) {
LOG(L_ERR, "ERROR: matching_3261: dlg parsing failed\n");
return 0;
}
}
ret=ack_matching(p_cell /* t w/invite */, p_msg /* ack */);
if (ret>0) {
e2e_ack_trans=p_cell;
break;
}
/* this ACK is neither local "negative" one, nor a proxied
* end-2-end one, nor an end-2-end one for a UAS transaction
* -- we failed to match */
continue;
}
/* now real tid matching occurs for negative ACKs and any
* other requests */
if (!via_matching(t_msg->via1 /* inv via */, via1 /* ack */ ))
continue;
/* all matched -- we found the transaction ! */
DBG("DEBUG: RFC3261 transaction matched, tid=%.*s\n",
via1->tid.len, via1->tid.s);
*trans=p_cell;
return 1;
}
/* :-( ... we didn't find any */
/* just check if it we found an e2e ACK previously */
if (e2e_ack_trans) {
*trans=e2e_ack_trans;
return ret;
}
DBG("DEBUG: RFC3261 transaction matching failed\n");
return 0;
}
/* function returns:
* negative - transaction wasn't found
* (-2 = possibly e2e ACK matched )
* positive - transaction found
*/
int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
{
struct cell *p_cell;
unsigned int isACK;
struct sip_msg *t_msg;
int ret;
struct via_param *branch;
int match_status;
struct cell *e2e_ack_trans;
/* parse all*/
if (check_transaction_quadruple(p_msg)==0)
{
LOG(L_ERR, "ERROR: TM module: t_lookup_request: too few headers\n");
set_t(0);
/* stop processing */
return 0;
}
/* start searching into the table */
if (!p_msg->hash_index)
p_msg->hash_index=hash( p_msg->callid->body , get_cseq(p_msg)->number ) ;
isACK = p_msg->REQ_METHOD==METHOD_ACK;
DBG("t_lookup_request: start searching: hash=%d, isACK=%d\n",
p_msg->hash_index,isACK);
/* assume not found */
ret=-1;
e2e_ack_trans=0;
/* first of all, look if there is RFC3261 magic cookie in branch; if
* so, we can do very quick matching and skip the old-RFC bizzar
* comparison of many header fields
*/
if (!p_msg->via1) {
LOG(L_ERR, "ERROR: t_lookup_request: no via\n");
set_t(0);
return 0;
}
branch=p_msg->via1->branch;
if (branch && branch->value.s && branch->value.len>MCOOKIE_LEN
&& memcmp(branch->value.s,MCOOKIE,MCOOKIE_LEN)==0) {
/* huhuhu! the cookie is there -- let's proceed fast */
LOCK_HASH(p_msg->hash_index);
match_status=matching_3261(p_msg,&p_cell,
/* skip transactions with different method; otherwise CANCEL would
* match the previous INVITE trans. */
isACK ? ~METHOD_INVITE: ~p_msg->REQ_METHOD);
switch(match_status) {
case 0: goto notfound; /* no match */
case 1: goto found; /* match */
case 2: goto e2e_ack; /* e2e proxy ACK */
}
}
/* ok -- it's ugly old-fashioned transaction matching -- it is
* a bit simplified to be fast -- we don't do all the comparisons
* of parsed uri, which was simply too bloated */
DBG("DEBUG: proceeding to pre-RFC3261 transaction matching\n");
/* lock the whole entry*/
LOCK_HASH(p_msg->hash_index);
/* all the transactions from the entry are compared */
for ( p_cell = get_tm_table()->entrys[p_msg->hash_index].first_cell;
p_cell; p_cell = p_cell->next_cell )
{
t_msg = p_cell->uas.request;
if (!t_msg) continue; /* skip UAC transactions */
if (!isACK) {
/* compare lengths first */
if (!EQ_LEN(callid)) continue;
if (!EQ_LEN(cseq)) continue;
if (!EQ_LEN(from)) continue;
if (!EQ_LEN(to)) continue;
if (ruri_matching && !EQ_REQ_URI_LEN) continue;
if (via1_matching && !EQ_VIA_LEN(via1)) continue;
/* length ok -- move on */
if (!EQ_STR(callid)) continue;
if (!EQ_STR(cseq)) continue;
if (!EQ_STR(from)) continue;
if (!EQ_STR(to)) continue;
if (ruri_matching && !EQ_REQ_URI_STR) continue;
if (via1_matching && !EQ_VIA_STR(via1)) continue;
/* request matched ! */
DBG("DEBUG: non-ACK matched\n");
goto found;
} else { /* it's an ACK request*/
/* ACK's relate only to INVITEs */
if (t_msg->REQ_METHOD!=METHOD_INVITE) continue;
/* From|To URI , CallID, CSeq # must be always there */
/* compare lengths now */
if (!EQ_LEN(callid)) continue;
/* CSeq only the number without method ! */
if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len)
continue;
if (! EQ_LEN(from)) continue;
/* To only the uri -- to many UACs screw up tags */
if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len)
continue;
if (!EQ_STR(callid)) continue;
if (memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s,
get_cseq(p_msg)->number.len)!=0) continue;
if (!EQ_STR(from)) continue;
if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s,
get_to(t_msg)->uri.len)!=0) continue;
/* it is e2e ACK/200 */
if (p_cell->uas.status<300 && e2e_ack_trans==0) {
/* all criteria for proxied ACK are ok */
if (p_cell->relaied_reply_branch!=-2) {
e2e_ack_trans=p_cell;
continue;
}
/* it's a local UAS transaction */
if (dlg_matching(p_cell, p_msg))
goto found;
continue;
}
/* it is not an e2e ACK/200 -- perhaps it is
* local negative case; in which case we will want
* more elements to match: r-uri and via; allow
* mismatching r-uri as an config option for broken
* UACs */
if (ruri_matching && !EQ_REQ_URI_LEN ) continue;
if (via1_matching && !EQ_VIA_LEN(via1)) continue;
if (ruri_matching && !EQ_REQ_URI_STR) continue;
if (via1_matching && !EQ_VIA_STR(via1)) continue;
/* wow -- we survived all the check! we matched! */
DBG("DEBUG: non-2xx ACK matched\n");
goto found;
} /* ACK */
} /* synonym loop */
notfound:
if (e2e_ack_trans) {
p_cell=e2e_ack_trans;
goto e2e_ack;
}
/* no transaction found */
set_t(0);
if (!leave_new_locked) {
UNLOCK_HASH(p_msg->hash_index);
}
DBG("DEBUG: t_lookup_request: no transaction found\n");
return -1;
e2e_ack:
t_ack=p_cell; /* e2e proxied ACK */
set_t(0);
if (!leave_new_locked) {
UNLOCK_HASH(p_msg->hash_index);
}
DBG("DEBUG: t_lookup_request: e2e proxy ACK found\n");
return -2;
found:
set_t(p_cell);
REF_UNSAFE( T );
set_kr(REQ_EXIST);
UNLOCK_HASH( p_msg->hash_index );
DBG("DEBUG: t_lookup_request: transaction found (T=%p)\n",T);
return 1;
}
/* function lookups transaction being canceled by CANCEL in p_msg;
* it returns:
* 0 - transaction wasn't found
* T - transaction found
*/
struct cell* t_lookupOriginalT( struct sip_msg* p_msg )
{
struct cell *p_cell;
unsigned int hash_index;
struct sip_msg *t_msg;
struct via_param *branch;
int ret;
/* start searching in the table */
hash_index = p_msg->hash_index;
DBG("DEBUG: t_lookupOriginalT: searching on hash entry %d\n",hash_index );
/* first of all, look if there is RFC3261 magic cookie in branch; if
* so, we can do very quick matching and skip the old-RFC bizzar
* comparison of many header fields
*/
if (!p_msg->via1) {
LOG(L_ERR, "ERROR: t_lookupOriginalT: no via\n");
set_t(0);
return 0;
}
branch=p_msg->via1->branch;
if (branch && branch->value.s && branch->value.len>MCOOKIE_LEN
&& memcmp(branch->value.s,MCOOKIE,MCOOKIE_LEN)==0) {
/* huhuhu! the cookie is there -- let's proceed fast */
LOCK_HASH(hash_index);
ret=matching_3261(p_msg, &p_cell,
/* we are seeking the original transaction --
* skip CANCEL transactions during search
*/
METHOD_CANCEL);
if (ret==1) goto found; else goto notfound;
}
/* no cookies --proceed to old-fashioned pre-3261 t-matching */
LOCK_HASH(hash_index);
/* all the transactions from the entry are compared */
for (p_cell=get_tm_table()->entrys[hash_index].first_cell;
p_cell; p_cell = p_cell->next_cell )
{
t_msg = p_cell->uas.request;
if (!t_msg) continue; /* skip UAC transactions */
/* we don't cancel CANCELs ;-) */
if (t_msg->REQ_METHOD==METHOD_CANCEL)
continue;
/* check lengths now */
if (!EQ_LEN(callid))
continue;
if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len)
continue;
if (!EQ_LEN(from))
continue;
#ifdef CANCEL_TAG
if (!EQ_LEN(to))
continue;
#else
/* relaxed matching -- we don't care about to-tags anymore,
* many broken UACs screw them up and ignoring them does not
* actually hurt
*/
if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len)
continue;
#endif
if (ruri_matching && !EQ_REQ_URI_LEN)
continue;
if (via1_matching && !EQ_VIA_LEN(via1))
continue;
/* check the content now */
if (!EQ_STR(callid))
continue;
if (memcmp(get_cseq(t_msg)->number.s,
get_cseq(p_msg)->number.s,get_cseq(p_msg)->number.len)!=0)
continue;
if (!EQ_STR(from))
continue;
#ifdef CANCEL_TAG
if (!EQ_STR(to))
continue;
#else
if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s,
get_to(t_msg)->uri.len)!=0)
continue;
#endif
if (ruri_matching && !EQ_REQ_URI_STR)
continue;
if (via1_matching && !EQ_VIA_STR(via1))
continue;
/* found */
goto found;
}
notfound:
/* no transaction found */
DBG("DEBUG: t_lookupOriginalT: no CANCEL matching found! \n" );
UNLOCK_HASH(hash_index);
DBG("DEBUG: t_lookupOriginalT completed\n");
return 0;
found:
DBG("DEBUG: t_lookupOriginalT: canceled transaction"
" found (%p)! \n",p_cell );
REF_UNSAFE( p_cell );
UNLOCK_HASH(hash_index);
DBG("DEBUG: t_lookupOriginalT completed\n");
return p_cell;
}
/* Returns 0 - nothing found
* 1 - T found
*/
int t_reply_matching( struct sip_msg *p_msg , int *p_branch )
{
struct cell* p_cell;
unsigned int hash_index = 0;
unsigned int entry_label = 0;
unsigned int branch_id = 0;
char *hashi, *branchi, *p, *n;
int hashl, branchl;
int scan_space;
str cseq_method;
str req_method;
char *loopi;
int loopl;
char *syni;
int synl;
short is_cancel;
/* make compiler warnings happy */
loopi=0;
loopl=0;
syni=0;
synl=0;
/* split the branch into pieces: loop_detection_check(ignored),
hash_table_id, synonym_id, branch_id */
if (!(p_msg->via1 && p_msg->via1->branch && p_msg->via1->branch->value.s))
goto nomatch2;
/* we do RFC 3261 tid matching and want to see first if there is
* magic cookie in branch */
if (p_msg->via1->branch->value.len<=MCOOKIE_LEN)
goto nomatch2;
if (memcmp(p_msg->via1->branch->value.s, MCOOKIE, MCOOKIE_LEN)!=0)
goto nomatch2;
p=p_msg->via1->branch->value.s+MCOOKIE_LEN;
scan_space=p_msg->via1->branch->value.len-MCOOKIE_LEN;
/* hash_id */
n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR);
hashl=n-p;
scan_space-=hashl;
if (!hashl || scan_space<2 || *n!=BRANCH_SEPARATOR) goto nomatch2;
hashi=p;
p=n+1;scan_space--;
if (!syn_branch) {
/* md5 value */
n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR );
loopl = n-p;
scan_space-= loopl;
if (n==p || scan_space<2 || *n!=BRANCH_SEPARATOR)
goto nomatch2;
loopi=p;
p=n+1; scan_space--;
} else {
/* synonym id */
n=eat_token2_end( p, p+scan_space, BRANCH_SEPARATOR);
synl=n-p;
scan_space-=synl;
if (!synl || scan_space<2 || *n!=BRANCH_SEPARATOR)
goto nomatch2;
syni=p;
p=n+1;scan_space--;
}
/* branch id - should exceed the scan_space */
n=eat_token_end( p, p+scan_space );
branchl=n-p;
if (!branchl ) goto nomatch2;
branchi=p;
/* sanity check */
if (reverse_hex2int(hashi, hashl, &hash_index)<0
||hash_index>=TABLE_ENTRIES
|| reverse_hex2int(branchi, branchl, &branch_id)<0
||branch_id>=MAX_BRANCHES
|| (syn_branch ? (reverse_hex2int(syni, synl, &entry_label))<0
: loopl!=MD5_LEN )
) {
DBG("DEBUG: t_reply_matching: poor reply labels %d label %d "
"branch %d\n", hash_index, entry_label, branch_id );
goto nomatch2;
}
DBG("DEBUG: t_reply_matching: hash %d label %d branch %d\n",
hash_index, entry_label, branch_id );
/* search the hash table list at entry 'hash_index'; lock the
entry first
*/
cseq_method=get_cseq(p_msg)->method;
is_cancel=cseq_method.len==CANCEL_LEN
&& memcmp(cseq_method.s, CANCEL, CANCEL_LEN)==0;
LOCK_HASH(hash_index);
for (p_cell = get_tm_table()->entrys[hash_index].first_cell; p_cell;
p_cell=p_cell->next_cell) {
/* first look if branch matches */
if (syn_branch) {
if (p_cell->label != entry_label)
continue;
} else {
if ( memcmp(p_cell->md5, loopi,MD5_LEN)!=0)
continue;
}
/* sanity check ... too high branch ? */
if ( branch_id>=p_cell->nr_of_outgoings )
continue;
/* does method match ? (remember -- CANCELs have the same branch
as canceled transactions) */
req_method=p_cell->method;
if ( /* method match */
! ((cseq_method.len==req_method.len
&& memcmp( cseq_method.s, req_method.s, cseq_method.len )==0)
/* or it is a local cancel */
|| (is_cancel && is_invite(p_cell)
/* commented out -- should_cancel_branch set it to
BUSY_BUFFER to avoid collisions with replies;
thus, we test here by buffer size
*/
/* && p_cell->uac[branch_id].local_cancel.buffer ))) */
&& p_cell->uac[branch_id].local_cancel.buffer_len )))
continue;
/* we passed all disqualifying factors .... the transaction has been
matched !
*/
set_t(p_cell);
*p_branch =(int) branch_id;
REF_UNSAFE( T );
UNLOCK_HASH(hash_index);
DBG("DEBUG: t_reply_matching: reply matched (T=%p)!\n",T);
/* if this is a 200 for INVITE, we will wish to store to-tags to be
* able to distinguish retransmissions later and not to call
* TMCB_RESPONSE_OUT uselessly; we do it only if callbacks are
* enabled -- except callback customers, nobody cares about
* retransmissions of multiple 200/INV or ACK/200s
*/
if (is_invite(p_cell) && p_msg->REPLY_STATUS>=200
&& p_msg->REPLY_STATUS<300
&& ( (!is_local(p_cell) &&
has_tran_tmcbs(p_cell,TMCB_RESPONSE_OUT|TMCB_E2EACK_IN) )
|| (is_local(p_cell)&&has_tran_tmcbs(p_cell,TMCB_LOCAL_COMPLETED))
)) {
if (parse_headers(p_msg, HDR_TO, 0)==-1) {
LOG(L_ERR, "ERROR: t_reply_matching: to parsing failed\n");
}
}
if (!is_local(p_cell)) {
run_trans_callbacks( TMCB_RESPONSE_IN, T, T->uas.request, p_msg,
p_msg->REPLY_STATUS);
}
return 1;
} /* for cycle */
/* nothing found */
UNLOCK_HASH(hash_index);
DBG("DEBUG: t_reply_matching: no matching transaction exists\n");
nomatch2:
DBG("DEBUG: t_reply_matching: failure to match a transaction\n");
*p_branch = -1;
set_t(0);
return -1;
}
/* Determine current transaction
*
* Found Not Found Error (e.g. parsing)
* Return Value 1 0 -1
* T ptr 0 T_UNDEFINED
*/
int t_check( struct sip_msg* p_msg , int *param_branch )
{
int local_branch;
/* is T still up-to-date ? */
DBG("DEBUG: t_check: msg id=%d global id=%d T start=%p\n",
p_msg->id,global_msg_id,T);
if ( p_msg->id != global_msg_id || T==T_UNDEFINED )
{
global_msg_id = p_msg->id;
T = T_UNDEFINED;
/* transaction lookup */
if ( p_msg->first_line.type==SIP_REQUEST ) {
/* force parsing all the needed headers*/
if (parse_headers(p_msg, HDR_EOH, 0 )==-1) {
LOG(L_ERR, "ERROR: t_check: parsing error\n");
return -1;
}
/* in case, we act as UAS for INVITE and reply with 200,
* we will need to run dialog-matching for subsequent
* ACK, for which we need From-tag; We also need from-tag
* in case people want to have proxied e2e ACKs accounted
*/
if (p_msg->REQ_METHOD==METHOD_INVITE
&& parse_from_header(p_msg)==-1) {
LOG(L_ERR, "ERROR: t_check: from parsing failed\n");
return -1;
}
t_lookup_request( p_msg , 0 /* unlock before returning */ );
} else {
/* we need Via for branch and Cseq method to distinguish
replies with the same branch/cseqNr (CANCEL)
*/
if ( parse_headers(p_msg, HDR_VIA1|HDR_CSEQ, 0 )==-1
|| !p_msg->via1 || !p_msg->cseq ) {
LOG(L_ERR, "ERROR: reply cannot be parsed\n");
return -1;
}
/* if that is an INVITE, we will also need to-tag
for later ACK matching
*/
if ( get_cseq(p_msg)->method.len==INVITE_LEN
&& memcmp( get_cseq(p_msg)->method.s, INVITE, INVITE_LEN )==0 ) {
if (parse_headers(p_msg, HDR_TO, 0)==-1
|| !p_msg->to) {
LOG(L_ERR, "ERROR: INVITE reply cannot be parsed\n");
return -1;
}
}
t_reply_matching( p_msg ,
param_branch!=0?param_branch:&local_branch );
}
#ifdef EXTRA_DEBUG
if ( T && T!=T_UNDEFINED && T->damocles) {
LOG( L_ERR, "ERROR: transaction %p scheduled for deletion "
"and called from t_check\n", T);
abort();
}
#endif
DBG("DEBUG: t_check: msg id=%d global id=%d T end=%p\n",
p_msg->id,global_msg_id,T);
} else {
if (T)
DBG("DEBUG: t_check: T already found!\n");
else
DBG("DEBUG: t_check: T previously sought and not found\n");
}
return T ? (T==T_UNDEFINED ? -1 : 1 ) : 0;
}
int init_rb( struct retr_buf *rb, struct sip_msg *msg)
{
/*struct socket_info* send_sock;*/
struct via_body* via;
int proto;
int backup_mhomed;
via=msg->via1;
if (!reply_to_via) {
update_sock_struct_from_ip( &rb->dst.to, msg );
proto=msg->rcv.proto;
} else {
/*init retrans buffer*/
if (update_sock_struct_from_via( &(rb->dst.to), msg, via )==-1) {
LOG(L_ERR, "ERROR: init_rb: cannot lookup reply dst: %.*s\n",
via->host.len, via->host.s );
ser_error=E_BAD_VIA;
return 0;
}
proto=via->proto;
}
rb->dst.proto=proto;
rb->dst.proto_reserved1=msg->rcv.proto_reserved1;
/* turn off mhomed for generating replies -- they are ideally sent to where
request came from to make life with NATs and other beasts easier
*/
backup_mhomed=mhomed;
mhomed=0;
mhomed=backup_mhomed;
/* use for sending replies the incoming interface of the request -bogdan */
/*send_sock=get_send_socket(msg, &rb->dst.to, proto);
if (send_sock==0) {
LOG(L_ERR, "ERROR: init_rb: cannot fwd to af %d, proto %d "
"no socket\n", rb->dst.to.s.sa_family, proto);
ser_error=E_BAD_VIA;
return 0;
}*/
rb->dst.send_sock=msg->rcv.bind_address;
return 1;
}
static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
{
struct sip_msg *shm_msg;
shm_msg=new_cell->uas.request;
new_cell->from.s=shm_msg->from->name.s;
new_cell->from.len=HF_LEN(shm_msg->from);
new_cell->to.s=shm_msg->to->name.s;
new_cell->to.len=HF_LEN(shm_msg->to);
new_cell->callid.s=shm_msg->callid->name.s;
new_cell->callid.len=HF_LEN(shm_msg->callid);
new_cell->cseq_n.s=shm_msg->cseq->name.s;
new_cell->cseq_n.len=get_cseq(shm_msg)->number.s
+get_cseq(shm_msg)->number.len
-shm_msg->cseq->name.s;
new_cell->method=new_cell->uas.request->first_line.u.request.method;
if (p_msg->REQ_METHOD==METHOD_INVITE) new_cell->flags |= T_IS_INVITE_FLAG;
new_cell->on_negative=get_on_negative();
new_cell->on_reply=get_on_reply();
}
static inline int new_t(struct sip_msg *p_msg)
{
struct cell *new_cell;
/* for ACK-dlw-wise matching, we want From-tags */
if (p_msg->REQ_METHOD==METHOD_INVITE && parse_from_header(p_msg)<0) {
LOG(L_ERR, "ERROR: new_t: no valid From in INVITE\n");
return E_BAD_REQ;
}
/* make sure uri will be parsed before cloning */
if (parse_sip_msg_uri(p_msg)<0) {
LOG(L_ERR, "ERROR: new_t: uri invalid\n");
return E_BAD_REQ;
}
/* add new transaction */
new_cell = build_cell( p_msg ) ;
if ( !new_cell ){
LOG(L_ERR, "ERROR: new_t: out of mem:\n");
return E_OUT_OF_MEM;
}
insert_into_hash_table_unsafe( new_cell, p_msg->hash_index );
set_t(new_cell);
INIT_REF_UNSAFE(T);
/* init pointers to headers needed to construct local
requests such as CANCEL/ACK
*/
init_new_t(new_cell, p_msg);
return 1;
}
/* atomic "new_tran" construct; it returns:
<0 on error
+1 if a request did not match a transaction
- it that was an ack, the calling function
shall forward statelessly
- otherwise it means, a new transaction was
introduced and the calling function
shall reply/relay/whatever_appropriate
0 on retransmission
*/
int t_newtran( struct sip_msg* p_msg )
{
int lret, my_err;
/* is T still up-to-date ? */
DBG("DEBUG: t_newtran: msg id=%d , global msg id=%d ,"
" T on entrance=%p\n",p_msg->id,global_msg_id,T);
if ( T && T!=T_UNDEFINED ) {
LOG(L_ERR, "ERROR: t_newtran: "
"transaction already in process %p\n", T );
return E_SCRIPT;
}
global_msg_id = p_msg->id;
T = T_UNDEFINED;
/* first of all, parse everything -- we will store in shared memory
and need to have all headers ready for generating potential replies
later; parsing later on demand is not an option since the request
will be in shmem and applying parse_headers to it would intermix
shmem with pkg_mem
*/
if (parse_headers(p_msg, HDR_EOH, 0 )) {
LOG(L_ERR, "ERROR: t_newtran: parse_headers failed\n");
return E_BAD_REQ;
}
if ((p_msg->parsed_flag & HDR_EOH)!=HDR_EOH) {
LOG(L_ERR, "ERROR: t_newtran: EoH not parsed\n");
return E_OUT_OF_MEM;
}
/* t_lookup_requests attempts to find the transaction;
it also calls check_transaction_quadruple -> it is
safe to assume we have from/callid/cseq/to
*/
lret = t_lookup_request( p_msg, 1 /* leave locked if not found */ );
/* on error, pass the error in the stack ... nothing is locked yet
if 0 is returned */
if (lret==0) return E_BAD_TUPEL;
/* transaction found, it's a retransmission */
if (lret>0) {
if (p_msg->REQ_METHOD==METHOD_ACK) {
t_release_transaction(T);
} else {
t_retransmit_reply(T);
}
/* things are done -- return from script */
return 0;
}
/* from now on, be careful -- hash table is locked */
if (lret==-2) { /* was it an e2e ACK ? if so, trigger a callback */
/* no callbacks? complete quickly */
if ( !has_tran_tmcbs(t_ack,TMCB_E2EACK_IN) ) {
UNLOCK_HASH(p_msg->hash_index);
return 1;
}
REF_UNSAFE(t_ack);
UNLOCK_HASH(p_msg->hash_index);
/* we don't call from within REPLY_LOCK -- that introduces
* a race condition; however, it is so unlikely and the
* impact is so small (callback called multiple times of
* multiple ACK/200s received in parallel), that we do not
* better waste time in locks */
if (unmatched_totag(t_ack, p_msg)) {
run_trans_callbacks( TMCB_E2EACK_IN , t_ack, p_msg, 0,
-p_msg->REQ_METHOD );
}
UNREF(t_ack);
return 1;
}
/* transaction not found, it's a new request (lret<0, lret!=-2);
establish a new transaction ... */
if (p_msg->REQ_METHOD==METHOD_ACK) { /* ... unless it is in ACK */
my_err=1;
goto new_err;
}
my_err=new_t(p_msg);
if (my_err<0) {
LOG(L_ERR, "ERROR: t_newtran: new_t failed\n");
goto new_err;
}
UNLOCK_HASH(p_msg->hash_index);
/* now, when the transaction state exists, check if
there is a meaningful Via and calculate it; better
do it now than later: state is established so that
subsequent retransmissions will be absorbed and will
not possibly block during Via DNS resolution; doing
it later would only burn more CPU as if there is an
error, we cannot relay later whatever comes out of the
the transaction
*/
if (!init_rb( &T->uas.response, p_msg)) {
LOG(L_ERR, "ERROR: t_newtran: unresolvable via1\n");
put_on_wait( T );
t_unref(p_msg);
return E_BAD_VIA;
}
return 1;
new_err:
UNLOCK_HASH(p_msg->hash_index);
return my_err;
}
int t_unref( struct sip_msg* p_msg )
{
enum kill_reason kr;
if (T==T_UNDEFINED || T==T_NULL_CELL)
return -1;
if (p_msg->first_line.type==SIP_REQUEST){
kr=get_kr();
if (kr==0
||(p_msg->REQ_METHOD==METHOD_ACK && !(kr & REQ_RLSD))) {
LOG(L_WARN, "WARNING: script writer didn't release transaction\n");
t_release_transaction(T);
}
}
UNREF( T );
set_t(T_UNDEFINED);
return 1;
}
int t_get_trans_ident(struct sip_msg* p_msg, unsigned int* hash_index, unsigned int* label)
{
struct cell* t;
if(t_check(p_msg,0) != 1){
LOG(L_ERR,"ERROR: t_get_trans_ident: no transaction found\n");
return -1;
}
t = get_t();
if(!t){
LOG(L_ERR,"ERROR: t_get_trans_ident: transaction found is NULL\n");
return -1;
}
*hash_index = t->hash_index;
*label = t->label;
return 1;
}
int t_lookup_ident(struct cell ** trans, unsigned int hash_index, unsigned int label)
{
struct cell* p_cell;
if(hash_index >= TABLE_ENTRIES){
LOG(L_ERR,"ERROR: t_lookup_ident: invalid hash_index=%u\n",hash_index);
return -1;
}
LOCK_HASH(hash_index);
/* all the transactions from the entry are compared */
for ( p_cell = get_tm_table()->entrys[hash_index].first_cell;
p_cell; p_cell = p_cell->next_cell )
{
if(p_cell->label == label){
REF_UNSAFE(p_cell);
UNLOCK_HASH(hash_index);
set_t(p_cell);
*trans=p_cell;
DBG("DEBUG: t_lookup_ident: transaction found\n");
return 1;
}
}
UNLOCK_HASH(hash_index);
set_t(0);
*trans=p_cell;
DBG("DEBUG: t_lookup_ident: transaction not found\n");
return -1;
}
int t_is_local(struct sip_msg* p_msg)
{
struct cell* t;
if(t_check(p_msg,0) != 1){
LOG(L_ERR,"ERROR: t_is_local: no transaction found\n");
return -1;
}
t = get_t();
if(!t){
LOG(L_ERR,"ERROR: t_is_local: transaction found is NULL\n");
return -1;
}
return is_local(t);
}
/* lookup a transaction by callid and cseq, parameters are pure
* header field content only, e.g. "123@10.0.0.1" and "11"
*/
int t_lookup_callid(struct cell ** trans, str callid, str cseq) {
struct cell* p_cell;
unsigned hash_index;
/* I use MAX_HEADER, not sure if this is a good choice... */
char callid_header[MAX_HEADER];
char cseq_header[MAX_HEADER];
/* save return value of print_* functions here */
char* endpos;
/* need method, which is always INVITE in our case */
/* CANCEL is only useful after INVITE */
str invite_method;
char* invite_string = INVITE;
invite_method.s = invite_string;
invite_method.len = INVITE_LEN;
/* lookup the hash index where the transaction is stored */
hash_index=hash(callid, cseq);
if(hash_index >= TABLE_ENTRIES){
LOG(L_ERR,"ERROR: t_lookup_callid: invalid hash_index=%u\n",hash_index);
return -1;
}
/* create header fields the same way tm does itself, then compare headers */
endpos = print_callid_mini(callid_header, callid);
DBG("created comparable call_id header field: >%.*s<\n",
(int)(endpos - callid_header), callid_header);
endpos = print_cseq_mini(cseq_header, &cseq, &invite_method);
DBG("created comparable cseq header field: >%.*s<\n",
(int)(endpos - cseq_header), cseq_header);
LOCK_HASH(hash_index);
DBG("just locked hash index %u, looking for transactions there:\n", hash_index);
/* all the transactions from the entry are compared */
for ( p_cell = get_tm_table()->entrys[hash_index].first_cell;
p_cell; p_cell = p_cell->next_cell ) {
/* compare complete header fields, casecmp to make sure invite=INVITE */
if ( (strncmp(callid_header, p_cell->callid.s, p_cell->callid.len) == 0)
&& (strncasecmp(cseq_header, p_cell->cseq_n.s, p_cell->cseq_n.len) == 0) ) {
DBG("we have a match: callid=>>%.*s<< cseq=>>%.*s<<\n", p_cell->callid.len,
p_cell->callid.s, p_cell->cseq_n.len, p_cell->cseq_n.s);
REF_UNSAFE(p_cell);
UNLOCK_HASH(hash_index);
set_t(p_cell);
*trans=p_cell;
DBG("DEBUG: t_lookup_callid: transaction found.\n");
return 1;
}
DBG("NO match: callid=%.*s cseq=%.*s\n", p_cell->callid.len,
p_cell->callid.s, p_cell->cseq_n.len, p_cell->cseq_n.s);
}
UNLOCK_HASH(hash_index);
DBG("DEBUG: t_lookup_callid: transaction not found.\n");
return -1;
}
syntax highlighted by Code2HTML, v. 0.9.1