]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
chan_pjsip: Store transport info for outgoing channels
authorMehrdad Seifzadeh <mehrdad.seifzadeh@gmail.com>
Fri, 5 Jun 2026 07:44:30 +0000 (11:14 +0330)
committergithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Tue, 4 Aug 2026 12:38:41 +0000 (12:38 +0000)
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
channels/chan_pjsip.c

index 008f41206b4ce0c51eab73d812db0a76e936f8be..c0e7611beb4d6ac1899dfe00dcc91630c7f23047 100644 (file)
@@ -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);