From: Dan Cropp Date: Mon, 7 Dec 2020 22:59:51 +0000 (-0600) Subject: chan_pjsip: Incorporate channel reference count into transfer_refer(). X-Git-Tag: 16.16.0-rc1~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e127a5776154d3f1abed916c0379d7a8610ccc7e;p=thirdparty%2Fasterisk.git chan_pjsip: Incorporate channel reference count into transfer_refer(). 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 --- diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index 649dd36dd9..274d37cc2e 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -1906,8 +1906,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; @@ -1915,12 +1914,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; } @@ -1946,7 +1940,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; } @@ -2011,7 +2007,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); } } @@ -2024,28 +2021,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 { @@ -2053,7 +2051,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)