inline static int t_check_trans(struct sip_msg* msg, char *foo, char *bar) { struct cell *trans; struct cell *bkup; int ret; if (msg->REQ_METHOD==METHOD_CANCEL) { /* parse needed hdrs*/ if (check_transaction_quadruple(msg)==0) { LOG(L_ERR, "ERROR:tm:t_check_trans: too few headers\n"); return 0; /*drop request!*/ } if (!msg->hash_index) msg->hash_index = hash(msg->callid->body,get_cseq(msg)->number); /* performe lookup */ trans = t_lookupOriginalT( msg ); if (trans) { UNREF( trans ); return 1; } else { return -1; } } else { bkup = get_t(); ret = t_lookup_request( msg , 0); if ( (trans=get_t())!=0 ) UNREF(trans); set_t( bkup ); switch (ret) { case 1: /* transaction found */ return 1; case -2: /* e2e ACK found */ return 1; default: /* notfound */ return -1; } } return ret; }
inline static int t_check_trans(struct sip_msg* msg) { struct cell *trans; if (msg->REQ_METHOD==METHOD_CANCEL) { /* parse needed hdrs*/ if (check_transaction_quadruple(msg)==0) { LM_ERR("too few headers\n"); return 0; /*drop request!*/ } if (!msg->hash_index) msg->hash_index = tm_hash(msg->callid->body,get_cseq(msg)->number); /* performe lookup */ trans = t_lookupOriginalT( msg ); return trans?1:-1; } else { trans = get_t(); if (trans==NULL) return -1; if (trans!=T_UNDEFINED) return 1; switch ( t_lookup_request( msg , 0) ) { case 1: /* transaction found -> is it local ACK? */ if (msg->REQ_METHOD==METHOD_ACK) return 1; /* .... else -> retransmission */ trans = get_t(); t_retransmit_reply(trans); UNREF(trans); set_t(0); return 0; case -2: /* e2e ACK found */ return 1; default: /* notfound */ 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; int canceled; /* 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_F, 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 */, &canceled); } else { /* we need Via for branch and Cseq method to distinguish replies with the same branch/cseqNr (CANCEL) and we need all the WWW/Proxy Authenticate headers for 401 & 407 replies */ if (tm_aggregate_auth && (p_msg->REPLY_STATUS==401 || p_msg->REPLY_STATUS==407)){ if (parse_headers(p_msg, HDR_EOH_F,0)==-1){ LOG(L_WARN, "WARNING: the reply cannot be " "completely parsed\n"); /* try to continue, via1 & cseq are checked below */ } }else if ( parse_headers(p_msg, HDR_VIA1_F|HDR_CSEQ_F, 0 )==-1) { LOG(L_ERR, "ERROR: reply cannot be parsed\n"); return -1; } if ((p_msg->via1==0) || (p_msg->cseq==0)){ LOG(L_ERR, "ERROR: reply doesn't have a via or cseq header\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_F, 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->flags & (T_IN_AGONY)) { LOG( L_ERR, "ERROR: transaction %p scheduled for deletion " "and called from t_check (flags=%x)\n", T, T->flags); 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; }
/* 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; int canceled; /* 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 ) { /* ERROR message moved to w_t_newtran */ DBG("DEBUG: 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_F, 0 )) { LOG(L_ERR, "ERROR: t_newtran: parse_headers failed\n"); return E_BAD_REQ; } if ((p_msg->parsed_flag & HDR_EOH_F)!=HDR_EOH_F) { 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 */, &canceled ); /* 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; } if (canceled) T->flags|=T_CANCELED; /* mark it for future ref. */ 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; }
/* 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 ? */ LM_DBG("start=%p\n", T); if ( 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_F, 0 )==-1) { LM_ERR("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)<0) { LM_ERR("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_F|HDR_CSEQ_F, 0 )==-1 || !p_msg->via1 || !p_msg->cseq ) { LM_ERR("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_id==METHOD_INVITE ) { if (parse_headers(p_msg, HDR_TO_F, 0)==-1 || !p_msg->to) { LM_ERR("INVITE reply cannot be parsed\n"); return -1; } } /* first check if the transaction is addressed to us */ if (!tm_reply_replicate(p_msg)) t_reply_matching(p_msg , param_branch!=0?param_branch:&local_branch); else T = NULL; /* reply replicated should have never got here */ } #ifdef EXTRA_DEBUG if ( T && T!=T_UNDEFINED && T->damocles) { LM_ERR("transaction %p scheduled for deletion " "and called from t_check\n", T); abort(); } #endif LM_DBG("end=%p\n",T); } else { if (T) LM_DBG("transaction already found!\n"); else LM_DBG("transaction previously sought and not found\n"); } return T ? (T==T_UNDEFINED ? -1 : 1 ) : 0; }
/* 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 full_uas ) { int lret, my_err; context_p ctx_backup; /* is T still up-to-date ? */ LM_DBG("transaction on entrance=%p\n",T); if ( T && T!=T_UNDEFINED ) { LM_DBG("transaction already in process %p\n", T ); return E_SCRIPT; } 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_F, 0 )<0) { LM_ERR("parse_headers failed\n"); return E_BAD_REQ; } if ((p_msg->parsed_flag & HDR_EOH_F)!=HDR_EOH_F) { LM_ERR("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); } /* hide the transaction from the upper layer */ t_unref( p_msg ); /* 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 */ if (e2eack_T->relaied_reply_branch==-2) { /* if a ACK for a local replied transaction, release the INVITE transaction and break the script -> simply absorb the ACK request */ t_release_transaction(e2eack_T); return 0; } LM_DBG("building branch for end2end ACK - flags=%X\n",e2eack_T->flags); /* to ensure unigueness acros time and space, compute the ACK * branch in the same maner as for INVITE, but put a t->branch * value that cannot exist for that INVITE - as it is compute as * an INVITE, it will not overlapp with other INVITEs or requests. * But the faked value for t->branch guarantee no overalap with * corresponding INVITE --bogdan */ if (!t_calc_branch(e2eack_T, e2eack_T->nr_of_outgoings+1, p_msg->add_to_branch_s, &p_msg->add_to_branch_len )) { LM_ERR("ACK branch computation failed\n"); } 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 */ return 1; my_err=new_t(p_msg, full_uas); if (my_err<0) { LM_ERR("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)) { LM_ERR("unresolvable via1\n"); put_on_wait( T ); t_unref(p_msg); return E_BAD_VIA; } if (auto_100trying && p_msg->REQ_METHOD==METHOD_INVITE) { ctx_backup = current_processing_ctx; current_processing_ctx = NULL; t_reply( T, p_msg , 100 , &relay_reason_100); current_processing_ctx = ctx_backup; } return 1; new_err: UNLOCK_HASH(p_msg->hash_index); return my_err; }
/* 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 alredy found!\n"); else DBG("DEBUG: t_check: T previously sought and not found\n"); } return T ? (T==T_UNDEFINED ? -1 : 1 ) : 0; }