Browse code

sip: b/f: find the correct transaction to cancel

In case the request has been sent to multiple destinations for failover purposes, new transactions might be created. To cancel such transactions, it necessary to find the last one by looking at the dialog-id and cseq, as only a reference to the original transaction has been saved in the dialog state.

Raphael Coeffic authored on 08/12/2013 13:55:05
Showing 7 changed files
... ...
@@ -719,7 +719,8 @@ int AmSipDialog::cancel()
719 719
 	  }
720 720
 	  else if(getStatus() != Cancelling){
721 721
 	    setStatus(Cancelling);
722
-	    return SipCtrlInterface::cancel(&t->second.tt,t->second.hdrs);
722
+	    return SipCtrlInterface::cancel(&t->second.tt, local_tag,
723
+					    t->first, t->second.hdrs);
723 724
 	  }
724 725
 	  else {
725 726
 	    ERROR("INVITE transaction has already been cancelled\n");
... ...
@@ -174,9 +174,11 @@ _SipCtrlInterface::_SipCtrlInterface()
174 174
     trans_layer::instance()->register_ua(this);
175 175
 }
176 176
 
177
-int _SipCtrlInterface::cancel(trans_ticket* tt, const string& hdrs)
177
+int _SipCtrlInterface::cancel(trans_ticket* tt, const string& dialog_id,
178
+			      unsigned int inv_cseq, const string& hdrs)
178 179
 {
179
-    return trans_layer::instance()->cancel(tt,stl2cstr(hdrs));
180
+    return trans_layer::instance()->cancel(tt,stl2cstr(dialog_id),
181
+					   inv_cseq,stl2cstr(hdrs));
180 182
 }
181 183
 
182 184
 int _SipCtrlInterface::send(AmSipRequest &req, const string& dialog_id,
... ...
@@ -184,7 +186,7 @@ int _SipCtrlInterface::send(AmSipRequest &req, const string& dialog_id,
184 186
 			    unsigned int flags, msg_logger* logger)
185 187
 {
186 188
     if(req.method == "CANCEL")
187
-	return cancel(&req.tt, req.hdrs);
189
+	return cancel(&req.tt, dialog_id, req.cseq, req.hdrs);
188 190
 
189 191
     sip_msg* msg = new sip_msg();
190 192
     
... ...
@@ -107,7 +107,8 @@ public:
107 107
      *
108 108
      * @param tt transaction ticket of the request to cancel.
109 109
      */
110
-    static int cancel(trans_ticket* tt, const string& hdrs);
110
+    static int cancel(trans_ticket* tt, const string& dialog_id,
111
+		      unsigned int inv_cseq, const string& hdrs);
111 112
 
112 113
     /**
113 114
      * From sip_ua
... ...
@@ -1255,7 +1255,8 @@ int _trans_layer::send_request(sip_msg* msg, trans_ticket* tt,
1255 1255
     return send_err;
1256 1256
 }
1257 1257
 
1258
-int _trans_layer::cancel(trans_ticket* tt, const cstring& hdrs)
1258
+int _trans_layer::cancel(trans_ticket* tt, const cstring& dialog_id,
1259
+			 unsigned int inv_cseq, const cstring& hdrs)
1259 1260
 {
1260 1261
     assert(tt);
1261 1262
     assert(tt->_bucket && tt->_t);
... ...
@@ -1264,7 +1265,14 @@ int _trans_layer::cancel(trans_ticket* tt, const cstring& hdrs)
1264 1265
     sip_trans*    t = tt->_t;
1265 1266
 
1266 1267
     bucket->lock();
1267
-    if(!bucket->exist(t)){
1268
+    if(!bucket->exist(t) || (t->state == TS_ABANDONED)){
1269
+	if(dialog_id.len)
1270
+	    t = bucket->find_uac_trans(dialog_id,inv_cseq);
1271
+	else
1272
+	    t = NULL;
1273
+    }
1274
+
1275
+    if(!t){
1268 1276
 	DBG("No transaction to cancel: wrong key or finally replied\n");
1269 1277
 	bucket->unlock();
1270 1278
 	return 0;
... ...
@@ -1644,7 +1652,7 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg*
1644 1652
 	    {
1645 1653
 		// send CANCEL
1646 1654
 		trans_ticket tt(t,bucket);
1647
-		cancel(&tt,cstring());
1655
+		cancel(&tt,cstring(),0,cstring());
1648 1656
 	    
1649 1657
 		// Now remove the transaction
1650 1658
 		bucket->lock();
... ...
@@ -1847,7 +1855,8 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg*
1847 1855
     return 0;
1848 1856
 }
1849 1857
 
1850
-int _trans_layer::update_uac_request(trans_bucket* bucket, sip_trans*& t, sip_msg* msg)
1858
+int _trans_layer::update_uac_request(trans_bucket* bucket, sip_trans*& t,
1859
+				     sip_msg* msg)
1851 1860
 {
1852 1861
     if(msg->u.request->method != sip_request::ACK){
1853 1862
 	t = bucket->add_trans(msg,TT_UAC);
... ...
@@ -2129,13 +2138,12 @@ void _trans_layer::timer_expired(trans_timer* t, trans_bucket* bucket,
2129 2138
 	tr->clear_timer(STIMER_C);
2130 2139
 	//if(tr->state != TS_PROCEEDING)
2131 2140
 	//  break; // shouldn't happen
2132
-
2133 2141
 	bucket->unlock();
2134 2142
 
2135 2143
 	{
2136 2144
 	    // send CANCEL
2137 2145
 	    trans_ticket tt(tr,bucket);
2138
-	    cancel(&tt,cstring());
2146
+	    cancel(&tt,cstring(),0,cstring());
2139 2147
 	    
2140 2148
 	    // Now remove the transaction
2141 2149
 	    bucket->lock();
... ...
@@ -170,7 +170,8 @@ public:
170 170
      * A CANCEL request is sent if necessary.
171 171
      * @param tt transaction ticket from the original INVITE.
172 172
      */
173
-    int cancel(trans_ticket* tt, const cstring& hdrs);
173
+    int cancel(trans_ticket* tt, const cstring& dialog_id,
174
+	       unsigned int inv_cseq, const cstring& hdrs);
174 175
     
175 176
     /**
176 177
      * Called by the transport layer
... ...
@@ -415,6 +415,33 @@ sip_trans* trans_bucket::match_1xx_prack(sip_msg* msg)
415 415
     return NULL;
416 416
 }
417 417
 
418
+sip_trans* trans_bucket::find_uac_trans(const cstring& dialog_id,
419
+					unsigned int inv_cseq)
420
+{
421
+    DBG("Matching dialog_id = '%.*s'\n",
422
+	dialog_id.len, dialog_id.s);
423
+
424
+    if(elmts.empty())
425
+	return NULL;
426
+    
427
+    trans_list::reverse_iterator it = elmts.rbegin();
428
+    for(;it!=elmts.rend();++it) {
429
+	    
430
+	sip_trans* t = *it;
431
+	if( t->type != TT_UAC ||
432
+	    t->msg->type != SIP_REQUEST ){
433
+	    continue;
434
+	}
435
+	sip_cseq* t_cseq = dynamic_cast<sip_cseq*>(t->msg->cseq->p);
436
+	if(t->dialog_id == dialog_id &&
437
+	   t_cseq && t_cseq->num == inv_cseq &&
438
+	   t->state != TS_ABANDONED)
439
+	    return t;
440
+    }
441
+
442
+    return NULL;
443
+}
444
+
418 445
 sip_trans* trans_bucket::add_trans(sip_msg* msg, unsigned int ttype)
419 446
 {
420 447
     sip_trans* t = new sip_trans();
... ...
@@ -33,6 +33,9 @@ public:
33 33
     // in this bucket
34 34
     sip_trans* match_reply(sip_msg* msg);
35 35
 
36
+    // Find the latest UAC transaction matching dialog_id
37
+    sip_trans* find_uac_trans(const cstring& dialog_id, unsigned int inv_cseq);
38
+
36 39
     // Add a new transaction using provided message and type
37 40
     sip_trans* add_trans(sip_msg* msg, unsigned int ttype);
38 41