]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Fix a situation in which Asterisk would not stop retransmitting 487s.
authorMark Michelson <mmichelson@digium.com>
Mon, 22 Jun 2009 14:42:55 +0000 (14:42 +0000)
committerMark Michelson <mmichelson@digium.com>
Mon, 22 Jun 2009 14:42:55 +0000 (14:42 +0000)
If a CANCEL were received by Asterisk, we would send a 487 in response
to the original INVITE and a 200 OK for the CANCEL. If there were a network
hiccup which caused the 200 OK and the 487 to be lost, then the UA communicating
with Asterisk may try to retransmit its CANCEL. Asterisk's response to this used
to be to try sending another 487 to the canceled INVITE and another 200 OK to the
CANCEL.

The problem here is that the originally-sent 487 was sent "reliably" meaning that
it will be retransmitted until it is received properly. So when we receive the second
CANCEL it is likely that the first batch of 487s we sent is still going strong and
reaches the UA. The result was that the second set of 487s would be retransmitted
constantly until the maximum number of retries had been reached.

The fix for this is that if we receive a second CANCEL for an INVITE, then we cancel
the retransmission of the first set of 487s and start a second set. This causes the
dialog to be terminated reasonably.

(closes issue #14584)
Reported by: klaus3000
Patches:
      14584_v2.patch uploaded by mmichelson (license 60)
Tested by: klaus3000

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@202341 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_sip.c

index 0483a592d3c2fdae0f292ed854b103f0cd0a3773..c1e83ae60e7b3d39d10b080ad628d4d4f7a71dcd 100644 (file)
@@ -1048,6 +1048,7 @@ struct sip_pkt {
        int retrans;                            /*!< Retransmission number */
        int method;                             /*!< SIP method for this packet */
        int seqno;                              /*!< Sequence number */
+       int response_code;              /*!< If this is a response, the response code */
        unsigned int flags;                     /*!< non-zero if this is a response packet (e.g. 200 OK) */
        struct sip_pvt *owner;                  /*!< Owner AST call */
        int retransid;                          /*!< Retransmission ID */
@@ -2052,6 +2053,7 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
        struct sip_pkt *pkt;
        int siptimer_a = DEFAULT_RETRANS;
        int xmitres = 0;
+       int respid;
 
        if (!(pkt = ast_calloc(1, sizeof(*pkt) + len + 1)))
                return AST_FAILURE;
@@ -2061,9 +2063,15 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
        pkt->next = p->packets;
        pkt->owner = p;
        pkt->seqno = seqno;
-       if (resp)
-               ast_set_flag(pkt, FLAG_RESPONSE);
        pkt->data[len] = '\0';
+       if (resp) {
+               ast_set_flag(pkt, FLAG_RESPONSE);
+               /* Parse out the response code */
+               if (sscanf(pkt->data, "SIP/2.0 %d", &respid) == 1) {
+                       pkt->response_code = respid;
+                       ast_log(LOG_NOTICE, "Hey, I just set the response code for this packet to %d\n", pkt->response_code);
+               }
+       }
        pkt->timer_t1 = p->timer_t1;    /* Set SIP timer T1 */
        pkt->retransid = -1;
        if (fatal)
@@ -15492,6 +15500,30 @@ static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
        else
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
        if (p->initreq.len > 0) {
+               struct sip_pkt *pkt, *prev_pkt;
+               /* If the CANCEL we are receiving is a retransmission, and we already have scheduled
+                * a reliable 487, then we don't want to schedule another one on top of the previous
+                * one.
+                *
+                * As odd as this may sound, we can't rely on the previously-transmitted "reliable" 
+                * response in this situation. What if we've sent all of our reliable responses 
+                * already and now all of a sudden, we get this second CANCEL?
+                *
+                * The only way to do this correctly is to cancel our previously-scheduled reliably-
+                * transmitted response and send a new one in its place.
+                */
+               for (pkt = p->packets, prev_pkt = NULL; pkt; prev_pkt = pkt, pkt = pkt->next) {
+                       if (pkt->seqno == p->lastinvite && pkt->response_code == 487) {
+                               AST_SCHED_DEL(sched, pkt->retransid);
+                               if (prev_pkt) {
+                                       prev_pkt->next = pkt->next;
+                               } else {
+                                       p->packets = pkt->next;
+                               }
+                               ast_free(pkt);
+                               break;
+                       }
+               }
                transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
                transmit_response(p, "200 OK", req);
                return 1;