Commit 3468d032e7 for asterisk.org

commit 3468d032e74d1578dc15e43a068c0745d3eb6f0f
Author: hishamway <hisham.t@waybeo.com>
Date:   Thu Jan 15 15:04:08 2026 +0530

    res_pjsip_session.c: Prevent INVITE failover when session is cancelled

    When an outbound INVITE transaction times out (408) or receives a 503 error,
    check_request_status() attempts to failover to the next available address by
    restarting the INVITE session. However, the function did not check if the
    inv_session was already cancelled before attempting the failover.

    This caused unexpected behavior when a caller hung up during a ring group
    scenario: after CANCEL was sent but the remote endpoint failed to respond
    with 487 (e.g., due to network disconnection), the transaction timeout
    would trigger a NEW outbound INVITE to the next address, even though the
    session was already terminated.

    This violates RFC 3261 Section 9.1 which states that if no final response
    is received after CANCEL within 64*T1 seconds, the client should consider
    the transaction cancelled and destroy it, not retry to another address.

    The fix adds a check for both PJSIP_INV_STATE_DISCONNECTED and inv->cancelling
    at the beginning of check_request_status(). This ensures that:
    - Failover is blocked when the user explicitly cancelled the call (CANCEL sent)
    - Failover is still allowed for legitimate timeout/503 scenarios where no
      CANCEL was initiated (e.g., SRV failover when first server is unreachable)

    Resolves: #1716

diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 135aa19279..a731d3ec65 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -4556,6 +4556,10 @@ static int check_request_status(pjsip_inv_session *inv, pjsip_event *e)
 	struct ast_sip_session *session = inv->mod_data[session_module.id];
 	pjsip_transaction *tsx = e->body.tsx_state.tsx;

+	if (inv->state == PJSIP_INV_STATE_DISCONNECTED && inv->cancelling) {
+		return 0;
+	}
+
 	if (tsx->status_code != 503 && tsx->status_code != 408) {
 		return 0;
 	}