chan_pjsip: Incorporate channel reference count into transfer_refer().
authorDan Cropp <dan@amtelco.com>
Mon, 7 Dec 2020 22:59:51 +0000 (16:59 -0600)
committerFriendly Automation <jenkins2@gerrit.asterisk.org>
Wed, 6 Jan 2021 16:45:41 +0000 (10:45 -0600)
Add channel reference count for PJSIP REFER. The call could be terminated
prior to the result of the transfer. In that scenario, when the SUBSCRIBE/NOTIFY
occurred several minutes later, it would attempt to access a session which was
no longer valid.  Terminate event subscription if pjsip_xfer_initiate() or
pjsip_xfer_send_request() fails in transfer_refer().

ASTERISK-29201 #close
Reported-by: Dan Cropp

Change-Id: I3fd92fd14b4e3844d3d7b0f60fe417a4df5f2435

channels/chan_pjsip.c

index b6944db..bb04d73 100644 (file)
@@ -1922,8 +1922,7 @@ static pjsip_module refer_callback_module = {
  */
 static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
 {
-       struct ast_sip_session *session;
-       struct ast_channel *chan = NULL;
+       struct ast_channel *chan;
        enum ast_control_transfer message = AST_TRANSFER_SUCCESS;
        int res = 0;
 
@@ -1931,12 +1930,7 @@ static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
                return;
        }
 
-       session = pjsip_evsub_get_mod_data(sub, refer_callback_module.id);
-       if (!session) {
-               return;
-       }
-
-       chan = session->channel;
+       chan = pjsip_evsub_get_mod_data(sub, refer_callback_module.id);
        if (!chan) {
                return;
        }
@@ -1962,7 +1956,9 @@ static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
                         */
                        if (refer_sub && !pj_stricmp2(&refer_sub->hvalue, "false")) {
                                /* Since no subscription is desired, assume that call has been transferred successfully. */
+                               /* Channel reference will be released at end of function */
                                /* Terminate subscription. */
+                               pjsip_evsub_set_mod_data(sub, refer_callback_module.id, NULL);
                                pjsip_evsub_terminate(sub, PJ_TRUE);
                                res = -1;
                        }
@@ -2027,7 +2023,8 @@ static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
        }
 
        if (res) {
-               ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
+               ast_queue_control_data(chan, AST_CONTROL_TRANSFER, &message, sizeof(message));
+               ao2_ref(chan, -1);
        }
 }
 
@@ -2040,28 +2037,29 @@ static void transfer_refer(struct ast_sip_session *session, const char *target)
        const char *ref_by_val;
        char local_info[pj_strlen(&session->inv_session->dlg->local.info_str) + 1];
        struct pjsip_evsub_user xfer_cb;
+       struct ast_channel *chan = session->channel;
 
        pj_bzero(&xfer_cb, sizeof(xfer_cb));
        xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
 
        if (pjsip_xfer_create_uac(session->inv_session->dlg, &xfer_cb, &sub) != PJ_SUCCESS) {
                message = AST_TRANSFER_FAILED;
-               ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
+               ast_queue_control_data(chan, AST_CONTROL_TRANSFER, &message, sizeof(message));
 
                return;
        }
 
-       pjsip_evsub_set_mod_data(sub, refer_callback_module.id, session);
+       /* refer_callback_module requires a reference to chan
+        * which will be released in xfer_client_on_evsub_state()
+        * when the implicit REFER subscription terminates */
+       pjsip_evsub_set_mod_data(sub, refer_callback_module.id, chan);
+       ao2_ref(chan, +1);
 
        if (pjsip_xfer_initiate(sub, pj_cstr(&tmp, target), &packet) != PJ_SUCCESS) {
-               message = AST_TRANSFER_FAILED;
-               ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
-               pjsip_evsub_terminate(sub, PJ_FALSE);
-
-               return;
+               goto failure;
        }
 
-       ref_by_val = pbx_builtin_getvar_helper(session->channel, "SIPREFERREDBYHDR");
+       ref_by_val = pbx_builtin_getvar_helper(chan, "SIPREFERREDBYHDR");
        if (!ast_strlen_zero(ref_by_val)) {
                ast_sip_add_header(packet, "Referred-By", ref_by_val);
        } else {
@@ -2069,7 +2067,17 @@ static void transfer_refer(struct ast_sip_session *session, const char *target)
                ast_sip_add_header(packet, "Referred-By", local_info);
        }
 
-       pjsip_xfer_send_request(sub, packet);
+       if (pjsip_xfer_send_request(sub, packet) == PJ_SUCCESS) {
+               return;
+       }
+
+failure:
+       message = AST_TRANSFER_FAILED;
+       ast_queue_control_data(chan, AST_CONTROL_TRANSFER, &message, sizeof(message));
+       pjsip_evsub_set_mod_data(sub, refer_callback_module.id, NULL);
+       pjsip_evsub_terminate(sub, PJ_FALSE);
+
+       ao2_ref(chan, -1);
 }
 
 static int transfer(void *data)