summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--channels/chan_sip.c32
1 files changed, 32 insertions, 0 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index a3f219b5d..2cc34c143 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -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;