/*
* $Id: tm.c 2246 2007-05-18 10:37:38Z bogdan_iancu $
*
* Copyright (C) 2001-2003 FhG Fokus
*
* This file is part of openser, a free SIP server.
*
* openser 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
*
* openser 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-02-18 added t_forward_nonack_{udp, tcp}, t_relay_to_{udp,tcp},
* t_replicate_{udp, tcp} (andrei)
* 2003-02-19 added t_rely_{udp, tcp} (andrei)
* 2003-03-06 voicemail changes accepted (jiri)
* 2003-03-10 module export interface updated to the new format (andrei)
* 2003-03-16 flags export parameter added (janakj)
* 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei)
* 2003-03-30 set_kr for requests only (jiri)
* 2003-04-05 s/reply_route/failure_route, onreply_route introduced (jiri)
* 2003-04-14 use protocol from uri (jiri)
* 2003-07-07 added t_relay_to_tls, t_replicate_tls, t_forward_nonack_tls
* added #ifdef USE_TCP, USE_TLS
* removed t_relay_{udp,tcp,tls} (andrei)
* 2003-09-26 added t_forward_nonack_uri() - same as t_forward_nonack() but
* takes no parameters -> forwards to uri (bogdan)
* 2004-02-11 FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri)
* 2004-02-18 t_reply exported via FIFO - imported from VM (bogdan)
* 2004-10-01 added a new param.: restart_fr_on_each_reply (andrei)
* 2005-05-30 light version of tm_load - find_export dropped -> module
* interface dosen't need to export internal functions (bogdan)
* 2006-01-15 merged functions which diff only via proto (like t_relay,
* t_replicate and t_forward_nonack) (bogdan)
* 2007-01-25 t_forward_nonack removed as it merged into t_relay,
* t_replicate also accepts flags for controlling DNS failover
* (bogdan)
*/
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include "../../sr_module.h"
#include "../../dprint.h"
#include "../../error.h"
#include "../../ut.h"
#include "../../script_cb.h"
#include "../../mi/mi.h"
#include "../../usr_avp.h"
#include "../../mem/mem.h"
#include "../../unixsock_server.h"
#include "../../items.h"
#include "sip_msg.h"
#include "h_table.h"
#include "ut.h"
#include "t_reply.h"
#include "uac_unixsock.h"
#include "t_fwd.h"
#include "t_lookup.h"
#include "callid.h"
#include "t_cancel.h"
#include "t_fifo.h"
#include "mi.h"
#include "tm_load.h"
MODULE_VERSION
/* item functions */
static int it_get_tm_branch_idx(struct sip_msg *msg, xl_value_t *res,
xl_param_t *param, int flags);
static int it_get_tm_reply_code(struct sip_msg *msg, xl_value_t *res,
xl_param_t *param, int flags);
/* fixup functions */
static int fixup_t_send_reply(void** param, int param_no);
static int fixup_str2int( void** param, int param_no);
static int fixup_str2regexp(void** param, int param_no);
static int fixup_local_replied(void** param, int param_no);
static int fixup_t_relay1(void** param, int param_no);
static int fixup_t_relay2(void** param, int param_no);
static int fixup_t_replicate(void** param, int param_no);
/* init functions */
static int mod_init(void);
static int child_init(int rank);
/* exported functions */
inline static int w_t_release(struct sip_msg* msg, char* , char* );
inline static int w_t_newtran(struct sip_msg* p_msg, char* , char* );
inline static int w_t_reply(struct sip_msg *msg, char* code, char* text);
inline static int w_t_relay(struct sip_msg *p_msg , char *proxy, char* flags);
inline static int w_t_replicate(struct sip_msg *p_msg, char *dst,char* );
inline static int w_t_on_negative(struct sip_msg* msg, char *go_to, char* );
inline static int w_t_on_reply(struct sip_msg* msg, char *go_to, char* );
inline static int w_t_on_branch(struct sip_msg* msg, char *go_to, char* );
inline static int t_check_status(struct sip_msg* msg, char *regexp, char* );
inline static int t_flush_flags(struct sip_msg* msg, char*, char* );
inline static int t_local_replied(struct sip_msg* msg, char *type, char* );
inline static int t_check_trans(struct sip_msg* msg, char* , char* );
inline static int t_was_cancelled(struct sip_msg* msg, char* , char* );
/* strings with avp definition */
static char *fr_timer_param = NULL;
static char *fr_inv_timer_param = NULL;
/* module parameteres */
int tm_enable_stats = 1;
/* statistic variables */
stat_var *tm_rcv_rpls;
stat_var *tm_rld_rpls;
stat_var *tm_loc_rpls;
stat_var *tm_uas_trans;
stat_var *tm_uac_trans;
stat_var *tm_trans_2xx;
stat_var *tm_trans_3xx;
stat_var *tm_trans_4xx;
stat_var *tm_trans_5xx;
stat_var *tm_trans_6xx;
stat_var *tm_trans_inuse;
static cmd_export_t cmds[]={
{"t_newtran", w_t_newtran, 0, 0,
REQUEST_ROUTE},
{"t_reply", w_t_reply, 2, fixup_t_send_reply,
REQUEST_ROUTE | FAILURE_ROUTE },
{"t_release", w_t_release, 0, 0,
REQUEST_ROUTE},
{"t_replicate", w_t_replicate, 1, fixup_t_replicate,
REQUEST_ROUTE},
{"t_replicate", w_t_replicate, 2, fixup_t_replicate,
REQUEST_ROUTE},
{"t_relay", w_t_relay, 0, 0,
REQUEST_ROUTE | FAILURE_ROUTE },
{"t_relay", w_t_relay, 1, fixup_t_relay1,
REQUEST_ROUTE | FAILURE_ROUTE },
{"t_relay", w_t_relay, 2, fixup_t_relay2,
REQUEST_ROUTE | FAILURE_ROUTE },
{"t_on_failure", w_t_on_negative, 1, fixup_str2int,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE },
{"t_on_reply", w_t_on_reply, 1, fixup_str2int,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE },
{"t_on_branch", w_t_on_branch, 1, fixup_str2int,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE },
{"t_check_status", t_check_status, 1, fixup_str2regexp,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE },
{"t_write_req", t_write_req, 2, fixup_t_write,
REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE },
{"t_write_unix", t_write_unix, 2, fixup_t_write,
REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE },
{"t_flush_flags", t_flush_flags, 0, 0,
REQUEST_ROUTE | BRANCH_ROUTE },
{"t_local_replied", t_local_replied, 1, fixup_local_replied,
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE },
{"t_check_trans", t_check_trans, 0, 0,
REQUEST_ROUTE | BRANCH_ROUTE },
{"t_was_cancelled", t_was_cancelled, 0, 0,
FAILURE_ROUTE | ONREPLY_ROUTE },
{"load_tm", (cmd_function)load_tm, 0, 0,
0},
{0,0,0,0,0}
};
static param_export_t params[]={
{"ruri_matching", INT_PARAM,
&ruri_matching},
{"via1_matching", INT_PARAM,
&via1_matching},
{"fr_timer", INT_PARAM,
&(timer_id2timeout[FR_TIMER_LIST])},
{"fr_inv_timer", INT_PARAM,
&(timer_id2timeout[FR_INV_TIMER_LIST])},
{"wt_timer", INT_PARAM,
&(timer_id2timeout[WT_TIMER_LIST])},
{"delete_timer", INT_PARAM,
&(timer_id2timeout[DELETE_LIST])},
{"T1_timer", INT_PARAM,
&(timer_id2timeout[RT_T1_TO_1])},
{"T2_timer", INT_PARAM,
&(timer_id2timeout[RT_T2])},
{"noisy_ctimer", INT_PARAM,
&noisy_ctimer},
{"unix_tx_timeout", INT_PARAM,
&tm_unix_tx_timeout},
{"restart_fr_on_each_reply", INT_PARAM,
&restart_fr_on_each_reply},
{"fr_timer_avp", STR_PARAM,
&fr_timer_param},
{"fr_inv_timer_avp", STR_PARAM,
&fr_inv_timer_param},
{"tw_append", STR_PARAM|USE_FUNC_PARAM,
(void*)parse_tw_append },
{ "enable_stats", INT_PARAM,
&tm_enable_stats },
{ "pass_provisional_replies", INT_PARAM,
&pass_provisional_replies },
{ "syn_branch", INT_PARAM,
&syn_branch },
{ "onreply_avp_mode", INT_PARAM,
&onreply_avp_mode },
{0,0,0}
};
static stat_export_t mod_stats[] = {
{"received_replies" , 0, &tm_rcv_rpls },
{"relayed_replies" , 0, &tm_rld_rpls },
{"local_replies" , 0, &tm_loc_rpls },
{"UAS_transactions" , 0, &tm_uas_trans },
{"UAC_transactions" , 0, &tm_uac_trans },
{"2xx_transactions" , 0, &tm_trans_2xx },
{"3xx_transactions" , 0, &tm_trans_3xx },
{"4xx_transactions" , 0, &tm_trans_4xx },
{"5xx_transactions" , 0, &tm_trans_5xx },
{"6xx_transactions" , 0, &tm_trans_6xx },
{"inuse_transactions" , STAT_NO_RESET, &tm_trans_inuse },
{0,0,0}
};
/**
* pseudo-variables exported by TM module
* { name, function, type, xl_param_t}
* type: can be 0 if you do not want to use it in identifying the PV
* xl_param_t: is given as parameter to 'function'
*/
static item_export_t mod_items[] = {
{ "T_branch_idx", it_get_tm_branch_idx, 100, {{0, 0}, 0, 0} },
{ "T_reply_code", it_get_tm_reply_code, 100, {{0, 0}, 0, 0} },
{ 0, 0, 0, {{0, 0}, 0, 0} }
};
static mi_export_t mi_cmds [] = {
{MI_TM_UAC, mi_tm_uac_dlg, MI_ASYNC_RPL_FLAG, 0, 0 },
{MI_TM_CANCEL, mi_tm_cancel, 0, 0, 0 },
{MI_TM_HASH, mi_tm_hash, MI_NO_INPUT_FLAG, 0, 0 },
{MI_TM_REPLY, mi_tm_reply, 0, 0, 0 },
{0,0,0,0,0}
};
#ifdef STATIC_TM
struct module_exports tm_exports = {
#else
struct module_exports exports= {
#endif
"tm", /* module name*/
DEFAULT_DLFLAGS, /* dlopen flags */
cmds, /* exported functions */
params, /* exported variables */
mod_stats, /* exported statistics */
mi_cmds, /* exported MI functions */
mod_items, /* exported pseudo-variables */
mod_init, /* module initialization function */
(response_function) reply_received,
(destroy_function) tm_shutdown,
child_init /* per-child init function */
};
/**************************** fixup functions ******************************/
static int flag_fixup(void** param, int param_no)
{
unsigned int flags;
str s;
if (param_no == 1) {
s.s = (char*)*param;
s.len = strlen(s.s);
if ( strno2int(&s, &flags )<0 ) {
return -1;
}
pkg_free(*param);
*param = (void*)(unsigned long int)(flags<<1);
}
return 0;
}
static int fixup_t_replicate(void** param, int param_no)
{
str *s;
if (param_no == 1) {
/* string */
s = (str*)pkg_malloc( sizeof(str) );
if (s==0) {
LOG(L_ERR,"ERROR:tm:fixup_str: no more pkg mem\n");
return E_OUT_OF_MEM;
}
s->s = (char*)*param;
s->len = strlen(s->s);
*param = (void*)s;
} else {
/* flags */
if (flag_fixup( param, 1)!=0) {
LOG(L_ERR, "ERROR:TM:fixup_t_relay2: bad flags <%s>\n",
(char *)(*param));
return E_CFG;
}
}
return 0;
}
static int fixup_str2int( void** param, int param_no)
{
unsigned long go_to;
int err;
if (param_no==1) {
go_to=str2s(*param, strlen(*param), &err );
if (err==0) {
pkg_free(*param);
*param=(void *)go_to;
return 0;
} else {
LOG(L_ERR, "ERROR:tm:fixup_str2int: bad number <%s>\n",
(char *)(*param));
return E_CFG;
}
}
return 0;
}
static int fixup_phostport2proxy(void** param, int param_no)
{
struct proxy_l *proxy;
char *s;
int port;
int proto;
str host;
if (param_no!=1) {
LOG(L_CRIT,"BUG:tm:fixup_phostport2proxy: called with more than "
" one parameter\n");
return E_BUG;
}
s = (char *) (*param);
if (s==0 || *s==0) {
LOG(L_CRIT,"ERROR:tm:fixup_phostport2proxy: empty parameter\n");
return E_UNSPEC;
}
if (parse_phostport( s, strlen(s), &host.s, &host.len, &port, &proto)!=0){
LOG(L_CRIT,"ERROR:tm:fixup_phostport2proxy: invalid parameter "
"<%s>\n",s);
return E_UNSPEC;
}
proxy = mk_proxy( &host, port, proto, 0);
if (proxy==0) {
LOG(L_ERR, "ERROR:tm:fixup_phostport2proxy: failed to resolve "
"<%.*s>\n", host.len, host.s );
return ser_error;
}
*(param)=proxy;
return 0;
}
static int fixup_t_relay1(void** param, int param_no)
{
if (flag_fixup( param, 1)==0) {
/* param is flag -> move it as second param */
*((void**)(((char*)param)+sizeof(action_elem_t))) = *param;
*param = 0;
return 0;
} else if (fixup_phostport2proxy( param, 1)==0 ) {
/* param is OBP -> nothing else to do */
return 0;
} else {
LOG(L_ERR,"ERROR:TM:fixup_t_relay1: param is neither flag, nor OBP "
"<%s>\n",(char *)(*param));
return E_CFG;
}
}
static int fixup_t_relay2(void** param, int param_no)
{
if (param_no==1) {
return fixup_phostport2proxy( param, param_no);
} else if (param_no==2) {
if (flag_fixup( param, 1)!=0) {
LOG(L_ERR, "ERROR:TM:fixup_t_relay2: bad flags <%s>\n",
(char *)(*param));
return E_CFG;
}
}
return 0;
}
/* (char *code, char *reason_phrase)==>(int code, r_p as is) */
static int fixup_t_send_reply(void** param, int param_no)
{
unsigned long code;
int err;
str *s;
if (param_no==1){
code=str2s(*param, strlen(*param), &err);
if (err==0){
pkg_free(*param);
*param=(void*)code;
return 0;
}else{
LOG(L_ERR, "ERROR:tm:fixup_t_send_reply: bad number <%s>\n",
(char*)(*param));
return E_UNSPEC;
}
} else if (param_no==2) {
s = (str*)pkg_malloc(sizeof(str));
if (s==0) {
LOG(L_ERR, "ERROR:sl:fixup_t_send_reply: no more pkg mem\n");
return E_OUT_OF_MEM;
}
s->s = (char*)*param;
s->len = strlen(s->s);
*param = (void*)s;
}
return 0;
}
static int fixup_str2regexp(void** param, int param_no)
{
regex_t* re;
if (param_no==1) {
if ((re=pkg_malloc(sizeof(regex_t)))==0)
return E_OUT_OF_MEM;
if (regcomp(re, *param, REG_EXTENDED|REG_ICASE|REG_NEWLINE) ) {
pkg_free(re);
LOG(L_ERR,"ERROR: %s : bad re %s\n", exports.name, (char*)*param);
return E_BAD_RE;
}
/* free string */
pkg_free(*param);
/* replace it with the compiled re */
*param=re;
} else {
LOG(L_ERR,"ERROR: fixup_str2regexp called with parameter != 1\n");
return E_BUG;
}
return 0;
}
static int fixup_local_replied(void** param, int param_no)
{
char *val;
int n = 0;
if (param_no==1) {
val = (char*)*param;
if (strcasecmp(val,"all")==0) {
n = 0;
} else if (strcasecmp(val,"branch")==0) {
n = 1;
} else if (strcasecmp(val,"last")==0) {
n = 2;
} else {
LOG(L_ERR,"ERROR:tm:fixup_local_replied: invalid param \"%s\"\n",
val);
return E_CFG;
}
/* free string */
pkg_free(*param);
/* replace it with the compiled re */
*param=(void*)(long)n;
} else {
LOG(L_ERR,"ERROR:fixup_local_replied: called with parameter != 1\n");
return E_BUG;
}
return 0;
}
/***************************** init functions *****************************/
int load_tm( struct tm_binds *tmb)
{
tmb->register_tmcb = register_tmcb;
/* relay function */
tmb->t_relay = w_t_relay;
/* reply functions */
tmb->t_reply = (treply_f)w_t_reply;
tmb->t_reply_with_body = t_reply_with_body;
/* transaction location/status functions */
tmb->t_newtran = t_newtran;
tmb->t_is_local = t_is_local;
tmb->t_get_trans_ident = t_get_trans_ident;
tmb->t_lookup_ident = t_lookup_ident;
tmb->t_gett = get_t;
tmb->t_get_picked = t_get_picked_branch;
tmb->t_lookup_original_t = t_lookupOriginalT;
tmb->t_cancel_uac = t_uac_cancel;
tmb->unref_cell = t_unref_cell;
tmb->t_setkr = set_kr;
/* tm uac functions */
tmb->t_addblind = add_blind_uac;
tmb->t_request_within = req_within;
tmb->t_request_outside = req_outside;
tmb->t_request = request;
tmb->new_dlg_uac = new_dlg_uac;
tmb->dlg_response_uac = dlg_response_uac;
tmb->new_dlg_uas = new_dlg_uas;
tmb->dlg_request_uas = dlg_request_uas;
tmb->free_dlg = free_dlg;
tmb->print_dlg = print_dlg;
return 1;
}
static int do_t_unref( struct sip_msg *foo, void *bar)
{
struct cell *t;
t = get_cancelled_t();
if (t!=NULL && t!=T_UNDEFINED)
t_unref_cell(t);
t = get_e2eack_t();
if (t!=NULL && t!=T_UNDEFINED)
t_unref_cell(t);
return t_unref(foo);
}
static int script_init( struct sip_msg *foo, void *bar)
{
/* we primarily reset all private memory here to make sure
* private values left over from previous message will
* not be used again */
/* make sure the new message will not inherit previous
* message's t_on_negative value
*/
t_on_negative( 0 );
t_on_reply(0);
t_on_branch(0);
set_t(T_UNDEFINED);
reset_cancelled_t();
reset_e2eack_t();
/* reset the kr status */
reset_kr();
return 1;
}
static int mod_init(void)
{
LOG(L_INFO,"TM - initializing...\n");
/* checking if we have sufficient bitmap capacity for given
maximum number of branches */
if (MAX_BRANCHES+1>31) {
LOG(L_CRIT, "Too many max UACs for UAC branch_bm_t bitmap: %d\n",
MAX_BRANCHES );
return -1;
}
/* if statistics are disabled, prevent their registration to core */
if (tm_enable_stats==0)
#ifdef STATIC_TM
tm_exports.stats = 0;
#else
exports.stats = 0;
#endif
if (init_callid() < 0) {
LOG(L_CRIT, "Error while initializing Call-ID generator\n");
return -1;
}
if (unixsock_register_cmd("t_uac_dlg", unixsock_uac) < 0) {
LOG(L_CRIT, "cannot register t_uac with the unix server\n");
return -1;
}
if (unixsock_register_cmd("t_uac_cancel", unixsock_uac_cancel) < 0) {
LOG(L_CRIT, "cannot register t_uac_cancel with the unix server\n");
return -1;
}
if (unixsock_register_cmd("t_hash", unixsock_hash) < 0) {
LOG(L_CRIT, "cannot register t_hash with the unix server\n");
return -1;
}
if (unixsock_register_cmd("t_reply", unixsock_t_reply) < 0) {
LOG(L_CRIT, "cannot register t_reply with the unix server\n");
return -1;
}
/* building the hash table*/
if (!init_hash_table()) {
LOG(L_ERR, "ERROR:tm:mod_init: initializing hash_table failed\n");
return -1;
}
/* init static hidden values */
init_t();
if (!tm_init_timers()) {
LOG(L_ERR, "ERROR:tm:mod_init: timer init failed\n");
return -1;
}
/* register the timer functions */
if (register_timer( timer_routine , 0, 1 )<0) {
LOG(L_ERR, "ERROR:tm:mod_init: failed to register timer\n");
return -1;
}
if (register_utimer( utimer_routine , 0, 100*1000 )<0) {
LOG(L_ERR, "ERROR:tm:mod_init: failed to register utimer\n");
return -1;
}
if (uac_init()==-1) {
LOG(L_ERR, "ERROR:tm:mod_init: uac_init failed\n");
return -1;
}
if (init_tmcb_lists()!=1) {
LOG(L_CRIT, "ERROR:tm:mod_init: failed to init tmcb lists\n");
return -1;
}
tm_init_tags();
init_twrite_lines();
if (init_twrite_sock() < 0) {
LOG(L_ERR, "ERROR:tm:mod_init: Unable to create socket\n");
return -1;
}
/* register post-script clean-up function */
if (register_script_cb( do_t_unref, POST_SCRIPT_CB|REQ_TYPE_CB, 0)<0 ) {
LOG(L_ERR,"ERROR:tm:mod_init: failed to register POST request "
"callback\n");
return -1;
}
if (register_script_cb( script_init, PRE_SCRIPT_CB|REQ_TYPE_CB , 0)<0 ) {
LOG(L_ERR,"ERROR:tm:mod_init: failed to register PRE request "
"callback\n");
return -1;
}
if ( init_avp_params( fr_timer_param, fr_inv_timer_param)<0 ){
LOG(L_ERR,"ERROR:tm:mod_init: failed to process timer AVPs\n");
return -1;
}
return 0;
}
static int child_init(int rank)
{
if (child_init_callid(rank) < 0) {
LOG(L_ERR, "ERROR:tm:child_init: Error while initializing "
"Call-ID generator\n");
return -2;
}
return 0;
}
/**************************** wrapper functions ***************************/
static int t_check_status(struct sip_msg* msg, char *regexp, char *foo)
{
regmatch_t pmatch;
struct cell *t;
char *status;
char backup;
int branch;
int n;
/* first get the transaction */
t = get_t();
if ( t==0 || t==T_UNDEFINED ) {
LOG(L_ERR, "ERROR: t_check_status: cannot check status for a reply "
"which has no T-state established\n");
return -1;
}
backup = 0;
switch (route_type) {
case REQUEST_ROUTE:
/* use the status of the last sent reply */
status = int2str( t->uas.status, 0);
break;
case ONREPLY_ROUTE:
/* use the status of the current reply */
status = msg->first_line.u.reply.status.s;
backup = status[msg->first_line.u.reply.status.len];
status[msg->first_line.u.reply.status.len] = 0;
break;
case FAILURE_ROUTE:
/* use the status of the winning reply */
if ( (branch=t_get_picked_branch())<0 ) {
LOG(L_CRIT,"BUG:t_check_status: no picked branch (%d) for"
" a final response in MODE_ONFAILURE\n", branch);
return -1;
}
status = int2str( t->uac[branch].last_received , 0);
break;
default:
LOG(L_ERR,"ERROR:t_check_status: unsupported route_type %d\n",
route_type);
return -1;
}
DBG("DEBUG:t_check_status: checked status is <%s>\n",status);
/* do the checking */
n = regexec((regex_t*)regexp, status, 1, &pmatch, 0);
if (backup) status[msg->first_line.u.reply.status.len] = backup;
if (n!=0) return -1;
return 1;
}
inline static int t_check_trans(struct sip_msg* msg, char *foo, char *bar)
{
struct cell *trans;
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 = 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;
}
}
}
static int t_flush_flags(struct sip_msg* msg, char *foo, char *bar)
{
struct cell *t;
/* first get the transaction */
t = get_t();
if ( t==0 || t==T_UNDEFINED) {
LOG(L_ERR, "ERROR: t_flush_flags: cannot flush flags for a message "
"which has no T-state established\n");
return -1;
}
/* do the flush */
t->uas.request->flags = msg->flags;
return 1;
}
inline static int t_local_replied(struct sip_msg* msg, char *type, char *bar)
{
struct cell *t;
int branch;
int i;
t = get_t();
if (t==0 || t==T_UNDEFINED) {
LOG(L_ERR,"ERROR:t_local_replied: no trasaction created\n");
return -1;
}
switch ( (int)(long)type ) {
/* check all */
case 0:
for( i=t->first_branch ; i<t->nr_of_outgoings ; i++ ) {
if (t->uac[i].flags&T_UAC_HAS_RECV_REPLY)
return -1;
}
return 1;
/* check branch */
case 1:
if (route_type==FAILURE_ROUTE) {
/* use the the winning reply */
if ( (branch=t_get_picked_branch())<0 ) {
LOG(L_CRIT,"BUG:t_local_replied: no picked branch (%d) for"
" a final response in MODE_ONFAILURE\n", branch);
return -1;
}
if (t->uac[branch].flags&T_UAC_HAS_RECV_REPLY)
return -1;
return 1;
}
return -1;
/* check last */
case 2:
if (route_type==FAILURE_ROUTE) {
/* use the the winning reply */
if ( (branch=t_get_picked_branch())<0 ) {
LOG(L_CRIT,"BUG:t_local_replied: no picked branch (%d) for"
" a final response in MODE_ONFAILURE\n", branch);
return -1;
}
if (t->uac[branch].reply==FAKED_REPLY)
return 1;
return -1;
}
return (t->relaied_reply_branch==-2)?1:-1;
default:
return -1;
}
}
static int t_was_cancelled(struct sip_msg* msg, char *foo, char *bar)
{
struct cell *t;
/* first get the transaction */
t = get_t();
if ( t==0 || t==T_UNDEFINED ) {
LOG(L_ERR, "ERROR:tm:t_was_cancelled: cannot check cancel flag for "
"a reply without a transaction\n");
return -1;
}
return (t->flags&T_WAS_CANCELLED_FLAG)?1:-1;
}
inline static int w_t_reply(struct sip_msg* msg, char* str1, char* str2)
{
struct cell *t;
if (msg->REQ_METHOD==METHOD_ACK) {
LOG(L_WARN, "WARNING: t_reply: ACKs are not replied\n");
return -1;
}
t=get_t();
if ( t==0 || t==T_UNDEFINED ) {
LOG(L_ERR, "ERROR: t_reply: cannot send a t_reply to a message "
"for which no T-state has been established\n");
return -1;
}
/* if called from reply_route, make sure that unsafe version
* is called; we are already in a mutex and another mutex in
* the safe version would lead to a deadlock
*/
switch (route_type) {
case FAILURE_ROUTE:
DBG("DEBUG: t_reply_unsafe called from w_t_reply\n");
return t_reply_unsafe(t, msg, (unsigned int)(long)str1,(str*)str2);
case REQUEST_ROUTE:
return t_reply( t, msg, (unsigned int)(long) str1, (str*)str2);
default:
LOG(L_CRIT, "BUG:tm:w_t_reply: unsupported route_type (%d)\n",
route_type);
return -1;
}
}
inline static int w_t_release(struct sip_msg* msg, char* str, char* str2)
{
struct cell *t;
t=get_t();
if ( t && t!=T_UNDEFINED )
return t_release_transaction( t );
return 1;
}
inline static int w_t_newtran( struct sip_msg* p_msg, char* foo, char* bar )
{
/* t_newtran returns 0 on error (negative value means
'transaction exists' */
return t_newtran( p_msg );
}
inline static int w_t_on_negative( struct sip_msg* msg, char *go_to, char *foo)
{
t_on_negative( (unsigned int )(long) go_to );
return 1;
}
inline static int w_t_on_reply( struct sip_msg* msg, char *go_to, char *foo )
{
t_on_reply( (unsigned int )(long) go_to );
return 1;
}
inline static int w_t_on_branch( struct sip_msg* msg, char *go_to, char *foo )
{
t_on_branch( (unsigned int )(long) go_to );
return 1;
}
inline static int w_t_replicate(struct sip_msg *p_msg, char *dst, char *flags)
{
return t_replicate( p_msg, (str*)dst, (int)(long)flags);
}
inline static int w_t_relay( struct sip_msg *p_msg , char *proxy, char *flags)
{
struct cell *t;
int ret;
t=get_t();
if (!t || t==T_UNDEFINED) {
/* no transaction yet */
if (route_type==FAILURE_ROUTE) {
LOG(L_CRIT, "BUG:tm:w_t_relay: undefined T\n");
return -1;
}
return t_relay_to( p_msg, (struct proxy_l *)proxy, (int)(long)flags );
} else {
/* transaction already created */
if ( route_type!=REQUEST_ROUTE && route_type!=FAILURE_ROUTE )
goto route_err;
if (p_msg->REQ_METHOD==METHOD_ACK) {
/* local ACK*/
t_release_transaction(t);
return 1;
}
if (((int)(long)flags)&TM_T_REPLY_nodnsfo_FLAG)
t->flags|=T_NO_DNS_FAILOVER_FLAG;
ret = t_forward_nonack( t, p_msg, (struct proxy_l *)proxy);
if (ret<=0 )
LOG(L_ERR, "ERROR:tm:w_t_relay: t_forward_nonack failed\n");
return ret;
}
route_err:
LOG(L_CRIT, "ERROR:tm:w_t_relay: unsupported route type: %d\n",
route_type);
return 0;
}
/* item functions */
static int it_get_tm_branch_idx(struct sip_msg *msg, xl_value_t *res,
xl_param_t *param, int flags)
{
extern int _tm_branch_index;
int l = 0;
char *ch = NULL;
if(msg==NULL || res==NULL)
return -1;
ch = int2str(_tm_branch_index, &l);
res->rs.s = ch;
res->rs.len = l;
res->ri = _tm_branch_index;
res->flags = XL_VAL_STR|XL_VAL_INT|XL_TYPE_INT;
return 0;
}
static int it_get_tm_reply_code(struct sip_msg *msg, xl_value_t *res,
xl_param_t *param, int flags)
{
struct cell *t;
int code;
int branch;
if(msg==NULL || res==NULL)
return -1;
/* first get the transaction */
if (t_check( msg , 0 )==-1) return -1;
if ( (t=get_t())==0) {
/* no T */
code = 0;
} else {
switch (route_type) {
case REQUEST_ROUTE:
/* use the status of the last sent reply */
code = t->uas.status;
break;
case ONREPLY_ROUTE:
/* use the status of the current reply */
code = msg->first_line.u.reply.statuscode;
break;
case FAILURE_ROUTE:
/* use the status of the winning reply */
if ( (branch=t_get_picked_branch())<0 ) {
LOG(L_CRIT,"BUG:tm:it_get_tm_reply_code: no picked "
"branch (%d) for a final response in MODE_ONFAILURE\n",
branch);
code = 0;
} else {
code = t->uac[branch].last_received;
}
break;
default:
LOG(L_ERR,"ERROR:tm:it_get_tm_reply_code: unsupported "
"route_type %d\n", route_type);
code = 0;
}
}
DBG("DEBUG:it_get_tm_reply_code: reply code is <%d>\n",code);
res->rs.s = int2str( code, &res->rs.len);
res->ri = code;
res->flags = XL_VAL_STR|XL_VAL_INT|XL_TYPE_INT;
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1