aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormmichelson <mmichelson@f38db490-d61c-443f-a65b-d21fe96a405b>2009-06-22 15:05:00 +0000
committermmichelson <mmichelson@f38db490-d61c-443f-a65b-d21fe96a405b>2009-06-22 15:05:00 +0000
commit6d5d0f4653ed5e172e7743cecf20d13f19221044 (patch)
treedd7bf591498303e5316a6e5d92770015f74562d1
parent68d880eec0795b249acbec50c401c3149f40e67d (diff)
Merged revisions 202343 via svnmerge from
https://origsvn.digium.com/svn/asterisk/trunk ................ r202343 | mmichelson | 2009-06-22 09:58:24 -0500 (Mon, 22 Jun 2009) | 36 lines Merged revisions 202341-202342 via svnmerge from https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r202341 | mmichelson | 2009-06-22 09:42:55 -0500 (Mon, 22 Jun 2009) | 26 lines Fix a situation in which Asterisk would not stop retransmitting 487s. 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 ........ r202342 | mmichelson | 2009-06-22 09:44:58 -0500 (Mon, 22 Jun 2009) | 3 lines Remove an extra debug line left from previous commit. ........ ................ git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.6.0@202344 f38db490-d61c-443f-a65b-d21fe96a405b
-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 325b0dd5b..8f1d0462b 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1387,6 +1387,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 */
@@ -2937,6 +2938,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 */
@@ -2969,6 +2971,12 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
pkt->owner = dialog_ref(p);
pkt->next = p->packets;
p->packets = pkt; /* Add it to the queue */
+ if (resp) {
+ /* Parse out the response code */
+ if (sscanf(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)
@@ -18328,6 +18336,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;