You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/tm/t_reply.c

2611 lines
81 KiB

/*
* $Id$
*
*
* 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-19 faked lump list created in on_reply handlers
* 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri)
* 2003-02-13 updated to use rb->dst (andrei)
* 2003-02-18 replaced TOTAG_LEN w/ TOTAG_VALUE_LEN (TOTAG_LEN was defined
* twice with different values!) (andrei)
* 2003-02-28 scratchpad compatibility abandoned (jiri)
* 2003-03-01 kr set through a function now (jiri)
* 2003-03-06 saving of to-tags for ACK/200 matching introduced,
* voicemail changes accepted, updated to new callback
* names (jiri)
* 2003-03-10 fixed new to tag bug/typo (if w/o {}) (andrei)
* 2003-03-16 removed _TOTAG (jiri)
* 2003-03-31 200 for INVITE/UAS resent even for UDP (jiri)
* 2003-03-31 removed msg->repl_add_rm (andrei)
* 2003-04-05 s/reply_route/failure_route, onreply_route introduced (jiri)
* 2003-04-14 local acks generated before reply processing to avoid
* delays in length reply processing (like opening TCP
* connection to an unavailable destination) (jiri)
* 2003-09-11 updates to new build_res_buf_from_sip_req() interface (bogdan)
* 2003-09-11 t_reply_with_body() reshaped to use reply_lumps +
* build_res_buf_from_sip_req() instead of
* build_res_buf_with_body_from_sip_req() (bogdan)
* 2003-11-05 flag context updated from failure/reply handlers back
* to transaction context (jiri)
* 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan)
* 2003-12-04 global TM callbacks switched to per transaction callbacks
* (bogdan)
* 2004-02-06: support for user pref. added - destroy_avps (bogdan)
* 2003-11-05 flag context updated from failure/reply handlers back
* to transaction context (jiri)
* 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan)
* 2004-02-13: t->is_invite and t->local replaced with flags (bogdan)
* 2004-02-18 fifo_t_reply imported from vm module (bogdan)
* 2004-08-23 avp list is available from failure/on_reply routes (bogdan)
* 2004-10-01 added a new param.: restart_fr_on_each_reply (andrei)
* 2005-03-01 force for statefull replies the incoming interface of
* the request (bogdan)
* 2005-09-01 reverted to the old way of checking response.dst.send_sock
* in t_retransmit_reply & reply_light (andrei)
* 2005-11-09 updated to the new timers interface (andrei)
* 2006-02-07 named routes support (andrei)
* 2006-09-13 t_pick_branch will skip also over branches with empty reply
* t_should_relay_response will re-pick the branch if failure
* route /handlers added new branches (andrei)
* 2006-10-05 better final reply selection: t_pick_branch will prefer 6xx,
* if no 6xx reply => lowest class/code; if class==4xx =>
* prefer 401, 407, 415, 420 and 484 (andrei)
* 2006-10-12 dns failover when a 503 is received
* replace a 503 final relayed reply by a 500 (andrei)
* 2006-10-16 aggregate all the authorization headers/challenges when
* the final response is 401 or 407 (andrei)
* 2007-03-08 membar_write() used in update_totag_set(...)(andrei)
* 2007-03-15 build_local_ack: removed next_hop and replaced with dst to
* avoid resolving next_hop twice
* added TMCB_ONSEND callbacks support for replies & ACKs (andrei)
* 2007-05-28: build_ack() constructs the ACK from the
* outgoing INVITE instead of the incomming one.
* (it can be disabled with reparse_invite=0) (Miklos)
* 2007-09-03: drop_replies() has been introduced (Miklos)
* 2008-03-12 use cancel_b_method on 6xx (andrei)
* 2008-05-30 make sure the wait timer is started after we don't need t
* anymore to allow safe calls from fr_timer (andrei)
* 2009-06-01 Pre- and post-script callbacks of branch route are
* executed (Miklos)
* 2009-12-10 reply route is executed under lock to protect the avps (andrei)
* 2010-02-22 _reply() will cleanup any reply lumps that it might have added
* (andrei)
* 2010-02-26 added experimental support for final reply dropping, not
* enabled by default (performance hit) (andrei)
* 2010-02-26 cancel reason (rfc3326) basic support (andrei)
*
*/
/* Defines:
* TM_ONREPLY_FINAL_DROP_OK - allows dropping the final reply
* from the tm onreply_routes, but comes with a small performance
* hit (extra unlock()/lock() for each final reply when a onreply
* route is set).
*/
#ifdef EXTRA_DEBUG
#include <assert.h>
#endif
#include "../../comp_defs.h"
#include "../../hash_func.h"
#include "../../dprint.h"
#include "../../config.h"
#include "../../parser/parser_f.h"
#include "../../parser/parse_to.h"
#include "../../ut.h"
#include "../../timer.h"
#include "../../error.h"
#include "../../action.h"
#include "../../script_cb.h"
#include "../../dset.h"
#include "../../tags.h"
#include "../../route.h"
#include "../../data_lump.h"
#include "../../data_lump_rpl.h"
#include "../../usr_avp.h"
#ifdef WITH_XAVP
#include "../../usr_avp.h"
#endif
#include "../../atomic_ops.h" /* membar_write() */
#include "../../compiler_opt.h"
#ifdef USE_DST_BLACKLIST
#include "../../dst_blacklist.h"
#endif
#ifdef USE_DNS_FAILOVER
#include "../../dns_cache.h"
#include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
#endif
#include "defs.h"
#include "config.h"
#include "h_table.h"
#include "t_hooks.h"
#include "t_funcs.h"
#include "t_reply.h"
#include "t_cancel.h"
#include "t_msgbuilder.h"
#include "t_lookup.h"
#include "t_fwd.h"
#include "../../fix_lumps.h"
#include "../../sr_compat.h"
#include "t_stats.h"
#include "uac.h"
#ifdef NO_TM_ONREPLY_FINAL_DROP_OK
#undef TM_ONREPLY_FINAL_DROP_OK
#endif
/* private place where we create to-tags for replies */
/* janakj: made public, I need to access this value to store it in dialogs */
char tm_tags[TOTAG_VALUE_LEN];
/* bogdan: pack tm_tag buffer and len into a str to pass them to
* build_res_buf_from_sip_req() */
static str tm_tag = {tm_tags,TOTAG_VALUE_LEN};
char *tm_tag_suffix;
/* where to go if there is no positive reply */
static int goto_on_negative=0;
/* where to go on receipt of reply */
static int goto_on_reply=0;
/* where to go on receipt of reply without transaction context */
int goto_on_sl_reply=0;
/* how to deal with winning branch reply selection in failure_route
* can be overwritten per transaction with t_drop_replies(...)
* Values:
* - 0 - all branches are kept (default, and default ser 2.1.x behaviour)
* - 1 - all branches are discarded
* - 2 - braches of last step of serial forking are discarded
* - 3 - all branches are discarded if a new leg of serial forking
* is started (default kamailio 1.5.x behaviour)
*/
int failure_reply_mode = 0;
/* responses priority (used by t_pick_branch)
* 0xx is used only for the initial value (=> should have no chance to be
* selected => the highest value); 1xx is not used */
static unsigned short resp_class_prio[]={
32000, /* 0-99, special */
11000, /* 1xx, special, should never be used */
0, /* 2xx, high priority (not used, 2xx are immediately
forwarded and t_pick_branch will never be called if
a 2xx was received) */
3000, /* 3xx */
4000, /* 4xx */
5000, /* 5xx */
1000 /* 6xx, highest priority */
};
int t_get_reply_totag(struct sip_msg *msg, str *totag)
{
if(msg==NULL || totag==NULL) {
return -1;
}
calc_crc_suffix(msg, tm_tag_suffix);
*totag = tm_tag;
return 1;
}
static int picked_branch = -1;
/*! \brief returns the picked branch */
int t_get_picked_branch(void)
{
return picked_branch;
}
/* we store the reply_route # in private memory which is
then processed during t_relay; we cannot set this value
before t_relay creates transaction context or after
t_relay when a reply may arrive after we set this
value; that's why we do it how we do it, i.e.,
*inside* t_relay using hints stored in private memory
before t_relay is called
*/
void t_on_negative( unsigned int go_to )
{
struct cell *t = get_t();
/* in REPLY_ROUTE and FAILURE_ROUTE T will be set to current transaction;
* in REQUEST_ROUTE T will be set only if the transaction was already
* created; if not -> use the static variable */
if (!t || t==T_UNDEFINED )
goto_on_negative=go_to;
else
get_t()->on_negative = go_to;
}
void t_on_reply( unsigned int go_to )
{
struct cell *t = get_t();
/* in REPLY_ROUTE and FAILURE_ROUTE T will be set to current transaction;
* in REQUEST_ROUTE T will be set only if the transaction was already
* created; if not -> use the static variable */
if (!t || t==T_UNDEFINED )
goto_on_reply=go_to;
else
get_t()->on_reply = go_to;
}
unsigned int get_on_negative()
{
return goto_on_negative;
}
unsigned int get_on_reply()
{
return goto_on_reply;
}
void tm_init_tags()
{
init_tags(tm_tags, &tm_tag_suffix,
"SER-TM/tags", TM_TAG_SEPARATOR );
}
/* returns 0 if the message was previously acknowledged
* (i.e., no E2EACK callback is needed) and one if the
* callback shall be executed */
int unmatched_totag(struct cell *t, struct sip_msg *ack)
{
struct totag_elem *i;
str *tag;
if (parse_headers(ack, HDR_TO_F,0)==-1 ||
!ack->to ) {
LOG(L_ERR, "ERROR: unmatched_totag: To invalid\n");
return 1;
}
tag=&get_to(ack)->tag_value;
i=t->fwded_totags;
while(i){
membar_depends(); /* make sure we don't see some old i content
(needed on CPUs like Alpha) */
if (i->tag.len==tag->len
&& memcmp(i->tag.s, tag->s, tag->len)==0) {
DBG("DEBUG: totag for e2e ACK found: %d\n", i->acked);
/* mark totag as acked and return 1 if this was the first ack
* and 0 otherwise */
return (atomic_get_and_set_int(&i->acked, 1)==0);
}
i=i->next;
}
/* surprising: to-tag never sighted before */
return 1;
}
static inline void update_local_tags(struct cell *trans,
struct bookmark *bm, char *dst_buffer,
char *src_buffer /* to which bm refers */)
{
if (bm->to_tag_val.s) {
trans->uas.local_totag.s=bm->to_tag_val.s-src_buffer+dst_buffer;
trans->uas.local_totag.len=bm->to_tag_val.len;
}
}
/* append a newly received tag from a 200/INVITE to
* transaction's set; (only safe if called from within
* a REPLY_LOCK); it returns 1 if such a to tag already
* exists
*/
inline static int update_totag_set(struct cell *t, struct sip_msg *ok)
{
struct totag_elem *i, *n;
str *tag;
char *s;
if (!ok->to || !ok->to->parsed) {
LOG(L_ERR, "ERROR: update_totag_set: to not parsed\n");
return 0;
}
tag=&get_to(ok)->tag_value;
if (!tag->s) {
DBG("ERROR: update_totag_set: no tag in to\n");
return 0;
}
for (i=t->fwded_totags; i; i=i->next) {
if (i->tag.len==tag->len
&& memcmp(i->tag.s, tag->s, tag->len) ==0 ){
/* to tag already recorded */
#ifdef XL_DEBUG
LOG(L_CRIT, "DEBUG: update_totag_set: totag retransmission\n");
#else
DBG("DEBUG: update_totag_set: totag retransmission\n");
#endif
return 1;
}
}
/* that's a new to-tag -- record it */
shm_lock();
n=(struct totag_elem*) shm_malloc_unsafe(sizeof(struct totag_elem));
s=(char *)shm_malloc_unsafe(tag->len);
shm_unlock();
if (!s || !n) {
LOG(L_ERR, "ERROR: update_totag_set: no memory \n");
if (n) shm_free(n);
if (s) shm_free(s);
return 0;
}
memset(n, 0, sizeof(struct totag_elem));
memcpy(s, tag->s, tag->len );
n->tag.s=s;n->tag.len=tag->len;
n->next=t->fwded_totags;
membar_write(); /* make sure all the changes to n are visible on all cpus
before we update t->fwded_totags. This is needed for
three reasons: the compiler might reorder some of the
writes, the cpu/cache could also reorder them with
respect to the visibility on other cpus
(e.g. some of the changes to n could be visible on
another cpu _after_ seeing t->fwded_totags=n) and
the "readers" (unmatched_tags()) do not use locks and
can be called simultaneously on another cpu.*/
t->fwded_totags=n;
DBG("DEBUG: update_totag_set: new totag \n");
return 0;
}
/*
* Build an ACK to a negative reply
*/
static char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,
unsigned int *ret_len)
{
str to;
if (parse_headers(rpl,HDR_TO_F, 0)==-1 || !rpl->to ) {
LOG(L_ERR, "ERROR: build_ack: "
"cannot generate a HBH ACK if key HFs in reply missing\n");
return NULL;
}
to.s=rpl->to->name.s;
to.len=rpl->to->len;
if (cfg_get(tm, tm_cfg, reparse_invite)) {
/* build the ACK from the INVITE which was sent out */
return build_local_reparse( trans, branch, ret_len,
ACK, ACK_LEN, &to
#ifdef CANCEL_REASON_SUPPORT
, 0
#endif /* CANCEL_REASON_SUPPORT */
);
} else {
/* build the ACK from the reveived INVITE */
return build_local( trans, branch, ret_len,
ACK, ACK_LEN, &to
#ifdef CANCEL_REASON_SUPPORT
, 0
#endif /* CANCEL_REASON_SUPPORT */
);
}
}
/*
* The function builds an ACK to 200 OK of local transactions, honoring the
* route set.
* The destination to which the message should be sent will be returned
* in the dst parameter.
* returns 0 on error and a pkg_malloc'ed buffer with length in ret_len
* and intended destination in dst on success.
*/
static char *build_local_ack(struct sip_msg* rpl, struct cell *trans,
int branch, unsigned int *ret_len,
struct dest_info* dst)
{
#ifdef WITH_AS_SUPPORT
struct retr_buf *local_ack, *old_lack;
/* do we have the ACK cache, previously build? */
if ((local_ack = trans->uac[0].local_ack) && local_ack->buffer_len) {
DEBUG("reusing ACK retr. buffer.\n");
*ret_len = local_ack->buffer_len;
*dst = local_ack->dst;
return local_ack->buffer;
}
/* the ACK will be built (and cached) by the AS (ack_local_uac()) */
if (trans->flags & T_NO_AUTO_ACK)
return NULL;
if (! (local_ack = local_ack_rb(rpl, trans, branch, /*hdrs*/NULL,
/*body*/NULL))) {
ERR("failed to build local ACK retransmission buffer (T@%p).\n",trans);
return NULL;
}
/* set the new buffer, but only if not already set (concurrent 2xx) */
/* a memory write barrier is needed to make sure the local_ack
content is fully written, before we try to add it to the transaction
-- andrei */
membar_write_atomic_op();
if ((old_lack = (struct retr_buf *)atomic_cmpxchg_long(
(void *)&trans->uac[0].local_ack, 0, (long)local_ack))) {
/* buffer already set: trash current and use the winning one */
INFO("concurrent 2xx to local INVITE detected (T@%p).\n", trans);
free_local_ack(local_ack);
local_ack = old_lack;
}
*ret_len = local_ack->buffer_len;
*dst = local_ack->dst;
return local_ack->buffer;
#else /* ! WITH_AS_SUPPORT */
return build_dlg_ack(rpl, trans, branch, /*hdrs*/NULL, /*body*/NULL,
ret_len, dst);
#endif /* WITH_AS_SUPPORT */
}
#if 0 /* candidate for removal --andrei */
/*
* The function is used to send a localy generated ACK to INVITE
* (tm generates the ACK on behalf of application using UAC
*/
static int send_local_ack(struct sip_msg* msg, str* next_hop,
char* ack, int ack_len)
{
struct dest_info dst;
#ifdef USE_DNS_FAILOVER
struct dns_srv_handle dns_h;
#endif
if (!next_hop) {
LOG(L_ERR, "send_local_ack: Invalid parameter value\n");
return -1;
}
#ifdef USE_DNS_FAILOVER
if (cfg_get(core, core_cfg, use_dns_failover)){
dns_srv_handle_init(&dns_h);
if ((uri2dst(&dns_h, &dst, msg, next_hop, PROTO_NONE)==0) ||
(dst.send_sock==0)){
dns_srv_handle_put(&dns_h);
LOG(L_ERR, "send_local_ack: no socket found\n");
return -1;
}
dns_srv_handle_put(&dns_h); /* not needed anymore */
}else{
if ((uri2dst(0, &dst, msg, next_hop, PROTO_NONE)==0) ||
(dst.send_sock==0)){
LOG(L_ERR, "send_local_ack: no socket found\n");
return -1;
}
}
#else
if ((uri2dst(&dst, msg, next_hop, PROTO_NONE)==0) || (dst.send_sock==0)){
LOG(L_ERR, "send_local_ack: no socket found\n");
return -1;
}
#endif
return msg_send(&dst, ack, ack_len);
}
#endif
inline static void start_final_repl_retr( struct cell *t )
{
if (unlikely(!is_local(t) && t->uas.request->REQ_METHOD==METHOD_INVITE )){
/* crank timers for negative replies */
if (t->uas.status>=300) {
if (start_retr(&t->uas.response)!=0)
LOG(L_CRIT, "BUG: start_final_repl_retr: start retr failed"
" for %p\n", &t->uas.response);
return;
}
/* local UAS retransmits too */
if (t->relayed_reply_branch==-2 && t->uas.status>=200) {
/* we retransmit 200/INVs regardless of transport --
even if TCP used, UDP could be used upstream and
loose the 200, which is not retransmitted by proxies
*/
if (force_retr( &t->uas.response )!=0)
LOG(L_CRIT, "BUG: start_final_repl_retr: force retr failed for"
" %p\n", &t->uas.response);
return;
}
}
}
static int _reply_light( struct cell *trans, char* buf, unsigned int len,
unsigned int code,
char *to_tag, unsigned int to_tag_len, int lock,
struct bookmark *bm )
{
struct retr_buf *rb;
unsigned int buf_len;
struct cancel_info cancel_data;
#ifdef TMCB_ONSEND
struct tmcb_params onsend_params;
#endif
init_cancel_info(&cancel_data);
if (!buf)
{
DBG("DEBUG: _reply_light: response building failed\n");
/* determine if there are some branches to be canceled */
if ( is_invite(trans) ) {
prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
}
/* and clean-up, including cancellations, if needed */
goto error;
}
if (lock) LOCK_REPLIES( trans );
if (trans->uas.status>=200) {
LOG( L_ERR, "ERROR: _reply_light: can't generate %d reply"
" when a final %d was sent out\n", code, trans->uas.status);
goto error2;
}
rb = & trans->uas.response;
rb->activ_type=code;
trans->uas.status = code;
buf_len = rb->buffer ? len : len + REPLY_OVERBUFFER_LEN;
rb->buffer = (char*)shm_resize( rb->buffer, buf_len );
/* puts the reply's buffer to uas.response */
if (! rb->buffer ) {
LOG(L_ERR, "ERROR: _reply_light: cannot allocate shmem buffer\n");
goto error3;
}
update_local_tags(trans, bm, rb->buffer, buf);
rb->buffer_len = len ;
memcpy( rb->buffer , buf , len );
/* needs to be protected too because what timers are set depends
on current transactions status */
/* t_update_timers_after_sending_reply( rb ); */
update_reply_stats( code );
trans->relayed_reply_branch=-2;
t_stats_replied_locally();
if (lock) UNLOCK_REPLIES( trans );
/* do UAC cleanup procedures in case we generated
a final answer whereas there are pending UACs */
if (code>=200) {
if (unlikely(is_local(trans))) {
if(unlikely(has_tran_tmcbs(trans, TMCB_LOCAL_COMPLETED)))
run_trans_callbacks(TMCB_LOCAL_COMPLETED, trans,
0, FAKED_REPLY, code);
} else {
if(unlikely(has_tran_tmcbs(trans, TMCB_RESPONSE_READY))) {
run_trans_callbacks(TMCB_RESPONSE_READY, trans,
trans->uas.request, FAKED_REPLY, code);
}
}
cleanup_uac_timers( trans );
if (is_invite(trans)){
prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
#ifdef CANCEL_REASON_SUPPORT
cancel_data.reason.cause=code;
#endif /* CANCEL_REASON_SUPPORT */
cancel_uacs( trans, &cancel_data, F_CANCEL_B_KILL );
}
start_final_repl_retr( trans );
}
/* send it out */
/* first check if we managed to resolve topmost Via -- if
not yet, don't try to retransmit
*/
/*
response.dst.send_sock might be unset if the process that created
the original transaction has not finished initialising the
retransmission buffer (see t_newtran/ init_rb).
If reply_to_via is set and via contains a host name (and not an ip)
the chances for this increase a lot.
*/
if (unlikely(!trans->uas.response.dst.send_sock)) {
LOG(L_ERR, "ERROR: _reply_light: no resolved dst to send reply to\n");
} else {
if (likely(SEND_PR_BUFFER( rb, buf, len )>=0)){
if (unlikely(code>=200 && !is_local(trans) &&
has_tran_tmcbs(trans, TMCB_RESPONSE_OUT)) )
run_trans_callbacks(TMCB_RESPONSE_OUT, trans,
trans->uas.request, FAKED_REPLY, code);
#ifdef TMCB_ONSEND
if (unlikely(has_tran_tmcbs(trans, TMCB_RESPONSE_SENT))){
INIT_TMCB_ONSEND_PARAMS(onsend_params, trans->uas.request,
FAKED_REPLY, rb, &rb->dst,
buf, len, TMCB_LOCAL_F, rb->branch, code);
run_onsend_callbacks2(TMCB_RESPONSE_SENT, trans,
&onsend_params);
}
#endif /* TMCB_ONSEND */
}
DBG("DEBUG: reply sent out. buf=%p: %.20s..., shmem=%p: %.20s\n",
buf, buf, rb->buffer, rb->buffer );
}
if (code>=200) {
/* start wait timer after finishing with t so that this function can
* be safely called from a fr_timer which allows quick timer dels
* (timer_allow_del()) (there's no chance of having the wait handler
* executed while we still need t) --andrei */
put_on_wait(trans);
}
pkg_free( buf ) ;
DBG("DEBUG: _reply_light: finished\n");
return 1;
error3:
prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
error2:
if (lock) UNLOCK_REPLIES( trans );
pkg_free ( buf );
error:
/* do UAC cleanup */
cleanup_uac_timers( trans );
if ( is_invite(trans) && cancel_data.cancel_bitmap )
cancel_uacs( trans, &cancel_data, F_CANCEL_B_KILL);
/* we did not succeed -- put the transaction on wait */
put_on_wait(trans);
return -1;
}
/* send a UAS reply
* returns 1 if everything was OK or -1 for error
*/
static int _reply( struct cell *trans, struct sip_msg* p_msg,
unsigned int code, char * text, int lock )
{
unsigned int len;
char * buf, *dset;
struct bookmark bm;
int dset_len;
struct lump_rpl* rpl_l;
str reason;
rpl_l=0;
if (code>=200) set_kr(REQ_RPLD);
/* compute the buffer in private memory prior to entering lock;
* create to-tag if needed */
/* if that is a redirection message, dump current message set to it */
if (code>=300 && code<400) {
dset=print_dset(p_msg, &dset_len);
if (dset) {
add_lump_rpl(p_msg, dset, dset_len, LUMP_RPL_HDR);
}
}
reason.s = text;
reason.len = strlen(text);
if (code>=180 && p_msg->to
&& (get_to(p_msg)->tag_value.s==0
|| get_to(p_msg)->tag_value.len==0)) {
calc_crc_suffix( p_msg, tm_tag_suffix );
buf = build_res_buf_from_sip_req(code, &reason, &tm_tag, p_msg,
&len, &bm);
if (unlikely(rpl_l)){
unlink_lump_rpl(p_msg, rpl_l);
free_lump_rpl(rpl_l);
}
return _reply_light( trans, buf, len, code,
tm_tag.s, TOTAG_VALUE_LEN, lock, &bm);
} else {
buf = build_res_buf_from_sip_req(code, &reason, 0 /*no to-tag*/,
p_msg, &len, &bm);
if (unlikely(rpl_l)){
unlink_lump_rpl(p_msg, rpl_l);
free_lump_rpl(rpl_l);
}
return _reply_light(trans,buf,len,code,
0, 0, /* no to-tag */lock, &bm);
}
}
/** create or restore a "fake environment" for running a failure_route.
*if msg is set -> it will fake the env. vars conforming with the msg; if NULL
* the env. will be restore to original.
* Side-effect: mark_ruri_consumed().
*/
void faked_env( struct cell *t, struct sip_msg *msg)
{
static int backup_route_type;
static struct cell *backup_t;
static int backup_branch;
static unsigned int backup_msgid;
static avp_list_t* backup_user_from, *backup_user_to;
static avp_list_t* backup_domain_from, *backup_domain_to;
static avp_list_t* backup_uri_from, *backup_uri_to;
#ifdef WITH_XAVP
static sr_xavp_t **backup_xavps;
#endif
static struct socket_info* backup_si;
if (msg) {
/* remember we are back in request processing, but process
* a shmem-ed replica of the request; advertise it in route type;
* for example t_reply needs to know that
*/
backup_route_type=get_route_type();
set_route_type(FAILURE_ROUTE);
/* don't bother backing up ruri state, since failure route
is called either on reply or on timer and in both cases
the ruri should not be used again for forking */
ruri_mark_consumed(); /* in failure route we assume ruri
should not be used again for forking */
/* also, tm actions look in beginning whether transaction is
* set -- whether we are called from a reply-processing
* or a timer process, we need to set current transaction;
* otherwise the actions would attempt to look the transaction
* up (unnecessary overhead, refcounting)
*/
/* backup */
backup_t=get_t();
backup_branch=get_t_branch();
backup_msgid=global_msg_id;
/* fake transaction and message id */
global_msg_id=msg->id;
set_t(t, T_BR_UNDEFINED);
/* make available the avp list from transaction */
backup_uri_from = set_avp_list(AVP_TRACK_FROM | AVP_CLASS_URI, &t->uri_avps_from );
backup_uri_to = set_avp_list(AVP_TRACK_TO | AVP_CLASS_URI, &t->uri_avps_to );
backup_user_from = set_avp_list(AVP_TRACK_FROM | AVP_CLASS_USER, &t->user_avps_from );
backup_user_to = set_avp_list(AVP_TRACK_TO | AVP_CLASS_USER, &t->user_avps_to );
backup_domain_from = set_avp_list(AVP_TRACK_FROM | AVP_CLASS_DOMAIN, &t->domain_avps_from );
backup_domain_to = set_avp_list(AVP_TRACK_TO | AVP_CLASS_DOMAIN, &t->domain_avps_to );
#ifdef WITH_XAVP
backup_xavps = xavp_set_list(&t->xavps_list);
#endif
/* set default send address to the saved value */
backup_si=bind_address;
bind_address=t->uac[0].request.dst.send_sock;
} else {
/* restore original environment */
set_t(backup_t, backup_branch);
global_msg_id=backup_msgid;
set_route_type(backup_route_type);
/* restore original avp list */
set_avp_list(AVP_TRACK_FROM | AVP_CLASS_USER, backup_user_from );
set_avp_list(AVP_TRACK_TO | AVP_CLASS_USER, backup_user_to );
set_avp_list(AVP_TRACK_FROM | AVP_CLASS_DOMAIN, backup_domain_from );
set_avp_list(AVP_TRACK_TO | AVP_CLASS_DOMAIN, backup_domain_to );
set_avp_list(AVP_TRACK_FROM | AVP_CLASS_URI, backup_uri_from );
set_avp_list(AVP_TRACK_TO | AVP_CLASS_URI, backup_uri_to );
#ifdef WITH_XAVP
xavp_set_list(backup_xavps);
#endif
bind_address=backup_si;
}
}
int fake_req(struct sip_msg *faked_req,
struct sip_msg *shmem_msg, int extra_flags, struct ua_client *uac)
{
/* on_negative_reply faked msg now copied from shmem msg (as opposed
* to zero-ing) -- more "read-only" actions (exec in particular) will
* work from reply_route as they will see msg->from, etc.; caution,
* rw actions may append some pkg stuff to msg, which will possibly be
* never released (shmem is released in a single block) */
memcpy( faked_req, shmem_msg, sizeof(struct sip_msg));
/* if we set msg_id to something different from current's message
* id, the first t_fork will properly clean new branch URIs */
faked_req->id=shmem_msg->id-1;
/* msg->parsed_uri_ok must be reset since msg_parsed_uri is
* not cloned (and cannot be cloned) */
faked_req->parsed_uri_ok = 0;
faked_req->msg_flags|=extra_flags; /* set the extra tm flags */
/* dst_uri can change ALSO!!! -- make a private copy */
if (shmem_msg->dst_uri.s!=0 && shmem_msg->dst_uri.len!=0) {
faked_req->dst_uri.s=pkg_malloc(shmem_msg->dst_uri.len+1);
if (!faked_req->dst_uri.s) {
LOG(L_ERR, "ERROR: fake_req: no uri/pkg mem\n");
goto error01;
}
faked_req->dst_uri.len=shmem_msg->dst_uri.len;
memcpy( faked_req->dst_uri.s, shmem_msg->dst_uri.s,
faked_req->dst_uri.len);
faked_req->dst_uri.s[faked_req->dst_uri.len]=0;
}else{
/* in case len==0, but shmem_msg->dst_uri.s!=0 (extra safety) */
faked_req->dst_uri.s = 0;
}
/* new_uri can change -- make a private copy */
if (shmem_msg->new_uri.s!=0 && shmem_msg->new_uri.len!=0) {
faked_req->new_uri.s=pkg_malloc(shmem_msg->new_uri.len+1);
if (!faked_req->new_uri.s) {
LOG(L_ERR, "ERROR: fake_req: no uri/pkg mem\n");
goto error00;
}
faked_req->new_uri.len=shmem_msg->new_uri.len;
memcpy( faked_req->new_uri.s, shmem_msg->new_uri.s,
faked_req->new_uri.len);
faked_req->new_uri.s[faked_req->new_uri.len]=0;
}else{
/* in case len==0, but shmem_msg->new_uri.s!=0 (extra safety)*/
faked_req->new_uri.s = 0;
}
if(uac) setbflagsval(0, uac->branch_flags);
else setbflagsval(0, 0);
return 1;
error00:
if (faked_req->dst_uri.s) {
pkg_free(faked_req->dst_uri.s);
faked_req->dst_uri.s = 0;
}
error01:
return 0;
}
void free_faked_req(struct sip_msg *faked_req, struct cell *t)
{
struct hdr_field *hdr;
if (faked_req->new_uri.s) {
pkg_free(faked_req->new_uri.s);
faked_req->new_uri.s = 0;
}
if (faked_req->dst_uri.s) {
pkg_free(faked_req->dst_uri.s);
faked_req->dst_uri.s = 0;
}
/* free all types of lump that were added in failure handlers */
del_nonshm_lump( &(faked_req->add_rm) );
del_nonshm_lump( &(faked_req->body_lumps) );
del_nonshm_lump_rpl( &(faked_req->reply_lump) );
/* free header's parsed structures that were added by failure handlers */
for( hdr=faked_req->headers ; hdr ; hdr=hdr->next ) {
if ( hdr->parsed && hdr_allocs_parse(hdr) &&
(hdr->parsed<(void*)t->uas.request ||
hdr->parsed>=(void*)t->uas.end_request)) {
/* header parsed filed doesn't point inside uas.request memory
* chunck -> it was added by failure funcs.-> free it as pkg */
DBG("DBG:free_faked_req: removing hdr->parsed %d\n",
hdr->type);
clean_hdr_field(hdr);
hdr->parsed = 0;
}
}
/* free parsed body added by failure handlers */
if (faked_req->body) {
if(faked_req->body->free)
faked_req->body->free(&faked_req->body);
faked_req->body = 0;
}
}
/* return 1 if a failure_route processes */
int run_failure_handlers(struct cell *t, struct sip_msg *rpl,
int code, int extra_flags)
{
static struct sip_msg faked_req;
struct sip_msg *shmem_msg = t->uas.request;
int on_failure;
/* failure_route for a local UAC? */
if (!shmem_msg) {
LOG(L_WARN,"Warning: run_failure_handlers: no UAC support (%d, %d) \n",
t->on_negative, t->tmcb_hl.reg_types);
return 0;
}
/* don't start faking anything if we don't have to */
if (unlikely(!t->on_negative && !has_tran_tmcbs( t, TMCB_ON_FAILURE))) {
LOG(L_WARN,
"Warning: run_failure_handlers: no negative handler (%d, %d)\n",
t->on_negative,
t->tmcb_hl.reg_types);
return 1;
}
if (!fake_req(&faked_req, shmem_msg, extra_flags, &t->uac[picked_branch])) {
LOG(L_ERR, "ERROR: run_failure_handlers: fake_req failed\n");
return 0;
}
/* fake also the env. conforming to the fake msg */
faked_env( t, &faked_req);
/* DONE with faking ;-) -> run the failure handlers */
if (unlikely(has_tran_tmcbs( t, TMCB_ON_FAILURE)) ) {
run_trans_callbacks( TMCB_ON_FAILURE, t, &faked_req, rpl, code);
}
if (t->on_negative) {
/* avoid recursion -- if failure_route forwards, and does not
* set next failure route, failure_route will not be reentered
* on failure */
on_failure = t->on_negative;
t->on_negative=0;
if (exec_pre_script_cb(&faked_req, FAILURE_CB_TYPE)>0) {
/* run a failure_route action if some was marked */
if (run_top_route(failure_rt.rlist[on_failure], &faked_req, 0)<0)
LOG(L_ERR, "ERROR: run_failure_handlers: Error in run_top_route\n");
exec_post_script_cb(&faked_req, FAILURE_CB_TYPE);
}
/* update message flags, if changed in failure route */
t->uas.request->flags = faked_req.flags;
}
/* restore original environment and free the fake msg */
faked_env( t, 0);
free_faked_req(&faked_req,t);
/* if failure handler changed flag, update transaction context */
shmem_msg->flags = faked_req.flags;
return 1;
}
/* 401, 407, 415, 420, and 484 have priority over the other 4xx*/
inline static short int get_4xx_prio(unsigned char xx)
{
switch(xx){
case 1:
case 7:
case 15:
case 20:
case 84:
return xx;
break;
}
return 100+xx;
}
/* returns response priority, lower number => highest prio
*
* responses priority val
* 0-99 32000+reponse (special)
* 1xx 11000+reponse (special)
* 700-999 10000+response (very low)
* 5xx 5000+xx (low)
* 4xx 4000+xx
* 3xx 3000+xx
* 6xx 1000+xx (high)
* 2xx 0000+xx (highest)
*/
inline static short int get_prio(unsigned int resp)
{
int class;
int xx;
class=resp/100;
if (class<7){
xx=resp%100;
return resp_class_prio[class]+((class==4)?get_4xx_prio(xx):xx);
}
return 10000+resp; /* unknown response class => return very low prio */
}
/* select a branch for forwarding; returns:
* 0..X ... branch number
* -1 ... error
* -2 ... can't decide yet -- incomplete branches present
*/
int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code)
{
int best_b, best_s, b;
best_b=-1; best_s=0;
for ( b=0; b<t->nr_of_outgoings ; b++ ) {
/* "fake" for the currently processed branch */
if (b==inc_branch) {
if (get_prio(inc_code)<get_prio(best_s)) {
best_b=b;
best_s=inc_code;
}
continue;
}
/* skip 'empty branches'
* An empty branch without a final response is still considered
* to be a pending, incomplete branch. */
if ((!t->uac[b].request.buffer) && (t->uac[b].last_received>=200))
continue;
/* there is still an unfinished UAC transaction; wait now! */
if ( t->uac[b].last_received<200 )
return -2;
/* if reply is null => t_send_branch "faked" reply, skip over it */
if ( t->uac[b].reply &&
get_prio(t->uac[b].last_received)<get_prio(best_s) ) {
best_b =b;
best_s = t->uac[b].last_received;
}
} /* find lowest branch */
*res_code=best_s;
return best_b;
}
/* The same as t_pick_branch(), but allows also
* blind branches to be picked up.
* This function should be used only in failure_route
* to check which response has been
* picked up by t_pick_branch().
* returns:
* 0..X ... branch number
* -1 ... error
* -2 ... can't decide yet -- incomplete branches present
*/
int t_pick_branch_blind(struct cell *t, int *res_code)
{
int best_b, best_s, b;
best_b=-1; best_s=0;
for ( b=0; b<t->nr_of_outgoings ; b++ ) {
/* there is still an unfinished UAC transaction; wait now! */
if ( t->uac[b].last_received<200 )
return -2;
/* if reply is null => t_send_branch "faked" reply, skip over it */
if ( t->uac[b].reply &&
get_prio(t->uac[b].last_received)<get_prio(best_s) ) {
best_b = b;
best_s = t->uac[b].last_received;
}
} /* find lowest branch */
*res_code=best_s;
return best_b;
}
/* flag indicating whether it is requested
* to drop the already saved replies or not */
static unsigned char drop_replies;
/* This is the neurological point of reply processing -- called
* from within a REPLY_LOCK, t_should_relay_response decides
* how a reply shall be processed and how transaction state is
* affected.
*
* Checks if the new reply (with new_code status) should be sent or not
* based on the current
* transaction status.
*
* @param cancel_data - initialized cancel_info structure.
* Returns - branch number (0,1,...) which should be relayed
* -1 if nothing to be relayed
* WARNING: cancel_data should be initialized prior to calling this function.
*/
static enum rps t_should_relay_response( struct cell *Trans , int new_code,
int branch , int *should_store, int *should_relay,
struct cancel_info *cancel_data, struct sip_msg *reply )
{
int branch_cnt;
int picked_code;
int new_branch;
int inv_through;
int extra_flags;
int i;
int replies_dropped;
/* note: this code never lets replies to CANCEL go through;
we generate always a local 200 for CANCEL; 200s are
not relayed because it's not an INVITE transaction;
>= 300 are not relayed because 200 was already sent
out
*/
DBG("->>>>>>>>> T_code=%d, new_code=%d\n",Trans->uas.status,new_code);
inv_through=new_code>=200 && new_code<300 && is_invite(Trans);
/* if final response sent out, allow only INVITE 2xx */
if ( Trans->uas.status >= 200 ) {
if (inv_through) {
DBG("DBG: t_should_relay_response: 200 INV after final sent\n");
*should_store=0;
Trans->uac[branch].last_received=new_code;
*should_relay=branch;
return RPS_PUSHED_AFTER_COMPLETION;
}
/* except the exception above, too late messages will
be discarded */
goto discard;
}
/* if final response received at this branch, allow only INVITE 2xx */
if (Trans->uac[branch].last_received>=200
&& !(inv_through && Trans->uac[branch].last_received<300)) {
/* don't report on retransmissions */
if (Trans->uac[branch].last_received==new_code) {
DBG("DEBUG: final reply retransmission\n");
goto discard;
}
/* if you FR-timed-out, faked a local 408 and 487 came or
* faked a CANCEL on a non-replied branch don't
* report on it either */
if ((Trans->uac[branch].last_received==487) ||
(Trans->uac[branch].last_received==408 && new_code==487)) {
DBG("DEBUG: %d came for a %d branch (ignored)\n",
new_code, Trans->uac[branch].last_received);
goto discard;
}
/* this looks however how a very strange status rewrite attempt;
* report on it */
LOG(L_ERR, "ERROR: t_should_relay_response: status rewrite by UAS: "
"stored: %d, received: %d\n",
Trans->uac[branch].last_received, new_code );
goto discard;
}
/* no final response sent yet */
/* negative replies subject to fork picking */
if (new_code >=300 ) {
Trans->uac[branch].last_received=new_code;
/* if all_final return lowest */
picked_branch=t_pick_branch(branch,new_code, Trans, &picked_code);
if (picked_branch==-2) { /* branches open yet */
*should_store=1;
*should_relay=-1;
if (new_code>=600 && new_code<=699){
if (!(Trans->flags & (T_6xx | T_DISABLE_6xx))){
/* cancel only the first time we get a 6xx and only
if the 6xx handling is not disabled */
prepare_to_cancel(Trans, &cancel_data->cancel_bitmap, 0);
Trans->flags|=T_6xx;
#ifdef CANCEL_REASON_SUPPORT
cancel_data->reason.cause=new_code;
#endif /* CANCEL_REASON_SUPPORT */
}
}
return RPS_STORE;
}
if (picked_branch==-1) {
LOG(L_CRIT, "ERROR: t_should_relay_response: lowest==-1\n");
goto error;
}
/* no more pending branches -- try if that changes after
a callback; save branch count to be able to determine
later if new branches were initiated */
branch_cnt=Trans->nr_of_outgoings;
/* also append the current reply to the transaction to
* make it available in failure routes - a kind of "fake"
* save of the final reply per branch */
Trans->uac[branch].reply = reply;
Trans->flags&=~T_6xx; /* clear the 6xx flag , we want to
allow new branches from the failure route */
drop_replies = failure_reply_mode;
replies_dropped = 0;
/* run ON_FAILURE handlers ( route and callbacks) */
if (unlikely(has_tran_tmcbs( Trans, TMCB_ON_FAILURE_RO|TMCB_ON_FAILURE)
|| Trans->on_negative )) {
extra_flags=
((Trans->uac[picked_branch].request.flags & F_RB_TIMEOUT)?
FL_TIMEOUT:0) |
((Trans->uac[picked_branch].request.flags & F_RB_REPLIED)?
FL_REPLIED:0);
run_failure_handlers( Trans, Trans->uac[picked_branch].reply,
picked_code, extra_flags);
if (unlikely((drop_replies==3 && branch_cnt<Trans->nr_of_outgoings) ||
(drop_replies!=0 && drop_replies!=3))
) {
/* drop all the replies that we have already saved */
i = 0;
if(drop_replies==2)
{
for(i=branch_cnt-1; i>=0; i--)
if(Trans->uac[i].flags&TM_UAC_FLAG_FB)
break;
if(i<0) i=0;
}
for (; i<branch_cnt; i++) {
if (Trans->uac[i].reply &&
(Trans->uac[i].reply != FAKED_REPLY) &&
(Trans->uac[i].reply->msg_flags & FL_SHM_CLONE))
/* we have to drop the reply which is already in shm mem */
sip_msg_free(Trans->uac[i].reply);
Trans->uac[i].reply = 0;
}
/* make sure that the selected reply is not relayed even if
there is not any new branch added -- should not happen */
picked_branch = -1;
replies_dropped = 1;
}
}
/* now reset it; after the failure logic, the reply may
* not be stored any more and we don't want to keep into
* transaction some broken reference */
Trans->uac[branch].reply = 0;
/* look if the callback perhaps replied transaction; it also
covers the case in which a transaction is replied localy
on CANCEL -- then it would make no sense to proceed to
new branches bellow
*/
if (Trans->uas.status >= 200) {
*should_store=0;
*should_relay=-1;
/* this might deserve an improvement -- if something
was already replied, it was put on wait and then,
returning RPS_COMPLETED will make t_on_reply
put it on wait again; perhaps splitting put_on_wait
from send_reply or a new RPS_ code would be healthy
*/
return RPS_COMPLETED;
}
/* look if the callback/failure_route introduced new branches ... */
if (branch_cnt<Trans->nr_of_outgoings){
/* the new branches might be already "finished" => we
* must use t_pick_branch again */
new_branch=t_pick_branch((replies_dropped==0)?
branch :
-1, /* make sure we do not pick
the current branch */
new_code,
Trans,
&picked_code);
if (new_branch<0){
if (likely(replies_dropped==0)) {
if (new_branch==-2) { /* branches open yet */
*should_store=1;
*should_relay=-1;
return RPS_STORE;
}
/* error, use the old picked_branch */
} else {
if (new_branch==-2) { /* branches open yet */
/* we are not allowed to relay the reply */
*should_store=0;
*should_relay=-1;
return RPS_DISCARDED;
} else {
/* There are no open branches,
and all the newly created branches failed
as well. We are not allowed to send back
the previously picked-up branch, thus,
let us reply with an error instead. */
goto branches_failed;
}
}
}else{
/* found a new_branch */
picked_branch=new_branch;
}
} else if (unlikely(replies_dropped)) {
/* Either the script writer did not add new branches
after calling t_drop_replies(), or tm was unable
to add the new branches to the transaction. */
goto branches_failed;
}
/* really no more pending branches -- return lowest code */
*should_store=0;
*should_relay=picked_branch;
/* we dont need 'prepare_to_cancel' here -- all branches
known to have completed */
/* prepare_to_cancel( Trans, cancel_bitmap, 0 ); */
return RPS_COMPLETED;
}
/* not >=300 ... it must be 2xx or provisional 1xx */
if (new_code>=100) {
#ifdef WITH_AS_SUPPORT
/* need a copy of the message for ACK generation */
*should_store = (inv_through && is_local(Trans) &&
(Trans->uac[branch].last_received < 200) &&
(Trans->flags & T_NO_AUTO_ACK)) ? 1 : 0;
#else
*should_store=0;
#endif
/* 1xx and 2xx except 100 will be relayed */
Trans->uac[branch].last_received=new_code;
*should_relay= new_code==100? -1 : branch;
if (new_code>=200 ) {
prepare_to_cancel( Trans, &cancel_data->cancel_bitmap, 0);
#ifdef CANCEL_REASON_SUPPORT
cancel_data->reason.cause=new_code;
#endif /* CANCEL_REASON_SUPPORT */
return RPS_COMPLETED;
} else return RPS_PROVISIONAL;
}
error:
/* reply_status didn't match -- it must be something weird */
LOG(L_CRIT, "ERROR: Oh my gooosh! We don't know whether to relay %d\n",
new_code);
discard:
*should_store=0;
*should_relay=-1;
return RPS_DISCARDED;
branches_failed:
*should_store=0;
if (is_local(Trans)){
/* for local transactions use the current reply */
*should_relay=branch;
}else{
*should_relay=-1;
/* We have hopefully set tm_error in failure_route when
the branches failed. If not, reply with E_UNSPEC */
if ((kill_transaction_unsafe(Trans,
tm_error ? tm_error : E_UNSPEC)) <=0 ){
LOG(L_ERR, "ERROR: t_should_relay_response: "
"reply generation failed\n");
}
}
return RPS_COMPLETED;
}
/* Retransmits the last sent inbound reply.
* input: p_msg==request for which I want to retransmit an associated reply
* Returns -1 - error
* 1 - OK
*/
int t_retransmit_reply( struct cell *t )
{
static char b[BUF_SIZE];
int len;
/* first check if we managed to resolve topmost Via -- if
not yet, don't try to retransmit
*/
/*
response.dst.send_sock might be unset if the process that created
the original transaction has not finished initialising the
retransmission buffer (see t_newtran/ init_rb).
If reply_to_via is set and via contains a host name (and not an ip)
the chances for this increase a lot.
*/
if (!t->uas.response.dst.send_sock) {
LOG(L_WARN, "WARNING: t_retransmit_reply: "
"no resolved dst to retransmit\n");
return -1;
}
/* we need to lock the transaction as messages from
upstream may change it continuously
*/
LOCK_REPLIES( t );
if (!t->uas.response.buffer) {
DBG("DBG: t_retransmit_reply: nothing to retransmit\n");
goto error;
}
len=t->uas.response.buffer_len;
if ( len==0 || len>BUF_SIZE ) {
DBG("DBG: t_retransmit_reply: "
"zero length or too big to retransmit: %d\n", len);
goto error;
}
memcpy( b, t->uas.response.buffer, len );
UNLOCK_REPLIES( t );
SEND_PR_BUFFER( & t->uas.response, b, len );
#ifdef TMCB_ONSEND
if (unlikely(has_tran_tmcbs(t, TMCB_RESPONSE_SENT))){
/* we don't know if it's a retransmission of a local reply or a
* forwarded reply */
run_onsend_callbacks(TMCB_RESPONSE_SENT, &t->uas.response, 0, 0,
TMCB_RETR_F);
}
#endif
DBG("DEBUG: reply retransmitted. buf=%p: %.9s..., shmem=%p: %.9s\n",
b, b, t->uas.response.buffer, t->uas.response.buffer );
return 1;
error:
UNLOCK_REPLIES(t);
return -1;
}
int t_reply( struct cell *t, struct sip_msg* p_msg, unsigned int code,
char * text )
{
return _reply( t, p_msg, code, text, 1 /* lock replies */ );
}
int t_reply_unsafe( struct cell *t, struct sip_msg* p_msg, unsigned int code,
char * text )
{
return _reply( t, p_msg, code, text, 0 /* don't lock replies */ );
}
void set_final_timer( struct cell *t )
{
start_final_repl_retr(t);
put_on_wait(t);
}
void cleanup_uac_timers( struct cell *t )
{
int i;
/* reset FR/retransmission timers */
for (i=0; i<t->nr_of_outgoings; i++ ){
stop_rb_timers(&t->uac[i].request);
}
DBG("DEBUG: cleanup_uac_timers: RETR/FR timers reset\n");
}
static int store_reply( struct cell *trans, int branch, struct sip_msg *rpl)
{
# ifdef EXTRA_DEBUG
if (trans->uac[branch].reply) {
LOG(L_ERR, "ERROR: replacing stored reply; aborting\n");
abort();
}
# endif
/* when we later do things such as challenge aggregation,
we should parse the message here before we conserve
it in shared memory; -jiri
*/
if (rpl==FAKED_REPLY)
trans->uac[branch].reply=FAKED_REPLY;
else
trans->uac[branch].reply = sip_msg_cloner( rpl, 0 );
if (! trans->uac[branch].reply ) {
LOG(L_ERR, "ERROR: store_reply: can't alloc' clone memory\n");
return 0;
}
return 1;
}
/* returns the number of authenticate replies (401 and 407) received so far
* (FAKED_REPLYes are excluded)
* It must be called with the REPLY_LOCK held */
inline static int auth_reply_count(struct cell *t, struct sip_msg* crt_reply)
{
int count;
int r;
count=0;
if (crt_reply && (crt_reply!=FAKED_REPLY) &&
(crt_reply->REPLY_STATUS ==401 || crt_reply->REPLY_STATUS ==407))
count=1;
for (r=0; r<t->nr_of_outgoings; r++){
if (t->uac[r].reply && (t->uac[r].reply!=FAKED_REPLY) &&
(t->uac[r].last_received==401 || t->uac[r].last_received==407))
count++;
}
return count;
}
/* must be called with the REPY_LOCK held */
inline static char* reply_aggregate_auth(int code, char* txt, str* new_tag,
struct cell* t, unsigned int* res_len,
struct bookmark* bm)
{
int r;
struct hdr_field* hdr;
struct lump_rpl** first;
struct lump_rpl** crt;
struct lump_rpl* lst;
struct lump_rpl* lst_end;
struct sip_msg* req;
char* buf;
str reason;
first=0;
lst_end=0;
req=t->uas.request;
for (r=0; r<t->nr_of_outgoings; r++){
if (t->uac[r].reply && (t->uac[r].reply!=FAKED_REPLY) &&
(t->uac[r].last_received==401 || t->uac[r].last_received==407)){
for (hdr=t->uac[r].reply->headers; hdr; hdr=hdr->next){
if (hdr->type==HDR_WWW_AUTHENTICATE_T ||
hdr->type==HDR_PROXY_AUTHENTICATE_T){
crt=add_lump_rpl2(req, hdr->name.s, hdr->len,
LUMP_RPL_HDR|LUMP_RPL_NODUP|LUMP_RPL_NOFREE);
if (crt==0){
/* some kind of error, better stop */
LOG(L_ERR, "ERROR: tm:reply_aggregate_auth:"
" add_lump_rpl2 failed\n");
goto skip;
}
lst_end=*crt;
if (first==0) first=crt;
}
}
}
}
skip:
reason.s = txt;
reason.len = strlen(txt);
buf=build_res_buf_from_sip_req(code, &reason, new_tag, req, res_len, bm);
/* clean the added lumps */
if (first){
lst=*first;
*first=lst_end->next; /* "detach" the list of added rpl_lumps */
lst_end->next=0; /* terminate lst */
del_nonshm_lump_rpl(&lst);
if (lst){
LOG(L_CRIT, "BUG: tm: repply_aggregate_auth: rpl_lump list"
"contains shm alloc'ed lumps\n");
abort();
}
}
return buf;
}
/* this is the code which decides what and when shall be relayed
upstream; note well -- it assumes it is entered locked with
REPLY_LOCK and it returns unlocked!
If do_put_on_wait==1 and this is the final reply, the transaction
wait timer will be started (put_on_wait(t)).
*
* @param cancel_data - initialized cancel_info structure.
*
* WARNING: cancel_data should be initialized prior to calling this function.
*/
enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
unsigned int msg_status, struct cancel_info *cancel_data,
int do_put_on_wait )
{
int relay;
int save_clone;
char *buf;
/* length of outbound reply */
unsigned int res_len;
int relayed_code;
struct sip_msg *relayed_msg;
struct sip_msg *reply_bak;
struct bookmark bm;
int totag_retr;
enum rps reply_status;
/* retransmission structure of outbound reply and request */
struct retr_buf *uas_rb;
str* to_tag;
str reason;
#ifdef TMCB_ONSEND
struct tmcb_params onsend_params;
#endif
/* keep compiler warnings about use of uninit vars silent */
res_len=0;
buf=0;
relayed_msg=0;
relayed_code=0;
totag_retr=0;
/* remember, what was sent upstream to know whether we are
* forwarding a first final reply or not */
/* *** store and relay message as needed *** */
reply_status = t_should_relay_response(t, msg_status, branch,
&save_clone, &relay, cancel_data, p_msg );
DBG("DEBUG: relay_reply: branch=%d, save=%d, relay=%d\n",
branch, save_clone, relay );
/* store the message if needed */
if (save_clone) /* save for later use, typically branch picking */
{
if (!store_reply( t, branch, p_msg ))
goto error01;
}
uas_rb = & t->uas.response;
if (relay >= 0 ) {
/* initialize sockets for outbound reply */
uas_rb->activ_type=msg_status;
/* only messages known to be relayed immediately will be
* be called on; we do not evoke this callback on messages
* stored in shmem -- they are fixed and one cannot change them
* anyway */
if (unlikely(msg_status<300 && branch==relay
&& has_tran_tmcbs(t,TMCB_RESPONSE_FWDED)) ) {
run_trans_callbacks( TMCB_RESPONSE_FWDED, t, t->uas.request,
p_msg, msg_status );
}
/* try building the outbound reply from either the current
* or a stored message */
relayed_msg = branch==relay ? p_msg : t->uac[relay].reply;
if (relayed_msg==FAKED_REPLY) {
relayed_code = branch==relay
? msg_status : t->uac[relay].last_received;
/* use to_tag from the original request, or if not present,
* generate a new one */
if (relayed_code>=180 && t->uas.request->to
&& (get_to(t->uas.request)->tag_value.s==0
|| get_to(t->uas.request)->tag_value.len==0)) {
calc_crc_suffix( t->uas.request, tm_tag_suffix );
to_tag=&tm_tag;
} else {
to_tag=0;
}
if (cfg_get(tm, tm_cfg, tm_aggregate_auth) &&
(relayed_code==401 || relayed_code==407) &&
(auth_reply_count(t, p_msg)>1)){
/* aggregate 401 & 407 www & proxy authenticate headers in
* a "FAKE" reply*/
/* temporarily "store" the current reply */
reply_bak=t->uac[branch].reply;
t->uac[branch].reply=p_msg;
buf=reply_aggregate_auth(relayed_code,
error_text(relayed_code), to_tag, t, &res_len, &bm);
/* revert the temporary "store" reply above */
t->uac[branch].reply=reply_bak;
}else{
reason.s = error_text(relayed_code);
reason.len = strlen(reason.s);
buf = build_res_buf_from_sip_req( relayed_code,
&reason, to_tag,
t->uas.request, &res_len, &bm );
}
} else {
relayed_code=relayed_msg->REPLY_STATUS;
if (relayed_code==503){
/* replace a final 503 with a 500:
* generate a "FAKE" reply and a new to_tag (for easier
* debugging)*/
relayed_msg=FAKED_REPLY;
if ((get_to(t->uas.request)->tag_value.s==0 ||
get_to(t->uas.request)->tag_value.len==0)) {
calc_crc_suffix( t->uas.request, tm_tag_suffix );
to_tag=&tm_tag;
} else {
to_tag=0;
}
/* don't relay a 503, replace it w/ 500 (rfc3261) */
reason.s = error_text(relayed_code);
reason.len = strlen(reason.s);
buf=build_res_buf_from_sip_req(500, &reason,
to_tag, t->uas.request, &res_len, &bm);
relayed_code=500;
}else if (cfg_get(tm, tm_cfg, tm_aggregate_auth) &&
(relayed_code==401 || relayed_code==407) &&
(auth_reply_count(t, p_msg)>1)){
/* aggregate 401 & 407 www & proxy authenticate headers in
* a "FAKE" reply*/
if ((get_to(t->uas.request)->tag_value.s==0 ||
get_to(t->uas.request)->tag_value.len==0)) {
calc_crc_suffix( t->uas.request, tm_tag_suffix );
to_tag=&tm_tag;
} else {
to_tag=0;
}
/* temporarily "store" the current reply */
reply_bak=t->uac[branch].reply;
t->uac[branch].reply=p_msg;
buf=reply_aggregate_auth(relayed_code,
error_text(relayed_code), to_tag, t, &res_len, &bm);
/* revert the temporary "store" reply above */
t->uac[branch].reply=reply_bak;;
relayed_msg=FAKED_REPLY; /* mark the relayed_msg as a "FAKE" */
}else{
buf = build_res_buf_from_sip_res( relayed_msg, &res_len );
/* if we build a message from shmem, we need to remove
via delete lumps which are now stirred in the shmem-ed
structure
*/
if (branch!=relay) {
free_via_clen_lump(&relayed_msg->add_rm);
}
/* update send_flags with possible additions from the
reply route */
SND_FLAGS_OR(&uas_rb->dst.send_flags, &uas_rb->dst.send_flags,
&relayed_msg->rpl_send_flags);
}
}
update_reply_stats( relayed_code );
if (!buf) {
LOG(L_ERR, "ERROR: relay_reply: "
"no mem for outbound reply buffer\n");
goto error02;
}
/* attempt to copy the message to UAS's shmem:
- copy to-tag for ACK matching as well
- allocate little a bit more for provisional as
larger messages are likely to follow and we will be
able to reuse the memory frag
*/
uas_rb->buffer = (char*)shm_resize( uas_rb->buffer, res_len +
(msg_status<200 ? REPLY_OVERBUFFER_LEN : 0));
if (!uas_rb->buffer) {
LOG(L_ERR, "ERROR: relay_reply: cannot alloc reply shmem\n");
goto error03;
}
uas_rb->buffer_len = res_len;
memcpy( uas_rb->buffer, buf, res_len );
if (relayed_msg==FAKED_REPLY) { /* to-tags for local replies */
update_local_tags(t, &bm, uas_rb->buffer, buf);
t_stats_replied_locally();
}
/* update the status ... */
t->uas.status = relayed_code;
t->relayed_reply_branch = relay;
if ( unlikely(is_invite(t) && relayed_msg!=FAKED_REPLY
&& relayed_code>=200 && relayed_code < 300
&& has_tran_tmcbs( t,
TMCB_RESPONSE_OUT|TMCB_RESPONSE_READY
|TMCB_E2EACK_IN|TMCB_E2EACK_RETR_IN))) {
totag_retr=update_totag_set(t, relayed_msg);
}
}; /* if relay ... */
UNLOCK_REPLIES( t );
/* send it now (from the private buffer) */
if (relay >= 0) {
if (unlikely(!totag_retr && has_tran_tmcbs(t, TMCB_RESPONSE_READY))){
run_trans_callbacks(TMCB_RESPONSE_READY, t,
t->uas.request, relayed_msg, relayed_code);
}
/* Set retransmission timer before the reply is sent out to avoid
* race conditions
*
* Call start_final_repl_retr/put_on_wait() only if we really send out
* the reply. It can happen that the reply has been already sent from
* failure_route or from a callback and the timer has been already
* started. (Miklos)
*/
if (reply_status == RPS_COMPLETED) {
start_final_repl_retr(t);
}
if (likely(uas_rb->dst.send_sock &&
SEND_PR_BUFFER( uas_rb, buf, res_len ) >= 0)){
if (unlikely(!totag_retr && has_tran_tmcbs(t, TMCB_RESPONSE_OUT))){
run_trans_callbacks( TMCB_RESPONSE_OUT, t, t->uas.request,
relayed_msg, relayed_code);
}
#ifdef TMCB_ONSEND
if (unlikely(has_tran_tmcbs(t, TMCB_RESPONSE_SENT))){
INIT_TMCB_ONSEND_PARAMS(onsend_params, t->uas.request,
relayed_msg, uas_rb, &uas_rb->dst, buf,
res_len,
(relayed_msg==FAKED_REPLY)?TMCB_LOCAL_F:0,
uas_rb->branch, relayed_code);
run_onsend_callbacks2(TMCB_RESPONSE_SENT, t, &onsend_params);
}
#endif
} else if (unlikely(uas_rb->dst.send_sock == 0))
ERR("no resolved dst to send reply to\n");
/* Call put_on_wait() only if we really send out
* the reply. It can happen that the reply has been already sent from
* failure_route or from a callback and the timer has been already
* started. (Miklos)
*
* put_on_wait() should always be called after we finished dealling
* with t, because otherwise the wait timer might fire before we
* finish with t, and by the time we want to use t it could
* be already deleted. This could happen only if this function is
* called from timer (fr_timer) (the timer doesn't refcnt) and the
* timer allows quick dels (timer_allow_del()). --andrei
*/
if (do_put_on_wait && (reply_status == RPS_COMPLETED)) {
put_on_wait(t);
}
pkg_free( buf );
}
/* success */
return reply_status;
error03:
pkg_free( buf );
error02:
if (save_clone) {
if (t->uac[branch].reply!=FAKED_REPLY)
sip_msg_free( t->uac[branch].reply );
t->uac[branch].reply = NULL;
}
error01:
t_reply_unsafe( t, t->uas.request, 500, "Reply processing error" );
cancel_data->cancel_bitmap=0; /* t_reply_unsafe already canceled
everything needed */
UNLOCK_REPLIES(t);
/* if (is_invite(t)) cancel_uacs( t, *cancel_bitmap, 0);
* -- not needed, t_reply_unsafe took care of this */
/* a serious error occurred -- attempt to send an error reply;
it will take care of clean-ups */
/* failure */
return RPS_ERROR;
}
/* this is the "UAC" above transaction layer; if a final reply
is received, it triggers a callback; note well -- it assumes
it is entered locked with REPLY_LOCK and it returns unlocked!
*
* @param cancel_data - initialized cancel_info structure.
*
* WARNING: cancel_data should be initialized prior to calling this function.
*/
enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
unsigned int msg_status, struct cancel_info *cancel_data)
{
/* how to deal with replies for local transaction */
int local_store, local_winner;
enum rps reply_status;
struct sip_msg *winning_msg;
int winning_code;
int totag_retr;
/* keep warning 'var might be used un-inited' silent */
winning_msg=0;
winning_code=0;
totag_retr=0;
cancel_data->cancel_bitmap=0;
reply_status=t_should_relay_response( t, msg_status, branch,
&local_store, &local_winner, cancel_data, p_msg );
DBG("DEBUG: local_reply: branch=%d, save=%d, winner=%d\n",
branch, local_store, local_winner );
if (local_store) {
if (!store_reply(t, branch, p_msg))
goto error;
}
if (local_winner>=0) {
winning_msg= branch==local_winner
? p_msg : t->uac[local_winner].reply;
if (winning_msg==FAKED_REPLY) {
t_stats_replied_locally();
winning_code = branch==local_winner
? msg_status : t->uac[local_winner].last_received;
} else {
winning_code=winning_msg->REPLY_STATUS;
}
t->uas.status = winning_code;
update_reply_stats( winning_code );
if (unlikely(is_invite(t) && winning_msg!=FAKED_REPLY &&
winning_code>=200 && winning_code <300 &&
has_tran_tmcbs(t, TMCB_LOCAL_COMPLETED) )) {
totag_retr=update_totag_set(t, winning_msg);
}
}
UNLOCK_REPLIES(t);
if (local_winner >= 0
&& cfg_get(tm, tm_cfg, pass_provisional_replies)
&& winning_code < 200) {
/* no retr. detection for provisional replies &
* TMCB_LOCAL_RESPONSE_OUT */
if (unlikely(has_tran_tmcbs(t, TMCB_LOCAL_RESPONSE_OUT) )) {
run_trans_callbacks( TMCB_LOCAL_RESPONSE_OUT, t, 0,
winning_msg, winning_code);
}
}
if (local_winner>=0 && winning_code>=200 ) {
DBG("DEBUG: local transaction completed\n");
if (!totag_retr) {
if (unlikely(has_tran_tmcbs(t,TMCB_LOCAL_COMPLETED) ))
run_trans_callbacks( TMCB_LOCAL_COMPLETED, t, 0,
winning_msg, winning_code );
}
}
return reply_status;
error:
prepare_to_cancel(t, &cancel_data->cancel_bitmap, 0);
UNLOCK_REPLIES(t);
cleanup_uac_timers(t);
if (p_msg && p_msg!=FAKED_REPLY && get_cseq(p_msg)->method.len==INVITE_LEN
&& memcmp( get_cseq(p_msg)->method.s, INVITE, INVITE_LEN)==0){
cancel_uacs( t, cancel_data, F_CANCEL_B_KILL);
}
cancel_data->cancel_bitmap=0; /* we've already took care of everything */
put_on_wait(t);
return RPS_ERROR;
}
/* This function is called whenever a reply for our module is received;
* we need to register this function on module initialization;
* Returns : 0 - core router stops
* 1 - core router relay statelessly
*/
int reply_received( struct sip_msg *p_msg )
{
int msg_status;
int last_uac_status;
char *ack;
unsigned int ack_len;
int branch;
/* has the transaction completed now and we need to clean-up? */
int reply_status;
int onreply_route;
struct cancel_info cancel_data;
struct ua_client *uac;
struct cell *t;
struct dest_info lack_dst;
avp_list_t* backup_user_from, *backup_user_to;
avp_list_t* backup_domain_from, *backup_domain_to;
avp_list_t* backup_uri_from, *backup_uri_to;
#ifdef WITH_XAVP
sr_xavp_t **backup_xavps;
#endif
int replies_locked;
#ifdef USE_DNS_FAILOVER
int branch_ret;
int prev_branch;
#endif
#ifdef USE_DST_BLACKLIST
int blst_503_timeout;
struct hdr_field* hf;
#endif
#ifdef TMCB_ONSEND
struct tmcb_params onsend_params;
#endif
struct run_act_ctx ctx;
/* make sure we know the associated transaction ... */
if (t_check( p_msg , &branch )==-1)
goto trans_not_found;
/*... if there is none, tell the core router to fwd statelessly */
t=get_t();
if ( (t==0)||(t==T_UNDEFINED))
goto trans_not_found;
if (unlikely(branch==T_BR_UNDEFINED))
BUG("invalid branch, please report to sr-dev@sip-router.org\n");
tm_ctx_set_branch_index(branch);
init_cancel_info(&cancel_data);
msg_status=p_msg->REPLY_STATUS;
replies_locked=0;
uac=&t->uac[branch];
DBG("DEBUG: reply_received: org. status uas=%d, "
"uac[%d]=%d local=%d is_invite=%d)\n",
t->uas.status, branch, uac->last_received,
is_local(t), is_invite(t));
last_uac_status=uac->last_received;
/* it's a cancel ... ? */
if (get_cseq(p_msg)->method.len==CANCEL_LEN
&& memcmp( get_cseq(p_msg)->method.s, CANCEL, CANCEL_LEN)==0
/* .. which is not e2e ? ... */
&& is_invite(t) ) {
/* ... then just stop timers */
if ( msg_status >= 200 )
stop_rb_timers(&uac->local_cancel); /* stop retr & fr */
else
stop_rb_retr(&uac->local_cancel); /* stop only retr */
DBG("DEBUG: reply to local CANCEL processed\n");
goto done;
}
onreply_route=t->on_reply;
if ( msg_status >= 200 ){
#ifdef TM_ONREPLY_FINAL_DROP_OK
#warning Experimental tm onreply_route final reply DROP support active
if (onreply_route)
/* stop only retr., but leave the final reply timers on, in case
the final reply is dropped in the on_reply route */
stop_rb_retr(&uac->request);
else
#endif /* TM_ONREPLY_FINAL_DROP_OK */
/* stop final response timer & retr. if I got a
final response */
stop_rb_timers(&uac->request);
/* acknowledge negative INVITE replies (do it before detailed
* on_reply processing, which may take very long, like if it
* is attempted to establish a TCP connection to a fail-over dst */
if (is_invite(t)) {
if (msg_status >= 300) {
ack = build_ack(p_msg, t, branch, &ack_len);
if (ack) {
#ifdef TMCB_ONSEND
if (SEND_PR_BUFFER(&uac->request, ack, ack_len)>=0)
if (unlikely(has_tran_tmcbs(t, TMCB_REQUEST_SENT))){
INIT_TMCB_ONSEND_PARAMS(onsend_params,
t->uas.request, p_msg, &uac->request,
&uac->request.dst, ack, ack_len,
TMCB_LOCAL_F, branch, TYPE_LOCAL_ACK);
run_onsend_callbacks2(TMCB_REQUEST_SENT, t,
&onsend_params);
}
#else
SEND_PR_BUFFER(&uac->request, ack, ack_len);
#endif
shm_free(ack);
}
} else if (is_local(t) /*&& 200 <= msg_status < 300*/) {
ack = build_local_ack(p_msg, t, branch, &ack_len, &lack_dst);
if (ack) {
if (msg_send(&lack_dst, ack, ack_len)<0)
LOG(L_ERR, "Error while sending local ACK\n");
#ifdef TMCB_ONSEND
else if (unlikely(has_tran_tmcbs(t, TMCB_REQUEST_SENT))){
INIT_TMCB_ONSEND_PARAMS(onsend_params,
t->uas.request, p_msg, &uac->request,
&lack_dst, ack, ack_len, TMCB_LOCAL_F,
branch, TYPE_LOCAL_ACK);
run_onsend_callbacks2(TMCB_REQUEST_SENT, t,
&onsend_params);
}
#endif
#ifndef WITH_AS_SUPPORT
shm_free(ack);
#endif
}
}
}
}else{
/* if branch already canceled re-transmit or generate cancel
* TODO: check if it really makes sense to do it for non-invites too */
if (uac->request.flags & F_RB_CANCELED){
if (uac->local_cancel.buffer_len){
membar_read(); /* make sure we get the current value of
local_cancel */
/* re-transmit if cancel already built */
DBG("tm: reply_received: branch CANCEL retransmit\n");
#ifdef TMCB_ONSEND
if (SEND_BUFFER( &uac->local_cancel)>=0){
if (unlikely (has_tran_tmcbs(t, TMCB_REQUEST_SENT)))
run_onsend_callbacks(TMCB_REQUEST_SENT,
&uac->local_cancel,
0, 0, TMCB_LOCAL_F);
}
#else
SEND_BUFFER( &uac->local_cancel );
#endif
/* retrs. should be already started so do nothing */
}else if (atomic_cmpxchg_long((void*)&uac->local_cancel.buffer, 0,
(long)BUSY_BUFFER)==0){
/* try to rebuild it if empty (not set or marked as BUSY).
* if BUSY or set just exit, a cancel will be (or was) sent
* shortly on this branch */
DBG("tm: reply_received: branch CANCEL created\n");
#ifdef CANCEL_REASON_SUPPORT
if (t->uas.cancel_reas) {
/* cancel reason was saved, use it */
cancel_branch(t, branch, t->uas.cancel_reas,
F_CANCEL_B_FORCE_C);
} else {
/* note that in this case we do not know the reason,
we only know it's a final reply (either locally
generated via script t_reply(), timeout, a received
2xx or 6xx) => try to use t->uas.status as the reason*/
cancel_data.reason.cause =
(t->uas.status>=200)?t->uas.status:CANCEL_REAS_UNKNOWN;
cancel_branch(t, branch, &cancel_data.reason,
F_CANCEL_B_FORCE_C);
}
#else /* CANCEL_REASON_SUPPORT */
cancel_branch(t, branch, F_CANCEL_B_FORCE_C);
#endif /* CANCEL_REASON_SUPPORT */
}
goto done; /* nothing to do */
}
if (is_invite(t)){
/* stop only retr. (and not fr) */
stop_rb_retr(&uac->request);
}else{
/* non-invite: increase retransmissions interval (slow now) */
switch_rb_retr_to_t2(&uac->request);
}
}
/* pre-set the ignore BLST_503 flag in the message, if the
corresponding branch had it set on send */
p_msg->fwd_send_flags.blst_imask|=
uac->request.dst.send_flags.blst_imask & BLST_503;
/* processing of on_reply block */
if (onreply_route) {
set_route_type(TM_ONREPLY_ROUTE);
/* transfer transaction flag to message context */
if (t->uas.request) p_msg->flags=t->uas.request->flags;
/* set the as avp_list the one from transaction */
backup_uri_from = set_avp_list(AVP_TRACK_FROM | AVP_CLASS_URI, &t->uri_avps_from );
backup_uri_to = set_avp_list(AVP_TRACK_TO | AVP_CLASS_URI, &t->uri_avps_to );
backup_user_from = set_avp_list(AVP_TRACK_FROM | AVP_CLASS_USER, &t->user_avps_from );
backup_user_to = set_avp_list(AVP_TRACK_TO | AVP_CLASS_USER, &t->user_avps_to );
backup_domain_from = set_avp_list(AVP_TRACK_FROM | AVP_CLASS_DOMAIN, &t->domain_avps_from );
backup_domain_to = set_avp_list(AVP_TRACK_TO | AVP_CLASS_DOMAIN, &t->domain_avps_to );
#ifdef WITH_XAVP
backup_xavps = xavp_set_list(&t->xavps_list);
#endif
setbflagsval(0, uac->branch_flags);
/* Pre- and post-script callbacks have already
* been executed by the core. (Miklos)
*/
/* lock onreply_route, for safe avp usage */
LOCK_REPLIES( t );
replies_locked=1;
run_top_route(onreply_rt.rlist[onreply_route], p_msg, &ctx);
/* transfer current message context back to t */
if (t->uas.request) t->uas.request->flags=p_msg->flags;
getbflagsval(0, &uac->branch_flags);
/* restore original avp list */
set_avp_list( AVP_TRACK_FROM | AVP_CLASS_URI, backup_uri_from );
set_avp_list( AVP_TRACK_TO | AVP_CLASS_URI, backup_uri_to );
set_avp_list( AVP_TRACK_FROM | AVP_CLASS_USER, backup_user_from );
set_avp_list( AVP_TRACK_TO | AVP_CLASS_USER, backup_user_to );
set_avp_list( AVP_TRACK_FROM | AVP_CLASS_DOMAIN, backup_domain_from );
set_avp_list( AVP_TRACK_TO | AVP_CLASS_DOMAIN, backup_domain_to );
#ifdef WITH_XAVP
xavp_set_list(backup_xavps);
#endif
/* handle a possible DROP in the script, but only if this
is not a final reply (final replies already stop the timers
and droping them might leave a transaction living forever) */
#ifdef TM_ONREPLY_FINAL_DROP_OK
if (unlikely(ctx.run_flags&DROP_R_F))
#else
if (unlikely((ctx.run_flags&DROP_R_F) && (msg_status<200)))
#endif /* TM_ONREPLY_FINAL_DROP_OK */
{
if (likely(replies_locked)) {
replies_locked = 0;
UNLOCK_REPLIES( t );
}
goto done;
}
#ifdef TM_ONREPLY_FINAL_DROP_OK
if (msg_status >= 200) {
/* stop final reply timers, now that we executed the onreply route
and the reply was not DROPed */
if (likely(replies_locked)){
/* if final reply => we have to execute stop_rb_timers, but
with replies unlocked to avoid a possible deadlock
(if the timer is currently running, stop_rb_timers()
will wait until the timer handler ends, but the
final_response_handler() will try to lock replies =>
deadlock).
*/
UNLOCK_REPLIES( t );
replies_locked=0;
}
stop_rb_timers(&uac->request);
}
#endif /* TM_ONREPLY_FINAL_DROP_OK */
/* Re-read the status code, it can be changed in the route block. */
#ifdef EXTRA_DEBUG
/* The class of provisional and positive final replies cannot be changed. */
if (((msg_status < 300) || (p_msg->REPLY_STATUS < 300))
&& (msg_status/100 != p_msg->REPLY_STATUS/100)
) {
LOG(L_ERR, "ERROR: The class of provisional and "
"positive final replies should not be changed!\n");
}
#endif /* EXTRA_DEBUG */
msg_status=p_msg->REPLY_STATUS;
}
#ifdef USE_DST_BLACKLIST
/* add temporary to the blacklist the source of a 503 reply */
if ( (msg_status==503) &&
cfg_get(tm, tm_cfg, tm_blst_503) &&
/* check if the request sent on the branch had the the
blst 503 ignore flags set or it was set in the onreply_r*/
should_blacklist_su(BLST_503, &p_msg->fwd_send_flags,
p_msg->rcv.proto, &p_msg->rcv.src_su)
){
blst_503_timeout=cfg_get(tm, tm_cfg, tm_blst_503_default);
if ((parse_headers(p_msg, HDR_RETRY_AFTER_F, 0)==0) &&
(p_msg->parsed_flag & HDR_RETRY_AFTER_F)){
for (hf=p_msg->headers; hf; hf=hf->next)
if (hf->type==HDR_RETRY_AFTER_T){
/* found */
blst_503_timeout=(unsigned)(unsigned long)hf->parsed;
blst_503_timeout=MAX_unsigned(blst_503_timeout,
cfg_get(tm, tm_cfg, tm_blst_503_min));
blst_503_timeout=MIN_unsigned(blst_503_timeout,
cfg_get(tm, tm_cfg, tm_blst_503_max));
break;
}
}
if (blst_503_timeout){
dst_blacklist_force_su_to(BLST_503, p_msg->rcv.proto,
&p_msg->rcv.src_su, p_msg,
S_TO_TICKS(blst_503_timeout));
}
}
#endif /* USE_DST_BLACKLIST */
#ifdef USE_DNS_FAILOVER
/* if this is a 503 reply, and the destination resolves to more ips,
* add another branch/uac.
* This code is out of LOCK_REPLIES() to minimize the time the
* reply lock is held (the lock won't be held while sending the
* message)*/
if (cfg_get(core, core_cfg, use_dns_failover) && (msg_status==503)) {
branch_ret=add_uac_dns_fallback(t, t->uas.request,
uac, !replies_locked);
prev_branch=-1;
/* unlock replies to avoid sending() while holding a lock */
if (unlikely(replies_locked)) {
UNLOCK_REPLIES( t );
replies_locked = 0;
}
while((branch_ret>=0) &&(branch_ret!=prev_branch)){
prev_branch=branch_ret;
branch_ret=t_send_branch(t, branch_ret, t->uas.request , 0, 1);
}
}
#endif
if (unlikely(!replies_locked)){
LOCK_REPLIES( t );
replies_locked=1;
}
if ( is_local(t) ) {
reply_status=local_reply( t, p_msg, branch, msg_status, &cancel_data );
if (reply_status == RPS_COMPLETED) {
/* no more UAC FR/RETR (if I received a 2xx, there may
* be still pending branches ...
*/
cleanup_uac_timers( t );
if (is_invite(t)) cancel_uacs(t, &cancel_data, F_CANCEL_B_KILL);
/* There is no need to call set_final_timer because we know
* that the transaction is local */
put_on_wait(t);
}else if (unlikely(cancel_data.cancel_bitmap)){
/* cancel everything, even non-INVITEs (e.g in case of 6xx), use
* cancel_b_method for canceling unreplied branches */
cancel_uacs(t, &cancel_data, cfg_get(tm,tm_cfg, cancel_b_flags));
}
} else {
reply_status=relay_reply( t, p_msg, branch, msg_status,
&cancel_data, 1 );
if (reply_status == RPS_COMPLETED) {
/* no more UAC FR/RETR (if I received a 2xx, there may
be still pending branches ...
*/
cleanup_uac_timers( t );
/* 2xx is a special case: we can have a COMPLETED request
* with branches still open => we have to cancel them */
if (is_invite(t) && cancel_data.cancel_bitmap)
cancel_uacs( t, &cancel_data, F_CANCEL_B_KILL);
/* FR for negative INVITES, WAIT anything else */
/* Call to set_final_timer is embedded in relay_reply to avoid
* race conditions when reply is sent out and an ACK to stop
* retransmissions comes before retransmission timer is set.*/
}else if (unlikely(cancel_data.cancel_bitmap)){
/* cancel everything, even non-INVITEs (e.g in case of 6xx), use
* cancel_b_method for canceling unreplied branches */
cancel_uacs(t, &cancel_data, cfg_get(tm,tm_cfg, cancel_b_flags));
}
}
uac->request.flags|=F_RB_REPLIED;
if (reply_status==RPS_ERROR)
goto done;
/* update FR/RETR timers on provisional replies */
if (is_invite(t) && msg_status<200 &&
( cfg_get(tm, tm_cfg, restart_fr_on_each_reply) ||
( (last_uac_status<msg_status) &&
((msg_status>=180) || (last_uac_status==0)) )
) ) { /* provisional now */
restart_rb_fr(& uac->request, t->fr_inv_timeout);
uac->request.flags|=F_RB_FR_INV; /* mark fr_inv */
} /* provisional replies */
done:
tm_ctx_set_branch_index(0);
/* we are done with the transaction, so unref it - the reference
* was incremented by t_check() function -bogdan*/
t_unref(p_msg);
/* don't try to relay statelessly neither on success
(we forwarded statefully) nor on error; on troubles,
simply do nothing; that will make the other party to
retransmit; hopefuly, we'll then be better off */
return 0;
trans_not_found:
/* transaction context was not found */
if (goto_on_sl_reply) {
/* The script writer has a chance to decide whether to
* forward the reply or not.
* Pre- and post-script callbacks have already
* been execueted by the core. (Miklos)
*/
return run_top_route(onreply_rt.rlist[goto_on_sl_reply], p_msg, 0);
} else {
/* let the core forward the reply */
return 1;
}
}
int t_reply_with_body( struct cell *trans, unsigned int code,
str *text, str *body, str *new_header, str *to_tag )
{
struct lump_rpl *hdr_lump;
struct lump_rpl *body_lump;
str s_to_tag;
str rpl;
int ret;
struct bookmark bm;
if(to_tag) {
s_to_tag.s = to_tag->s;
s_to_tag.len = to_tag->len;
} else {
s_to_tag.len = 0;
}
/* mark the transaction as replied */
if (code>=200) set_kr(REQ_RPLD);
/* add the lumps for new_header and for body (by bogdan) */
if (new_header && new_header->len) {
hdr_lump = add_lump_rpl( trans->uas.request, new_header->s,
new_header->len, LUMP_RPL_HDR );
if ( !hdr_lump ) {
LOG(L_ERR,"ERROR:tm:t_reply_with_body: cannot add hdr lump\n");
goto error;
}
} else {
hdr_lump = 0;
}
/* body lump */
if(body && body->len) {
body_lump = add_lump_rpl( trans->uas.request, body->s, body->len,
LUMP_RPL_BODY );
if (body_lump==0) {
LOG(L_ERR,"ERROR:tm:t_reply_with_body: cannot add body lump\n");
goto error_1;
}
} else {
body_lump = 0;
}
rpl.s = build_res_buf_from_sip_req(
code, text, &s_to_tag,
trans->uas.request, (unsigned int*)&rpl.len, &bm);
/* since the msg (trans->uas.request) is a clone into shm memory, to avoid
* memory leak or crashing (lumps are create in private memory) I will
* remove the lumps by myself here (bogdan) */
if ( hdr_lump ) {
unlink_lump_rpl( trans->uas.request, hdr_lump);
free_lump_rpl( hdr_lump );
}
if( body_lump ) {
unlink_lump_rpl( trans->uas.request, body_lump);
free_lump_rpl( body_lump );
}
if (rpl.s==0) {
LOG(L_ERR,"ERROR:tm:t_reply_with_body: failed in doing "
"build_res_buf_from_sip_req()\n");
goto error;
}
DBG("t_reply_with_body: buffer computed\n");
// frees 'res.s' ... no panic !
ret=_reply_light( trans, rpl.s, rpl.len, code,
s_to_tag.s, s_to_tag.len, 1 /* lock replies */, &bm );
/* this is ugly hack -- the function caller may wish to continue with
* transaction and I unref; however, there is now only one use from
* vm/fifo_vm_reply and I'm currently to lazy to export UNREF; -jiri
*/
UNREF(trans);
return ret;
error_1:
if ( hdr_lump ) {
unlink_lump_rpl( trans->uas.request, hdr_lump);
free_lump_rpl( hdr_lump );
}
error:
return -1;
}
/* drops all the replies to make sure
* that none of them is picked up again
*/
void t_drop_replies(int v)
{
/* It is too risky to free the replies that are in shm mem
at the middle of failure_route block, because other functions might
need them as well. And it can also happen that the current reply is not yet
in shm mem, we are just going to clone it. So better to set a flag
and check it after failure_route has ended. (Miklos) */
drop_replies = v;
}
#if 0
static int send_reply(struct cell *trans, unsigned int code, str* text, str* body, str* headers, str* to_tag)
{
struct lump_rpl *hdr_lump, *body_lump;
str rpl;
int ret;
struct bookmark bm;
/* mark the transaction as replied */
if (code >= 200) set_kr(REQ_RPLD);
/* add the lumps for new_header and for body (by bogdan) */
if (headers && headers->len) {
hdr_lump = add_lump_rpl(trans->uas.request, headers->s, headers->len, LUMP_RPL_HDR);
if (!hdr_lump) {
LOG(L_ERR, "send_reply: cannot add hdr lump\n");
goto sr_error;
}
} else {
hdr_lump = 0;
}
/* body lump */
if (body && body->len) {
body_lump = add_lump_rpl(trans->uas.request, body->s, body->len, LUMP_RPL_BODY);
if (body_lump == 0) {
LOG(L_ERR,"send_reply: cannot add body lump\n");
goto sr_error_1;
}
} else {
body_lump = 0;
}
/* We can safely zero-terminate the text here, because it is followed
* by next line in the received message
*/
text->s[text->len] = '\0';
rpl.s = build_res_buf_from_sip_req(code, text->s, to_tag, trans->uas.request, (unsigned int*)&rpl.len, &bm);
/* since the msg (trans->uas.request) is a clone into shm memory, to avoid
* memory leak or crashing (lumps are create in private memory) I will
* remove the lumps by myself here (bogdan) */
if (hdr_lump) {
unlink_lump_rpl(trans->uas.request, hdr_lump);
free_lump_rpl(hdr_lump);
}
if (body_lump) {
unlink_lump_rpl(trans->uas.request, body_lump);
free_lump_rpl(body_lump);
}
if (rpl.s == 0) {
LOG(L_ERR,"send_reply: failed in build_res_buf_from_sip_req\n");
goto sr_error;
}
ret = _reply_light(trans, rpl.s, rpl.len, code, to_tag->s, to_tag->len,
1 /* lock replies */, &bm);
/* this is ugly hack -- the function caller may wish to continue with
* transaction and I unref; however, there is now only one use from
* vm/fifo_vm_reply and I'm currently to lazy to export UNREF; -jiri
*/
UNREF(trans);
return ret;
sr_error_1:
if (hdr_lump) {
unlink_lump_rpl(trans->uas.request, hdr_lump);
free_lump_rpl(hdr_lump);
}
sr_error:
return -1;
}
#endif
/*
Syntax:
":tm.reply:[response file]\n
code\n
reason\n
trans_id\n
to_tag\n
[new headers]\n
\n
[Body]\n
.\n
\n"
*/
void rpc_reply(rpc_t* rpc, void* c)
{
int ret;
struct cell *trans;
unsigned int hash_index, label, code;
str ti, body, headers, tag, reason;
if (rpc->scan(c, "d", &code) < 1) {
rpc->fault(c, 400, "Reply code expected");
return;
}
if (rpc->scan(c, "S", &reason) < 1) {
rpc->fault(c, 400, "Reason phrase expected");
return;
}
if (rpc->scan(c, "S", &ti) < 1) {
rpc->fault(c, 400, "Transaction ID expected");
return;
}
if (rpc->scan(c, "S", &tag) < 1) {
rpc->fault(c, 400, "To tag expected");
return;
}
if (rpc->scan(c, "S", &headers) < 0) return;
if (rpc->scan(c, "S", &body) < 0) return;
if(sscanf(ti.s,"%u:%u", &hash_index, &label) != 2) {
ERR("Invalid trans_id (%s)\n", ti.s);
rpc->fault(c, 400, "Invalid transaction ID");
return;
}
DBG("hash_index=%u label=%u\n", hash_index, label);
if( t_lookup_ident(&trans, hash_index, label) < 0 ) {
ERR("Lookup failed\n");
rpc->fault(c, 481, "No such transaction");
return;
}
/* it's refcounted now, t_reply_with body unrefs for me -- I can
* continue but may not use T anymore */
ret = t_reply_with_body(trans, code, &reason, &body, &headers, &tag);
if (ret < 0) {
ERR("Reply failed\n");
rpc->fault(c, 500, "Reply failed");
return;
}
}