📄 t_lookup.c
字号:
/* * $Id: t_lookup.c,v 1.91.2.3 2005/09/01 13:52:58 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->via1->bsize-(p_msg->_via->name.s-(p_msg->_via->hdr.s+p_msg->_via->hdr.len)))==\ (t_msg->via1->bsize-(t_msg->_via->name.s-(t_msg->_via->hdr.s+t_msg->_via->hdr.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->via1->bsize-(t_msg->_via->name.s-(t_msg->_via->hdr.s+t_msg->_via->hdr.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 (inv_via->port!=ack_via->port) return 0; 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; 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; } /* 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 2; } 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 )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -