Asterisk - The Open Source Telephony Project GIT-master-0a46be9
Data Structures | Macros | Functions | Variables
res_http_websocket.c File Reference

WebSocket support for the Asterisk internal HTTP server. More...

#include "asterisk.h"
#include "asterisk/module.h"
#include "asterisk/http.h"
#include "asterisk/astobj2.h"
#include "asterisk/strings.h"
#include "asterisk/file.h"
#include "asterisk/unaligned.h"
#include "asterisk/uri.h"
#include "asterisk/uuid.h"
#include "asterisk/http_websocket.h"
Include dependency graph for res_http_websocket.c:

Go to the source code of this file.

Data Structures

struct  ast_websocket
 Structure definition for session. More...
 
struct  ast_websocket_server
 Structure for a WebSocket server. More...
 
struct  websocket_client
 

Macros

#define AST_API_MODULE
 
#define CLIENT_KEY_SIZE   16
 Length of a websocket's client key. More...
 
#define DEFAULT_RECONSTRUCTION_CEILING   AST_WEBSOCKET_MAX_RX_PAYLOAD_SIZE
 Default reconstruction size for multi-frame payload reconstruction. If exceeded the next frame will start a payload. More...
 
#define MAX_PROTOCOL_BUCKETS   7
 Number of buckets for registered protocols. More...
 
#define MAX_WS_HDR_SZ   14
 Maximum size of a websocket frame header 1 byte flags and opcode 1 byte mask flag + payload len 8 bytes max extended length 4 bytes optional masking key ... payload follows ... More...
 
#define MAXIMUM_RECONSTRUCTION_CEILING   AST_WEBSOCKET_MAX_RX_PAYLOAD_SIZE
 Maximum reconstruction size for multi-frame payload reconstruction. More...
 
#define MIN_WS_HDR_SZ   2
 
#define optional_header_spec   "%s%s%s"
 
#define print_optional_header(test, name, value)
 
#define WEBSOCKET_GUID   "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
 GUID used to compute the accept key, defined in the specifications. More...
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
int AST_OPTIONAL_API_NAME() ast_websocket_add_protocol (const char *name, ast_websocket_callback callback)
 Add a sub-protocol handler to the default /ws server. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_add_protocol2 (struct ast_websocket_protocol *protocol)
 Add a sub-protocol handler to the default /ws server. More...
 
const char *AST_OPTIONAL_API_NAME() ast_websocket_client_accept_protocol (struct ast_websocket *ws)
 Retrieve the server accepted sub-protocol on the client. More...
 
struct ast_websocket *AST_OPTIONAL_API_NAME() ast_websocket_client_create (const char *uri, const char *protocols, struct ast_tls_config *tls_cfg, enum ast_websocket_result *result)
 Create, and connect, a websocket client. More...
 
struct ast_websocket *AST_OPTIONAL_API_NAME() ast_websocket_client_create_with_options (struct ast_websocket_client_options *options, enum ast_websocket_result *result)
 Create, and connect, a websocket client using given options. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_close (struct ast_websocket *session, uint16_t reason)
 Close function for websocket session. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_fd (struct ast_websocket *session)
 Get the file descriptor for a WebSocket session. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_is_secure (struct ast_websocket *session)
 Get whether the WebSocket session is using a secure transport or not. More...
 
struct ast_sockaddr *AST_OPTIONAL_API_NAME() ast_websocket_local_address (struct ast_websocket *session)
 Get the local address for a WebSocket connection session. More...
 
int AST_OPTIONAL_API_NAME() 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. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_read_string (struct ast_websocket *ws, char **buf)
 Read a WebSocket frame containing string data. More...
 
void AST_OPTIONAL_API_NAME() ast_websocket_reconstruct_disable (struct ast_websocket *session)
 Disable multi-frame reconstruction. More...
 
void AST_OPTIONAL_API_NAME() ast_websocket_reconstruct_enable (struct ast_websocket *session, size_t bytes)
 Enable multi-frame reconstruction up to a certain number of bytes. More...
 
void AST_OPTIONAL_API_NAME() ast_websocket_ref (struct ast_websocket *session)
 Increase the reference count for a WebSocket session. More...
 
struct ast_sockaddr *AST_OPTIONAL_API_NAME() ast_websocket_remote_address (struct ast_websocket *session)
 Get the remote address for a WebSocket connected session. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_remove_protocol (const char *name, ast_websocket_callback callback)
 Remove a sub-protocol handler from the default /ws server. More...
 
const char *AST_OPTIONAL_API_NAME() ast_websocket_result_to_str (enum ast_websocket_result result)
 Convert a websocket result code to a string. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_server_add_protocol (struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
 Add a sub-protocol handler to the given server. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_server_add_protocol2 (struct ast_websocket_server *server, struct ast_websocket_protocol *protocol)
 Add a sub-protocol handler to the given server. More...
 
struct ast_websocket_server *AST_OPTIONAL_API_NAME() ast_websocket_server_create (void)
 Creates a ast_websocket_server. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_server_remove_protocol (struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
 Remove a sub-protocol handler from the given server. More...
 
const char *AST_OPTIONAL_API_NAME() ast_websocket_session_id (struct ast_websocket *session)
 Get the session ID for a WebSocket session. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_set_nonblock (struct ast_websocket *session)
 Set the socket of a WebSocket session to be non-blocking. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_set_timeout (struct ast_websocket *session, int timeout)
 Set the timeout on a non-blocking WebSocket session. More...
 
struct ast_websocket_protocol *AST_OPTIONAL_API_NAME() ast_websocket_sub_protocol_alloc (const char *name)
 Allocate a websocket sub-protocol instance. More...
 
const char * ast_websocket_type_to_str (enum ast_websocket_type type)
 
void AST_OPTIONAL_API_NAME() ast_websocket_unref (struct ast_websocket *session)
 Decrease the reference count for a WebSocket session. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_uri_cb (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
 Callback suitable for use with a ast_http_uri. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_wait_for_input (struct ast_websocket *session, int timeout)
 Wait for the WebSocket session to be ready to be read. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_write (struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t payload_size)
 Write function for websocket traffic. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_write_string (struct ast_websocket *ws, const char *buf)
 Construct and transmit a WebSocket frame containing string data. More...
 
static int load_module (void)
 
static struct ast_websocket_protocolone_protocol (struct ast_websocket_server *server)
 If the server has exactly one configured protocol, return it. More...
 
static int protocol_cmp_fn (void *obj, void *arg, int flags)
 Comparison function for protocols. More...
 
static void protocol_destroy_fn (void *obj)
 Destructor function for protocols. More...
 
static int protocol_hash_fn (const void *obj, const int flags)
 Hashing function for protocols. More...
 
static void session_destroy_fn (void *obj)
 Destructor function for sessions. More...
 
static int unload_module (void)
 
static int websocket_add_protocol_internal (const char *name, ast_websocket_callback callback)
 
static void websocket_bad_request (struct ast_tcptls_session_instance *ser)
 
static struct ast_tcptls_session_argswebsocket_client_args_create (const char *host, struct ast_tls_config *tls_cfg, enum ast_websocket_result *result)
 
static void websocket_client_args_destroy (void *obj)
 
static enum ast_websocket_result websocket_client_connect (struct ast_websocket *ws, int timeout)
 
static struct ast_websocketwebsocket_client_create (struct ast_websocket_client_options *options, enum ast_websocket_result *result)
 
static char * websocket_client_create_key (void)
 
static void websocket_client_destroy (void *obj)
 
static enum ast_websocket_result websocket_client_handle_response_code (struct websocket_client *client, int response_code)
 
static enum ast_websocket_result websocket_client_handshake (struct websocket_client *client)
 
static enum ast_websocket_result websocket_client_handshake_get_response (struct websocket_client *client)
 
static int websocket_client_parse_uri (const char *uri, char **host, struct ast_str **path, char **userinfo)
 Parse the given uri into a path and remote address. More...
 
static char * websocket_combine_key (const char *key, char *res, int res_size)
 
static void websocket_echo_callback (struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
 Simple echo implementation which echoes received text and binary frames. More...
 
static void websocket_mask_payload (struct ast_websocket *session, char *frame, char *payload, uint64_t payload_size)
 Perform payload masking for client sessions. More...
 
static const char * websocket_opcode2str (enum ast_websocket_opcode opcode)
 
static int websocket_remove_protocol_internal (const char *name, ast_websocket_callback callback)
 
static struct ast_websocket_serverwebsocket_server_create_impl (void)
 
static void websocket_server_dtor (void *obj)
 
static struct ast_websocket_serverwebsocket_server_internal_create (void)
 
static int ws_safe_read (struct ast_websocket *session, char *buf, size_t len, enum ast_websocket_opcode *opcode)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "HTTP WebSocket 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_CHANNEL_DEPEND, .requires = "http", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static const char * opcode_map []
 
const char * websocket_result_string_map []
 
static struct ast_http_uri websocketuri
 

Detailed Description

WebSocket support for the Asterisk internal HTTP server.

Author
Joshua Colp jcolp.nosp@m.@dig.nosp@m.ium.c.nosp@m.om

Definition in file res_http_websocket.c.

Macro Definition Documentation

◆ AST_API_MODULE

#define AST_API_MODULE

Definition at line 41 of file res_http_websocket.c.

◆ CLIENT_KEY_SIZE

#define CLIENT_KEY_SIZE   16

Length of a websocket's client key.

Definition at line 48 of file res_http_websocket.c.

◆ DEFAULT_RECONSTRUCTION_CEILING

#define DEFAULT_RECONSTRUCTION_CEILING   AST_WEBSOCKET_MAX_RX_PAYLOAD_SIZE

Default reconstruction size for multi-frame payload reconstruction. If exceeded the next frame will start a payload.

Definition at line 65 of file res_http_websocket.c.

◆ MAX_PROTOCOL_BUCKETS

#define MAX_PROTOCOL_BUCKETS   7

Number of buckets for registered protocols.

Definition at line 51 of file res_http_websocket.c.

◆ MAX_WS_HDR_SZ

#define MAX_WS_HDR_SZ   14

Maximum size of a websocket frame header 1 byte flags and opcode 1 byte mask flag + payload len 8 bytes max extended length 4 bytes optional masking key ... payload follows ...

Definition at line 78 of file res_http_websocket.c.

◆ MAXIMUM_RECONSTRUCTION_CEILING

#define MAXIMUM_RECONSTRUCTION_CEILING   AST_WEBSOCKET_MAX_RX_PAYLOAD_SIZE

Maximum reconstruction size for multi-frame payload reconstruction.

Definition at line 68 of file res_http_websocket.c.

◆ MIN_WS_HDR_SZ

#define MIN_WS_HDR_SZ   2

Definition at line 79 of file res_http_websocket.c.

◆ optional_header_spec

#define optional_header_spec   "%s%s%s"

Definition at line 1442 of file res_http_websocket.c.

◆ print_optional_header

#define print_optional_header (   test,
  name,
  value 
)
Value:
test ? name : "", \
test ? value : "", \
test ? "\r\n" : ""
static const char name[]
Definition: format_mp3.c:68
int value
Definition: syslog.c:37

Definition at line 1443 of file res_http_websocket.c.

◆ WEBSOCKET_GUID

#define WEBSOCKET_GUID   "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"

GUID used to compute the accept key, defined in the specifications.

Definition at line 45 of file res_http_websocket.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1663 of file res_http_websocket.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1663 of file res_http_websocket.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 1663 of file res_http_websocket.c.

◆ ast_websocket_add_protocol()

int AST_OPTIONAL_API_NAME() ast_websocket_add_protocol ( const char *  name,
ast_websocket_callback  callback 
)

Add a sub-protocol handler to the default /ws server.

Parameters
nameName of the sub-protocol to register
callbackCallback called when a new connection requesting the sub-protocol is established
Return values
0success
-1if sub-protocol handler could not be registered

Definition at line 1068 of file res_http_websocket.c.

1069{
1071}
static struct ast_channel * callback(struct ast_channelstorage_instance *driver, ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags)
static int websocket_add_protocol_internal(const char *name, ast_websocket_callback callback)

References callback(), name, and websocket_add_protocol_internal().

Referenced by AST_TEST_DEFINE(), and load_module().

◆ ast_websocket_add_protocol2()

int AST_OPTIONAL_API_NAME() ast_websocket_add_protocol2 ( struct ast_websocket_protocol protocol)

Add a sub-protocol handler to the default /ws server.

Parameters
protocolThe sub-protocol to register. Note that this must be allocated using /ref ast_websocket_sub_protocol_alloc.
Note
This method is reference stealing. It will steal the reference to protocol on success.
Return values
0success
-1if sub-protocol handler could not be registered
Since
13.5.0

Definition at line 1073 of file res_http_websocket.c.

1074{
1075 struct ast_websocket_server *ws_server = websocketuri.data;
1076
1077 if (!ws_server) {
1078 return -1;
1079 }
1080
1081 if (ast_websocket_server_add_protocol2(ws_server, protocol)) {
1082 return -1;
1083 }
1084
1085 return 0;
1086}
static struct ast_http_uri websocketuri
int AST_OPTIONAL_API_NAME() ast_websocket_server_add_protocol2(struct ast_websocket_server *server, struct ast_websocket_protocol *protocol)
Add a sub-protocol handler to the given server.
void * data
Definition: http.h:116
Structure for a WebSocket server.

References ast_websocket_server_add_protocol2(), ast_http_uri::data, and websocketuri.

◆ ast_websocket_client_accept_protocol()

const char *AST_OPTIONAL_API_NAME() ast_websocket_client_accept_protocol ( struct ast_websocket ws)

Retrieve the server accepted sub-protocol on the client.

Parameters
wsthe websocket client
Returns
the accepted client sub-protocol.
Since
13

Definition at line 1318 of file res_http_websocket.c.

1320{
1321 return ws->client->accept_protocol;
1322}
struct websocket_client * client

Referenced by AST_TEST_DEFINE().

◆ ast_websocket_client_create()

struct ast_websocket *AST_OPTIONAL_API_NAME() ast_websocket_client_create ( const char *  uri,
const char *  protocols,
struct ast_tls_config tls_cfg,
enum ast_websocket_result result 
)

Create, and connect, a websocket client.

If the client websocket successfully connects, then the accepted protocol can be checked via a call to ast_websocket_client_accept_protocol.

Note
While connecting this will block until a response is received from the remote host.
Expected uri form:
ws[s]://<address>[:port][/<path>] 
The address (can be a host name) and port are parsed out and used to connect to the remote server. If multiple IPs are returned during address resolution then the first one is chosen.
Parameters
uriuri to connect to
protocolsa comma separated string of supported protocols
tls_cfgsecure websocket credentials
resultresult code set on client failure
Returns
a client websocket.
Return values
NULLif object could not be created or connected
Since
13

Definition at line 1515 of file res_http_websocket.c.

1518{
1520 .uri = uri,
1521 .protocols = protocols,
1522 .timeout = -1,
1523 .tls_cfg = tls_cfg,
1524 };
1525
1527}
static PGresult * result
Definition: cel_pgsql.c:84
struct ast_websocket *AST_OPTIONAL_API_NAME() ast_websocket_client_create_with_options(struct ast_websocket_client_options *options, enum ast_websocket_result *result)
Create, and connect, a websocket client using given options.
Options used for a websocket client.
struct ast_tls_config * tls_cfg
static struct test_options options

References ast_websocket_client_create_with_options(), options, ast_websocket_client_options::protocols, result, ast_websocket_client_options::tls_cfg, and ast_websocket_client_options::uri.

Referenced by AST_TEST_DEFINE().

◆ ast_websocket_client_create_with_options()

struct ast_websocket *AST_OPTIONAL_API_NAME() ast_websocket_client_create_with_options ( struct ast_websocket_client_options options,
enum ast_websocket_result result 
)

Create, and connect, a websocket client using given options.

If the client websocket successfully connects, then the accepted protocol can be checked via a call to ast_websocket_client_accept_protocol.

Note
While connecting this will block until a response is received from the remote host, or the connection timeout is reached
Parameters
optionsWebsocket client options
resultresult code set on client failure
Returns
a client websocket.
Return values
NULLif object could not be created or connected

Definition at line 1529 of file res_http_websocket.c.

1531{
1533
1534 if (!ws) {
1535 return NULL;
1536 }
1537
1538 if ((*result = websocket_client_connect(ws, options->timeout)) != WS_OK) {
1539 ao2_ref(ws, -1);
1540 return NULL;
1541 }
1542
1543 return ws;
1544}
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
@ WS_OK
static enum ast_websocket_result websocket_client_connect(struct ast_websocket *ws, int timeout)
static struct ast_websocket * websocket_client_create(struct ast_websocket_client_options *options, enum ast_websocket_result *result)
#define NULL
Definition: resample.c:96
Structure definition for session.

References ao2_ref, NULL, options, result, websocket_client_connect(), websocket_client_create(), and WS_OK.

Referenced by ast_websocket_client_connect(), ast_websocket_client_create(), and websocket_connect().

◆ ast_websocket_close()

int AST_OPTIONAL_API_NAME() ast_websocket_close ( struct ast_websocket session,
uint16_t  reason 
)

Close function for websocket session.

Close a WebSocket session by sending a message with the CLOSE opcode and an optional code.

Definition at line 324 of file res_http_websocket.c.

325{
327 /* The header is either 2 or 6 bytes and the
328 * reason code takes up another 2 bytes */
329 char frame[8] = { 0, };
330 int header_size, fsize, res;
331
332 if (session->close_sent) {
333 return 0;
334 }
335
336 /* clients need space for an additional 4 byte masking key */
337 header_size = session->client ? 6 : 2;
338 fsize = header_size + 2;
339
340 frame[0] = opcode | 0x80;
341 frame[1] = 2; /* The reason code is always 2 bytes */
342
343 /* If no reason has been specified assume 1000 which is normal closure */
344 put_unaligned_uint16(&frame[header_size], htons(reason ? reason : 1000));
345
346 websocket_mask_payload(session, frame, &frame[header_size], 2);
347
348 session->closing = 1;
349 session->close_sent = 1;
350
353 res = ast_iostream_write(session->stream, frame, fsize);
355
356 /* If an error occurred when trying to close this connection explicitly terminate it now.
357 * Doing so will cause the thread polling on it to wake up and terminate.
358 */
359 if (res != fsize) {
361 session->stream = NULL;
362 ast_verb(2, "WebSocket connection %s '%s' forcefully closed due to fatal write error\n",
363 session->client ? "to" : "from", ast_sockaddr_stringify(&session->remote_address));
364 }
365
367 return res == sizeof(frame);
368}
static struct ast_mansession session
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
ast_websocket_opcode
WebSocket operation codes.
@ AST_WEBSOCKET_OPCODE_CLOSE
#define ast_verb(level,...)
void ast_iostream_set_timeout_inactivity(struct ast_iostream *stream, int timeout)
Set the iostream inactivity timeout timer.
Definition: iostream.c:122
ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t count)
Write data to an iostream.
Definition: iostream.c:385
int ast_iostream_close(struct ast_iostream *stream)
Close an iostream.
Definition: iostream.c:539
void ast_iostream_set_timeout_disable(struct ast_iostream *stream)
Disable the iostream timeout timer.
Definition: iostream.c:114
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
static void websocket_mask_payload(struct ast_websocket *session, char *frame, char *payload, uint64_t payload_size)
Perform payload masking for client sessions.
static void put_unaligned_uint16(void *p, unsigned short datum)
Definition: unaligned.h:65

References ao2_lock, ao2_unlock, ast_iostream_close(), ast_iostream_set_timeout_disable(), ast_iostream_set_timeout_inactivity(), ast_iostream_write(), ast_sockaddr_stringify(), ast_verb, AST_WEBSOCKET_OPCODE_CLOSE, NULL, ast_websocket::opcode, put_unaligned_uint16(), session, and websocket_mask_payload().

Referenced by ast_websocket_read(), ast_websocket_write(), incoming_ws_established_cb(), outbound_session_apply_config(), outbound_session_handler_thread(), outbound_session_pc_close_thread(), outbound_sessions_load(), session_destroy_fn(), session_shutdown_cb(), webchan_hangup(), and ws_shutdown().

◆ ast_websocket_fd()

int AST_OPTIONAL_API_NAME() ast_websocket_fd ( struct ast_websocket session)

Get the file descriptor for a WebSocket session.

Returns
file descriptor
Note
You must not directly read from or write to this file descriptor. It should only be used for polling.

Definition at line 477 of file res_http_websocket.c.

478{
479 return session->closing ? -1 : ast_iostream_get_fd(session->stream);
480}
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream's file descriptor.
Definition: iostream.c:85

References ast_iostream_get_fd(), and session.

Referenced by incoming_ws_established_cb(), read_from_ws_and_queue(), session_read(), speech_test_server_cb(), webchan_call(), websocket_echo_callback(), websocket_read(), and ws_shutdown().

◆ ast_websocket_is_secure()

int AST_OPTIONAL_API_NAME() ast_websocket_is_secure ( struct ast_websocket session)

Get whether the WebSocket session is using a secure transport or not.

Return values
0if unsecure
1if secure

Definition at line 497 of file res_http_websocket.c.

498{
499 return session->secure;
500}

References session.

Referenced by transport_create().

◆ ast_websocket_local_address()

struct ast_sockaddr *AST_OPTIONAL_API_NAME() ast_websocket_local_address ( struct ast_websocket session)

Get the local address for a WebSocket connection session.

Returns
Local address
Since
13.19.0

Definition at line 492 of file res_http_websocket.c.

493{
494 return &session->local_address;
495}

References session.

Referenced by transport_create().

◆ ast_websocket_read()

int AST_OPTIONAL_API_NAME() 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.

Parameters
sessionPointer to the WebSocket session
payloadPointer to a char* which will be populated with a pointer to the payload if present
payload_lenPointer to a uint64_t which will be populated with the length of the payload if present
opcodePointer to an enum which will be populated with the opcode of the frame
fragmentedPointer to an int which is set to 1 if payload is fragmented and 0 if not
Return values
-1on error
0on success
Note
Once an AST_WEBSOCKET_OPCODE_CLOSE opcode is received the socket will be closed

Definition at line 618 of file res_http_websocket.c.

619{
620 int fin = 0;
621 int mask_present = 0;
622 char *mask = NULL, *new_payload = NULL;
623 size_t options_len = 0, frame_size = 0;
624
625 *payload = NULL;
626 *payload_len = 0;
627 *fragmented = 0;
628
629 if (ws_safe_read(session, &session->buf[0], MIN_WS_HDR_SZ, opcode)) {
630 return -1;
631 }
633
634 /* ok, now we have the first 2 bytes, so we know some flags, opcode and payload length (or whether payload length extension will be required) */
635 *opcode = session->buf[0] & 0xf;
636 *payload_len = session->buf[1] & 0x7f;
639 fin = (session->buf[0] >> 7) & 1;
640 mask_present = (session->buf[1] >> 7) & 1;
641
642 /* Based on the mask flag and payload length, determine how much more we need to read before start parsing the rest of the header */
643 options_len += mask_present ? 4 : 0;
644 options_len += (*payload_len == 126) ? 2 : (*payload_len == 127) ? 8 : 0;
645 if (options_len) {
646 /* read the rest of the header options */
647 if (ws_safe_read(session, &session->buf[frame_size], options_len, opcode)) {
648 return -1;
649 }
650 frame_size += options_len;
651 }
652
653 if (*payload_len == 126) {
654 /* Grab the 2-byte payload length */
655 *payload_len = ntohs(get_unaligned_uint16(&session->buf[2]));
656 mask = &session->buf[4];
657 } else if (*payload_len == 127) {
658 /* Grab the 8-byte payload length */
659 *payload_len = ntohll(get_unaligned_uint64(&session->buf[2]));
660 mask = &session->buf[10];
661 } else {
662 /* Just set the mask after the small 2-byte header */
663 mask = &session->buf[2];
664 }
665
666 /* Now read the rest of the payload */
667 *payload = &session->buf[frame_size]; /* payload will start here, at the end of the options, if any */
668 frame_size = frame_size + (*payload_len); /* final frame size is header + optional headers + payload data */
670 ast_log(LOG_WARNING, "Cannot fit huge websocket frame of %zu bytes\n", frame_size);
671 /* The frame won't fit :-( */
673 return -1;
674 }
675
676 if (*payload_len) {
677 if (ws_safe_read(session, *payload, *payload_len, opcode)) {
678 return -1;
679 }
680 }
681
682 /* If a mask is present unmask the payload */
683 if (mask_present) {
684 unsigned int pos;
685 for (pos = 0; pos < *payload_len; pos++) {
686 (*payload)[pos] ^= mask[pos % 4];
687 }
688 }
689
690 /* Per the RFC for PING we need to send back an opcode with the application data as received */
691 if (*opcode == AST_WEBSOCKET_OPCODE_PING) {
692 if (ast_websocket_write(session, AST_WEBSOCKET_OPCODE_PONG, *payload, *payload_len)) {
694 }
695 *payload_len = 0;
696 return 0;
697 }
698
699 /* Stop PONG processing here */
700 if (*opcode == AST_WEBSOCKET_OPCODE_PONG) {
701 *payload_len = 0;
702 return 0;
703 }
704
705 /* Save the CLOSE status code which will be sent in our own CLOSE in the destructor */
706 if (*opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
707 session->closing = 1;
708 if (*payload_len >= 2) {
709 session->close_status_code = ntohs(get_unaligned_uint16(*payload));
710 }
711 *payload_len = 0;
712 return 0;
713 }
714
715 /* Below this point we are handling TEXT, BINARY or CONTINUATION opcodes */
716 if (*payload_len) {
717 if (!(new_payload = ast_realloc(session->payload, (session->payload_len + *payload_len)))) {
718 ast_log(LOG_WARNING, "Failed allocation: %p, %zu, %"PRIu64"\n",
719 session->payload, session->payload_len, *payload_len);
720 *payload_len = 0;
722 return -1;
723 }
724
725 session->payload = new_payload;
726 memcpy((session->payload + session->payload_len), (*payload), (*payload_len));
727 session->payload_len += *payload_len;
728 } else if (!session->payload_len && session->payload) {
729 ast_free(session->payload);
730 session->payload = NULL;
731 }
732
733 if (!fin && session->reconstruct && (session->payload_len < session->reconstruct)) {
734 /* If this is not a final message we need to defer returning it until later */
735 if (*opcode != AST_WEBSOCKET_OPCODE_CONTINUATION) {
736 session->opcode = *opcode;
737 }
739 *payload_len = 0;
740 *payload = NULL;
741 } else {
742 if (*opcode == AST_WEBSOCKET_OPCODE_CONTINUATION) {
743 if (!fin) {
744 /* If this was not actually the final message tell the user it is fragmented so they can deal with it accordingly */
745 *fragmented = 1;
746 } else {
747 /* Final frame in multi-frame so push up the actual opcode */
748 *opcode = session->opcode;
749 }
750 }
751 *payload_len = session->payload_len;
752 *payload = session->payload;
753 session->payload_len = 0;
754 }
755 } else {
756 ast_log(LOG_WARNING, "WebSocket unknown opcode %u\n", *opcode);
757 /* We received an opcode that we don't understand, the RFC states that 1003 is for a type of data that can't be accepted... opcodes
758 * fit that, I think. */
760 }
761
762 return 0;
763}
uint64_t ntohll(uint64_t net64)
Definition: strcompat.c:364
#define ast_free(a)
Definition: astmm.h:180
#define ast_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:226
#define ast_log
Definition: astobj2.c:42
static int frame_size[4]
Definition: format_g726.c:52
#define AST_WEBSOCKET_MAX_RX_PAYLOAD_SIZE
Size of the pre-determined buffer for WebSocket frames.
@ AST_WEBSOCKET_OPCODE_PING
@ AST_WEBSOCKET_OPCODE_PONG
@ AST_WEBSOCKET_OPCODE_CONTINUATION
@ AST_WEBSOCKET_OPCODE_BINARY
@ AST_WEBSOCKET_OPCODE_TEXT
#define LOG_WARNING
int AST_OPTIONAL_API_NAME() ast_websocket_write(struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t payload_size)
Write function for websocket traffic.
#define MIN_WS_HDR_SZ
static int ws_safe_read(struct ast_websocket *session, char *buf, size_t len, enum ast_websocket_opcode *opcode)
int AST_OPTIONAL_API_NAME() ast_websocket_close(struct ast_websocket *session, uint16_t reason)
Close function for websocket session.
static unsigned short get_unaligned_uint16(const void *p)
Definition: unaligned.h:44
static uint64_t get_unaligned_uint64(const void *p)
Definition: unaligned.h:32

References ast_free, ast_log, ast_realloc, ast_websocket_close(), AST_WEBSOCKET_MAX_RX_PAYLOAD_SIZE, AST_WEBSOCKET_OPCODE_BINARY, AST_WEBSOCKET_OPCODE_CLOSE, AST_WEBSOCKET_OPCODE_CONTINUATION, AST_WEBSOCKET_OPCODE_PING, AST_WEBSOCKET_OPCODE_PONG, AST_WEBSOCKET_OPCODE_TEXT, ast_websocket_write(), frame_size, get_unaligned_uint16(), get_unaligned_uint64(), LOG_WARNING, MIN_WS_HDR_SZ, ntohll(), NULL, ast_websocket::opcode, ast_websocket::payload, ast_websocket::payload_len, session, and ws_safe_read().

Referenced by ast_websocket_read_string(), read_from_ws_and_queue(), session_read(), speech_test_server_cb(), websocket_cb(), websocket_echo_callback(), and websocket_read().

◆ ast_websocket_read_string()

int AST_OPTIONAL_API_NAME() ast_websocket_read_string ( struct ast_websocket ws,
char **  buf 
)

Read a WebSocket frame containing string data.

Note
The caller is responsible for freeing the output "buf".
Parameters
wspointer to the websocket
bufstring buffer to populate with data read from socket
Return values
-1on error
Returns
number of bytes read on success
Note
Once an AST_WEBSOCKET_OPCODE_CLOSE opcode is received the socket will be closed

Definition at line 1546 of file res_http_websocket.c.

1548{
1549 char *payload;
1550 uint64_t payload_len;
1551 enum ast_websocket_opcode opcode;
1552 int fragmented = 1;
1553
1554 while (fragmented) {
1555 if (ast_websocket_read(ws, &payload, &payload_len,
1556 &opcode, &fragmented)) {
1557 ast_log(LOG_ERROR, "Client WebSocket string read - "
1558 "error reading string data\n");
1559 return -1;
1560 }
1561
1562 if (opcode == AST_WEBSOCKET_OPCODE_PING) {
1563 /* Try read again, we have sent pong already */
1564 fragmented = 1;
1565 continue;
1566 }
1567
1568 if (opcode == AST_WEBSOCKET_OPCODE_CONTINUATION) {
1569 continue;
1570 }
1571
1572 if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
1573 return -1;
1574 }
1575
1576 if (opcode != AST_WEBSOCKET_OPCODE_TEXT) {
1577 ast_log(LOG_ERROR, "Client WebSocket string read - "
1578 "non string data received\n");
1579 return -1;
1580 }
1581 }
1582
1583 if (!(*buf = ast_strndup(payload, payload_len))) {
1584 return -1;
1585 }
1586
1587 return payload_len + 1;
1588}
#define ast_strndup(str, len)
A wrapper for strndup()
Definition: astmm.h:256
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define LOG_ERROR
int AST_OPTIONAL_API_NAME() 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.

References ast_log, ast_strndup, AST_WEBSOCKET_OPCODE_CLOSE, AST_WEBSOCKET_OPCODE_CONTINUATION, AST_WEBSOCKET_OPCODE_PING, AST_WEBSOCKET_OPCODE_TEXT, ast_websocket_read(), buf, LOG_ERROR, ast_websocket::opcode, ast_websocket::payload, and ast_websocket::payload_len.

Referenced by AST_TEST_DEFINE().

◆ ast_websocket_reconstruct_disable()

void AST_OPTIONAL_API_NAME() ast_websocket_reconstruct_disable ( struct ast_websocket session)

Disable multi-frame reconstruction.

Parameters
sessionPointer to the WebSocket session
Note
If reconstruction is disabled each message that is part of a multi-frame message will be sent up to the user when ast_websocket_read is called.

Definition at line 462 of file res_http_websocket.c.

463{
464 session->reconstruct = 0;
465}

References session.

◆ ast_websocket_reconstruct_enable()

void AST_OPTIONAL_API_NAME() ast_websocket_reconstruct_enable ( struct ast_websocket session,
size_t  bytes 
)

Enable multi-frame reconstruction up to a certain number of bytes.

Parameters
sessionPointer to the WebSocket session
bytesIf a reconstructed payload exceeds the specified number of bytes the payload will be returned and upon reception of the next multi-frame a new reconstructed payload will begin.

Definition at line 457 of file res_http_websocket.c.

458{
459 session->reconstruct = MIN(bytes, MAXIMUM_RECONSTRUCTION_CEILING);
460}
#define MAXIMUM_RECONSTRUCTION_CEILING
Maximum reconstruction size for multi-frame payload reconstruction.
#define MIN(a, b)
Definition: utils.h:249

References MAXIMUM_RECONSTRUCTION_CEILING, MIN, and session.

◆ ast_websocket_ref()

void AST_OPTIONAL_API_NAME() ast_websocket_ref ( struct ast_websocket session)

Increase the reference count for a WebSocket session.

Parameters
sessionPointer to the WebSocket session

Definition at line 467 of file res_http_websocket.c.

468{
469 ao2_ref(session, +1);
470}

References ao2_ref, and session.

Referenced by transport_create().

◆ ast_websocket_remote_address()

struct ast_sockaddr *AST_OPTIONAL_API_NAME() ast_websocket_remote_address ( struct ast_websocket session)

Get the remote address for a WebSocket connected session.

Returns
Remote address

Definition at line 487 of file res_http_websocket.c.

488{
489 return &session->remote_address;
490}

References session.

Referenced by ari_websocket_send_event(), session_update(), session_write(), transport_create(), transport_read(), and websocket_established_cb().

◆ ast_websocket_remove_protocol()

int AST_OPTIONAL_API_NAME() ast_websocket_remove_protocol ( const char *  name,
ast_websocket_callback  callback 
)

Remove a sub-protocol handler from the default /ws server.

Parameters
nameName of the sub-protocol to unregister
callbackSession Established callback that was previously registered with the sub-protocol
Return values
0success
-1if sub-protocol was not found or if callback did not match

Definition at line 1097 of file res_http_websocket.c.

1098{
1100}
static int websocket_remove_protocol_internal(const char *name, ast_websocket_callback callback)

References callback(), name, and websocket_remove_protocol_internal().

Referenced by AST_TEST_DEFINE(), and unload_module().

◆ ast_websocket_result_to_str()

const char *AST_OPTIONAL_API_NAME() ast_websocket_result_to_str ( enum ast_websocket_result  result)

Convert a websocket result code to a string.

Parameters
resultThe result code to convert
Returns
A string representation of the result code

Definition at line 1626 of file res_http_websocket.c.

1628{
1630 return "unknown";
1631 }
1633}
const char * websocket_result_string_map[]
#define ARRAY_IN_BOUNDS(v, a)
Checks to see if value is within the bounds of the given array.
Definition: utils.h:724

References ARRAY_IN_BOUNDS, result, and websocket_result_string_map.

Referenced by ast_websocket_client_connect(), and webchan_call().

◆ ast_websocket_server_add_protocol()

int AST_OPTIONAL_API_NAME() ast_websocket_server_add_protocol ( struct ast_websocket_server server,
const char *  name,
ast_websocket_callback  callback 
)

Add a sub-protocol handler to the given server.

Parameters
serverThe server to add the sub-protocol to
nameName of the sub-protocol to register
callbackCallback called when a new connection requesting the sub-protocol is established
Return values
0success
-1if sub-protocol handler could not be registered
Since
12

Definition at line 226 of file res_http_websocket.c.

227{
228 struct ast_websocket_protocol *protocol;
229
230 if (!server->protocols) {
231 return -1;
232 }
233
235 if (!protocol) {
236 return -1;
237 }
238 protocol->session_established = callback;
239
240 if (ast_websocket_server_add_protocol2(server, protocol)) {
241 ao2_ref(protocol, -1);
242 return -1;
243 }
244
245 return 0;
246}
struct ast_websocket_protocol *AST_OPTIONAL_API_NAME() ast_websocket_sub_protocol_alloc(const char *name)
Allocate a websocket sub-protocol instance.
A websocket protocol implementation.
ast_websocket_callback session_established
Callback called when a new session is established. Mandatory.
struct ao2_container * protocols

References ao2_ref, ast_websocket_server_add_protocol2(), ast_websocket_sub_protocol_alloc(), callback(), name, ast_websocket_server::protocols, and ast_websocket_protocol::session_established.

Referenced by websocket_add_protocol_internal().

◆ ast_websocket_server_add_protocol2()

int AST_OPTIONAL_API_NAME() ast_websocket_server_add_protocol2 ( struct ast_websocket_server server,
struct ast_websocket_protocol protocol 
)

Add a sub-protocol handler to the given server.

Parameters
serverThe server to add the sub-protocol to.
protocolThe sub-protocol to register. Note that this must be allocated using /ref ast_websocket_sub_protocol_alloc.
Note
This method is reference stealing. It will steal the reference to protocol on success.
Return values
0success
-1if sub-protocol handler could not be registered
Since
13.5.0

Definition at line 248 of file res_http_websocket.c.

249{
250 struct ast_websocket_protocol *existing;
251
252 if (!server->protocols) {
253 return -1;
254 }
255
256 if (protocol->version != AST_WEBSOCKET_PROTOCOL_VERSION) {
257 ast_log(LOG_WARNING, "WebSocket could not register sub-protocol '%s': "
258 "expected version '%u', got version '%u'\n",
259 protocol->name, AST_WEBSOCKET_PROTOCOL_VERSION, protocol->version);
260 return -1;
261 }
262
263 ao2_lock(server->protocols);
264
265 /* Ensure a second protocol handler is not registered for the same protocol */
266 existing = ao2_find(server->protocols, protocol->name, OBJ_KEY | OBJ_NOLOCK);
267 if (existing) {
268 ao2_ref(existing, -1);
269 ao2_unlock(server->protocols);
270 return -1;
271 }
272
273 ao2_link_flags(server->protocols, protocol, OBJ_NOLOCK);
274 ao2_unlock(server->protocols);
275
276 ast_debug(1, "WebSocket registered sub-protocol '%s'\n", protocol->name);
277 ao2_ref(protocol, -1);
278
279 return 0;
280}
#define OBJ_KEY
Definition: astobj2.h:1151
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
Definition: astobj2.h:1554
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
@ OBJ_NOLOCK
Assume that the ao2_container is already locked.
Definition: astobj2.h:1063
#define AST_WEBSOCKET_PROTOCOL_VERSION
Protocol version. This prevents dynamically loadable modules from registering if this struct is chang...
#define ast_debug(level,...)
Log a DEBUG message.
unsigned int version
Protocol version. Should be set to /ref AST_WEBSOCKET_PROTOCOL_VERSION.
char * name
Name of the protocol.

References ao2_find, ao2_link_flags, ao2_lock, ao2_ref, ao2_unlock, ast_debug, ast_log, AST_WEBSOCKET_PROTOCOL_VERSION, LOG_WARNING, OBJ_KEY, OBJ_NOLOCK, and ast_websocket_server::protocols.

Referenced by ari_websocket_load_module(), ast_websocket_add_protocol2(), ast_websocket_server_add_protocol(), and load_module().

◆ ast_websocket_server_create()

struct ast_websocket_server *AST_OPTIONAL_API_NAME() ast_websocket_server_create ( void  )

Creates a ast_websocket_server.

Returns
New ast_websocket_server instance
Return values
NULLon error
Since
12

Definition at line 183 of file res_http_websocket.c.

184{
186}
static struct ast_websocket_server * websocket_server_create_impl(void)

References websocket_server_create_impl().

Referenced by ari_websocket_load_module(), and load_module().

◆ ast_websocket_server_remove_protocol()

int AST_OPTIONAL_API_NAME() ast_websocket_server_remove_protocol ( struct ast_websocket_server server,
const char *  name,
ast_websocket_callback  callback 
)

Remove a sub-protocol handler from the given server.

Parameters
serverThe server to unregister the sub-protocol from
nameName of the sub-protocol to unregister
callbackCallback that was previously registered with the sub-protocol
Return values
0success
-1if sub-protocol was not found or if callback did not match
Since
12

Definition at line 282 of file res_http_websocket.c.

283{
284 struct ast_websocket_protocol *protocol;
285
286 if (!(protocol = ao2_find(server->protocols, name, OBJ_KEY))) {
287 return -1;
288 }
289
290 if (protocol->session_established != callback) {
291 ao2_ref(protocol, -1);
292 return -1;
293 }
294
295 ao2_unlink(server->protocols, protocol);
296 ao2_ref(protocol, -1);
297
298 ast_debug(1, "WebSocket unregistered sub-protocol '%s'\n", name);
299
300 return 0;
301}
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578

References ao2_find, ao2_ref, ao2_unlink, ast_debug, callback(), name, OBJ_KEY, ast_websocket_server::protocols, and ast_websocket_protocol::session_established.

Referenced by websocket_remove_protocol_internal().

◆ ast_websocket_session_id()

const char *AST_OPTIONAL_API_NAME() ast_websocket_session_id ( struct ast_websocket session)

Get the session ID for a WebSocket session.

Returns
session id

Definition at line 516 of file res_http_websocket.c.

517{
518 return session->session_id;
519}

References session.

Referenced by websocket_established_cb().

◆ ast_websocket_set_nonblock()

int AST_OPTIONAL_API_NAME() ast_websocket_set_nonblock ( struct ast_websocket session)

Set the socket of a WebSocket session to be non-blocking.

Return values
0on success
-1on failure

Definition at line 502 of file res_http_websocket.c.

503{
506 return 0;
507}
void ast_iostream_set_exclusive_input(struct ast_iostream *stream, int exclusive_input)
Set the iostream if it can exclusively depend upon the set timeouts.
Definition: iostream.c:149
void ast_iostream_nonblock(struct ast_iostream *stream)
Make an iostream non-blocking.
Definition: iostream.c:104

References ast_iostream_nonblock(), ast_iostream_set_exclusive_input(), and session.

Referenced by session_update(), and websocket_cb().

◆ ast_websocket_set_timeout()

int AST_OPTIONAL_API_NAME() ast_websocket_set_timeout ( struct ast_websocket session,
int  timeout 
)

Set the timeout on a non-blocking WebSocket session.

Since
11.11.0
12.4.0
Return values
0on success
-1on failure

Definition at line 509 of file res_http_websocket.c.

510{
511 session->timeout = timeout;
512
513 return 0;
514}

References session, and ast_websocket::timeout.

Referenced by session_update(), and websocket_cb().

◆ ast_websocket_sub_protocol_alloc()

struct ast_websocket_protocol *AST_OPTIONAL_API_NAME() ast_websocket_sub_protocol_alloc ( const char *  name)

Allocate a websocket sub-protocol instance.

Returns
An instance of ast_websocket_protocol on success
Return values
NULLon error
Since
13.5.0

Definition at line 207 of file res_http_websocket.c.

208{
209 struct ast_websocket_protocol *protocol;
210
211 protocol = ao2_alloc(sizeof(*protocol), protocol_destroy_fn);
212 if (!protocol) {
213 return NULL;
214 }
215
216 protocol->name = ast_strdup(name);
217 if (!protocol->name) {
218 ao2_ref(protocol, -1);
219 return NULL;
220 }
222
223 return protocol;
224}
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
static void protocol_destroy_fn(void *obj)
Destructor function for protocols.

References ao2_alloc, ao2_ref, ast_strdup, AST_WEBSOCKET_PROTOCOL_VERSION, name, ast_websocket_protocol::name, NULL, protocol_destroy_fn(), and ast_websocket_protocol::version.

Referenced by ari_websocket_load_module(), ast_websocket_server_add_protocol(), and load_module().

◆ ast_websocket_type_to_str()

const char * ast_websocket_type_to_str ( enum ast_websocket_type  type)

Definition at line 100 of file res_http_websocket.c.

101{
102 switch (type) {
104 return "persistent";
106 return "per_call";
108 return "per_call_config";
110 return "client";
112 return "inbound";
114 return "server";
115 case AST_WS_TYPE_ANY:
116 return "any";
117 default:
118 return "unknown";
119 }
120}
static const char type[]
Definition: chan_ooh323.c:109
@ AST_WS_TYPE_ANY
@ AST_WS_TYPE_CLIENT
@ AST_WS_TYPE_INBOUND
@ AST_WS_TYPE_CLIENT_PER_CALL_CONFIG
@ AST_WS_TYPE_SERVER
@ AST_WS_TYPE_CLIENT_PERSISTENT
@ AST_WS_TYPE_CLIENT_PER_CALL

References AST_WS_TYPE_ANY, AST_WS_TYPE_CLIENT, AST_WS_TYPE_CLIENT_PER_CALL, AST_WS_TYPE_CLIENT_PER_CALL_CONFIG, AST_WS_TYPE_CLIENT_PERSISTENT, AST_WS_TYPE_INBOUND, AST_WS_TYPE_SERVER, and type.

◆ ast_websocket_unref()

void AST_OPTIONAL_API_NAME() ast_websocket_unref ( struct ast_websocket session)

Decrease the reference count for a WebSocket session.

Parameters
sessionPointer to the WebSocket session

Definition at line 472 of file res_http_websocket.c.

473{
475}
#define ao2_cleanup(obj)
Definition: astobj2.h:1934

References ao2_cleanup, and session.

Referenced by incoming_ws_established_cb(), outbound_session_handler_thread(), session_dtor(), speech_test_server_cb(), transport_dtor(), webchan_hangup(), websocket_cb(), websocket_destructor(), websocket_disconnect(), websocket_echo_callback(), and websocket_established_cb().

◆ ast_websocket_uri_cb()

int AST_OPTIONAL_API_NAME() ast_websocket_uri_cb ( struct ast_tcptls_session_instance ser,
const struct ast_http_uri urih,
const char *  uri,
enum ast_http_method  method,
struct ast_variable get_vars,
struct ast_variable headers 
)

Callback suitable for use with a ast_http_uri.

Set the data field of the ast_http_uri to ast_websocket_server.

Since
12

Definition at line 806 of file res_http_websocket.c.

807{
808 struct ast_variable *v;
809 const char *upgrade = NULL, *key = NULL, *key1 = NULL, *key2 = NULL, *protos = NULL;
810 char *requested_protocols = NULL, *protocol = NULL;
811 int version = 0, flags = 1;
812 struct ast_websocket_protocol *protocol_handler = NULL;
813 struct ast_websocket *session;
814 struct ast_websocket_server *server;
815
817
818 /* Upgrade requests are only permitted on GET methods */
819 if (method != AST_HTTP_GET) {
820 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
821 return 0;
822 }
823
824 server = urih->data;
825
826 /* Get the minimum headers required to satisfy our needs */
827 for (v = headers; v; v = v->next) {
828 if (!strcasecmp(v->name, "Upgrade")) {
829 upgrade = v->value;
830 } else if (!strcasecmp(v->name, "Sec-WebSocket-Key")) {
831 key = v->value;
832 } else if (!strcasecmp(v->name, "Sec-WebSocket-Key1")) {
833 key1 = v->value;
834 } else if (!strcasecmp(v->name, "Sec-WebSocket-Key2")) {
835 key2 = v->value;
836 } else if (!strcasecmp(v->name, "Sec-WebSocket-Protocol")) {
837 protos = v->value;
838 } else if (!strcasecmp(v->name, "Sec-WebSocket-Version")) {
839 if (sscanf(v->value, "%30d", &version) != 1) {
840 version = 0;
841 }
842 }
843 }
844
845 /* If this is not a websocket upgrade abort */
846 if (!upgrade || strcasecmp(upgrade, "websocket")) {
847 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - did not request WebSocket\n",
849 ast_http_error(ser, 426, "Upgrade Required", NULL);
850 return 0;
851 } else if (ast_strlen_zero(protos)) {
852 /* If there's only a single protocol registered, and the
853 * client doesn't specify what protocol it's using, go ahead
854 * and accept the connection */
855 protocol_handler = one_protocol(server);
856 if (!protocol_handler) {
857 /* Multiple registered subprotocols; client must specify */
858 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols requested\n",
861 return 0;
862 }
863 } else if (key1 && key2) {
864 /* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 and
865 * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -- not currently supported*/
866 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '00/76' chosen\n",
869 return 0;
870 }
871
872 if (!protocol_handler && protos) {
873 requested_protocols = ast_strdupa(protos);
874 /* Iterate through the requested protocols trying to find one that we have a handler for */
875 while (!protocol_handler && (protocol = strsep(&requested_protocols, ","))) {
876 protocol_handler = ao2_find(server->protocols, ast_strip(protocol), OBJ_KEY);
877 }
878 }
879
880 /* If no protocol handler exists bump this back to the requester */
881 if (!protocol_handler) {
882 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols out of '%s' supported\n",
885 return 0;
886 }
887
888 /* Determine how to respond depending on the version */
889 if (version == 7 || version == 8 || version == 13) {
890 char base64[64];
891
892 if (!key || strlen(key) + strlen(WEBSOCKET_GUID) + 1 > 8192) { /* no stack overflows please */
894 ao2_ref(protocol_handler, -1);
895 return 0;
896 }
897
898 if (ast_http_body_discard(ser)) {
900 ao2_ref(protocol_handler, -1);
901 return 0;
902 }
903
904 if (!(session = ao2_alloc(sizeof(*session) + AST_UUID_STR_LEN + 1, session_destroy_fn))) {
905 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted\n",
908 ao2_ref(protocol_handler, -1);
909 return 0;
910 }
912
913 /* Generate the session id */
914 if (!ast_uuid_generate_str(session->session_id, sizeof(session->session_id))) {
915 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to generate a session id\n",
917 ast_http_error(ser, 500, "Internal Server Error", "Allocation failed");
918 ao2_ref(protocol_handler, -1);
919 return 0;
920 }
921
922 if (protocol_handler->session_attempted
923 && protocol_handler->session_attempted(ser, get_vars, headers, session->session_id)) {
924 ast_debug(3, "WebSocket connection from '%s' rejected by protocol handler '%s'\n",
925 ast_sockaddr_stringify(&ser->remote_address), protocol_handler->name);
927 ao2_ref(protocol_handler, -1);
928 return 0;
929 }
930
931 /* RFC 6455, Section 4.1:
932 *
933 * 6. If the response includes a |Sec-WebSocket-Protocol| header
934 * field and this header field indicates the use of a
935 * subprotocol that was not present in the client's handshake
936 * (the server has indicated a subprotocol not requested by
937 * the client), the client MUST _Fail the WebSocket
938 * Connection_.
939 */
940 if (protocol) {
942 "HTTP/1.1 101 Switching Protocols\r\n"
943 "Upgrade: %s\r\n"
944 "Connection: Upgrade\r\n"
945 "Sec-WebSocket-Accept: %s\r\n"
946 "Sec-WebSocket-Protocol: %s\r\n\r\n",
947 upgrade,
948 websocket_combine_key(key, base64, sizeof(base64)),
949 protocol);
950 } else {
952 "HTTP/1.1 101 Switching Protocols\r\n"
953 "Upgrade: %s\r\n"
954 "Connection: Upgrade\r\n"
955 "Sec-WebSocket-Accept: %s\r\n\r\n",
956 upgrade,
957 websocket_combine_key(key, base64, sizeof(base64)));
958 }
959 } else {
960
961 /* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 or completely unknown */
962 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '%d' chosen\n",
965 ao2_ref(protocol_handler, -1);
966 return 0;
967 }
968
969 /* Enable keepalive on all sessions so the underlying user does not have to */
970 if (setsockopt(ast_iostream_get_fd(ser->stream), SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
971 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to enable keepalive\n",
974 ao2_ref(session, -1);
975 ao2_ref(protocol_handler, -1);
976 return 0;
977 }
978
979 /* Get our local address for the connected socket */
980 if (ast_getsockname(ast_iostream_get_fd(ser->stream), &session->local_address)) {
981 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to get local address\n",
984 ao2_ref(session, -1);
985 ao2_ref(protocol_handler, -1);
986 return 0;
987 }
988
989 ast_debug(3, "WebSocket connection from '%s' for protocol '%s' accepted using version '%d'\n", ast_sockaddr_stringify(&ser->remote_address), protocol ? : "", version);
990
991 /* Populate the session with all the needed details */
992 session->stream = ser->stream;
993 ast_sockaddr_copy(&session->remote_address, &ser->remote_address);
994 session->opcode = -1;
996 session->secure = ast_iostream_get_ssl(ser->stream) ? 1 : 0;
997
998 /* Give up ownership of the socket and pass it to the protocol handler */
1000 protocol_handler->session_established(session, get_vars, headers);
1001 ao2_ref(protocol_handler, -1);
1002
1003 /*
1004 * By dropping the stream from the session the connection
1005 * won't get closed when the HTTP server cleans up because we
1006 * passed the connection to the protocol handler.
1007 */
1008 ser->stream = NULL;
1009
1010 return 0;
1011}
char * strsep(char **str, const char *delims)
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
static char version[AST_MAX_EXTENSION]
Definition: chan_ooh323.c:391
@ AST_HTTP_GET
Definition: http.h:60
int ast_http_body_discard(struct ast_tcptls_session_instance *ser)
Read and discard any unread HTTP request body.
Definition: http.c:1135
void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const char *title, const char *text)
Send HTTP error message and close socket.
Definition: http.c:664
#define AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT
Default websocket write timeout, in ms.
ssize_t ast_iostream_printf(struct ast_iostream *stream, const char *format,...)
Write a formatted string to an iostream.
Definition: iostream.c:502
SSL * ast_iostream_get_ssl(struct ast_iostream *stream)
Get a pointer to an iostream's OpenSSL SSL structure.
Definition: iostream.c:109
#define SCOPED_MODULE_USE(module)
Definition: module.h:679
int ast_getsockname(int sockfd, struct ast_sockaddr *addr)
Wrapper around getsockname(2) that uses struct ast_sockaddr.
Definition: netsock2.c:600
static void ast_sockaddr_copy(struct ast_sockaddr *dst, const struct ast_sockaddr *src)
Copies the data from one ast_sockaddr to another.
Definition: netsock2.h:167
static void websocket_bad_request(struct ast_tcptls_session_instance *ser)
static struct ast_websocket_protocol * one_protocol(struct ast_websocket_server *server)
If the server has exactly one configured protocol, return it.
#define DEFAULT_RECONSTRUCTION_CEILING
Default reconstruction size for multi-frame payload reconstruction. If exceeded the next frame will s...
static char * websocket_combine_key(const char *key, char *res, int res_size)
#define WEBSOCKET_GUID
GUID used to compute the accept key, defined in the specifications.
static void session_destroy_fn(void *obj)
Destructor function for sessions.
const char * method
Definition: res_pjsip.c:1279
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
struct ast_module * self
Definition: module.h:356
struct ast_iostream * stream
Definition: tcptls.h:162
struct ast_sockaddr remote_address
Definition: tcptls.h:153
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
ast_websocket_pre_callback session_attempted
Callback called when a new session is attempted. Optional.
static char base64[64]
Definition: utils.c:80
#define AST_UUID_STR_LEN
Definition: uuid.h:27
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Definition: uuid.c:141

References ao2_alloc, ao2_find, ao2_ref, ast_debug, AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT, ast_getsockname(), ast_http_body_discard(), ast_http_error(), AST_HTTP_GET, ast_iostream_get_fd(), ast_iostream_get_ssl(), ast_iostream_printf(), ast_iostream_set_exclusive_input(), ast_log, ast_sockaddr_copy(), ast_sockaddr_stringify(), ast_strdupa, ast_strip(), ast_strlen_zero(), ast_uuid_generate_str(), AST_UUID_STR_LEN, base64, ast_http_uri::data, DEFAULT_RECONSTRUCTION_CEILING, LOG_WARNING, method, ast_variable::name, ast_websocket_protocol::name, ast_variable::next, NULL, OBJ_KEY, one_protocol(), ast_websocket_server::protocols, ast_tcptls_session_instance::remote_address, SCOPED_MODULE_USE, ast_module_info::self, session, ast_websocket_protocol::session_attempted, session_destroy_fn(), ast_websocket_protocol::session_established, ast_tcptls_session_instance::stream, strsep(), 210693f3123d_create_cdr_table::upgrade(), ast_variable::value, version, websocket_bad_request(), websocket_combine_key(), and WEBSOCKET_GUID.

Referenced by ari_handle_websocket(), and incoming_ws_http_callback().

◆ ast_websocket_wait_for_input()

int AST_OPTIONAL_API_NAME() ast_websocket_wait_for_input ( struct ast_websocket session,
int  timeout 
)

Wait for the WebSocket session to be ready to be read.

Since
16.8.0
17.2.0
Parameters
sessionPointer to the WebSocket session
timeoutthe number of milliseconds to wait
Return values
-1if error occurred
0if the timeout expired
1if the WebSocket session is ready for reading

Definition at line 482 of file res_http_websocket.c.

483{
484 return session->closing ? -1 : ast_iostream_wait_for_input(session->stream, timeout);
485}
int ast_iostream_wait_for_input(struct ast_iostream *stream, int timeout)
Wait for input on the iostream's file descriptor.
Definition: iostream.c:90

References ast_iostream_wait_for_input(), session, and ast_websocket::timeout.

Referenced by speech_test_server_cb(), websocket_cb(), websocket_echo_callback(), and websocket_read().

◆ ast_websocket_write()

int AST_OPTIONAL_API_NAME() ast_websocket_write ( struct ast_websocket session,
enum ast_websocket_opcode  opcode,
char *  payload,
uint64_t  payload_size 
)

Write function for websocket traffic.

Construct and transmit a WebSocket frame.

Definition at line 390 of file res_http_websocket.c.

391{
392 size_t header_size = 2; /* The minimum size of a websocket frame is 2 bytes */
393 char *frame;
394 uint64_t length;
395 uint64_t frame_size;
396
397 ast_debug(3, "Writing websocket %s frame, length %" PRIu64 "\n",
398 websocket_opcode2str(opcode), payload_size);
399
400 if (payload_size < 126) {
401 length = payload_size;
402 } else if (payload_size < (1 << 16)) {
403 length = 126;
404 /* We need an additional 2 bytes to store the extended length */
405 header_size += 2;
406 } else {
407 length = 127;
408 /* We need an additional 8 bytes to store the really really extended length */
409 header_size += 8;
410 }
411
412 if (session->client) {
413 /* Additional 4 bytes for the client masking key */
414 header_size += 4;
415 }
416
417 frame_size = header_size + payload_size;
418
419 frame = ast_alloca(frame_size + 1);
420 memset(frame, 0, frame_size + 1);
421
422 frame[0] = opcode | 0x80;
423 frame[1] = length;
424
425 /* Use the additional available bytes to store the length */
426 if (length == 126) {
427 put_unaligned_uint16(&frame[2], htons(payload_size));
428 } else if (length == 127) {
429 put_unaligned_uint64(&frame[2], htonll(payload_size));
430 }
431
432 memcpy(&frame[header_size], payload, payload_size);
433
434 websocket_mask_payload(session, frame, &frame[header_size], payload_size);
435
437 if (session->closing) {
439 return -1;
440 }
441
443 if (ast_iostream_write(session->stream, frame, frame_size) != frame_size) {
445 /* 1011 - server terminating connection due to not being able to fulfill the request */
446 ast_debug(1, "Closing WS with 1011 because we can't fulfill a write request\n");
448 return -1;
449 }
450
453
454 return 0;
455}
uint64_t htonll(uint64_t host64)
Definition: strcompat.c:390
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
void ast_iostream_set_timeout_sequence(struct ast_iostream *stream, struct timeval start, int timeout)
Set the iostream I/O sequence timeout timer.
Definition: iostream.c:140
static const char * websocket_opcode2str(enum ast_websocket_opcode opcode)
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
static void put_unaligned_uint64(void *p, uint64_t datum)
Definition: unaligned.h:51

References ao2_lock, ao2_unlock, ast_alloca, ast_debug, ast_iostream_set_timeout_disable(), ast_iostream_set_timeout_sequence(), ast_iostream_write(), ast_tvnow(), ast_websocket_close(), frame_size, htonll(), ast_websocket::opcode, ast_websocket::payload, put_unaligned_uint16(), put_unaligned_uint64(), session, websocket_mask_payload(), and websocket_opcode2str().

Referenced by ast_websocket_read(), ast_websocket_write_string(), speech_test_server_cb(), webchan_write(), websocket_echo_callback(), websocket_write(), and ws_send_msg().

◆ ast_websocket_write_string()

int AST_OPTIONAL_API_NAME() ast_websocket_write_string ( struct ast_websocket ws,
const char *  buf 
)

Construct and transmit a WebSocket frame containing string data.

Parameters
wspointer to the websocket
bufstring data to write to socket
Return values
0if successfully written
-1if error occurred

Definition at line 1590 of file res_http_websocket.c.

1592{
1593 uint64_t len = strlen(buf);
1594
1595 ast_debug(3, "Writing websocket string of length %" PRIu64 "\n", len);
1596
1597 /* We do not pass strlen(buf) to ast_websocket_write() directly because the
1598 * size_t returned by strlen() may not require the same storage size
1599 * as the uint64_t that ast_websocket_write() uses. This normally
1600 * would not cause a problem, but since ast_websocket_write() uses
1601 * the optional API, this function call goes through a series of macros
1602 * that may cause a 32-bit to 64-bit conversion to go awry.
1603 */
1605 (char *)buf, len);
1606}
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)

References ast_debug, AST_WEBSOCKET_OPCODE_TEXT, ast_websocket_write(), buf, and len().

Referenced by AST_TEST_DEFINE(), dequeue_frame(), process_text_message(), queue_frame_from_buffer(), read_thread_handler(), session_write(), and speech_test_server_handle_request().

◆ load_module()

static int load_module ( void  )
static

Definition at line 1635 of file res_http_websocket.c.

1636{
1638 if (!websocketuri.data) {
1640 }
1643
1644 return 0;
1645}
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:689
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static void websocket_echo_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
Simple echo implementation which echoes received text and binary frames.
static struct ast_websocket_server * websocket_server_internal_create(void)

References ast_http_uri_link(), AST_MODULE_LOAD_DECLINE, ast_http_uri::data, websocket_add_protocol_internal(), websocket_echo_callback(), websocket_server_internal_create(), and websocketuri.

◆ one_protocol()

static struct ast_websocket_protocol * one_protocol ( struct ast_websocket_server server)
static

If the server has exactly one configured protocol, return it.

Definition at line 768 of file res_http_websocket.c.

770{
771 SCOPED_AO2LOCK(lock, server->protocols);
772
773 if (ao2_container_count(server->protocols) != 1) {
774 return NULL;
775 }
776
777 return ao2_callback(server->protocols, OBJ_NOLOCK, NULL, NULL);
778}
ast_mutex_t lock
Definition: app_sla.c:337
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:611

References ao2_callback, ao2_container_count(), lock, NULL, OBJ_NOLOCK, ast_websocket_server::protocols, and SCOPED_AO2LOCK.

Referenced by ast_websocket_uri_cb().

◆ protocol_cmp_fn()

static int protocol_cmp_fn ( void *  obj,
void *  arg,
int  flags 
)
static

Comparison function for protocols.

Definition at line 132 of file res_http_websocket.c.

133{
134 const struct ast_websocket_protocol *protocol1 = obj, *protocol2 = arg;
135 const char *protocol = arg;
136
137 return !strcasecmp(protocol1->name, flags & OBJ_KEY ? protocol : protocol2->name) ? CMP_MATCH | CMP_STOP : 0;
138}
@ CMP_MATCH
Definition: astobj2.h:1027
@ CMP_STOP
Definition: astobj2.h:1028

References CMP_MATCH, CMP_STOP, ast_websocket_protocol::name, and OBJ_KEY.

Referenced by websocket_server_create_impl().

◆ protocol_destroy_fn()

static void protocol_destroy_fn ( void *  obj)
static

Destructor function for protocols.

Definition at line 141 of file res_http_websocket.c.

142{
143 struct ast_websocket_protocol *protocol = obj;
144 ast_free(protocol->name);
145}

References ast_free, and ast_websocket_protocol::name.

Referenced by ast_websocket_sub_protocol_alloc().

◆ protocol_hash_fn()

static int protocol_hash_fn ( const void *  obj,
const int  flags 
)
static

Hashing function for protocols.

Definition at line 123 of file res_http_websocket.c.

124{
125 const struct ast_websocket_protocol *protocol = obj;
126 const char *name = obj;
127
128 return ast_str_case_hash(flags & OBJ_KEY ? name : protocol->name);
129}
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition: strings.h:1303

References ast_str_case_hash(), name, ast_websocket_protocol::name, and OBJ_KEY.

Referenced by websocket_server_create_impl().

◆ session_destroy_fn()

static void session_destroy_fn ( void *  obj)
static

Destructor function for sessions.

Definition at line 189 of file res_http_websocket.c.

190{
191 struct ast_websocket *session = obj;
192
193 if (session->stream) {
194 ast_websocket_close(session, session->close_status_code);
195 if (session->stream) {
197 session->stream = NULL;
198 ast_debug(3, "WebSocket connection %s '%s' closed\n", session->client ? "to" : "from",
199 ast_sockaddr_stringify(&session->remote_address));
200 }
201 }
202
203 ao2_cleanup(session->client);
204 ast_free(session->payload);
205}

References ao2_cleanup, ast_debug, ast_free, ast_iostream_close(), ast_sockaddr_stringify(), ast_websocket_close(), NULL, and session.

Referenced by ast_websocket_uri_cb(), and websocket_client_create().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1647 of file res_http_websocket.c.

1648{
1653
1654 return 0;
1655}
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:721

References ao2_ref, ast_http_uri_unlink(), ast_http_uri::data, NULL, websocket_echo_callback(), websocket_remove_protocol_internal(), and websocketuri.

◆ websocket_add_protocol_internal()

static int websocket_add_protocol_internal ( const char *  name,
ast_websocket_callback  callback 
)
static

Definition at line 1059 of file res_http_websocket.c.

1060{
1061 struct ast_websocket_server *ws_server = websocketuri.data;
1062 if (!ws_server) {
1063 return -1;
1064 }
1066}
int AST_OPTIONAL_API_NAME() ast_websocket_server_add_protocol(struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
Add a sub-protocol handler to the given server.

References ast_websocket_server_add_protocol(), callback(), ast_http_uri::data, name, and websocketuri.

Referenced by ast_websocket_add_protocol(), and load_module().

◆ websocket_bad_request()

static void websocket_bad_request ( struct ast_tcptls_session_instance ser)
static

Definition at line 793 of file res_http_websocket.c.

794{
795 struct ast_str *http_header = ast_str_create(64);
796
797 if (!http_header) {
799 ast_http_error(ser, 500, "Server Error", "Out of memory");
800 return;
801 }
802 ast_str_set(&http_header, 0, "Sec-WebSocket-Version: 7, 8, 13\r\n");
803 ast_http_send(ser, AST_HTTP_UNKNOWN, 400, "Bad Request", http_header, NULL, 0, 0);
804}
void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, int fd, unsigned int static_content)
Generic function for sending HTTP/1.1 response.
Definition: http.c:472
@ AST_HTTP_UNKNOWN
Definition: http.h:59
void ast_http_request_close_on_completion(struct ast_tcptls_session_instance *ser)
Request the HTTP connection be closed after this HTTP request.
Definition: http.c:853
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
Support for dynamic strings.
Definition: strings.h:623

References ast_http_error(), ast_http_request_close_on_completion(), ast_http_send(), AST_HTTP_UNKNOWN, ast_str_create, ast_str_set(), and NULL.

Referenced by ast_websocket_uri_cb().

◆ websocket_client_args_create()

static struct ast_tcptls_session_args * websocket_client_args_create ( const char *  host,
struct ast_tls_config tls_cfg,
enum ast_websocket_result result 
)
static

Definition at line 1158 of file res_http_websocket.c.

1161{
1162 struct ast_sockaddr *addr;
1165
1166 if (!args) {
1168 return NULL;
1169 }
1170
1171 args->accept_fd = -1;
1172 args->tls_cfg = tls_cfg;
1173 args->name = "websocket client";
1174
1175 if (!ast_sockaddr_resolve(&addr, host, 0, 0)) {
1176 ast_log(LOG_ERROR, "Unable to resolve address %s\n",
1177 host);
1178 ao2_ref(args, -1);
1180 return NULL;
1181 }
1182 ast_sockaddr_copy(&args->remote_address, addr);
1183 ast_free(addr);
1184
1185 /* We need to save off the hostname but it may contain a port spec */
1186 snprintf(args->hostname, sizeof(args->hostname),
1187 "%.*s",
1188 (int) strcspn(host, ":"), host);
1189
1190 return args;
1191}
@ WS_ALLOCATE_ERROR
@ WS_URI_RESOLVE_ERROR
int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str, int flags, int family)
Parses a string with an IPv4 or IPv6 address and place results into an array.
Definition: netsock2.c:280
static void websocket_client_args_destroy(void *obj)
Socket address structure.
Definition: netsock2.h:97
arguments for the accepting thread
Definition: tcptls.h:130
struct ast_tls_config * tls_cfg
Definition: tcptls.h:135
const char * args

References ao2_alloc, ao2_ref, args, ast_free, ast_log, ast_sockaddr_copy(), ast_sockaddr_resolve(), LOG_ERROR, NULL, result, ast_tcptls_session_args::tls_cfg, websocket_client_args_destroy(), WS_ALLOCATE_ERROR, and WS_URI_RESOLVE_ERROR.

Referenced by websocket_client_create().

◆ websocket_client_args_destroy()

static void websocket_client_args_destroy ( void *  obj)
static

Definition at line 1142 of file res_http_websocket.c.

1143{
1144 struct ast_tcptls_session_args *args = obj;
1145
1146 if (args->tls_cfg) {
1147 ast_free(args->tls_cfg->certfile);
1148 ast_free(args->tls_cfg->pvtfile);
1149 ast_free(args->tls_cfg->cipher);
1150 ast_free(args->tls_cfg->cafile);
1151 ast_free(args->tls_cfg->capath);
1152
1153 ast_ssl_teardown(args->tls_cfg);
1154 }
1155 ast_free(args->tls_cfg);
1156}
void ast_ssl_teardown(struct ast_tls_config *cfg)
free resources used by an SSL server
Definition: tcptls.c:582

References args, ast_free, and ast_ssl_teardown().

Referenced by websocket_client_args_create().

◆ websocket_client_connect()

static enum ast_websocket_result websocket_client_connect ( struct ast_websocket ws,
int  timeout 
)
static

Definition at line 1492 of file res_http_websocket.c.

1493{
1494 enum ast_websocket_result res;
1495 /* create and connect the client - note client_start
1496 releases the session instance on failure */
1498 ast_tcptls_client_create(ws->client->args), timeout))) {
1499 return WS_CLIENT_START_ERROR;
1500 }
1501
1502 if ((res = websocket_client_handshake(ws->client)) != WS_OK) {
1503 ao2_ref(ws->client->ser, -1);
1504 ws->client->ser = NULL;
1505 return res;
1506 }
1507
1508 ws->stream = ws->client->ser->stream;
1509 ws->secure = ast_iostream_get_ssl(ws->stream) ? 1 : 0;
1510 ws->client->ser->stream = NULL;
1512 return WS_OK;
1513}
ast_websocket_result
Result code for a websocket client.
@ WS_CLIENT_START_ERROR
static enum ast_websocket_result websocket_client_handshake(struct websocket_client *client)
struct ast_iostream * stream
struct ast_sockaddr remote_address
unsigned int secure
struct ast_tcptls_session_args * args
struct ast_tcptls_session_instance * ser
struct ast_tcptls_session_instance * ast_tcptls_client_create(struct ast_tcptls_session_args *desc)
Creates a client connection's ast_tcptls_session_instance.
Definition: tcptls.c:686
struct ast_tcptls_session_instance * ast_tcptls_client_start_timeout(struct ast_tcptls_session_instance *tcptls_session, int timeout)
Attempt to connect and start a tcptls session within the given timeout.
Definition: tcptls.c:652

References ao2_ref, websocket_client::args, ast_iostream_get_ssl(), ast_sockaddr_copy(), ast_tcptls_client_create(), ast_tcptls_client_start_timeout(), ast_websocket::client, NULL, ast_tcptls_session_instance::remote_address, ast_websocket::remote_address, ast_websocket::secure, websocket_client::ser, ast_tcptls_session_instance::stream, ast_websocket::stream, websocket_client_handshake(), WS_CLIENT_START_ERROR, and WS_OK.

Referenced by ast_websocket_client_create_with_options().

◆ websocket_client_create()

static struct ast_websocket * websocket_client_create ( struct ast_websocket_client_options options,
enum ast_websocket_result result 
)
static

Definition at line 1254 of file res_http_websocket.c.

1256{
1257 struct ast_websocket *ws = ao2_alloc(sizeof(*ws), session_destroy_fn);
1258
1259 if (!ws) {
1260 ast_log(LOG_ERROR, "Unable to allocate websocket\n");
1262 return NULL;
1263 }
1264
1265 if (!ast_uuid_generate_str(ws->session_id, sizeof(ws->session_id))) {
1266 ast_log(LOG_ERROR, "Unable to allocate websocket session_id\n");
1267 ao2_ref(ws, -1);
1269 return NULL;
1270 }
1271
1272 if (!(ws->client = ao2_alloc(
1273 sizeof(*ws->client), websocket_client_destroy))) {
1274 ast_log(LOG_ERROR, "Unable to allocate websocket client\n");
1275 ao2_ref(ws, -1);
1277 return NULL;
1278 }
1279
1280 if (!(ws->client->key = websocket_client_create_key())) {
1281 ao2_ref(ws, -1);
1283 return NULL;
1284 }
1285
1287 options->uri, &ws->client->host, &ws->client->resource_name,
1288 &ws->client->userinfo)) {
1289 ao2_ref(ws, -1);
1291 return NULL;
1292 }
1293
1295 && !ast_strlen_zero(options->username)
1296 && !ast_strlen_zero(options->password)) {
1297 ast_asprintf(&ws->client->userinfo, "%s:%s", options->username,
1298 options->password);
1299 }
1300
1302 ws->client->host, options->tls_cfg, result))) {
1303 ao2_ref(ws, -1);
1304 return NULL;
1305 }
1306
1307 ws->client->suppress_connection_msgs = options->suppress_connection_msgs;
1308 ws->client->args->suppress_connection_msgs = options->suppress_connection_msgs;
1309 ws->client->protocols = ast_strdup(options->protocols);
1310 ws->client->version = 13;
1311 ws->opcode = -1;
1313 ws->timeout = options->timeout;
1314
1315 return ws;
1316}
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
@ WS_KEY_ERROR
@ WS_URI_PARSE_ERROR
static int websocket_client_parse_uri(const char *uri, char **host, struct ast_str **path, char **userinfo)
Parse the given uri into a path and remote address.
static void websocket_client_destroy(void *obj)
static char * websocket_client_create_key(void)
static struct ast_tcptls_session_args * websocket_client_args_create(const char *host, struct ast_tls_config *tls_cfg, enum ast_websocket_result *result)
char session_id[AST_UUID_STR_LEN]
enum ast_websocket_opcode opcode
struct ast_str * resource_name

References ao2_alloc, ao2_ref, websocket_client::args, ast_asprintf, ast_log, ast_strdup, ast_strlen_zero(), ast_uuid_generate_str(), ast_websocket::client, DEFAULT_RECONSTRUCTION_CEILING, websocket_client::host, websocket_client::key, LOG_ERROR, NULL, ast_websocket::opcode, options, websocket_client::protocols, ast_websocket::reconstruct, websocket_client::resource_name, result, session_destroy_fn(), ast_websocket::session_id, ast_tcptls_session_args::suppress_connection_msgs, websocket_client::suppress_connection_msgs, ast_websocket::timeout, websocket_client::userinfo, websocket_client::version, websocket_client_args_create(), websocket_client_create_key(), websocket_client_destroy(), websocket_client_parse_uri(), WS_ALLOCATE_ERROR, WS_KEY_ERROR, and WS_URI_PARSE_ERROR.

Referenced by ast_websocket_client_create_with_options().

◆ websocket_client_create_key()

static char * websocket_client_create_key ( void  )
static

Definition at line 1193 of file res_http_websocket.c.

1194{
1195 static int encoded_size = CLIENT_KEY_SIZE * 2 * sizeof(char) + 1;
1196 /* key is randomly selected 16-byte base64 encoded value */
1197 unsigned char key[CLIENT_KEY_SIZE + sizeof(long) - 1];
1198 char *encoded = ast_malloc(encoded_size);
1199 long i = 0;
1200
1201 if (!encoded) {
1202 ast_log(LOG_ERROR, "Unable to allocate client websocket key\n");
1203 return NULL;
1204 }
1205
1206 while (i < CLIENT_KEY_SIZE) {
1207 long num = ast_random();
1208 memcpy(key + i, &num, sizeof(long));
1209 i += sizeof(long);
1210 }
1211
1212 ast_base64encode(encoded, key, CLIENT_KEY_SIZE, encoded_size);
1213 return encoded;
1214}
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define CLIENT_KEY_SIZE
Length of a websocket's client key.
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
Definition: utils.c:406
long int ast_random(void)
Definition: utils.c:2348

References ast_base64encode(), ast_log, ast_malloc, ast_random(), CLIENT_KEY_SIZE, LOG_ERROR, and NULL.

Referenced by websocket_client_create().

◆ websocket_client_destroy()

static void websocket_client_destroy ( void *  obj)
static

◆ websocket_client_handle_response_code()

static enum ast_websocket_result websocket_client_handle_response_code ( struct websocket_client client,
int  response_code 
)
static

Definition at line 1324 of file res_http_websocket.c.

1326{
1327 if (response_code <= 0) {
1328 return WS_INVALID_RESPONSE;
1329 }
1330
1331 switch (response_code) {
1332 case 101:
1333 return 0;
1334 case 400:
1335 if (!client->suppress_connection_msgs) {
1336 ast_log(LOG_ERROR, "Received response 400 - Bad Request "
1337 "- from %s\n", client->host);
1338 }
1339 return WS_BAD_REQUEST;
1340 case 401:
1341 if (!client->suppress_connection_msgs) {
1342 ast_log(LOG_ERROR, "Received response 401 - Unauthorized "
1343 "- from %s\n", client->host);
1344 }
1345 return WS_UNAUTHORIZED;
1346 case 404:
1347 if (!client->suppress_connection_msgs) {
1348 ast_log(LOG_ERROR, "Received response 404 - Request URL not "
1349 "found - from %s\n", client->host);
1350 }
1351 return WS_URL_NOT_FOUND;
1352 }
1353
1354 if (!client->suppress_connection_msgs) {
1355 ast_log(LOG_ERROR, "Invalid HTTP response code %d from %s\n",
1356 response_code, client->host);
1357 }
1358 return WS_INVALID_RESPONSE;
1359}
@ WS_BAD_REQUEST
@ WS_URL_NOT_FOUND
@ WS_UNAUTHORIZED
@ WS_INVALID_RESPONSE

References ast_log, ast_websocket::client, websocket_client::host, LOG_ERROR, websocket_client::suppress_connection_msgs, WS_BAD_REQUEST, WS_INVALID_RESPONSE, WS_UNAUTHORIZED, and WS_URL_NOT_FOUND.

Referenced by websocket_client_handshake_get_response().

◆ websocket_client_handshake()

static enum ast_websocket_result websocket_client_handshake ( struct websocket_client client)
static

Definition at line 1448 of file res_http_websocket.c.

1450{
1451 size_t protocols_len = 0;
1452 struct ast_variable *auth_header = NULL;
1453 size_t res;
1454
1455 if (!ast_strlen_zero(client->userinfo)) {
1456 auth_header = ast_http_create_basic_auth_header(client->userinfo, NULL);
1457 if (!auth_header) {
1458 ast_log(LOG_ERROR, "Unable to allocate client websocket userinfo\n");
1459 return WS_ALLOCATE_ERROR;
1460 }
1461 }
1462
1463 protocols_len = client->protocols ? strlen(client->protocols) : 0;
1464
1465 res = ast_iostream_printf(client->ser->stream,
1466 "GET /%s HTTP/1.1\r\n"
1467 "Sec-WebSocket-Version: %d\r\n"
1468 "Upgrade: websocket\r\n"
1469 "Connection: Upgrade\r\n"
1470 "Host: %s\r\n"
1473 "Sec-WebSocket-Key: %s\r\n"
1474 "\r\n",
1475 client->resource_name ? ast_str_buffer(client->resource_name) : "",
1476 client->version,
1477 client->host,
1478 print_optional_header(auth_header, "Authorization: ", auth_header->value),
1479 print_optional_header(protocols_len, "Sec-WebSocket-Protocol: ", client->protocols),
1480 client->key
1481 );
1482
1483 ast_variables_destroy(auth_header);
1484 if (res < 0) {
1485 ast_log(LOG_ERROR, "Failed to send handshake.\n");
1486 return WS_WRITE_ERROR;
1487 }
1488 /* wait for a response before doing anything else */
1490}
struct ast_variable * ast_http_create_basic_auth_header(const char *userid, const char *password)
Create an HTTP authorization header.
Definition: http.c:1668
@ WS_WRITE_ERROR
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
static enum ast_websocket_result websocket_client_handshake_get_response(struct websocket_client *client)
#define optional_header_spec
#define print_optional_header(test, name, value)
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761

References ast_http_create_basic_auth_header(), ast_iostream_printf(), ast_log, ast_str_buffer(), ast_strlen_zero(), ast_variables_destroy(), websocket_client::host, websocket_client::key, LOG_ERROR, NULL, optional_header_spec, print_optional_header, websocket_client::protocols, websocket_client::resource_name, websocket_client::ser, ast_tcptls_session_instance::stream, websocket_client::userinfo, ast_variable::value, websocket_client::version, websocket_client_handshake_get_response(), WS_ALLOCATE_ERROR, and WS_WRITE_ERROR.

Referenced by websocket_client_connect().

◆ websocket_client_handshake_get_response()

static enum ast_websocket_result websocket_client_handshake_get_response ( struct websocket_client client)
static

Definition at line 1361 of file res_http_websocket.c.

1363{
1364 enum ast_websocket_result res;
1365 char buf[4096];
1366 char base64[64];
1367 int has_upgrade = 0;
1368 int has_connection = 0;
1369 int has_accept = 0;
1370 int has_protocol = 0;
1371
1372 while (ast_iostream_gets(client->ser->stream, buf, sizeof(buf)) <= 0) {
1373 if (errno == EINTR || errno == EAGAIN) {
1374 continue;
1375 }
1376
1377 ast_log(LOG_ERROR, "Unable to retrieve HTTP status line.");
1378 return WS_BAD_STATUS;
1379 }
1380
1381 if ((res = websocket_client_handle_response_code(client,
1383 buf, "HTTP/1.1", 101))) != WS_OK) {
1384 return res;
1385 }
1386
1387 /* Ignoring line folding - assuming header field values are contained
1388 within a single line */
1389 while (1) {
1390 ssize_t len = ast_iostream_gets(client->ser->stream, buf, sizeof(buf));
1391 char *name, *value;
1392 int parsed;
1393
1394 if (len <= 0) {
1395 if (errno == EINTR || errno == EAGAIN) {
1396 continue;
1397 }
1398 break;
1399 }
1400
1401 parsed = ast_http_header_parse(buf, &name, &value);
1402 if (parsed < 0) {
1403 break;
1404 }
1405
1406 if (parsed > 0) {
1407 continue;
1408 }
1409
1410 if (!has_upgrade &&
1411 (has_upgrade = ast_http_header_match(
1412 name, "upgrade", value, "websocket")) < 0) {
1413 return WS_HEADER_MISMATCH;
1414 } else if (!has_connection &&
1415 (has_connection = ast_http_header_match(
1416 name, "connection", value, "upgrade")) < 0) {
1417 return WS_HEADER_MISMATCH;
1418 } else if (!has_accept &&
1419 (has_accept = ast_http_header_match(
1420 name, "sec-websocket-accept", value,
1422 client->key, base64, sizeof(base64)))) < 0) {
1423 return WS_HEADER_MISMATCH;
1424 } else if (!has_protocol &&
1425 (has_protocol = ast_http_header_match_in(
1426 name, "sec-websocket-protocol", value, client->protocols))) {
1427 if (has_protocol < 0) {
1428 return WS_HEADER_MISMATCH;
1429 }
1430 client->accept_protocol = ast_strdup(value);
1431 } else if (!strcasecmp(name, "sec-websocket-extensions")) {
1432 ast_log(LOG_ERROR, "Extensions received, but not "
1433 "supported by client\n");
1434 return WS_NOT_SUPPORTED;
1435 }
1436 }
1437
1438 return has_upgrade && has_connection && has_accept ?
1440}
int ast_http_header_parse(char *buf, char **name, char **value)
Parse a header into the given name/value strings.
Definition: http.c:1764
int ast_http_header_match_in(const char *name, const char *expected_name, const char *value, const char *expected_value)
Check if the header name matches the expected header name. If so, then check to see if the value can ...
Definition: http.c:1802
int ast_http_header_match(const char *name, const char *expected_name, const char *value, const char *expected_value)
Check if the header and value match (case insensitive) their associated expected values.
Definition: http.c:1786
int ast_http_response_status_line(const char *buf, const char *version, int code)
Parse the http response status line.
Definition: http.c:1712
@ WS_NOT_SUPPORTED
@ WS_HEADER_MISMATCH
@ WS_BAD_STATUS
@ WS_HEADER_MISSING
ssize_t ast_iostream_gets(struct ast_iostream *stream, char *buffer, size_t size)
Read a LF-terminated string from an iostream.
Definition: iostream.c:311
int errno
static enum ast_websocket_result websocket_client_handle_response_code(struct websocket_client *client, int response_code)

References websocket_client::accept_protocol, ast_http_header_match(), ast_http_header_match_in(), ast_http_header_parse(), ast_http_response_status_line(), ast_iostream_gets(), ast_log, ast_strdup, base64, buf, ast_websocket::client, errno, websocket_client::key, len(), LOG_ERROR, name, websocket_client::protocols, websocket_client::ser, ast_tcptls_session_instance::stream, value, websocket_client_handle_response_code(), websocket_combine_key(), WS_BAD_STATUS, WS_HEADER_MISMATCH, WS_HEADER_MISSING, WS_NOT_SUPPORTED, and WS_OK.

Referenced by websocket_client_handshake().

◆ websocket_client_parse_uri()

static int websocket_client_parse_uri ( const char *  uri,
char **  host,
struct ast_str **  path,
char **  userinfo 
)
static

Parse the given uri into a path and remote address.

Expected uri form:

[ws[s]]://<host>[:port][/<path>] 

The returned host will contain the address and optional port while path will contain everything after the address/port if included.

Definition at line 1110 of file res_http_websocket.c.

1112{
1113 struct ast_uri *parsed_uri = ast_uri_parse_websocket(uri);
1114
1115 if (!parsed_uri) {
1116 return -1;
1117 }
1118
1119 *host = ast_uri_make_host_with_port(parsed_uri);
1120 *userinfo = ast_strdup(ast_uri_user_info(parsed_uri));
1121
1122 if (ast_uri_path(parsed_uri) || ast_uri_query(parsed_uri)) {
1123 *path = ast_str_create(64);
1124 if (!*path) {
1125 ao2_ref(parsed_uri, -1);
1126 return -1;
1127 }
1128
1129 if (ast_uri_path(parsed_uri)) {
1130 ast_str_set(path, 0, "%s", ast_uri_path(parsed_uri));
1131 }
1132
1133 if (ast_uri_query(parsed_uri)) {
1134 ast_str_append(path, 0, "?%s", ast_uri_query(parsed_uri));
1135 }
1136 }
1137
1138 ao2_ref(parsed_uri, -1);
1139 return 0;
1140}
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
Stores parsed uri information.
Definition: uri.c:30
char * host
Definition: uri.c:36
char * path
Definition: uri.c:40
char uri[0]
Definition: uri.c:44
const char * ast_uri_path(const struct ast_uri *uri)
Retrieve the uri path.
Definition: uri.c:135
struct ast_uri * ast_uri_parse_websocket(const char *uri)
Parse the given websocket uri into a structure.
Definition: uri.c:295
const char * ast_uri_query(const struct ast_uri *uri)
Retrieve the uri query parameters.
Definition: uri.c:140
const char * ast_uri_user_info(const struct ast_uri *uri)
Retrieve the uri user information.
Definition: uri.c:120
char * ast_uri_make_host_with_port(const struct ast_uri *uri)
Retrieve a string of the host and port.
Definition: uri.c:300

References ao2_ref, ast_str_append(), ast_str_create, ast_str_set(), ast_strdup, ast_uri_make_host_with_port(), ast_uri_parse_websocket(), ast_uri_path(), ast_uri_query(), ast_uri_user_info(), ast_uri::host, ast_uri::path, and ast_uri::uri.

Referenced by websocket_client_create().

◆ websocket_combine_key()

static char * websocket_combine_key ( const char *  key,
char *  res,
int  res_size 
)
static

Definition at line 780 of file res_http_websocket.c.

781{
782 char *combined;
783 unsigned combined_length = strlen(key) + strlen(WEBSOCKET_GUID) + 1;
784 uint8_t sha[20];
785
786 combined = ast_alloca(combined_length);
787 snprintf(combined, combined_length, "%s%s", key, WEBSOCKET_GUID);
788 ast_sha1_hash_uint(sha, combined);
789 ast_base64encode(res, (const unsigned char*)sha, 20, res_size);
790 return res;
791}
void ast_sha1_hash_uint(uint8_t *digest, const char *input)
Produces SHA1 hash based on input string, stored in uint8_t array.
Definition: utils.c:284

References ast_alloca, ast_base64encode(), ast_sha1_hash_uint(), and WEBSOCKET_GUID.

Referenced by ast_websocket_uri_cb(), and websocket_client_handshake_get_response().

◆ websocket_echo_callback()

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

Simple echo implementation which echoes received text and binary frames.

Definition at line 1023 of file res_http_websocket.c.

1024{
1025 int res;
1026
1027 ast_debug(1, "Entering WebSocket echo loop\n");
1028
1029 if (ast_fd_set_flags(ast_websocket_fd(session), O_NONBLOCK)) {
1030 goto end;
1031 }
1032
1033 while ((res = ast_websocket_wait_for_input(session, -1)) > 0) {
1034 char *payload;
1035 uint64_t payload_len;
1036 enum ast_websocket_opcode opcode;
1037 int fragmented;
1038
1039 if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) {
1040 /* We err on the side of caution and terminate the session if any error occurs */
1041 ast_log(LOG_WARNING, "Read failure during WebSocket echo loop\n");
1042 break;
1043 }
1044
1045 if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
1046 ast_websocket_write(session, opcode, payload, payload_len);
1047 } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
1048 break;
1049 } else {
1050 ast_debug(1, "Ignored WebSocket opcode %u\n", opcode);
1051 }
1052 }
1053
1054end:
1055 ast_debug(1, "Exiting WebSocket echo loop\n");
1057}
char * end
Definition: eagi_proxy.c:73
int AST_OPTIONAL_API_NAME() ast_websocket_wait_for_input(struct ast_websocket *session, int timeout)
Wait for the WebSocket session to be ready to be read.
int AST_OPTIONAL_API_NAME() ast_websocket_fd(struct ast_websocket *session)
Get the file descriptor for a WebSocket session.
void AST_OPTIONAL_API_NAME() ast_websocket_unref(struct ast_websocket *session)
Decrease the reference count for a WebSocket session.
#define ast_fd_set_flags(fd, flags)
Set flags on the given file descriptor.
Definition: utils.h:1076

References ast_debug, ast_fd_set_flags, ast_log, ast_websocket_fd(), AST_WEBSOCKET_OPCODE_BINARY, AST_WEBSOCKET_OPCODE_CLOSE, AST_WEBSOCKET_OPCODE_TEXT, ast_websocket_read(), ast_websocket_unref(), ast_websocket_wait_for_input(), ast_websocket_write(), end, LOG_WARNING, and session.

Referenced by load_module(), and unload_module().

◆ websocket_mask_payload()

static void websocket_mask_payload ( struct ast_websocket session,
char *  frame,
char *  payload,
uint64_t  payload_size 
)
static

Perform payload masking for client sessions.

Definition at line 304 of file res_http_websocket.c.

305{
306 /* RFC 6455 5.1 - clients MUST mask frame data */
307 if (session->client) {
308 uint64_t i;
309 uint8_t mask_key_idx;
310 uint32_t mask_key = ast_random();
311 uint8_t length = frame[1] & 0x7f;
312 frame[1] |= 0x80; /* set mask bit to 1 */
313 /* The mask key octet position depends on the length */
314 mask_key_idx = length == 126 ? 4 : length == 127 ? 10 : 2;
315 put_unaligned_uint32(&frame[mask_key_idx], mask_key);
316 for (i = 0; i < payload_size; i++) {
317 payload[i] ^= ((char *)&mask_key)[i % 4];
318 }
319 }
320}
static void put_unaligned_uint32(void *p, unsigned int datum)
Definition: unaligned.h:58

References ast_random(), put_unaligned_uint32(), and session.

Referenced by ast_websocket_close(), and ast_websocket_write().

◆ websocket_opcode2str()

static const char * websocket_opcode2str ( enum ast_websocket_opcode  opcode)
static

Definition at line 379 of file res_http_websocket.c.

380{
382 opcode > AST_WEBSOCKET_OPCODE_PONG) {
383 return "<unknown>";
384 } else {
385 return opcode_map[opcode];
386 }
387}
static const char * opcode_map[]

References AST_WEBSOCKET_OPCODE_CONTINUATION, AST_WEBSOCKET_OPCODE_PONG, ast_websocket::opcode, and opcode_map.

Referenced by ast_websocket_write().

◆ websocket_remove_protocol_internal()

static int websocket_remove_protocol_internal ( const char *  name,
ast_websocket_callback  callback 
)
static

Definition at line 1088 of file res_http_websocket.c.

1089{
1090 struct ast_websocket_server *ws_server = websocketuri.data;
1091 if (!ws_server) {
1092 return -1;
1093 }
1095}
int AST_OPTIONAL_API_NAME() ast_websocket_server_remove_protocol(struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
Remove a sub-protocol handler from the given server.

References ast_websocket_server_remove_protocol(), callback(), ast_http_uri::data, name, and websocketuri.

Referenced by ast_websocket_remove_protocol(), and unload_module().

◆ websocket_server_create_impl()

static struct ast_websocket_server * websocket_server_create_impl ( void  )
static

Definition at line 159 of file res_http_websocket.c.

160{
161 RAII_VAR(struct ast_websocket_server *, server, NULL, ao2_cleanup);
162
163 server = ao2_alloc(sizeof(*server), websocket_server_dtor);
164 if (!server) {
165 return NULL;
166 }
167
170 if (!server->protocols) {
171 return NULL;
172 }
173
174 ao2_ref(server, +1);
175 return server;
176}
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
#define MAX_PROTOCOL_BUCKETS
Number of buckets for registered protocols.
static void websocket_server_dtor(void *obj)
static int protocol_cmp_fn(void *obj, void *arg, int flags)
Comparison function for protocols.
static int protocol_hash_fn(const void *obj, const int flags)
Hashing function for protocols.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:978

References ao2_alloc, AO2_ALLOC_OPT_LOCK_MUTEX, ao2_cleanup, ao2_container_alloc_hash, ao2_ref, MAX_PROTOCOL_BUCKETS, NULL, protocol_cmp_fn(), protocol_hash_fn(), RAII_VAR, and websocket_server_dtor().

Referenced by ast_websocket_server_create(), and websocket_server_internal_create().

◆ websocket_server_dtor()

static void websocket_server_dtor ( void *  obj)
static

Definition at line 152 of file res_http_websocket.c.

153{
154 struct ast_websocket_server *server = obj;
155 ao2_cleanup(server->protocols);
156 server->protocols = NULL;
157}

References ao2_cleanup, NULL, and ast_websocket_server::protocols.

Referenced by websocket_server_create_impl().

◆ websocket_server_internal_create()

static struct ast_websocket_server * websocket_server_internal_create ( void  )
static

Definition at line 178 of file res_http_websocket.c.

179{
181}

References websocket_server_create_impl().

Referenced by load_module().

◆ ws_safe_read()

static int ws_safe_read ( struct ast_websocket session,
char *  buf,
size_t  len,
enum ast_websocket_opcode opcode 
)
inlinestatic

Definition at line 550 of file res_http_websocket.c.

551{
552 ssize_t rlen;
553 int xlen = len;
554 char *rbuf = buf;
555 int sanity = 10;
556
557 ast_assert(len > 0);
558
559 if (!len) {
560 errno = EINVAL;
561 return -1;
562 }
563
565 if (!session->stream) {
567 errno = ECONNABORTED;
568 return -1;
569 }
570
571 for (;;) {
572 rlen = ast_iostream_read(session->stream, rbuf, xlen);
573 if (rlen != xlen) {
574 if (rlen == 0) {
575 ast_log(LOG_WARNING, "Web socket closed abruptly\n");
577 session->closing = 1;
579 return -1;
580 }
581
582 if (rlen < 0 && errno != EAGAIN) {
583 ast_log(LOG_ERROR, "Error reading from web socket: %s\n", strerror(errno));
585 session->closing = 1;
587 return -1;
588 }
589
590 if (!--sanity) {
591 ast_log(LOG_WARNING, "Websocket seems unresponsive, disconnecting ...\n");
593 session->closing = 1;
595 return -1;
596 }
597 }
598 if (rlen > 0) {
599 xlen = xlen - rlen;
600 rbuf = rbuf + rlen;
601 if (!xlen) {
602 break;
603 }
604 }
605 if (ast_iostream_wait_for_input(session->stream, 1000) < 0) {
606 ast_log(LOG_ERROR, "ast_iostream_wait_for_input returned err: %s\n", strerror(errno));
608 session->closing = 1;
610 return -1;
611 }
612 }
613
615 return 0;
616}
ssize_t ast_iostream_read(struct ast_iostream *stream, void *buffer, size_t count)
Read data from an iostream.
Definition: iostream.c:284
#define ast_assert(a)
Definition: utils.h:776

References ao2_lock, ao2_unlock, ast_assert, ast_iostream_read(), ast_iostream_wait_for_input(), ast_log, AST_WEBSOCKET_OPCODE_CLOSE, buf, errno, len(), LOG_ERROR, LOG_WARNING, ast_websocket::opcode, and session.

Referenced by ast_websocket_read().

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "HTTP WebSocket 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_CHANNEL_DEPEND, .requires = "http", }
static

Definition at line 1663 of file res_http_websocket.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1663 of file res_http_websocket.c.

◆ opcode_map

const char* opcode_map[]
static

Definition at line 370 of file res_http_websocket.c.

Referenced by websocket_opcode2str().

◆ websocket_result_string_map

const char* websocket_result_string_map[]

Definition at line 1608 of file res_http_websocket.c.

Referenced by ast_websocket_result_to_str().

◆ websocketuri

struct ast_http_uri websocketuri
static