Ensure Asterisk sends a BYE when pending on the final response to a re-INVITE
authorMatthew Jordan <mjordan@digium.com>
Wed, 21 Mar 2012 13:31:09 +0000 (13:31 +0000)
committerMatthew Jordan <mjordan@digium.com>
Wed, 21 Mar 2012 13:31:09 +0000 (13:31 +0000)
When Asterisk detects a hangup and cannot send a BYE due to a pending
INVITE, it sets the pendingbye flag and waits for the final response to that
INVITE.  When the response is received, it transmits the BYE.  If, however,
that INVITE request is a pending re-INVITE, it needs to first send a CANCEL
request to terminate the pending re-INVITE.  In that circumstance, Asterisk
was, in some scenarios, clearing the pendingbye flag after processing the
CANCEL request and not checking for a pending BYE when receiving the final
487 response to the INVITE.

This patch ensures that if the pendingbye flag is set, it is honored
regardless of the nature of the INVITE request currently in flight.

(closes issue ASTERISK-19365)
Reported by: Thomas Arimont
Tested by: Thomas Arimont
Patches:
  bugASTERISK-19365_2012_03_08.patch uploaded by mjordan (license 6283)

Review: https://reviewboard.asterisk.org/r/1807
........

Merged revisions 360086 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........

Merged revisions 360088 from http://svn.asterisk.org/svn/asterisk/branches/10

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

channels/chan_sip.c

index b1258a7..21e463f 100644 (file)
@@ -12635,7 +12635,7 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init,
                /* If init=1, we should not generate a new branch. If it's 0, we need a new branch. */
                reqprep(&req, p, sipmethod, 0, init ? 0 : 1);
        }
-               
+
        if (p->options && p->options->auth) {
                add_header(&req, p->options->authheader, p->options->auth);
        }
@@ -20552,6 +20552,10 @@ static void check_pendings(struct sip_pvt *p)
                if (p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA) {
                        p->invitestate = INV_CANCELLED;
                        transmit_request(p, SIP_CANCEL, p->lastinvite, XMIT_RELIABLE, FALSE);
+                       /* If the cancel occurred on an initial invite, cancel the pending BYE */
+                       if (!ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) {
+                               ast_clear_flag(&p->flags[0], SIP_PENDINGBYE);
+                       }
                        /* Actually don't destroy us yet, wait for the 487 on our original
                           INVITE, but do set an autodestruct just in case we never get it. */
                } else {
@@ -20565,8 +20569,8 @@ static void check_pendings(struct sip_pvt *p)
                        }
                        /* Perhaps there is an SD change INVITE outstanding */
                        transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, TRUE);
+                       ast_clear_flag(&p->flags[0], SIP_PENDINGBYE);
                }
-               ast_clear_flag(&p->flags[0], SIP_PENDINGBYE);
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
        } else if (ast_test_flag(&p->flags[0], SIP_NEEDREINVITE)) {
                /* if we can't REINVITE, hold it for later */
@@ -20728,7 +20732,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
        int outgoing = ast_test_flag(&p->flags[0], SIP_OUTGOING);
        int res = 0;
        int xmitres = 0;
-       int reinvite = (p->owner && ast_channel_state(p->owner) == AST_STATE_UP);
+       int reinvite = ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
        char *p_hdrval;
        int rtn;
        struct ast_party_connected_line connected;
@@ -20934,10 +20938,11 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
                p->authtries = 0;
                if (find_sdp(req)) {
                        if ((res = process_sdp(p, req, SDP_T38_ACCEPT)) && !req->ignore)
-                               if (!reinvite)
+                               if (!reinvite) {
                                        /* This 200 OK's SDP is not acceptable, so we need to ack, then hangup */
                                        /* For re-invites, we try to recover */
                                        ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
+                               }
                        ast_rtp_instance_activate(p->rtp);
                }
 
@@ -20984,7 +20989,6 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
                        if (!reinvite) {
                                build_route(p, req, 1, resp);
                        }
-
                        if(set_address_from_contact(p)) {
                                /* Bad contact - we don't know how to reach this device */
                                /* We need to ACK, but then send a bye */
@@ -21138,6 +21142,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
                        update_call_counter(p, DEC_CALL_LIMIT);
                        append_history(p, "Hangup", "Got 487 on CANCEL request from us on call without owner. Killing this dialog.");
                }
+               check_pendings(p);
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
                break;
        case 415: /* Unsupported media type */
@@ -21834,8 +21839,9 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
        }
 
        /* If this is a NOTIFY for a subscription clear the flag that indicates that we have a NOTIFY pending */
-       if (!p->owner && sipmethod == SIP_NOTIFY && p->pendinginvite)
+       if (!p->owner && sipmethod == SIP_NOTIFY && p->pendinginvite) {
                p->pendinginvite = 0;
+       }
 
        /* Get their tag if we haven't already */
        if (ast_strlen_zero(p->theirtag) || (resp >= 200)) {