浏览代码

tm: avoid tryin to mitigate relaying 487 for local canceling branches when a reply is forced from config

- it can result in deadlock if there is a local retransmission timeout
  and the response code is overwritten by admin in failure route with a
  t_reply()
- reported by Jason Penton
Daniel-Constantin Mierla 11 年之前
父节点
当前提交
198e123e89
共有 4 个文件被更改,包括 10 次插入1 次删除
  1. 2 0
      modules/tm/h_table.h
  2. 6 1
      modules/tm/t_cancel.c
  3. 1 0
      modules/tm/timer.c
  4. 1 0
      modules/tm/tm.c

+ 2 - 0
modules/tm/h_table.h

@@ -155,6 +155,7 @@ enum kill_reason { REQ_FWDED=1, REQ_RPLD=2, REQ_RLSD=4, REQ_EXIST=8,
 #define F_RB_NH_STRICT	0x200 /* next hop is a strict router */
 /* must detect when neither loose nor strict flag is set -> two flags.
  * alternatively, 1x flag for strict/loose and 1x for loose|strict set/not */
+#define F_RB_RELAYREPLY	0x400 /* branch under relay reply condition */
 
 
 /* if canceled or intended to be canceled, return true */
@@ -320,6 +321,7 @@ typedef struct async_state {
 #define T_ASYNC_CONTINUE (1<<12) /* Is this transaction in a continuation after being suspended */
 
 #define T_DISABLE_INTERNAL_REPLY (1<<13) /* don't send internal negative reply */
+#define T_ADMIN_REPLY (1<<14) /* t reply sent by admin (e.g., from cfg script) */
 
 /* unsigned short should be enough for a retr. timer: max. 65535 ms =>
  * max retr. = 65 s which should be enough and saves us 2*2 bytes */

+ 6 - 1
modules/tm/t_cancel.c

@@ -245,7 +245,12 @@ int cancel_branch( struct cell *t, int branch,
 			/* remove BUSY_BUFFER -- mark cancel buffer as not used */
 			pcbuf=&crb->buffer; /* workaround for type punning warnings */
 			atomic_set_long(pcbuf, 0);
-			if (flags & F_CANCEL_B_FAKE_REPLY){
+			/* try to relay auto-generated 487 canceling response only when
+			 * another one is not under relaying on the branch and there is
+			 * no forced response per transaction from script */
+			if((flags & F_CANCEL_B_FAKE_REPLY)
+					&& !(irb->flags&F_RB_RELAYREPLY)
+					&& !(t->flags&T_ADMIN_REPLY)) {
 				LOCK_REPLIES(t);
 				if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_cd, 1) == 
 										RPS_ERROR){

+ 1 - 0
modules/tm/timer.c

@@ -345,6 +345,7 @@ static void fake_reply(struct cell *t, int branch, int code )
 	do_cancel_branch = is_invite(t) && prepare_cancel_branch(t, branch, 0);
 	/* mark branch as canceled */
 	t->uac[branch].request.flags|=F_RB_CANCELED;
+	t->uac[branch].request.flags|=F_RB_RELAYREPLY;
 	if ( is_local(t) ) {
 		reply_status=local_reply( t, FAKED_REPLY, branch, 
 					  code, &cancel_data );

+ 1 - 0
modules/tm/tm.c

@@ -1319,6 +1319,7 @@ inline static int w_t_reply(struct sip_msg* msg, char* p1, char* p2)
 	 * the safe version would lead to a deadlock
 	 */
 	 
+	t->flags |= T_ADMIN_REPLY;
 	if (is_route_type(FAILURE_ROUTE)) {
 		DBG("DEBUG: t_reply_unsafe called from w_t_reply\n");
 		ret = t_reply_unsafe(t, msg, code, r);