Asterisk - The Open Source Telephony Project GIT-master-f3e88d3
Data Structures | Macros | Functions | Variables
res_pjsip_transport_websocket.c File Reference
#include "asterisk.h"
#include <pjsip.h>
#include <pjsip_ua.h>
#include "asterisk/module.h"
#include "asterisk/http_websocket.h"
#include "asterisk/res_pjsip.h"
#include "asterisk/res_pjsip_session.h"
#include "asterisk/taskprocessor.h"
Include dependency graph for res_pjsip_transport_websocket.c:

Go to the source code of this file.

Data Structures

struct  transport_create_data
 
struct  transport_read_data
 
struct  ws_transport
 Wrapper for pjsip_transport, for storing the WebSocket session. More...
 

Macros

#define COLON_LEN   1
 
#define MAX_PORT_LEN   5
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static struct ast_taskprocessorcreate_websocket_serializer (void)
 
static int get_write_timeout (void)
 
static int load_module (void)
 
static void save_orig_contact_host (pjsip_rx_data *rdata, pjsip_sip_uri *uri)
 
static int transport_create (void *data)
 Create a pjsip transport. More...
 
static void transport_dtor (void *arg)
 
static int transport_read (void *data)
 Pass WebSocket data into pjsip transport manager. More...
 
static int transport_shutdown (void *data)
 
static int unload_module (void)
 
static void websocket_cb (struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
 WebSocket connection handler. More...
 
static pj_bool_t websocket_on_rx_msg (pjsip_rx_data *rdata)
 Store the transport a message came in on, so it can be used for outbound messages to that contact. More...
 
static void websocket_outgoing_invite_request (struct ast_sip_session *session, struct pjsip_tx_data *tdata)
 Function called when an INVITE goes out. More...
 
static pj_status_t ws_destroy (pjsip_transport *transport)
 Destroy the pjsip transport. More...
 
static pj_status_t ws_send_msg (pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback callback)
 Send a message over the WebSocket connection. More...
 
static pj_status_t ws_shutdown (pjsip_transport *transport)
 Shut down the pjsip transport. More...
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PJSIP WebSocket Transport Support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, .requires = "res_pjsip,res_http_websocket", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static int transport_type_wss
 WebSocket transport module. More...
 
static int transport_type_wss_ipv6
 
static pjsip_module websocket_module
 
static struct ast_sip_session_supplement websocket_supplement
 Supplement for adding Websocket functionality to dialog. More...
 
static int ws_obj_name_serial
 

Macro Definition Documentation

◆ COLON_LEN

#define COLON_LEN   1

◆ MAX_PORT_LEN

#define MAX_PORT_LEN   5

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 582 of file res_pjsip_transport_websocket.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 582 of file res_pjsip_transport_websocket.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 582 of file res_pjsip_transport_websocket.c.

◆ create_websocket_serializer()

static struct ast_taskprocessor * create_websocket_serializer ( void  )
static

Definition at line 364 of file res_pjsip_transport_websocket.c.

365{
366 char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
367
368 /* Create name with seq number appended. */
369 ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/websocket");
370
371 return ast_sip_create_serializer(tps_name);
372}
struct ast_taskprocessor * ast_sip_create_serializer(const char *name)
Create a new serializer for SIP tasks.
Definition: res_pjsip.c:2094
void ast_taskprocessor_build_name(char *buf, unsigned int size, const char *format,...)
Build a taskprocessor name with a sequence number on the end.
#define AST_TASKPROCESSOR_MAX_NAME
Suggested maximum taskprocessor name length (less null terminator).
Definition: taskprocessor.h:61

References ast_sip_create_serializer(), ast_taskprocessor_build_name(), and AST_TASKPROCESSOR_MAX_NAME.

Referenced by websocket_cb().

◆ get_write_timeout()

static int get_write_timeout ( void  )
static

Definition at line 326 of file res_pjsip_transport_websocket.c.

327{
328 int write_timeout = -1;
330
332
333 if (transport_states) {
334 struct ao2_iterator it_transport_states = ao2_iterator_init(transport_states, 0);
335 struct ast_sip_transport_state *transport_state;
336
337 for (; (transport_state = ao2_iterator_next(&it_transport_states)); ao2_cleanup(transport_state)) {
338 struct ast_sip_transport *transport;
339
340 if (transport_state->type != AST_TRANSPORT_WS && transport_state->type != AST_TRANSPORT_WSS) {
341 continue;
342 }
343 transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_state->id);
344 if (!transport) {
345 continue;
346 }
347 ast_debug(5, "Found %s transport with write timeout: %d\n",
348 transport->type == AST_TRANSPORT_WS ? "WS" : "WSS",
349 transport->write_timeout);
351 }
352 ao2_iterator_destroy(&it_transport_states);
354 }
355
356 if (write_timeout < 0) {
358 }
359
360 ast_debug(1, "Write timeout for WS/WSS transports: %d\n", write_timeout);
361 return write_timeout;
362}
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
static struct ao2_container * transport_states
#define AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT
Default websocket write timeout, in ms.
#define ast_debug(level,...)
Log a DEBUG message.
@ AST_TRANSPORT_WSS
Definition: netsock2.h:64
@ AST_TRANSPORT_WS
Definition: netsock2.h:63
struct ao2_container * ast_sip_get_transport_states(void)
Retrieves all transport states.
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition: sorcery.c:1853
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Structure for SIP transport information.
Definition: res_pjsip.h:119
enum ast_transport type
Definition: res_pjsip.h:133
Transport to bind to.
Definition: res_pjsip.h:221
enum ast_transport type
Definition: res_pjsip.h:243
#define MAX(a, b)
Definition: utils.h:233

References ao2_cleanup, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ast_debug, AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT, ast_sip_get_sorcery(), ast_sip_get_transport_states(), ast_sorcery_retrieve_by_id(), AST_TRANSPORT_WS, AST_TRANSPORT_WSS, ast_sip_transport_state::id, MAX, transport_states, ast_sip_transport_state::type, ast_sip_transport::type, and ast_sip_transport::write_timeout.

Referenced by websocket_cb().

◆ load_module()

static int load_module ( void  )
static

Definition at line 537 of file res_pjsip_transport_websocket.c.

538{
539 /*
540 * We only need one transport type name (ws) defined. Firefox
541 * and Chrome do not support anything other than secure websockets
542 * anymore.
543 *
544 * Also we really cannot have two transports with the same name
545 * and address family because it would be ambiguous. Outgoing
546 * requests may try to find the transport by name and pjproject
547 * only finds the first one registered.
548 */
549 pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE, "ws", 5060, &transport_type_wss);
550 pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_IPV6, "ws", 5060, &transport_type_wss_ipv6);
551
552 if (ast_sip_register_service(&websocket_module) != PJ_SUCCESS) {
554 }
555
557
562 }
563
565}
int ast_websocket_add_protocol(const char *name, ast_websocket_callback callback)
Add a sub-protocol handler to the default /ws server.
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
void ast_sip_unregister_service(pjsip_module *module)
Definition: res_pjsip.c:133
int ast_sip_register_service(pjsip_module *module)
Register a SIP service in Asterisk.
Definition: res_pjsip.c:117
#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.
Definition: pjsip_session.c:63
static pjsip_module websocket_module
static int transport_type_wss_ipv6
static struct ast_sip_session_supplement websocket_supplement
Supplement for adding Websocket functionality to dialog.
static void websocket_cb(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
WebSocket connection handler.
static int transport_type_wss
WebSocket transport module.

References AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_sip_register_service(), ast_sip_session_register_supplement, ast_sip_session_unregister_supplement(), ast_sip_unregister_service(), ast_websocket_add_protocol(), transport_type_wss, transport_type_wss_ipv6, websocket_cb(), websocket_module, and websocket_supplement.

◆ save_orig_contact_host()

static void save_orig_contact_host ( pjsip_rx_data *  rdata,
pjsip_sip_uri *  uri 
)
static

Definition at line 435 of file res_pjsip_transport_websocket.c.

436{
437 pjsip_param *x_orig_host;
438 pj_str_t p_value;
439#define COLON_LEN 1
440#define MAX_PORT_LEN 5
441
442 if (rdata->msg_info.msg->type != PJSIP_REQUEST_MSG ||
443 rdata->msg_info.msg->line.req.method.id != PJSIP_REGISTER_METHOD) {
444 return;
445 }
446
447 ast_debug(1, "Saving contact '%.*s:%d'\n",
448 (int)uri->host.slen, uri->host.ptr, uri->port);
449
450 x_orig_host = PJ_POOL_ALLOC_T(rdata->tp_info.pool, pjsip_param);
451 x_orig_host->name = pj_strdup3(rdata->tp_info.pool, "x-ast-orig-host");
452 p_value.slen = pj_strlen(&uri->host) + COLON_LEN + MAX_PORT_LEN;
453 p_value.ptr = (char*)pj_pool_alloc(rdata->tp_info.pool, p_value.slen + 1);
454 p_value.slen = snprintf(p_value.ptr, p_value.slen + 1, "%.*s:%d", (int)uri->host.slen, uri->host.ptr, uri->port);
455 pj_strassign(&x_orig_host->value, &p_value);
456 pj_list_insert_before(&uri->other_param, x_orig_host);
457
458 return;
459}
#define MAX_PORT_LEN
#define COLON_LEN

References ast_debug, COLON_LEN, and MAX_PORT_LEN.

Referenced by websocket_on_rx_msg().

◆ transport_create()

static int transport_create ( void *  data)
static

Create a pjsip transport.

Definition at line 161 of file res_pjsip_transport_websocket.c.

162{
163 struct transport_create_data *create_data = data;
164 struct ws_transport *newtransport = NULL;
165 pjsip_tp_state_callback state_cb;
166
167 pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
168 struct pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt);
169
170 char *ws_addr_str;
171 pj_pool_t *pool;
172 pj_str_t buf;
173 pj_status_t status;
174
175 newtransport = ao2_t_alloc_options(sizeof(*newtransport), transport_dtor,
176 AO2_ALLOC_OPT_LOCK_NOLOCK, "pjsip websocket transport");
177 if (!newtransport) {
178 ast_log(LOG_ERROR, "Failed to allocate WebSocket transport.\n");
179 goto on_error;
180 }
181
182 /* Give websocket transport a unique name for its lifetime */
183 snprintf(newtransport->transport.obj_name, PJ_MAX_OBJ_NAME, "ws%p-%d",
185
186 newtransport->transport.endpt = endpt;
187
188 if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) {
189 ast_log(LOG_ERROR, "Failed to allocate WebSocket endpoint pool.\n");
190 goto on_error;
191 }
192
193 newtransport->transport.pool = pool;
194 newtransport->ws_session = create_data->ws_session;
195
196 /* Keep the session until transport dies */
197 ast_websocket_ref(newtransport->ws_session);
198
199 status = pj_atomic_create(pool, 0, &newtransport->transport.ref_cnt);
200 if (status != PJ_SUCCESS) {
201 goto on_error;
202 }
203
204 status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &newtransport->transport.lock);
205 if (status != PJ_SUCCESS) {
206 goto on_error;
207 }
208
209 /*
210 * The type_name here is mostly used by log messages eihter in
211 * pjproject or Asterisk. Other places are reconstituting subscriptions
212 * after a restart (which could never work for a websocket connection anyway),
213 * received MESSAGE requests to set PJSIP_TRANSPORT, and most importantly
214 * by pjproject when generating the Via header.
215 */
216 newtransport->transport.type_name = ast_websocket_is_secure(newtransport->ws_session)
217 ? "WSS" : "WS";
218
220 ast_debug(4, "Creating websocket transport for %s:%s\n",
221 newtransport->transport.type_name, ws_addr_str);
222
223 newtransport->transport.info = (char *) pj_pool_alloc(newtransport->transport.pool,
224 strlen(newtransport->transport.type_name) + strlen(ws_addr_str) + sizeof(" to "));
225 sprintf(newtransport->transport.info, "%s to %s", newtransport->transport.type_name, ws_addr_str);
226
227 pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ws_addr_str), &newtransport->transport.key.rem_addr);
228 if (newtransport->transport.key.rem_addr.addr.sa_family == pj_AF_INET6()) {
229 newtransport->transport.key.type = transport_type_wss_ipv6;
230 } else {
231 newtransport->transport.key.type = transport_type_wss;
232 }
233
234 newtransport->transport.addr_len = pj_sockaddr_get_len(&newtransport->transport.key.rem_addr);
235
237 pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ws_addr_str), &newtransport->transport.local_addr);
238 pj_strdup2(pool, &newtransport->transport.local_name.host, ast_sockaddr_stringify_addr(ast_websocket_local_address(newtransport->ws_session)));
239 newtransport->transport.local_name.port = ast_sockaddr_port(ast_websocket_local_address(newtransport->ws_session));
240 pj_strdup2(pool, &newtransport->transport.remote_name.host, ast_sockaddr_stringify_addr(ast_websocket_remote_address(newtransport->ws_session)));
241 newtransport->transport.remote_name.port = ast_sockaddr_port(ast_websocket_remote_address(newtransport->ws_session));
242
243 newtransport->transport.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)newtransport->transport.key.type);
244 newtransport->transport.dir = PJSIP_TP_DIR_INCOMING;
245 newtransport->transport.tpmgr = tpmgr;
246 newtransport->transport.send_msg = &ws_send_msg;
247 newtransport->transport.do_shutdown = &ws_shutdown;
248 newtransport->transport.destroy = &ws_destroy;
249
250 status = pjsip_transport_register(newtransport->transport.tpmgr,
251 (pjsip_transport *)newtransport);
252 if (status != PJ_SUCCESS) {
253 goto on_error;
254 }
255
256 /* Add a reference for pjsip transport manager */
257 ao2_ref(newtransport, +1);
258
259 newtransport->rdata.tp_info.transport = &newtransport->transport;
260 newtransport->rdata.tp_info.pool = pjsip_endpt_create_pool(endpt, "rtd%p",
261 PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC);
262 if (!newtransport->rdata.tp_info.pool) {
263 ast_log(LOG_ERROR, "Failed to allocate WebSocket rdata.\n");
264 pjsip_transport_destroy((pjsip_transport *)newtransport);
265 goto on_error;
266 }
267
268 create_data->transport = newtransport;
269
270 /* Notify application of transport state */
271 state_cb = pjsip_tpmgr_get_state_cb(newtransport->transport.tpmgr);
272 if (state_cb) {
273 pjsip_transport_state_info state_info;
274
275 memset(&state_info, 0, sizeof(state_info));
276 state_cb(&newtransport->transport, PJSIP_TP_STATE_CONNECTED, &state_info);
277 }
278
279 return 0;
280
281on_error:
282 ao2_cleanup(newtransport);
283 return -1;
284}
jack_status_t status
Definition: app_jack.c:146
#define ast_log
Definition: astobj2.c:42
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
#define ao2_t_alloc_options(data_size, destructor_fn, options, debug_msg)
Allocate and initialize an object.
Definition: astobj2.h:402
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
int ast_websocket_is_secure(struct ast_websocket *session)
Get whether the WebSocket session is using a secure transport or not.
struct ast_sockaddr * ast_websocket_remote_address(struct ast_websocket *session)
Get the remote address for a WebSocket connected session.
struct ast_sockaddr * ast_websocket_local_address(struct ast_websocket *session)
Get the local address for a WebSocket connection session.
void ast_websocket_ref(struct ast_websocket *session)
Increase the reference count for a WebSocket session.
#define LOG_ERROR
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:757
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:517
static char * ast_sockaddr_stringify_addr(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only.
Definition: netsock2.h:286
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition: res_pjsip.c:520
static void transport_dtor(void *arg)
static int ws_obj_name_serial
static pj_status_t ws_send_msg(pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback callback)
Send a message over the WebSocket connection.
static pj_status_t ws_shutdown(pjsip_transport *transport)
Shut down the pjsip transport.
static pj_status_t ws_destroy(pjsip_transport *transport)
Destroy the pjsip transport.
#define NULL
Definition: resample.c:96
Wrapper for pjsip_transport, for storing the WebSocket session.
struct ast_websocket * ws_session

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_cleanup, ao2_ref, ao2_t_alloc_options, ast_atomic_fetchadd_int(), ast_debug, ast_log, ast_sip_get_pjsip_endpoint(), ast_sockaddr_port, ast_sockaddr_stringify(), ast_sockaddr_stringify_addr(), ast_websocket_is_secure(), ast_websocket_local_address(), ast_websocket_ref(), ast_websocket_remote_address(), buf, LOG_ERROR, NULL, ws_transport::rdata, status, ws_transport::transport, transport_create_data::transport, transport_dtor(), transport_type_wss, transport_type_wss_ipv6, ws_destroy(), ws_obj_name_serial, ws_send_msg(), ws_transport::ws_session, transport_create_data::ws_session, and ws_shutdown().

Referenced by load_module(), unload_module(), and websocket_cb().

◆ transport_dtor()

static void transport_dtor ( void *  arg)
static

Definition at line 112 of file res_pjsip_transport_websocket.c.

113{
114 struct ws_transport *wstransport = arg;
115
116 if (wstransport->ws_session) {
117 ast_websocket_unref(wstransport->ws_session);
118 }
119
120 if (wstransport->transport.ref_cnt) {
121 pj_atomic_destroy(wstransport->transport.ref_cnt);
122 }
123
124 if (wstransport->transport.lock) {
125 pj_lock_destroy(wstransport->transport.lock);
126 }
127
128 if (wstransport->transport.endpt && wstransport->transport.pool) {
129 pjsip_endpt_release_pool(wstransport->transport.endpt, wstransport->transport.pool);
130 }
131
132 if (wstransport->rdata.tp_info.pool) {
133 pjsip_endpt_release_pool(wstransport->transport.endpt, wstransport->rdata.tp_info.pool);
134 }
135}
void ast_websocket_unref(struct ast_websocket *session)
Decrease the reference count for a WebSocket session.

References ast_websocket_unref(), ws_transport::rdata, ws_transport::transport, and ws_transport::ws_session.

Referenced by transport_create().

◆ transport_read()

static int transport_read ( void *  data)
static

Pass WebSocket data into pjsip transport manager.

Definition at line 295 of file res_pjsip_transport_websocket.c.

296{
297 struct transport_read_data *read_data = data;
298 struct ws_transport *newtransport = read_data->transport;
299 struct ast_websocket *session = newtransport->ws_session;
300
301 pjsip_rx_data *rdata = &newtransport->rdata;
302 int recvd;
303 pj_str_t buf;
304 int pjsip_pkt_len;
305
306 pj_gettimeofday(&rdata->pkt_info.timestamp);
307
308 pjsip_pkt_len = PJSIP_MAX_PKT_LEN < read_data->payload_len ? PJSIP_MAX_PKT_LEN : read_data->payload_len;
309 pj_memcpy(rdata->pkt_info.packet, read_data->payload, pjsip_pkt_len);
310 rdata->pkt_info.len = pjsip_pkt_len;
311 rdata->pkt_info.zero = 0;
312
313 pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(session))), &rdata->pkt_info.src_addr);
314 rdata->pkt_info.src_addr_len = sizeof(rdata->pkt_info.src_addr);
315
316 pj_ansi_strcpy(rdata->pkt_info.src_name, ast_sockaddr_stringify_addr(ast_websocket_remote_address(session)));
317 rdata->pkt_info.src_port = ast_sockaddr_port(ast_websocket_remote_address(session));
318
319 recvd = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata);
320
321 pj_pool_reset(rdata->tp_info.pool);
322
323 return (read_data->payload_len == recvd) ? 0 : -1;
324}
static struct ast_mansession session
Structure definition for session.

References ast_sockaddr_port, ast_sockaddr_stringify(), ast_sockaddr_stringify_addr(), ast_websocket_remote_address(), buf, transport_read_data::payload, transport_read_data::payload_len, ws_transport::rdata, session, transport_read_data::transport, and ws_transport::ws_session.

Referenced by websocket_cb().

◆ transport_shutdown()

static int transport_shutdown ( void *  data)
static

Definition at line 137 of file res_pjsip_transport_websocket.c.

138{
139 struct ws_transport *wstransport = data;
140
141 if (!wstransport->transport.is_shutdown && !wstransport->transport.is_destroying) {
142 pjsip_transport_shutdown(&wstransport->transport);
143 }
144
145 /* Note that the destructor calls PJSIP functions,
146 * therefore it must be called in a PJSIP thread.
147 */
148 ao2_ref(wstransport, -1);
149
150 return 0;
151}

References ao2_ref, and ws_transport::transport.

Referenced by websocket_cb().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 567 of file res_pjsip_transport_websocket.c.

568{
572
573 return 0;
574}
int ast_websocket_remove_protocol(const char *name, ast_websocket_callback callback)
Remove a sub-protocol handler from the default /ws server.

References ast_sip_session_unregister_supplement(), ast_sip_unregister_service(), ast_websocket_remove_protocol(), websocket_cb(), websocket_module, and websocket_supplement.

◆ websocket_cb()

static void websocket_cb ( struct ast_websocket session,
struct ast_variable parameters,
struct ast_variable headers 
)
static

WebSocket connection handler.

Definition at line 375 of file res_pjsip_transport_websocket.c.

376{
378 struct transport_create_data create_data;
379 struct ws_transport *transport;
380 struct transport_read_data read_data;
381
384 return;
385 }
386
389 return;
390 }
391
393 if (!serializer) {
395 return;
396 }
397
398 create_data.ws_session = session;
399
401 ast_log(LOG_ERROR, "Could not create WebSocket transport.\n");
404 return;
405 }
406
407 transport = create_data.transport;
408 read_data.transport = transport;
409
410 pjsip_transport_add_ref(&transport->transport);
411 while (ast_websocket_wait_for_input(session, -1) > 0) {
412 enum ast_websocket_opcode opcode;
413 int fragmented;
414
415 if (ast_websocket_read(session, &read_data.payload, &read_data.payload_len, &opcode, &fragmented)) {
416 break;
417 }
418
419 if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
420 if (read_data.payload_len) {
422 }
423 } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
424 break;
425 }
426 }
427 pjsip_transport_dec_ref(&transport->transport);
428
430
433}
int ast_sip_push_task_wait_serializer(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Push a task to the serializer and wait for it to complete.
Definition: res_pjsip.c:2179
int ast_websocket_set_timeout(struct ast_websocket *session, int timeout)
Set the timeout on a non-blocking WebSocket session.
int ast_websocket_set_nonblock(struct ast_websocket *session)
Set the socket of a WebSocket session to be non-blocking.
int ast_websocket_read(struct ast_websocket *session, char **payload, uint64_t *payload_len, enum ast_websocket_opcode *opcode, int *fragmented)
Read a WebSocket frame and handle it.
int ast_websocket_wait_for_input(struct ast_websocket *session, int timeout)
Wait for the WebSocket session to be ready to be read.
ast_websocket_opcode
WebSocket operation codes.
@ AST_WEBSOCKET_OPCODE_BINARY
@ AST_WEBSOCKET_OPCODE_CLOSE
@ AST_WEBSOCKET_OPCODE_TEXT
static int get_write_timeout(void)
static int transport_create(void *data)
Create a pjsip transport.
static struct ast_taskprocessor * create_websocket_serializer(void)
static int transport_shutdown(void *data)
static int transport_read(void *data)
Pass WebSocket data into pjsip transport manager.
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:69
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.

References ast_log, ast_sip_push_task_wait_serializer(), ast_taskprocessor_unreference(), AST_WEBSOCKET_OPCODE_BINARY, AST_WEBSOCKET_OPCODE_CLOSE, AST_WEBSOCKET_OPCODE_TEXT, ast_websocket_read(), ast_websocket_set_nonblock(), ast_websocket_set_timeout(), ast_websocket_unref(), ast_websocket_wait_for_input(), create_websocket_serializer(), get_write_timeout(), LOG_ERROR, transport_read_data::payload, transport_read_data::payload_len, session, ws_transport::transport, transport_create_data::transport, transport_read_data::transport, transport_create(), transport_read(), transport_shutdown(), and transport_create_data::ws_session.

Referenced by load_module(), and unload_module().

◆ websocket_on_rx_msg()

static pj_bool_t websocket_on_rx_msg ( pjsip_rx_data *  rdata)
static

Store the transport a message came in on, so it can be used for outbound messages to that contact.

Definition at line 464 of file res_pjsip_transport_websocket.c.

465{
466 static const pj_str_t STR_WS = { "ws", 2 };
467 pjsip_contact_hdr *contact;
468
469 long type = rdata->tp_info.transport->key.type;
470
471 if (type != (long) transport_type_wss && type != (long) transport_type_wss_ipv6) {
472 return PJ_FALSE;
473 }
474
475 contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
476 if (contact
477 && !contact->star
478 && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
479 pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
480 const pj_str_t *txp_str = &STR_WS;
481
482 /* Saving the contact on REGISTER so it can be restored on outbound response
483 * This will actually be done by restore_orig_contact_host in res_pjsip_nat, via nat_on_tx_message */
484 save_orig_contact_host(rdata, uri);
485
486 if (DEBUG_ATLEAST(4)) {
487 char src_addr_buffer[AST_SOCKADDR_BUFLEN];
488 const char *ipv6_s = "", *ipv6_e = "";
489
490 if (pj_strchr(&uri->host, ':')) {
491 ipv6_s = "[";
492 ipv6_e = "]";
493 }
494
495 ast_log(LOG_DEBUG, "%s re-writing Contact URI from %s%.*s%s:%d%s%.*s to %s;transport=%s\n",
496 pjsip_rx_data_get_info(rdata),
497 ipv6_s, (int) pj_strlen(&uri->host), pj_strbuf(&uri->host), ipv6_e, uri->port,
498 pj_strlen(&uri->transport_param) ? ";transport=" : "",
499 (int) pj_strlen(&uri->transport_param), pj_strbuf(&uri->transport_param),
500 pj_sockaddr_print(&rdata->pkt_info.src_addr, src_addr_buffer, sizeof(src_addr_buffer), 3),
501 pj_strbuf(txp_str));
502 }
503
504 pj_strdup2(rdata->tp_info.pool, &uri->host, rdata->pkt_info.src_name);
505 uri->port = rdata->pkt_info.src_port;
506 pj_strdup(rdata->tp_info.pool, &uri->transport_param, txp_str);
507 }
508
509 rdata->msg_info.via->rport_param = 0;
510
511 return PJ_FALSE;
512}
static const char type[]
Definition: chan_ooh323.c:109
#define DEBUG_ATLEAST(level)
#define LOG_DEBUG
#define AST_SOCKADDR_BUFLEN
Definition: netsock2.h:46
static void save_orig_contact_host(pjsip_rx_data *rdata, pjsip_sip_uri *uri)

References ast_log, AST_SOCKADDR_BUFLEN, DEBUG_ATLEAST, LOG_DEBUG, NULL, save_orig_contact_host(), transport_type_wss, transport_type_wss_ipv6, and type.

◆ websocket_outgoing_invite_request()

static void websocket_outgoing_invite_request ( struct ast_sip_session session,
struct pjsip_tx_data *  tdata 
)
static

Function called when an INVITE goes out.

Definition at line 523 of file res_pjsip_transport_websocket.c.

524{
525 if (session->inv_session->state == PJSIP_INV_STATE_NULL) {
526 pjsip_dlg_add_usage(session->inv_session->dlg, &websocket_module, NULL);
527 }
528}

References NULL, session, and websocket_module.

◆ ws_destroy()

static pj_status_t ws_destroy ( pjsip_transport *  transport)
static

Destroy the pjsip transport.

Called by pjsip transport manager.

Definition at line 85 of file res_pjsip_transport_websocket.c.

86{
87 struct ws_transport *wstransport = (struct ws_transport *)transport;
88
89 ao2_ref(wstransport, -1);
90
91 return PJ_SUCCESS;
92}

References ao2_ref, and ws_transport::transport.

Referenced by transport_create().

◆ ws_send_msg()

static pj_status_t ws_send_msg ( pjsip_transport *  transport,
pjsip_tx_data *  tdata,
const pj_sockaddr_t *  rem_addr,
int  addr_len,
void *  token,
pjsip_transport_callback  callback 
)
static

Send a message over the WebSocket connection.

Called by pjsip transport manager.

Definition at line 63 of file res_pjsip_transport_websocket.c.

69{
70 struct ws_transport *wstransport = (struct ws_transport *)transport;
71 uint64_t len = tdata->buf.cur - tdata->buf.start;
72
73 if (ast_websocket_write(wstransport->ws_session, AST_WEBSOCKET_OPCODE_TEXT, tdata->buf.start, len)) {
74 return PJ_EUNKNOWN;
75 }
76
77 return PJ_SUCCESS;
78}
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int ast_websocket_write(struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t payload_size)
Construct and transmit a WebSocket frame.

References AST_WEBSOCKET_OPCODE_TEXT, ast_websocket_write(), len(), ws_transport::transport, and ws_transport::ws_session.

Referenced by transport_create().

◆ ws_shutdown()

static pj_status_t ws_shutdown ( pjsip_transport *  transport)
static

Shut down the pjsip transport.

Called by pjsip transport manager.

Definition at line 99 of file res_pjsip_transport_websocket.c.

100{
101 struct ws_transport *wstransport = (struct ws_transport *)transport;
102 int fd = ast_websocket_fd(wstransport->ws_session);
103
104 if (fd > 0) {
105 ast_websocket_close(wstransport->ws_session, 1000);
106 shutdown(fd, SHUT_RDWR);
107 }
108
109 return PJ_SUCCESS;
110}
int ast_websocket_close(struct ast_websocket *session, uint16_t reason)
Close a WebSocket session by sending a message with the CLOSE opcode and an optional code.
int ast_websocket_fd(struct ast_websocket *session)
Get the file descriptor for a WebSocket session.

References ast_websocket_close(), ast_websocket_fd(), ws_transport::transport, and ws_transport::ws_session.

Referenced by transport_create().

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PJSIP WebSocket Transport Support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, .requires = "res_pjsip,res_http_websocket", }
static

Definition at line 582 of file res_pjsip_transport_websocket.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 582 of file res_pjsip_transport_websocket.c.

◆ transport_type_wss

int transport_type_wss
static

WebSocket transport module.

Definition at line 41 of file res_pjsip_transport_websocket.c.

Referenced by load_module(), transport_create(), and websocket_on_rx_msg().

◆ transport_type_wss_ipv6

int transport_type_wss_ipv6
static

◆ websocket_module

pjsip_module websocket_module
static

◆ websocket_supplement

struct ast_sip_session_supplement websocket_supplement
static
Initial value:
= {
.method = "INVITE",
.outgoing_request = websocket_outgoing_invite_request,
}
@ AST_SIP_SUPPLEMENT_PRIORITY_FIRST
Definition: res_pjsip.h:3179
static void websocket_outgoing_invite_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
Function called when an INVITE goes out.

Supplement for adding Websocket functionality to dialog.

Definition at line 531 of file res_pjsip_transport_websocket.c.

Referenced by load_module(), and unload_module().

◆ ws_obj_name_serial

int ws_obj_name_serial
static

Used to ensure uniqueness among WS transport names

Definition at line 47 of file res_pjsip_transport_websocket.c.

Referenced by transport_create().