Asterisk - The Open Source Telephony Project GIT-master-57e2743
Loading...
Searching...
No Matches
channel_info.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2026, Sangoma Technologies Corporation
5 *
6 * Michael Bradeen <mbradeen@sangoma.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*!
20 * \file
21 * \brief Channel-locked PJSIP information for CHANNEL() reads
22 *
23 * The channel datastore owns cached dialog URIs, the remote tag, signaling
24 * security, RTP instance references, and media security and hold flags.
25 * Readers and writers access the cache under the channel lock. A datastore
26 * lookup returns a borrowed pointer valid only while that lock is held.
27 *
28 * Session serializer tasks refresh the cache through session->channel.
29 * SIP supplements refresh it during incoming message processing and queue
30 * refreshes for subsequent changes. Masquerades suspend the serializer while
31 * changing the channel association and move the datastore with the channel.
32 *
33 * CHANNEL() reads use the cache for values that SIP and SDP processing can
34 * change without the channel lock. Stable identity and original request
35 * information are read directly from the session. Direct-media addresses are
36 * read under the channel lock. RTP APIs supply RTP addresses and statistics.
37 *
38 * These reads do not wait for the session serializer or acquire the dialog
39 * lock while holding the channel lock to avoid a deadlock with masquerade
40 * processing.
41 *
42 */
43
44#include "asterisk.h"
45
46#include <pjsip.h>
47
48#include "asterisk/astobj2.h"
49#include "asterisk/channel.h"
50#include "asterisk/datastore.h"
51#include "asterisk/res_pjsip.h"
53#include "asterisk/rtp_engine.h"
54
56
57static void channel_info_destroy(void *data)
58{
59 struct pjsip_channel_info *info = data;
60
61 ast_free(info->remote_tag);
62 ao2_cleanup(info->audio_rtp);
63 ao2_cleanup(info->video_rtp);
64 ast_free(info);
65}
66
67static void channel_info_breakdown(void *data, struct ast_channel *old_chan,
68 struct ast_channel *new_chan)
69{
70 struct pjsip_channel_info *info = data;
71 struct ast_datastore *datastore = info->datastore;
72
73 /*
74 * Core calls breakdown on the destination's old datastores before moving
75 * the source's datastores over. Both channels are locked. The moved
76 * datastore needs no fixup because it contains no channel pointer.
77 */
78 if (!ast_channel_datastore_remove(new_chan, datastore)) {
79 ast_datastore_free(datastore);
80 }
81}
82
84 .type = "chan_pjsip_channel_info",
85 .destroy = channel_info_destroy,
86 .chan_breakdown = channel_info_breakdown,
87};
88
90{
91 struct ast_datastore *datastore;
92
94 return datastore ? datastore->data : NULL;
95}
96
97/* Return whether the value changed, preserving print failures for the reader. */
98static int copy_uri(struct pjsip_channel_uri *dest, pjsip_uri_context_e context,
99 const void *uri, const char *type)
100{
101 char value[PJSIP_MAX_URL_SIZE] = "";
102 int length = 0;
103 int changed;
104
105 if (uri) {
106 length = pjsip_uri_print(context, uri, value, sizeof(value));
107 if (length < 0 || length >= sizeof(value)) {
108 ast_log(LOG_ERROR, "Unable to capture PJSIP %s in %zu bytes\n",
109 type, sizeof(value));
110 value[0] = '\0';
111 length = -1;
112 } else {
113 value[length] = '\0';
114 }
115 }
116 changed = dest->length != length || strcmp(dest->value, value);
117 ast_copy_string(dest->value, value, sizeof(dest->value));
118 dest->length = length;
119 return changed;
120}
121
123 struct ast_sip_session *session)
124{
125 pjsip_dialog *dlg = session->inv_session ? session->inv_session->dlg : NULL;
126 struct ast_sip_session_media *media;
127
128 if (dlg) {
129 int target_changed;
130 const pj_str_t *tag = &dlg->remote.info->tag;
131
132 target_changed = copy_uri(&info->target_uri, PJSIP_URI_IN_REQ_URI,
133 dlg->target, "target_uri");
134 copy_uri(&info->local_uri, PJSIP_URI_IN_FROMTO_HDR,
135 dlg->local.info->uri, "local_uri");
136 copy_uri(&info->remote_uri, PJSIP_URI_IN_FROMTO_HDR,
137 dlg->remote.info->uri, "remote_uri");
138 /* PJSIP_MAX_TAG_LEN describes generated tags, not a received-tag limit. */
139 if (!info->remote_tag || strlen(info->remote_tag) != tag->slen
140 || (tag->slen && memcmp(info->remote_tag, tag->ptr, tag->slen))) {
141 char *copy = ast_strndup(tag->slen ? tag->ptr : "", tag->slen);
142
143 ast_free(info->remote_tag);
144 info->remote_tag = copy;
145 }
146#ifdef HAVE_PJSIP_GET_DEST_INFO
147 if (target_changed || info->target_uri.length < 0 || info->secure < 0) {
148 pjsip_host_info dest;
149 pj_pool_t *pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
150 "channel-info", 128, 128);
151
152 info->secure = -1;
153 if (pool) {
154 if (pjsip_get_dest_info(dlg->target, NULL, pool, &dest) == PJ_SUCCESS) {
155 info->secure = !!(dest.flag & PJSIP_TRANSPORT_SECURE);
156 }
157 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
158 }
159 }
160#else
161 ast_log(LOG_WARNING, "Asterisk has been built against a version of pjproject which does not have the required functionality to support the 'secure' argument. Please upgrade to version 2.3 or later.\n");
162 (void) target_changed;
163#endif
164 } else {
165 copy_uri(&info->target_uri, PJSIP_URI_IN_REQ_URI, NULL, "target_uri");
166 copy_uri(&info->local_uri, PJSIP_URI_IN_FROMTO_HDR, NULL, "local_uri");
167 copy_uri(&info->remote_uri, PJSIP_URI_IN_FROMTO_HDR, NULL, "remote_uri");
168 ast_free(info->remote_tag);
169 info->remote_tag = NULL;
170 info->secure = -1;
171 }
172
173 media = session->active_media_state
174 ? session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO] : NULL;
175 ao2_replace(info->audio_rtp, media ? media->rtp : NULL);
176 info->audio_secure = media && media->srtp
178 info->audio_held = media && media->remotely_held;
179 media = session->active_media_state
180 ? session->active_media_state->default_session[AST_MEDIA_TYPE_VIDEO] : NULL;
181 ao2_replace(info->video_rtp, media ? media->rtp : NULL);
182 info->video_secure = media && media->srtp
184 info->video_held = media && media->remotely_held;
185}
186
188 struct ast_sip_session *session)
189{
190 struct ast_datastore *datastore;
191 struct pjsip_channel_info *info;
192
194 if (!datastore) {
195 return -1;
196 }
197 info = ast_calloc(1, sizeof(*info));
198 if (!info) {
200 return -1;
201 }
202 info->datastore = datastore;
203 info->secure = -1;
204 datastore->data = info;
205
206 /* The caller holds the channel lock and is running in the serializer. */
209 return 0;
210}
211
213{
214 struct ast_channel *chan = session->channel;
215 struct pjsip_channel_info *info;
216
217 /* Serializer execution stabilizes session->channel across a masquerade. */
218 if (!chan) {
219 return;
220 }
221 ast_channel_lock(chan);
222 info = pjsip_channel_info_get(chan);
223 if (info) {
225 }
226 ast_channel_unlock(chan);
227}
228
229static int channel_info_update_task(void *data)
230{
231 struct ast_sip_session *session = data;
232
234 ao2_ref(session, -1);
235 return 0;
236}
237
239{
240 /*
241 * Capture changes made after supplements or from an outgoing transport
242 * callback outside the serializer. Never wait for the queued refresh.
243 */
245 ao2_bump(session))) {
246 ast_log(LOG_WARNING, "Unable to queue PJSIP channel information refresh for %s\n",
248 ao2_ref(session, -1);
249 }
250}
251
253 struct pjsip_rx_data *rdata)
254{
257 return 0;
258}
259
266
268 struct pjsip_tx_data *tdata)
269{
271}
272
274 struct pjsip_tx_data *tdata)
275{
277}
278
280 /* Refresh before channel supplements publish snapshots or queue control frames. */
282 .incoming_request = channel_info_incoming_request,
283 .incoming_response = channel_info_incoming_response,
284 .outgoing_request = channel_info_outgoing_request,
285 .outgoing_response = channel_info_outgoing_response,
286 .response_priority = AST_SIP_SESSION_AFTER_MEDIA,
287};
288
293
static int copy(char *infile, char *outfile)
Utility function to copy a file.
Asterisk main include file. File version handling, generic pbx functions.
static struct ast_mansession session
#define ast_free(a)
Definition astmm.h:180
#define ast_strndup(str, len)
A wrapper for strndup()
Definition astmm.h:256
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
#define ast_log
Definition astobj2.c:42
#define ao2_cleanup(obj)
Definition astobj2.h:1934
#define ao2_replace(dst, src)
Replace one object reference with another cleaning up the original.
Definition astobj2.h:501
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition astobj2.h:459
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition astobj2.h:480
static const char type[]
General Asterisk PBX channel definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition channel.c:2376
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition channel.c:2385
#define ast_channel_lock(chan)
Definition channel.h:2983
#define ast_channel_unlock(chan)
Definition channel.h:2984
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition channel.c:2390
static const struct ast_datastore_info channel_info_datastore
static void channel_info_outgoing_response(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
void pjsip_channel_info_schedule_update(struct ast_sip_session *session)
Schedule a refresh from any thread without waiting for the serializer.
static void channel_info_update_locked(struct pjsip_channel_info *info, struct ast_sip_session *session)
void pjsip_channel_info_register(void)
Register the channel information session supplement.
static void channel_info_incoming_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
static void channel_info_breakdown(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
static int channel_info_update_task(void *data)
struct pjsip_channel_info * pjsip_channel_info_get(struct ast_channel *chan)
Find the channel's information, or NULL if it has none.
static void channel_info_destroy(void *data)
static int channel_info_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
void pjsip_channel_info_unregister(void)
Unregister the channel information session supplement.
void pjsip_channel_info_update(struct ast_sip_session *session)
Refresh channel information under the channel lock.
static void channel_info_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
static int copy_uri(struct pjsip_channel_uri *dest, pjsip_uri_context_e context, const void *uri, const char *type)
static struct ast_sip_session_supplement channel_info_supplement
int pjsip_channel_info_create(struct ast_channel *chan, struct ast_sip_session *session)
Create channel-owned PJSIP information before channel publication.
Channel-locked PJSIP information interface.
@ AST_MEDIA_TYPE_AUDIO
Definition codec.h:32
@ AST_MEDIA_TYPE_VIDEO
Definition codec.h:33
Asterisk datastore objects.
#define ast_datastore_alloc(info, uid)
Definition datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition datastore.c:68
#define ast_sip_push_task(serializer, sip_task, task_data)
Definition res_pjsip.h:2126
#define LOG_ERROR
#define LOG_WARNING
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition res_pjsip.c:518
@ AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL
Definition res_pjsip.h:3413
@ AST_SIP_SESSION_AFTER_MEDIA
#define ast_sip_session_register_supplement(supplement)
void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement)
Unregister a an supplement to SIP session processing.
const char * ast_sip_session_get_name(const struct ast_sip_session *session)
Get the channel or endpoint name associated with the session.
#define NULL
Definition resample.c:96
Pluggable RTP Architecture.
#define AST_SRTP_CRYPTO_OFFER_OK
Definition sdp_srtp.h:45
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
Main Channel structure associated with a channel.
Structure for a data store type.
Definition datastore.h:31
const char * type
Definition datastore.h:32
Structure for a data store object.
Definition datastore.h:64
const struct ast_datastore_info * info
Definition datastore.h:67
void * data
Definition datastore.h:66
A structure containing SIP session media information.
struct ast_sdp_srtp * srtp
Holds SRTP information.
unsigned int remotely_held
Stream is on hold by remote side.
struct ast_rtp_instance * rtp
RTP instance itself.
A supplement to SIP message processing.
enum ast_sip_supplement_priority priority
A structure describing a SIP session.
struct ast_datastore * datastore
char value[PJSIP_MAX_URL_SIZE]
int value
Definition syslog.c:37
#define ast_test_flag(p, flag)
Definition utils.h:64