Merged revisions 202341-202342 via svnmerge from
[asterisk/asterisk.git] / channels / chan_sip.c
index a3f219b..2cc34c1 100644 (file)
@@ -1930,6 +1930,7 @@ struct sip_pkt {
        int seqno;                              /*!< Sequence number */
        char is_resp;                           /*!< 1 if this is a response packet (e.g. 200 OK), 0 if it is a request */
        char is_fatal;                          /*!< non-zero if there is a fatal error */
+       int response_code;              /*!< If this is a response, the response code */
        struct sip_pvt *owner;                  /*!< Owner AST call */
        int retransid;                          /*!< Retransmission ID */
        int timer_a;                            /*!< SIP timer A, retransmission timer */
@@ -3727,6 +3728,7 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
        struct sip_pkt *pkt = NULL;
        int siptimer_a = DEFAULT_RETRANS;
        int xmitres = 0;
+       int respid;
 
        if (sipmethod == SIP_INVITE) {
                /* Note this is a pending invite */
@@ -3763,6 +3765,12 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
        pkt->owner = dialog_ref(p, "__sip_reliable_xmit: setting pkt->owner");
        pkt->next = p->packets;
        p->packets = pkt;       /* Add it to the queue */
+       if (resp) {
+               /* Parse out the response code */
+               if (sscanf(ast_str_buffer(pkt->data), "SIP/2.0 %d", &respid) == 1) {
+                       pkt->response_code = respid;
+               }
+       }
        pkt->timer_t1 = p->timer_t1;    /* Set SIP timer T1 */
        pkt->retransid = -1;
        if (pkt->timer_t1)
@@ -21103,6 +21111,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;