Fix a race condition where a canceled call was answered.
authorMark Michelson <mmichelson@digium.com>
Thu, 29 Aug 2013 22:25:16 +0000 (22:25 +0000)
committerMark Michelson <mmichelson@digium.com>
Thu, 29 Aug 2013 22:25:16 +0000 (22:25 +0000)
RFC 5407 section 3.1.2 details a scenario where a UAC sends
a CANCEL at the same time that a UAS sends a 200 OK for the
INVITE that the UAC is canceling. When this occurs, it is the
role of the UAC to immediately send a BYE to terminate
the call.

This scenario was reproducible by have a Digium phone with two lines
place a call to a second phone that forwarded the call to the second
line on the original phone. The Digium phone, upon realizing that it
was connecting to itself, would attempt to cancel the call. The timing
of this happened to trigger the aforementioned race condition about
80% of the time. Asterisk was not doing its job of sending a BYE
when receiving a 200 OK on a cancelled INVITE. The result was that
the ast_channel structure was destroyed but the underlying SIP
session, as well as the PJSIP inv_session and dialog, were still
alive. Attempting to perform an action such as a transfer, once in
this state, would result in Asterisk crashing.

The circumstances are now detected properly and the session is ended
as recommended in RFC 5407.

(closes issue AST-1209)
reported by John Bigelow
........

Merged revisions 397945 from http://svn.asterisk.org/svn/asterisk/branches/12

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

res/res_pjsip_session.c

index 13c1e1d..226e43f 100644 (file)
@@ -1868,17 +1868,33 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
                break;
        case PJSIP_EVENT_RX_MSG:
                if (tsx->method.id == PJSIP_INVITE_METHOD) {
-                       if (tsx->role == PJSIP_ROLE_UAC && tsx->state == PJSIP_TSX_STATE_COMPLETED) {
-                               /* This means we got a non 2XX final response to our outgoing INVITE */
-                               if (tsx->status_code == PJSIP_SC_REQUEST_PENDING) {
-                                       reschedule_reinvite(session, tsx->mod_data[session_module.id], tsx->last_tx);
-                                       return;
-                               } else if (inv->state == PJSIP_INV_STATE_CONFIRMED &&
-                                          tsx->status_code != 488) {
-                                       /* Other reinvite failures (except 488) result in destroying the session. */
-                                       pjsip_tx_data *tdata;
-                                       if (pjsip_inv_end_session(inv, 500, NULL, &tdata) == PJ_SUCCESS) {
-                                               ast_sip_session_send_request(session, tdata);
+                       if (tsx->role == PJSIP_ROLE_UAC) {
+                               if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
+                                       /* This means we got a non 2XX final response to our outgoing INVITE */
+                                       if (tsx->status_code == PJSIP_SC_REQUEST_PENDING) {
+                                               reschedule_reinvite(session, tsx->mod_data[session_module.id], tsx->last_tx);
+                                               return;
+                                       } else if (inv->state == PJSIP_INV_STATE_CONFIRMED &&
+                                                  tsx->status_code != 488) {
+                                               /* Other reinvite failures (except 488) result in destroying the session. */
+                                               pjsip_tx_data *tdata;
+                                               if (pjsip_inv_end_session(inv, 500, NULL, &tdata) == PJ_SUCCESS) {
+                                                       ast_sip_session_send_request(session, tdata);
+                                               }
+                                       }
+                               } else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+                                       if (inv->cancelling && tsx->status_code == PJSIP_SC_OK) {
+                                               /* This is a race condition detailed in RFC 5407 section 3.1.2.
+                                                * We sent a CANCEL at the same time that the UAS sent us a 200 OK for
+                                                * the original INVITE. As a result, we have now received a 200 OK for
+                                                * a cancelled call. Our role is to immediately send a BYE to end the
+                                                * dialog.
+                                                */
+                                               pjsip_tx_data *tdata;
+
+                                               if (pjsip_inv_end_session(inv, 500, NULL, &tdata) == PJ_SUCCESS) {
+                                                       ast_sip_session_send_request(session, tdata);
+                                               }
                                        }
                                }
                        }