From: Mehrdad Seifzadeh Date: Fri, 5 Jun 2026 07:44:30 +0000 (+0330) Subject: chan_pjsip: Store transport info for outgoing channels X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8cd58fd39d7871b16cac3fec72a142fa984b4254;p=thirdparty%2Fasterisk.git chan_pjsip: Store transport info for outgoing channels Outgoing PJSIP channels did not have transport information stored in their session datastore when CHANNEL(pjsip,local_addr) or CHANNEL(pjsip,remote_addr) was read. As a result, the address fields were empty on B-leg channels even though other dialog fields such as call-id and URIs were available. Store the selected local transport address and destination address for outgoing UAC session requests once the request is transmitted and the transport information is known. Fixes: #871 --- diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index 008f41206b..c0e7611beb 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -271,6 +271,102 @@ static struct ast_datastore_info transport_info = { .destroy = transport_info_destroy, }; + +/*! + * \brief Determine the local signaling address used for an outgoing request. + * + * For unreliable transports, the transport local address may be a wildcard + * address. Ask the PJSIP transport manager for the local interface selected + * for the request destination, then fall back to the transport local address + * if needed. + */ +static int set_transport_info_local_addr(struct pjsip_tx_data *tdata, pj_sockaddr *local_addr) +{ + if (!(tdata->tp_info.transport->flag & PJSIP_TRANSPORT_RELIABLE)) { + pjsip_tpmgr_fla2_param prm; + + pjsip_tpmgr_fla2_param_default(&prm); + prm.tp_type = tdata->tp_info.transport->key.type; + pj_strset2(&prm.dst_host, tdata->tp_info.dst_name); + prm.local_if = PJ_TRUE; + + if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), + tdata->pool, &prm) == PJ_SUCCESS) { + int af = prm.tp_type & PJSIP_TRANSPORT_IPV6 ? pj_AF_INET6() : pj_AF_INET(); + + if (prm.tp_type == PJSIP_TRANSPORT_UDP || prm.tp_type == PJSIP_TRANSPORT_UDP6) { + prm.ret_port = tdata->tp_info.transport->local_name.port; + } + + if (pj_sockaddr_init(af, local_addr, &prm.ret_addr, prm.ret_port) == PJ_SUCCESS) { + return 0; + } + } + } + + pj_sockaddr_cp(local_addr, &tdata->tp_info.transport->local_addr); + return pj_sockaddr_has_addr(local_addr) ? 0 : -1; +} + +/*! + * \brief Store transport information for outgoing PJSIP session requests. + * + * Incoming PJSIP channels get their transport information from the received + * request. Outgoing channels may not have this information available through + * the channel datastore, so store the selected transport addresses once the + * outgoing request has been created and a destination is known. + */ +static pj_status_t transport_info_on_tx_request(pjsip_tx_data *tdata) +{ + RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup); + RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup); + struct transport_info_data *transport_data; + pjsip_dialog *dlg; + + if (!tdata->tp_info.transport + || !pj_sockaddr_has_addr(&tdata->tp_info.dst_addr)) { + return PJ_SUCCESS; + } + + dlg = pjsip_tdata_get_dlg(tdata); + if (!dlg) { + return PJ_SUCCESS; + } + + session = ast_sip_dialog_get_session(dlg); + if (!session + || !session->inv_session + || session->inv_session->role != PJSIP_ROLE_UAC) { + return PJ_SUCCESS; + } + + datastore = ast_sip_session_get_datastore(session, "transport_info"); + if (datastore) { + return PJ_SUCCESS; + } + + datastore = ast_sip_session_alloc_datastore(&transport_info, "transport_info"); + if (!datastore) { + return PJ_SUCCESS; + } + + transport_data = ast_calloc(1, sizeof(*transport_data)); + if (!transport_data) { + return PJ_SUCCESS; + } + + if (set_transport_info_local_addr(tdata, &transport_data->local_addr)) { + ast_free(transport_data); + return PJ_SUCCESS; + } + pj_sockaddr_cp(&transport_data->remote_addr, &tdata->tp_info.dst_addr); + + datastore->data = transport_data; + ast_sip_session_add_datastore(session, datastore); + + return PJ_SUCCESS; +} + static struct ast_datastore_info direct_media_mitigation_info = { }; static int direct_media_mitigate_glare(struct ast_sip_session *session) @@ -1956,10 +2052,11 @@ static void transfer_redirect(struct ast_sip_session *session, const char *targe ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message)); } -/*! \brief REFER Callback module, used to attach session data structure to subscription */ -static pjsip_module refer_callback_module = { - .name = { "REFER Callback", 14 }, +/*! \brief General PJSIP module for chan_pjsip, currently used for REFER subscription data and outgoing request callbacks */ +static pjsip_module chan_pjsip_module = { + .name = { "chan_pjsip", 10 }, .id = -1, + .on_tx_request = transport_info_on_tx_request, }; /*! @@ -1979,7 +2076,7 @@ static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event) return; } - chan = pjsip_evsub_get_mod_data(sub, refer_callback_module.id); + chan = pjsip_evsub_get_mod_data(sub, chan_pjsip_module.id); if (!chan) { return; } @@ -2007,7 +2104,7 @@ static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event) /* 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_set_mod_data(sub, chan_pjsip_module.id, NULL); pjsip_evsub_terminate(sub, PJ_TRUE); res = -1; } @@ -2071,7 +2168,7 @@ static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event) } } /* Finished. Remove session from subscription */ - pjsip_evsub_set_mod_data(sub, refer_callback_module.id, NULL); + pjsip_evsub_set_mod_data(sub, chan_pjsip_module.id, NULL); ast_debug(3, "Transfer channel %s completed: %d %.*s (%s)\n", ast_channel_name(chan), status_line.code, @@ -2107,10 +2204,10 @@ static void transfer_refer(struct ast_sip_session *session, const char *target) return; } - /* refer_callback_module requires a reference to chan + /* chan_pjsip_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); + pjsip_evsub_set_mod_data(sub, chan_pjsip_module.id, chan); ao2_ref(chan, +1); if (pjsip_xfer_initiate(sub, pj_cstr(&tmp, target), &packet) != PJ_SUCCESS) { @@ -2132,7 +2229,7 @@ static void transfer_refer(struct ast_sip_session *session, const char *target) 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_set_mod_data(sub, chan_pjsip_module.id, NULL); pjsip_evsub_terminate(sub, PJ_FALSE); ao2_ref(chan, -1); @@ -3493,7 +3590,7 @@ static int load_module(void) ast_manager_register_xml(app_pjsip_hangup, EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, pjsip_action_hangup); - ast_sip_register_service(&refer_callback_module); + ast_sip_register_service(&chan_pjsip_module); ast_sip_session_register_supplement(&chan_pjsip_supplement); ast_sip_session_register_supplement(&chan_pjsip_supplement_response); @@ -3533,7 +3630,7 @@ end: ast_sip_session_unregister_supplement(&chan_pjsip_supplement_response); ast_sip_session_unregister_supplement(&chan_pjsip_supplement); ast_sip_session_unregister_supplement(&call_pickup_supplement); - ast_sip_unregister_service(&refer_callback_module); + ast_sip_unregister_service(&chan_pjsip_module); ast_custom_function_unregister(&dtmf_mode_function); ast_custom_function_unregister(&moh_passthrough_function); ast_custom_function_unregister(&media_offer_function); @@ -3566,7 +3663,7 @@ static int unload_module(void) ast_sip_session_unregister_supplement(&chan_pjsip_prack_supplement); ast_sip_session_unregister_supplement(&call_pickup_supplement); - ast_sip_unregister_service(&refer_callback_module); + ast_sip_unregister_service(&chan_pjsip_module); ast_custom_function_unregister(&dtmf_mode_function); ast_custom_function_unregister(&moh_passthrough_function);