Asterisk - The Open Source Telephony Project GIT-master-6144b6b
Loading...
Searching...
No Matches
Data Structures | Macros | Enumerations | 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/sched.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  status_map
 
struct  websocket_client
 

Macros

#define ARE_PINGPONGS_ENABLED(_session)   (_session && _session->client && _session->client->options->pingpongs && _session->client->ping_sched_timer >= 0)
 
#define AST_API_MODULE
 
#define CLIENT_KEY_SIZE   16
 Length of a websocket's client key.
 
#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.
 
#define MAX_PROTOCOL_BUCKETS   7
 Number of buckets for registered protocols.
 
#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 ...
 
#define MAXIMUM_RECONSTRUCTION_CEILING   AST_WEBSOCKET_MAX_RX_PAYLOAD_SIZE
 Maximum reconstruction size for multi-frame payload reconstruction.
 
#define MIN_WS_HDR_SZ   2
 
#define optional_header_spec   "%s%s%s"
 
#define print_optional_header(test, name, value)
 
#define SAFE_STRDUP_WITH_ERROR_RTN(_clone, _str)
 
#define WEBSOCKET_GUID   "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
 GUID used to compute the accept key, defined in the specifications.
 
#define WS_PING_PAYLOAD   "WS_CLIENT_PING"
 WS_PING_PAYLOAD It's possible that a user of this API could be sending their own PINGs and expecting to see PONGs so we use the PING_PAYLOAD in the PINGs we send so we can detect that any PONGs we receive are from our own PINGs.
 
#define WS_PING_PAYLOAD_LEN   14
 
#define WS_SESSION_REMOTE(_session)   (_session ? (_session->client ? _session->client->options->uri : ast_sockaddr_stringify(&_session->remote_address)) : "NULL")
 

Enumerations

enum  ws_closed_by { WS_NOT_CLOSED = 0 , WS_CLOSED_BY_REMOTE , WS_CLOSED_BY_US }
 

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.
 
int AST_OPTIONAL_API_NAME() ast_websocket_add_protocol2 (struct ast_websocket_protocol *protocol)
 Add a sub-protocol handler to the default /ws server.
 
const char *AST_OPTIONAL_API_NAME() ast_websocket_client_accept_protocol (struct ast_websocket *ws)
 Retrieve the server accepted sub-protocol on the client.
 
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.
 
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.
 
int AST_OPTIONAL_API_NAME() ast_websocket_close (struct ast_websocket *session, uint16_t reason)
 Close function for websocket session.
 
int AST_OPTIONAL_API_NAME() ast_websocket_fd (struct ast_websocket *session)
 Get the file descriptor for a WebSocket session.
 
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.
 
struct ast_sockaddr *AST_OPTIONAL_API_NAME() ast_websocket_local_address (struct ast_websocket *session)
 Get the local address for a WebSocket connection session.
 
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.
 
int AST_OPTIONAL_API_NAME() ast_websocket_read_string (struct ast_websocket *ws, char **buf)
 Read a WebSocket frame containing string data.
 
void AST_OPTIONAL_API_NAME() ast_websocket_reconstruct_disable (struct ast_websocket *session)
 Disable multi-frame reconstruction.
 
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.
 
void AST_OPTIONAL_API_NAME() ast_websocket_ref (struct ast_websocket *session)
 Increase the reference count for a WebSocket session.
 
struct ast_sockaddr *AST_OPTIONAL_API_NAME() ast_websocket_remote_address (struct ast_websocket *session)
 Get the remote address for a WebSocket connected session.
 
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.
 
const char *AST_OPTIONAL_API_NAME() ast_websocket_result_to_str (enum ast_websocket_result result)
 Convert a websocket result code to a string.
 
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.
 
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.
 
struct ast_websocket_server *AST_OPTIONAL_API_NAME() ast_websocket_server_create (void)
 Creates a ast_websocket_server.
 
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.
 
const char *AST_OPTIONAL_API_NAME() ast_websocket_session_id (struct ast_websocket *session)
 Get the session ID for a WebSocket session.
 
int AST_OPTIONAL_API_NAME() ast_websocket_set_nonblock (struct ast_websocket *session)
 Set the socket of a WebSocket session to be non-blocking.
 
int AST_OPTIONAL_API_NAME() ast_websocket_set_timeout (struct ast_websocket *session, int timeout)
 Set the timeout on a non-blocking WebSocket session.
 
const char *AST_OPTIONAL_API_NAME() ast_websocket_status_to_str (enum ast_websocket_status_code code)
 Convert a websocket status code to a string.
 
struct ast_websocket_protocol *AST_OPTIONAL_API_NAME() ast_websocket_sub_protocol_alloc (const char *name)
 Allocate a websocket sub-protocol instance.
 
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.
 
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.
 
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_write (struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t payload_size)
 Write function for websocket traffic.
 
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.
 
static struct ast_websocket_client_optionsclient_options_clone (struct ast_websocket_client_options *options)
 
static void client_options_destroy (void *obj)
 
static const char * closed_by_to_str (enum ws_closed_by closed_by)
 
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.
 
static int ping_scheduler_callback (const void *obj)
 
static void ping_scheduler_cancel (struct ast_websocket *session)
 
static int protocol_cmp_fn (void *obj, void *arg, int flags)
 Comparison function for protocols.
 
static void protocol_destroy_fn (void *obj)
 Destructor function for protocols.
 
static int protocol_hash_fn (const void *obj, const int flags)
 Hashing function for protocols.
 
static void session_destroy_fn (void *obj)
 Destructor function for sessions.
 
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 (struct ast_websocket *ws, struct ast_websocket_client_options *options, 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, struct ast_websocket_client_options *options)
 
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, int proxy)
 
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, int proxy)
 
static int websocket_client_parse_uri (const char *uri, char **host, struct ast_str **path, char **userinfo, int proxy)
 Parse the given uri into a path and remote address.
 
static void websocket_client_start_handshake_timer (struct websocket_client *client)
 
static void websocket_client_stop_handshake_timer (struct websocket_client *client)
 
static int websocket_close (struct ast_websocket *session, uint16_t reason, int force)
 
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.
 
static int websocket_handled_pong_or_close (struct ast_websocket *session, char *payload, uint64_t payload_len, enum ast_websocket_opcode opcode)
 
static void websocket_mask_payload (struct ast_websocket *session, char *frame, char *payload, uint64_t payload_size)
 Perform payload masking for client sessions.
 
static const char * websocket_opcode2str (enum ast_websocket_opcode opcode)
 
static enum ast_websocket_result websocket_proxy_handshake (struct websocket_client *client)
 
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 = ASTERISK_GPL_KEY , .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 * closed_by_str []
 
static const char * opcode_map []
 
static struct ast_sched_contextping_scheduler
 
const char * websocket_result_string_map []
 
static const struct status_map websocket_status_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

◆ ARE_PINGPONGS_ENABLED

#define ARE_PINGPONGS_ENABLED (   _session)    (_session && _session->client && _session->client->options->pingpongs && _session->client->ping_sched_timer >= 0)

Definition at line 151 of file res_http_websocket.c.

◆ AST_API_MODULE

#define AST_API_MODULE

Definition at line 42 of file res_http_websocket.c.

◆ CLIENT_KEY_SIZE

#define CLIENT_KEY_SIZE   16

Length of a websocket's client key.

Definition at line 49 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 66 of file res_http_websocket.c.

◆ MAX_PROTOCOL_BUCKETS

#define MAX_PROTOCOL_BUCKETS   7

Number of buckets for registered protocols.

Definition at line 52 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 79 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 69 of file res_http_websocket.c.

◆ MIN_WS_HDR_SZ

#define MIN_WS_HDR_SZ   2

Definition at line 80 of file res_http_websocket.c.

◆ optional_header_spec

#define optional_header_spec   "%s%s%s"

Definition at line 1741 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 1742 of file res_http_websocket.c.

1743 : "", \
1744 test ? value : "", \
1745 test ? "\r\n" : ""

◆ SAFE_STRDUP_WITH_ERROR_RTN

#define SAFE_STRDUP_WITH_ERROR_RTN (   _clone,
  _str 
)

Definition at line 1428 of file res_http_websocket.c.

1429 { \
1430 char *_duped = NULL; \
1431 if (_str) { \
1432 _duped = ast_strdup(_str); \
1433 if (!_duped) { \
1434 ao2_cleanup(_clone); \
1435 return NULL; \
1436 } \
1437 } \
1438 _duped; \
1439})
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
#define NULL
Definition resample.c:96

◆ WEBSOCKET_GUID

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

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

Definition at line 46 of file res_http_websocket.c.

◆ WS_PING_PAYLOAD

#define WS_PING_PAYLOAD   "WS_CLIENT_PING"

WS_PING_PAYLOAD It's possible that a user of this API could be sending their own PINGs and expecting to see PONGs so we use the PING_PAYLOAD in the PINGs we send so we can detect that any PONGs we receive are from our own PINGs.

Definition at line 87 of file res_http_websocket.c.

◆ WS_PING_PAYLOAD_LEN

#define WS_PING_PAYLOAD_LEN   14

Definition at line 88 of file res_http_websocket.c.

◆ WS_SESSION_REMOTE

#define WS_SESSION_REMOTE (   _session)    (_session ? (_session->client ? _session->client->options->uri : ast_sockaddr_stringify(&_session->remote_address)) : "NULL")

Definition at line 150 of file res_http_websocket.c.

Enumeration Type Documentation

◆ ws_closed_by

Enumerator
WS_NOT_CLOSED 
WS_CLOSED_BY_REMOTE 
WS_CLOSED_BY_US 

Definition at line 92 of file res_http_websocket.c.

92 {
93 WS_NOT_CLOSED = 0,
96};
@ WS_NOT_CLOSED
@ WS_CLOSED_BY_REMOTE
@ WS_CLOSED_BY_US

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 2219 of file res_http_websocket.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 2219 of file res_http_websocket.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 2219 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 1242 of file res_http_websocket.c.

1243{
1245}
static struct ast_channel * callback(struct ast_channelstorage_instance *driver, ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags, int rdlock)
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 1247 of file res_http_websocket.c.

1248{
1249 struct ast_websocket_server *ws_server = websocketuri.data;
1250
1251 if (!ws_server) {
1252 return -1;
1253 }
1254
1255 if (ast_websocket_server_add_protocol2(ws_server, protocol)) {
1256 return -1;
1257 }
1258
1259 return 0;
1260}
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 1563 of file res_http_websocket.c.

1565{
1566 return ws->client->accept_protocol;
1567}
struct websocket_client * client

References websocket_client::accept_protocol, and ast_websocket::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
Warning
The returned websocket must be closed with ast_websocket_close and its reference count decremented with ast_websocket_unref when it's no longer needed.

Definition at line 1914 of file res_http_websocket.c.

1917{
1919 .uri = uri,
1920 .protocols = protocols,
1921 .timeout = -1,
1922 .tls_cfg = tls_cfg,
1923 };
1924
1926}
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_TEST_DEFINE(), AST_TEST_DEFINE(), and 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
Warning
The returned websocket must be closed with ast_websocket_close and its reference count decremented with ast_websocket_unref when it's no longer needed.

Definition at line 2006 of file res_http_websocket.c.

2008{
2009 struct ast_websocket *ws = NULL;
2010 SCOPE_ENTER(2, "%s: Creating client\n", options->uri);
2011
2013 if (!ws) {
2014 SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Failed to create: %s\n", options->uri, ast_websocket_result_to_str(*result));
2015 }
2016
2017 ast_trace(-1, "%s: Connecting\n", ws->client->options->uri);
2018
2019 if ((*result = websocket_client_connect(ws, options)) != WS_OK) {
2020 ao2_ref(ws, -1);
2021 SCOPE_EXIT_RTN_VALUE(NULL, "%s: Failed to connect: %s\n", options->uri, ast_websocket_result_to_str(*result));
2022 }
2023 ast_trace(-1, "%s: Connected\n", ws->client->options->uri);
2024
2025 if (ws->client->options->tcp_keepalives) {
2026 int sockfd = ast_iostream_get_fd(ws->stream);
2027 int enabled = 1;
2028
2029 setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled));
2030 setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &options->tcp_keepalive_time, sizeof(options->tcp_keepalive_time));
2031 setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &options->tcp_keepalive_interval, sizeof(&options->tcp_keepalive_interval));
2032 setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &options->tcp_keepalive_probes, sizeof(&options->tcp_keepalive_probes));
2033 ast_trace(-1, "%s: Enabled TCP keepalives\n", ws->client->options->uri);
2034 }
2035
2036 if (ws->client->options->pingpongs) {
2039 if (ws->client->ping_sched_timer < 0) {
2040 ast_log(LOG_WARNING, "%s: Unable to schedule PING/PONG keepalives\n", ws->client->options->uri);
2041 ao2_ref(ws, -1);
2042 } else {
2043 ast_trace(-1, "%s: Enabled PING/PONG keepalives\n", ws->client->options->uri);
2044 }
2045 }
2046 SCOPE_EXIT_RTN_VALUE(ws, "%s: Client created and connected %p %p\n", options->uri, ws, ws->client);
2047}
#define ast_log
Definition astobj2.c:42
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition astobj2.h:459
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition astobj2.h:480
static int enabled
Definition dnsmgr.c:91
#define SCOPE_EXIT_RTN_VALUE(__return_value,...)
#define SCOPE_EXIT_LOG_RTN_VALUE(__value, __log_level,...)
#define SCOPE_ENTER(level,...)
#define ast_trace(level,...)
@ WS_OK
#define LOG_ERROR
#define LOG_WARNING
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream's file descriptor.
Definition iostream.c:85
static int ping_scheduler_callback(const void *obj)
static struct ast_websocket * websocket_client_create(struct ast_websocket_client_options *options, enum ast_websocket_result *result)
static struct ast_sched_context * ping_scheduler
static enum ast_websocket_result websocket_client_connect(struct ast_websocket *ws, struct ast_websocket_client_options *options)
const char *AST_OPTIONAL_API_NAME() ast_websocket_result_to_str(enum ast_websocket_result result)
Convert a websocket result code to a string.
int ast_sched_add(struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data) attribute_warn_unused_result
Adds a scheduled event.
Definition sched.c:567
Structure definition for session.
struct ast_iostream * stream
struct ast_websocket_client_options * options

References ao2_bump, ao2_ref, ast_iostream_get_fd(), ast_log, ast_sched_add(), ast_trace, ast_websocket_result_to_str(), ast_websocket::client, enabled, LOG_ERROR, LOG_WARNING, NULL, websocket_client::options, options, websocket_client::ping_sched_timer, ping_scheduler, ping_scheduler_callback(), ast_websocket_client_options::pingpong_interval, ast_websocket_client_options::pingpongs, result, SCOPE_ENTER, SCOPE_EXIT_LOG_RTN_VALUE, SCOPE_EXIT_RTN_VALUE, ast_websocket::stream, ast_websocket_client_options::tcp_keepalives, ast_websocket_client_options::uri, 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 553 of file res_http_websocket.c.

554{
555 if (session->closed_by == WS_NOT_CLOSED) {
556 session->closed_by = WS_CLOSED_BY_US;
557 }
558
559 return websocket_close(session, reason, 0);
560}
static struct ast_mansession session
static int websocket_close(struct ast_websocket *session, uint16_t reason, int force)

References session, websocket_close(), WS_CLOSED_BY_US, and WS_NOT_CLOSED.

Referenced by ast_websocket_read(), 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(), websocket_disconnect(), websocket_established_cb(), 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 662 of file res_http_websocket.c.

663{
664 return session->closing ? -1 : ast_iostream_get_fd(session->stream);
665}

References ast_iostream_get_fd(), and session.

Referenced by session_read(), speech_test_server_cb(), websocket_echo_callback(), websocket_handoff_to_channel(), 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 682 of file res_http_websocket.c.

683{
684 return session->secure;
685}

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 677 of file res_http_websocket.c.

678{
679 return &session->local_address;
680}

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 803 of file res_http_websocket.c.

804{
805 int fin = 0;
806 int mask_present = 0;
807 char *mask = NULL, *new_payload = NULL;
808 size_t options_len = 0, frame_size = 0;
809 SCOPE_ENTER(4, "%s: Reading\n", WS_SESSION_REMOTE(session));
810
811 *payload = NULL;
812 *payload_len = 0;
813 *fragmented = 0;
814
815 if (ws_safe_read(session, &session->buf[0], MIN_WS_HDR_SZ, opcode)) {
816 SCOPE_EXIT_RTN_VALUE(-1, "%s: Initial ws_safe_read failed\n", WS_SESSION_REMOTE(session));
817 }
819
820 /* 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) */
821 *opcode = session->buf[0] & 0xf;
822 *payload_len = session->buf[1] & 0x7f;
825 fin = (session->buf[0] >> 7) & 1;
826 mask_present = (session->buf[1] >> 7) & 1;
827
828 /* Based on the mask flag and payload length, determine how much more we need to read before start parsing the rest of the header */
829 options_len += mask_present ? 4 : 0;
830 options_len += (*payload_len == 126) ? 2 : (*payload_len == 127) ? 8 : 0;
831 if (options_len) {
832 /* read the rest of the header options */
833 if (ws_safe_read(session, &session->buf[frame_size], options_len, opcode)) {
834 SCOPE_EXIT_RTN_VALUE(-1, "%s: ws_safe_read of options failed\n", WS_SESSION_REMOTE(session));
835 }
836 frame_size += options_len;
837 }
838
839 if (*payload_len == 126) {
840 /* Grab the 2-byte payload length */
841 *payload_len = ntohs(get_unaligned_uint16(&session->buf[2]));
842 mask = &session->buf[4];
843 } else if (*payload_len == 127) {
844 /* Grab the 8-byte payload length */
845 *payload_len = ntohll(get_unaligned_uint64(&session->buf[2]));
846 mask = &session->buf[10];
847 } else {
848 /* Just set the mask after the small 2-byte header */
849 mask = &session->buf[2];
850 }
851
852 /* Now read the rest of the payload */
853 *payload = &session->buf[frame_size]; /* payload will start here, at the end of the options, if any */
854 frame_size = frame_size + (*payload_len); /* final frame size is header + optional headers + payload data */
856 /* The frame won't fit :-( */
858 SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s: Cannot fit huge websocket frame of %zu bytes\n",
860 }
861
862 if (*payload_len) {
863 if (ws_safe_read(session, *payload, *payload_len, opcode)) {
864 SCOPE_EXIT_RTN_VALUE(-1, "%s: ws_safe_read of payload failed\n", WS_SESSION_REMOTE(session));
865 }
866 }
867
868 /* If a mask is present unmask the payload */
869 if (mask_present) {
870 unsigned int pos;
871 for (pos = 0; pos < *payload_len; pos++) {
872 (*payload)[pos] ^= mask[pos % 4];
873 }
874 }
875
876 /* Per the RFC for PING we need to send back an opcode with the application data as received */
877 if (*opcode == AST_WEBSOCKET_OPCODE_PING) {
878 if (ast_websocket_write(session, AST_WEBSOCKET_OPCODE_PONG, *payload, *payload_len)) {
880 }
881 *payload_len = 0;
882 SCOPE_EXIT_RTN_VALUE(0, "%s: PING received. Sent PONG\n", WS_SESSION_REMOTE(session));
883 }
884
885 if (websocket_handled_pong_or_close(session, *payload, *payload_len, *opcode)) {
886 SCOPE_EXIT_RTN_VALUE(0, "%s: Handled PONG or CLOSE\n", WS_SESSION_REMOTE(session));
887 }
888
889 /* Below this point we are handling TEXT, BINARY or CONTINUATION opcodes */
890 if (*payload_len) {
891 if (!(new_payload = ast_realloc(session->payload, (session->payload_len + *payload_len)))) {
892 *payload_len = 0;
894 SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s: Failed allocation: %p, %zu, %"PRIu64"\n",
895 WS_SESSION_REMOTE(session), session->payload, session->payload_len, *payload_len);
896 }
897
898 session->payload = new_payload;
899 memcpy((session->payload + session->payload_len), (*payload), (*payload_len));
900 session->payload_len += *payload_len;
901 } else if (!session->payload_len && session->payload) {
902 ast_free(session->payload);
903 session->payload = NULL;
904 }
905
906 if (!fin && session->reconstruct && (session->payload_len < session->reconstruct)) {
907 /* If this is not a final message we need to defer returning it until later */
908 if (*opcode != AST_WEBSOCKET_OPCODE_CONTINUATION) {
909 session->opcode = *opcode;
910 }
912 *payload_len = 0;
913 *payload = NULL;
914 } else {
915 if (*opcode == AST_WEBSOCKET_OPCODE_CONTINUATION) {
916 if (!fin) {
917 /* If this was not actually the final message tell the user it is fragmented so they can deal with it accordingly */
918 *fragmented = 1;
919 } else {
920 /* Final frame in multi-frame so push up the actual opcode */
921 *opcode = session->opcode;
922 }
923 }
924 *payload_len = session->payload_len;
925 *payload = session->payload;
926 session->payload_len = 0;
927 }
928 } else {
929 ast_log(LOG_WARNING, "WebSocket unknown opcode %u\n", *opcode);
930 /* 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
931 * fit that, I think. */
933 }
934
935 SCOPE_EXIT_RTN_VALUE(0, "%s: Read complete. Opcode: %s Length: %"PRIu64"\n",
936 WS_SESSION_REMOTE(session), websocket_opcode2str(*opcode), *payload_len);
937}
uint64_t ntohll(uint64_t net64)
Definition strcompat.c:365
#define ast_free(a)
Definition astmm.h:180
#define ast_realloc(p, len)
A wrapper for realloc()
Definition astmm.h:226
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_CLOSE
@ AST_WEBSOCKET_OPCODE_TEXT
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)
static int websocket_handled_pong_or_close(struct ast_websocket *session, char *payload, uint64_t payload_len, enum ast_websocket_opcode opcode)
#define WS_SESSION_REMOTE(_session)
int AST_OPTIONAL_API_NAME() ast_websocket_close(struct ast_websocket *session, uint16_t reason)
Close function for websocket session.
static const char * websocket_opcode2str(enum ast_websocket_opcode opcode)
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, SCOPE_ENTER, SCOPE_EXIT_LOG_RTN_VALUE, SCOPE_EXIT_RTN_VALUE, session, websocket_handled_pong_or_close(), websocket_opcode2str(), ws_safe_read(), and WS_SESSION_REMOTE.

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 2049 of file res_http_websocket.c.

2051{
2052 char *payload;
2053 uint64_t payload_len;
2054 enum ast_websocket_opcode opcode;
2055 int fragmented = 1;
2056
2057 while (fragmented) {
2058 if (ast_websocket_read(ws, &payload, &payload_len,
2059 &opcode, &fragmented)) {
2060 ast_log(LOG_ERROR, "Client WebSocket string read - "
2061 "error reading string data\n");
2062 return -1;
2063 }
2064
2065 if (opcode == AST_WEBSOCKET_OPCODE_PING) {
2066 /* Try read again, we have sent pong already */
2067 fragmented = 1;
2068 continue;
2069 }
2070
2071 if (opcode == AST_WEBSOCKET_OPCODE_CONTINUATION) {
2072 continue;
2073 }
2074
2075 if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
2076 return -1;
2077 }
2078
2079 if (opcode != AST_WEBSOCKET_OPCODE_TEXT) {
2080 ast_log(LOG_ERROR, "Client WebSocket string read - "
2081 "non string data received\n");
2082 return -1;
2083 }
2084 }
2085
2086 if (!(*buf = ast_strndup(payload, payload_len))) {
2087 return -1;
2088 }
2089
2090 return payload_len + 1;
2091}
#define ast_strndup(str, len)
A wrapper for strndup()
Definition astmm.h:256
char buf[BUFSIZE]
Definition eagi_proxy.c:66
ast_websocket_opcode
WebSocket operation codes.
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 638 of file res_http_websocket.c.

639{
640 session->reconstruct = 0;
641}

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 633 of file res_http_websocket.c.

634{
635 session->reconstruct = MIN(bytes, MAXIMUM_RECONSTRUCTION_CEILING);
636}
#define MAXIMUM_RECONSTRUCTION_CEILING
Maximum reconstruction size for multi-frame payload reconstruction.
#define MIN(a, b)
Definition utils.h:252

References MAXIMUM_RECONSTRUCTION_CEILING, MIN, and session.

Referenced by websocket_handoff_to_channel().

◆ 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 643 of file res_http_websocket.c.

644{
646 int refcount = session ? ao2_ref(session, 0) : 0;
647 SCOPE_ENTER(2, "%s: Reffing. Refcount: %d\n", id, refcount);
648 ao2_ref(session, +1);
649 SCOPE_EXIT("%s: Reffed. Refcount: %d\n", id, session ? refcount - 1 : 0);
650}
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
#define SCOPE_EXIT(...)

References ao2_ref, ast_strdupa, SCOPE_ENTER, SCOPE_EXIT, session, and WS_SESSION_REMOTE.

Referenced by session_update(), and 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 672 of file res_http_websocket.c.

673{
674 return &session->remote_address;
675}

References session.

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

◆ 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 1271 of file res_http_websocket.c.

1272{
1274}
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 2129 of file res_http_websocket.c.

2131{
2133 return "unknown";
2134 }
2136}
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:727

References ARRAY_IN_BOUNDS, result, and websocket_result_string_map.

Referenced by ast_websocket_client_connect(), ast_websocket_client_create_with_options(), webchan_call(), websocket_client_handshake(), websocket_client_handshake_get_response(), and websocket_proxy_handshake().

◆ 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 295 of file res_http_websocket.c.

296{
297 struct ast_websocket_protocol *protocol;
298
299 if (!server->protocols) {
300 return -1;
301 }
302
304 if (!protocol) {
305 return -1;
306 }
307 protocol->session_established = callback;
308
309 if (ast_websocket_server_add_protocol2(server, protocol)) {
310 ao2_ref(protocol, -1);
311 return -1;
312 }
313
314 return 0;
315}
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 317 of file res_http_websocket.c.

318{
319 struct ast_websocket_protocol *existing;
320
321 if (!server->protocols) {
322 return -1;
323 }
324
325 if (protocol->version != AST_WEBSOCKET_PROTOCOL_VERSION) {
326 ast_log(LOG_WARNING, "WebSocket could not register sub-protocol '%s': "
327 "expected version '%u', got version '%u'\n",
328 protocol->name, AST_WEBSOCKET_PROTOCOL_VERSION, protocol->version);
329 return -1;
330 }
331
332 ao2_lock(server->protocols);
333
334 /* Ensure a second protocol handler is not registered for the same protocol */
335 existing = ao2_find(server->protocols, protocol->name, OBJ_KEY | OBJ_NOLOCK);
336 if (existing) {
337 ao2_ref(existing, -1);
338 ao2_unlock(server->protocols);
339 return -1;
340 }
341
342 ao2_link_flags(server->protocols, protocol, OBJ_NOLOCK);
343 ao2_unlock(server->protocols);
344
345 ast_debug(1, "WebSocket registered sub-protocol '%s'\n", protocol->name);
346 ao2_ref(protocol, -1);
347
348 return 0;
349}
#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
#define ao2_unlock(a)
Definition astobj2.h:729
#define ao2_lock(a)
Definition astobj2.h:717
@ 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 250 of file res_http_websocket.c.

251{
253}
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 351 of file res_http_websocket.c.

352{
353 struct ast_websocket_protocol *protocol;
354
355 if (!(protocol = ao2_find(server->protocols, name, OBJ_KEY))) {
356 return -1;
357 }
358
359 if (protocol->session_established != callback) {
360 ao2_ref(protocol, -1);
361 return -1;
362 }
363
364 ao2_unlink(server->protocols, protocol);
365 ao2_ref(protocol, -1);
366
367 ast_debug(1, "WebSocket unregistered sub-protocol '%s'\n", name);
368
369 return 0;
370}
#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 701 of file res_http_websocket.c.

702{
703 return session->session_id;
704}

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 687 of file res_http_websocket.c.

688{
691 return 0;
692}
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:154
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 694 of file res_http_websocket.c.

695{
696 session->timeout = timeout;
697
698 return 0;
699}

References session, and ast_websocket::timeout.

Referenced by session_update(), and websocket_cb().

◆ ast_websocket_status_to_str()

const char *AST_OPTIONAL_API_NAME() ast_websocket_status_to_str ( enum ast_websocket_status_code  code)

Convert a websocket status code to a string.

Parameters
codeThe code to convert
Returns
A string representation of the code

Definition at line 2162 of file res_http_websocket.c.

2164{
2165 int i;
2166
2167 for (i = 0; i < ARRAY_LEN(websocket_status_map); i++) {
2168 if (websocket_status_map[i].code == code)
2169 return websocket_status_map[i].desc;
2170 }
2171
2172 return "Unknown";
2173}
static const struct status_map websocket_status_map[]
const char * desc
#define ARRAY_LEN(a)
Definition utils.h:706

References ARRAY_LEN, status_map::code, status_map::desc, and websocket_status_map.

Referenced by _websocket_request_hangup(), websocket_close(), and websocket_handled_pong_or_close().

◆ 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 276 of file res_http_websocket.c.

277{
278 struct ast_websocket_protocol *protocol;
279
280 protocol = ao2_alloc(sizeof(*protocol), protocol_destroy_fn);
281 if (!protocol) {
282 return NULL;
283 }
284
285 protocol->name = ast_strdup(name);
286 if (!protocol->name) {
287 ao2_ref(protocol, -1);
288 return NULL;
289 }
291
292 return protocol;
293}
#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 167 of file res_http_websocket.c.

168{
169 switch (type) {
171 return "persistent";
173 return "per_call";
175 return "per_call_config";
177 return "client";
179 return "inbound";
181 return "server";
182 case AST_WS_TYPE_ANY:
183 return "any";
184 default:
185 return "unknown";
186 }
187}
static const char type[]
@ 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.

Referenced by session_cleanup().

◆ 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 652 of file res_http_websocket.c.

653{
655 int refcount = session ? ao2_ref(session, 0) : 0;
656 SCOPE_ENTER(2, "%s: Unreffing. Refcount: %d\n", id, refcount);
657
659 SCOPE_EXIT("%s: Unreffed. Refcount: %d\n", id, session ? refcount - 1 : 0);
660}
#define ao2_cleanup(obj)
Definition astobj2.h:1934

References ao2_cleanup, ao2_ref, ast_strdupa, SCOPE_ENTER, SCOPE_EXIT, session, and WS_SESSION_REMOTE.

Referenced by incoming_ws_established_cb(), outbound_session_handler_thread(), session_dtor(), speech_test_server_cb(), transport_dtor(), webchan_hangup(), websocket_cb(), websocket_destroy(), websocket_destructor(), 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 980 of file res_http_websocket.c.

981{
982 struct ast_variable *v;
983 const char *upgrade = NULL, *key = NULL, *key1 = NULL, *key2 = NULL, *protos = NULL;
984 char *requested_protocols = NULL, *protocol = NULL;
985 int version = 0, flags = 1;
986 struct ast_websocket_protocol *protocol_handler = NULL;
987 struct ast_websocket *session;
988 struct ast_websocket_server *server;
989
991
992 /* Upgrade requests are only permitted on GET methods */
993 if (method != AST_HTTP_GET) {
994 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
995 return 0;
996 }
997
998 server = urih->data;
999
1000 /* Get the minimum headers required to satisfy our needs */
1001 for (v = headers; v; v = v->next) {
1002 if (!strcasecmp(v->name, "Upgrade")) {
1003 upgrade = v->value;
1004 } else if (!strcasecmp(v->name, "Sec-WebSocket-Key")) {
1005 key = v->value;
1006 } else if (!strcasecmp(v->name, "Sec-WebSocket-Key1")) {
1007 key1 = v->value;
1008 } else if (!strcasecmp(v->name, "Sec-WebSocket-Key2")) {
1009 key2 = v->value;
1010 } else if (!strcasecmp(v->name, "Sec-WebSocket-Protocol")) {
1011 protos = v->value;
1012 } else if (!strcasecmp(v->name, "Sec-WebSocket-Version")) {
1013 if (sscanf(v->value, "%30d", &version) != 1) {
1014 version = 0;
1015 }
1016 }
1017 }
1018
1019 /* If this is not a websocket upgrade abort */
1020 if (!upgrade || strcasecmp(upgrade, "websocket")) {
1021 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - did not request WebSocket\n",
1023 ast_http_error(ser, 426, "Upgrade Required", NULL);
1024 return 0;
1025 } else if (ast_strlen_zero(protos)) {
1026 /* If there's only a single protocol registered, and the
1027 * client doesn't specify what protocol it's using, go ahead
1028 * and accept the connection */
1029 protocol_handler = one_protocol(server);
1030 if (!protocol_handler) {
1031 /* Multiple registered subprotocols; client must specify */
1032 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols requested\n",
1035 return 0;
1036 }
1037 } else if (key1 && key2) {
1038 /* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 and
1039 * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -- not currently supported*/
1040 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '00/76' chosen\n",
1043 return 0;
1044 }
1045
1046 if (!protocol_handler && protos) {
1047 requested_protocols = ast_strdupa(protos);
1048 /* Iterate through the requested protocols trying to find one that we have a handler for */
1049 while (!protocol_handler && (protocol = strsep(&requested_protocols, ","))) {
1050 protocol_handler = ao2_find(server->protocols, ast_strip(protocol), OBJ_KEY);
1051 }
1052 }
1053
1054 /* If no protocol handler exists bump this back to the requester */
1055 if (!protocol_handler) {
1056 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols out of '%s' supported\n",
1057 ast_sockaddr_stringify(&ser->remote_address), protos);
1059 return 0;
1060 }
1061
1062 /* Determine how to respond depending on the version */
1063 if (version == 7 || version == 8 || version == 13) {
1064 char base64[64];
1065
1066 if (!key || strlen(key) + strlen(WEBSOCKET_GUID) + 1 > 8192) { /* no stack overflows please */
1068 ao2_ref(protocol_handler, -1);
1069 return 0;
1070 }
1071
1072 if (ast_http_body_discard(ser)) {
1074 ao2_ref(protocol_handler, -1);
1075 return 0;
1076 }
1077
1078 if (!(session = ao2_alloc(sizeof(*session) + AST_UUID_STR_LEN + 1, session_destroy_fn))) {
1079 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted\n",
1082 ao2_ref(protocol_handler, -1);
1083 return 0;
1084 }
1086
1087 /* Generate the session id */
1088 if (!ast_uuid_generate_str(session->session_id, sizeof(session->session_id))) {
1089 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to generate a session id\n",
1091 ast_http_error(ser, 500, "Internal Server Error", "Allocation failed");
1092 ao2_ref(protocol_handler, -1);
1093 return 0;
1094 }
1095
1096 if (protocol_handler->session_attempted
1097 && protocol_handler->session_attempted(ser, get_vars, headers, session->session_id)) {
1098 ast_debug(3, "WebSocket connection from '%s' rejected by protocol handler '%s'\n",
1099 ast_sockaddr_stringify(&ser->remote_address), protocol_handler->name);
1101 ao2_ref(protocol_handler, -1);
1102 return 0;
1103 }
1104
1105 /* RFC 6455, Section 4.1:
1106 *
1107 * 6. If the response includes a |Sec-WebSocket-Protocol| header
1108 * field and this header field indicates the use of a
1109 * subprotocol that was not present in the client's handshake
1110 * (the server has indicated a subprotocol not requested by
1111 * the client), the client MUST _Fail the WebSocket
1112 * Connection_.
1113 */
1114 if (protocol) {
1116 "HTTP/1.1 101 Switching Protocols\r\n"
1117 "Upgrade: %s\r\n"
1118 "Connection: Upgrade\r\n"
1119 "Sec-WebSocket-Accept: %s\r\n"
1120 "Sec-WebSocket-Protocol: %s\r\n\r\n",
1121 upgrade,
1122 websocket_combine_key(key, base64, sizeof(base64)),
1123 protocol);
1124 } else {
1126 "HTTP/1.1 101 Switching Protocols\r\n"
1127 "Upgrade: %s\r\n"
1128 "Connection: Upgrade\r\n"
1129 "Sec-WebSocket-Accept: %s\r\n\r\n",
1130 upgrade,
1131 websocket_combine_key(key, base64, sizeof(base64)));
1132 }
1133 } else {
1134
1135 /* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 or completely unknown */
1136 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '%d' chosen\n",
1139 ao2_ref(protocol_handler, -1);
1140 return 0;
1141 }
1142
1143 /* Enable keepalive on all sessions so the underlying user does not have to */
1144 if (setsockopt(ast_iostream_get_fd(ser->stream), SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
1145 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to enable keepalive\n",
1148 ao2_ref(session, -1);
1149 ao2_ref(protocol_handler, -1);
1150 return 0;
1151 }
1152
1153 /* Get our local address for the connected socket */
1154 if (ast_getsockname(ast_iostream_get_fd(ser->stream), &session->local_address)) {
1155 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to get local address\n",
1158 ao2_ref(session, -1);
1159 ao2_ref(protocol_handler, -1);
1160 return 0;
1161 }
1162
1163 ast_debug(3, "WebSocket connection from '%s' for protocol '%s' accepted using version '%d'\n", ast_sockaddr_stringify(&ser->remote_address), protocol ? : "", version);
1164
1165 /* Populate the session with all the needed details */
1166 session->stream = ser->stream;
1167 ast_sockaddr_copy(&session->remote_address, &ser->remote_address);
1168 session->opcode = -1;
1170 session->secure = ast_iostream_get_ssl(ser->stream) ? 1 : 0;
1171
1172 /* Give up ownership of the socket and pass it to the protocol handler */
1174 protocol_handler->session_established(session, get_vars, headers);
1175 ao2_ref(protocol_handler, -1);
1176
1177 /*
1178 * By dropping the stream from the session the connection
1179 * won't get closed when the HTTP server cleans up because we
1180 * passed the connection to the protocol handler.
1181 */
1182 ser->stream = NULL;
1183
1184 return 0;
1185}
char * strsep(char **str, const char *delims)
static char version[AST_MAX_EXTENSION]
@ 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:1185
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:714
#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:507
SSL * ast_iostream_get_ssl(struct ast_iostream *stream)
Get a pointer to an iostream's OpenSSL SSL structure.
Definition iostream.c:114
#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 char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition netsock2.h:256
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:1273
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(), 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 667 of file res_http_websocket.c.

668{
669 return session->closing ? -1 : ast_iostream_wait_for_input(session->stream, timeout);
670}
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 563 of file res_http_websocket.c.

565{
566 size_t header_size = 2; /* The minimum size of a websocket frame is 2 bytes */
567 char *frame;
568 uint64_t length;
569 uint64_t frame_size;
570 SCOPE_ENTER(4, "%s: Opcode: %s Length: %"PRIu64"\n",
571 WS_SESSION_REMOTE(session), websocket_opcode2str(opcode), payload_size);
572
573 if (payload_size < 126) {
574 length = payload_size;
575 } else if (payload_size < (1 << 16)) {
576 length = 126;
577 /* We need an additional 2 bytes to store the extended length */
578 header_size += 2;
579 } else {
580 length = 127;
581 /* We need an additional 8 bytes to store the really really extended length */
582 header_size += 8;
583 }
584
585 if (session->client) {
586 /* Additional 4 bytes for the client masking key */
587 header_size += 4;
588 }
589
590 frame_size = header_size + payload_size;
591
592 frame = ast_alloca(frame_size + 1);
593 memset(frame, 0, frame_size + 1);
594
595 frame[0] = opcode | 0x80;
596 frame[1] = length;
597
598 /* Use the additional available bytes to store the length */
599 if (length == 126) {
600 put_unaligned_uint16(&frame[2], htons(payload_size));
601 } else if (length == 127) {
602 put_unaligned_uint64(&frame[2], htonll(payload_size));
603 }
604
605 memcpy(&frame[header_size], payload, payload_size);
606
607 websocket_mask_payload(session, frame, &frame[header_size], payload_size);
608
610 if (session->closing) {
612 SCOPE_EXIT_RTN_VALUE(-1, "%s: Websocket already closing\n", WS_SESSION_REMOTE(session));
613 }
614
616 if (ast_iostream_write(session->stream, frame, frame_size) != frame_size) {
618 /* 1011 - server terminating connection due to not being able to fulfill the request */
619 ast_trace(-1, "%s: Closing WS with 1011 because we can't fulfill a write request\n",
621 websocket_close(session, 1011, 1);
622 SCOPE_EXIT_RTN_VALUE(-1, "%s: Closed WS with 1011 because we couldn't fulfill a write request\n",
624 }
625
628
629 SCOPE_EXIT_RTN_VALUE(0, "%s: Wrote opcode: %s length: %"PRIu64"\n",
630 WS_SESSION_REMOTE(session), websocket_opcode2str(opcode), payload_size);
631}
uint64_t htonll(uint64_t host64)
Definition strcompat.c:391
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition astmm.h:288
ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t count)
Write data to an iostream.
Definition iostream.c:390
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:145
void ast_iostream_set_timeout_disable(struct ast_iostream *stream)
Disable the iostream timeout timer.
Definition iostream.c:119
static void websocket_mask_payload(struct ast_websocket *session, char *frame, char *payload, uint64_t payload_size)
Perform payload masking for client sessions.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition time.h:159
static void put_unaligned_uint16(void *p, unsigned short datum)
Definition unaligned.h:65
static void put_unaligned_uint64(void *p, uint64_t datum)
Definition unaligned.h:51

References ao2_lock, ao2_unlock, ast_alloca, ast_iostream_set_timeout_disable(), ast_iostream_set_timeout_sequence(), ast_iostream_write(), ast_trace, ast_tvnow(), frame_size, htonll(), ast_websocket::opcode, ast_websocket::payload, put_unaligned_uint16(), put_unaligned_uint64(), SCOPE_ENTER, SCOPE_EXIT_RTN_VALUE, session, websocket_close(), websocket_mask_payload(), websocket_opcode2str(), and WS_SESSION_REMOTE.

Referenced by ast_websocket_read(), ast_websocket_write_string(), ping_scheduler_callback(), 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 2093 of file res_http_websocket.c.

2095{
2096 uint64_t len = strlen(buf);
2097
2098 ast_debug(3, "Writing websocket string of length %" PRIu64 "\n", len);
2099
2100 /* We do not pass strlen(buf) to ast_websocket_write() directly because the
2101 * size_t returned by strlen() may not require the same storage size
2102 * as the uint64_t that ast_websocket_write() uses. This normally
2103 * would not cause a problem, but since ast_websocket_write() uses
2104 * the optional API, this function call goes through a series of macros
2105 * that may cause a 32-bit to 64-bit conversion to go awry.
2106 */
2108 (char *)buf, len);
2109}
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(), session_write(), and speech_test_server_handle_request().

◆ client_options_clone()

static struct ast_websocket_client_options * client_options_clone ( struct ast_websocket_client_options options)
static

Definition at line 1441 of file res_http_websocket.c.

1443{
1444 struct ast_websocket_client_options *clone = NULL;
1445
1446 clone = ao2_alloc(sizeof(*clone), client_options_destroy);
1447 if (!clone) {
1448 ast_log(LOG_ERROR, "Unable to clone client options\n");
1449 return NULL;
1450 }
1451
1452 memcpy(clone, options, sizeof(*options));
1453 clone->uri = SAFE_STRDUP_WITH_ERROR_RTN(clone, options->uri);
1454 clone->protocols = SAFE_STRDUP_WITH_ERROR_RTN(clone, options->protocols);
1455 clone->username = SAFE_STRDUP_WITH_ERROR_RTN(clone, options->username);
1456 clone->password = SAFE_STRDUP_WITH_ERROR_RTN(clone, options->password);
1457 clone->proxy_host = SAFE_STRDUP_WITH_ERROR_RTN(clone, options->proxy_host);
1458 clone->proxy_username = SAFE_STRDUP_WITH_ERROR_RTN(clone, options->proxy_username);
1459 clone->proxy_password = SAFE_STRDUP_WITH_ERROR_RTN(clone, options->proxy_password);
1460 if (options->tls_cfg) {
1461 clone->tls_cfg = ast_calloc(1, sizeof(*options->tls_cfg));
1462 if (!clone->tls_cfg) {
1463 ao2_cleanup(clone);
1464 return NULL;
1465 }
1466 memcpy(clone->tls_cfg, options->tls_cfg, sizeof(*options->tls_cfg));
1467 }
1468
1469 return clone;
1470}
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
#define SAFE_STRDUP_WITH_ERROR_RTN(_clone, _str)
static void client_options_destroy(void *obj)

References ao2_alloc, ao2_cleanup, ast_calloc, ast_log, client_options_destroy(), LOG_ERROR, NULL, options, ast_websocket_client_options::password, ast_websocket_client_options::protocols, ast_websocket_client_options::proxy_host, ast_websocket_client_options::proxy_password, ast_websocket_client_options::proxy_username, SAFE_STRDUP_WITH_ERROR_RTN, ast_websocket_client_options::tls_cfg, ast_websocket_client_options::uri, and ast_websocket_client_options::username.

Referenced by websocket_client_create().

◆ client_options_destroy()

static void client_options_destroy ( void *  obj)
static

◆ closed_by_to_str()

static const char * closed_by_to_str ( enum ws_closed_by  closed_by)
static

Definition at line 159 of file res_http_websocket.c.

160{
161 if (!ARRAY_IN_BOUNDS(closed_by, closed_by_str)) {
162 return "unknown";
163 }
164 return closed_by_str[closed_by];
165}
static const char * closed_by_str[]

References ARRAY_IN_BOUNDS, and closed_by_str.

Referenced by websocket_close().

◆ load_module()

static int load_module ( void  )
static

Definition at line 2190 of file res_http_websocket.c.

2191{
2193 if (!websocketuri.data) {
2195 }
2198
2200 if (!ping_scheduler) {
2201 unload_module();
2203 }
2204
2206 unload_module();
2208 }
2209
2210 return 0;
2211}
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition http.c:739
@ 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)
static int unload_module(void)
int ast_sched_start_thread(struct ast_sched_context *con)
Start a thread for processing scheduler entries.
Definition sched.c:197
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Definition sched.c:238

References ast_http_uri_link(), AST_MODULE_LOAD_DECLINE, ast_sched_context_create(), ast_sched_start_thread(), ast_http_uri::data, ping_scheduler, unload_module(), 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 942 of file res_http_websocket.c.

944{
945 SCOPED_AO2LOCK(lock, server->protocols);
946
947 if (ao2_container_count(server->protocols) != 1) {
948 return NULL;
949 }
950
951 return ao2_callback(server->protocols, OBJ_NOLOCK, NULL, NULL);
952}
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().

◆ ping_scheduler_callback()

static int ping_scheduler_callback ( const void *  obj)
static

Definition at line 1928 of file res_http_websocket.c.

1929{
1930 struct ast_websocket *session = (struct ast_websocket *)obj;
1931
1932 if (!session->client) {
1933 /*
1934 * We should never get here because we can only start pingpongs from a client
1935 * but just in case...
1936 */
1937 return 0;
1938 }
1939
1941 if (session->closing) {
1943 return 0;
1944 }
1945
1946 if (session->client->missed_pong_count > 1) {
1947 ast_debug(2, "%s: Missed PONG count is now %d\n", WS_SESSION_REMOTE(session),
1948 session->client->missed_pong_count);
1949 }
1950
1951 if (session->client->missed_pong_count >= session->client->options->pingpong_probes) {
1953 ast_log(LOG_WARNING, "%s: %d missed PONGs. Closing connection.\n",
1954 WS_SESSION_REMOTE(session), session->client->missed_pong_count);
1955 session->client->ping_sched_timer = -1;
1957 return 0;
1958 }
1959
1961 session->client->missed_pong_count++;
1962
1964 return session->client->options->pingpong_interval * 1000;
1965}
@ AST_WEBSOCKET_STATUS_GOING_AWAY
#define WS_PING_PAYLOAD
WS_PING_PAYLOAD It's possible that a user of this API could be sending their own PINGs and expecting ...
#define WS_PING_PAYLOAD_LEN

References ao2_lock, ao2_unlock, ast_debug, ast_log, AST_WEBSOCKET_OPCODE_PING, AST_WEBSOCKET_STATUS_GOING_AWAY, ast_websocket_write(), LOG_WARNING, session, websocket_close(), WS_PING_PAYLOAD, WS_PING_PAYLOAD_LEN, and WS_SESSION_REMOTE.

Referenced by ast_websocket_client_create_with_options().

◆ ping_scheduler_cancel()

static void ping_scheduler_cancel ( struct ast_websocket session)
static

Definition at line 408 of file res_http_websocket.c.

409{
411 SCOPE_ENTER(2, "%s: Cancelling PING/PONG keepalives\n", WS_SESSION_REMOTE(session));
412
413 if (!enabled) {
414 SCOPE_EXIT_RTN("%s: Not enabled, cancel not needed\n", WS_SESSION_REMOTE(session));
415 }
416 AST_SCHED_DEL(ping_scheduler, session->client->ping_sched_timer);
417 ao2_ref(session, -1);
418 SCOPE_EXIT_RTN("%s: Cancelled PING/PONG keepalives\n", WS_SESSION_REMOTE(session));
419}
#define SCOPE_EXIT_RTN(...)
#define ARE_PINGPONGS_ENABLED(_session)
#define AST_SCHED_DEL(sched, id)
Remove a scheduler entry.
Definition sched.h:46

References ao2_ref, ARE_PINGPONGS_ENABLED, AST_SCHED_DEL, enabled, ping_scheduler, SCOPE_ENTER, SCOPE_EXIT_RTN, session, and WS_SESSION_REMOTE.

Referenced by websocket_close().

◆ protocol_cmp_fn()

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

Comparison function for protocols.

Definition at line 199 of file res_http_websocket.c.

200{
201 const struct ast_websocket_protocol *protocol1 = obj, *protocol2 = arg;
202 const char *protocol = arg;
203
204 return !strcasecmp(protocol1->name, flags & OBJ_KEY ? protocol : protocol2->name) ? CMP_MATCH | CMP_STOP : 0;
205}
@ 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 208 of file res_http_websocket.c.

209{
210 struct ast_websocket_protocol *protocol = obj;
211 ast_free(protocol->name);
212}

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 190 of file res_http_websocket.c.

191{
192 const struct ast_websocket_protocol *protocol = obj;
193 const char *name = obj;
194
195 return ast_str_case_hash(flags & OBJ_KEY ? name : protocol->name);
196}
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 256 of file res_http_websocket.c.

257{
258 struct ast_websocket *session = obj;
260 SCOPE_ENTER(2, "%s: Session %p destructor\n", id, obj);
261
262 if (session->stream) {
263 ast_websocket_close(session, session->close_status_code);
264 if (session->stream) {
266 session->stream = NULL;
267 ast_trace(-1, "%s: WebSocket connection closed\n", WS_SESSION_REMOTE(session));
268 }
269 }
270
271 ao2_cleanup(session->client);
272 ast_free(session->payload);
273 SCOPE_EXIT_RTN("%s; Session %p destructor complete\n", id, obj);
274}
int ast_iostream_close(struct ast_iostream *stream)
Close an iostream.
Definition iostream.c:544

References ao2_cleanup, ast_free, ast_iostream_close(), ast_strdupa, ast_trace, ast_websocket_close(), NULL, SCOPE_ENTER, SCOPE_EXIT_RTN, session, and WS_SESSION_REMOTE.

Referenced by ast_websocket_uri_cb(), and websocket_client_create().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 2175 of file res_http_websocket.c.

2176{
2177 if (ping_scheduler) {
2180 }
2181
2186
2187 return 0;
2188}
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition http.c:771
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
Definition sched.c:271

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

Referenced by load_module().

◆ websocket_add_protocol_internal()

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

Definition at line 1233 of file res_http_websocket.c.

1234{
1235 struct ast_websocket_server *ws_server = websocketuri.data;
1236 if (!ws_server) {
1237 return -1;
1238 }
1240}
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 967 of file res_http_websocket.c.

968{
969 struct ast_str *http_header = ast_str_create(64);
970
971 if (!http_header) {
973 ast_http_error(ser, 500, "Server Error", "Out of memory");
974 return;
975 }
976 ast_str_set(&http_header, 0, "Sec-WebSocket-Version: 7, 8, 13\r\n");
977 ast_http_send(ser, AST_HTTP_UNKNOWN, 400, "Bad Request", http_header, NULL, 0, 0);
978}
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:522
@ 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:903
#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 ( struct ast_websocket ws,
struct ast_websocket_client_options options,
enum ast_websocket_result result 
)
static

Definition at line 1331 of file res_http_websocket.c.

1333{
1334 struct ast_sockaddr *addr;
1337 const char *resolve_host = NULL;
1338
1339 if (!args) {
1341 return NULL;
1342 }
1343
1344 args->accept_fd = -1;
1345 args->tls_cfg = options->tls_cfg;
1346 args->name = "websocket client";
1347
1349 resolve_host = ws->client->options->proxy_host;
1350 } else {
1351 resolve_host = ws->client->host;
1352 }
1353 if (!ast_sockaddr_resolve(&addr, resolve_host, 0, 0)) {
1354 ast_log(LOG_ERROR, "Unable to resolve address %s\n",
1355 resolve_host);
1356 ao2_ref(args, -1);
1358 return NULL;
1359 }
1360 ast_sockaddr_copy(&args->remote_address, addr);
1361 ast_free(addr);
1362
1363 /* We need to save off the hostname but it may contain a port spec */
1364 snprintf(args->hostname, sizeof(args->hostname),
1365 "%.*s",
1366 (int) strcspn(ws->client->host, ":"), ws->client->host);
1367
1368 return args;
1369}
@ 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)
static struct @522 args
Socket address structure.
Definition netsock2.h:97
arguments for the accepting thread
Definition tcptls.h:130

References ao2_alloc, ao2_ref, args, ast_free, ast_log, ast_sockaddr_copy(), ast_sockaddr_resolve(), ast_strlen_zero(), ast_websocket::client, websocket_client::host, LOG_ERROR, NULL, websocket_client::options, options, ast_websocket_client_options::proxy_host, result, 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 1315 of file res_http_websocket.c.

1316{
1317 struct ast_tcptls_session_args *args = obj;
1318
1319 if (args->tls_cfg) {
1320 ast_free(args->tls_cfg->certfile);
1321 ast_free(args->tls_cfg->pvtfile);
1322 ast_free(args->tls_cfg->cipher);
1323 ast_free(args->tls_cfg->cafile);
1324 ast_free(args->tls_cfg->capath);
1325
1326 ast_ssl_teardown(args->tls_cfg);
1327 }
1328 ast_free(args->tls_cfg);
1329}
void ast_ssl_teardown(struct ast_tls_config *cfg)
free resources used by an SSL server
Definition tcptls.c:597

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,
struct ast_websocket_client_options options 
)
static

Definition at line 1838 of file res_http_websocket.c.

1840{
1841 enum ast_websocket_result res;
1842 int original_tls_enabled = ws->client->args->tls_cfg ? ws->client->args->tls_cfg->enabled : 0;
1843 int proxy = !ast_strlen_zero(ws->client->options->proxy_host);
1844 SCOPE_ENTER(2, "%s: proxy: %s tls_enabled: %s\n", options->uri, S_OR(ws->client->options->proxy_host, "N/A"),
1845 AST_YESNO(original_tls_enabled));
1846
1847
1848 /* create and connect the client - note client_start
1849 releases the session instance on failure */
1850
1851 if (proxy && original_tls_enabled ) {
1852 ast_trace(-1, "%s: Disabling TLS while handshaking with proxy\n", options->uri);
1853 if (ws->client->args->tls_cfg) {
1854 ws->client->args->tls_cfg->enabled = 0;
1855 }
1856 }
1857
1858 ast_trace(-1, "%s: Creating tcptls client\n", options->uri);
1860 if (!ws->client->ser) {
1861 SCOPE_EXIT_LOG_RTN_VALUE(WS_CLIENT_START_ERROR, LOG_ERROR, "%s: Unable to create tcptls client\n", options->uri);
1862 }
1863
1864 ast_trace(-1, "%s: Connecting%s%s\n", options->uri, proxy ? " via proxy " : "", S_OR(ws->client->options->proxy_host, ""));
1865 if (!ast_tcptls_client_start_timeout(ws->client->ser, options->timeout)) {
1866 ws->client->ser = NULL;
1867 SCOPE_EXIT_LOG_RTN_VALUE(WS_CLIENT_START_ERROR, LOG_ERROR, "%s: Unable to connect%s%s\n", options->uri,
1868 proxy ? " via proxy " : "", S_OR(ws->client->options->proxy_host, ""));
1869 }
1870
1871 ast_trace(-1, "%s: Connected%s%s\n", options->uri, proxy ? " via proxy " : "", S_OR(ws->client->options->proxy_host, ""));
1872 if (proxy) {
1874 if (res != WS_OK) {
1875 ao2_ref(ws->client->ser, -1);
1876 ws->client->ser = NULL;
1877 SCOPE_EXIT_LOG_RTN_VALUE(res, LOG_ERROR, "%s: Unable to perform proxy handshake with %s\n", options->uri,
1878 ws->client->options->proxy_host);
1879 }
1880 }
1881
1882 if (proxy && original_tls_enabled && !ws->client->args->tls_cfg->enabled) {
1883 int rc = 0;
1884 ast_trace(-1, "%s: Re-enabling TLS after handshaking with proxy %s\n", options->uri, ws->client->options->proxy_host);
1885 ws->client->args->tls_cfg->enabled = 1;
1887 if (rc != 1) {
1888 ao2_cleanup(ws->client->ser);
1889 ws->client->ser = NULL;
1890 SCOPE_EXIT_LOG_RTN_VALUE(WS_TLS_ERROR, LOG_ERROR, "%s: TLS context setup failed after handshake with %s\n",
1891 options->uri, ws->client->options->proxy_host);
1892 }
1893 if (!ast_tcptls_start_tls(ws->client->ser)) {
1894 ws->client->ser = NULL;
1895 SCOPE_EXIT_LOG_RTN_VALUE(WS_TLS_ERROR, LOG_ERROR, "%s: TLS with websocket server failed after handshake with %s\n",
1896 options->uri, ws->client->options->proxy_host);
1897 }
1898 }
1899
1900 if ((res = websocket_client_handshake(ws->client)) != WS_OK) {
1901 ao2_ref(ws->client->ser, -1);
1902 ws->client->ser = NULL;
1903 SCOPE_EXIT_LOG_RTN_VALUE(res, LOG_ERROR, "%s: Unable to perform websocket handshake\n", options->uri);
1904 }
1905
1906 ws->stream = ws->client->ser->stream;
1907 ws->secure = ast_iostream_get_ssl(ws->stream) ? 1 : 0;
1908 ws->client->ser->stream = NULL;
1910
1911 SCOPE_EXIT_RTN_VALUE(WS_OK, "%s: Handshake complete\n", options->uri);
1912}
ast_websocket_result
Result code for a websocket client.
@ WS_TLS_ERROR
@ WS_CLIENT_START_ERROR
static enum ast_websocket_result websocket_client_handshake(struct websocket_client *client)
static enum ast_websocket_result websocket_proxy_handshake(struct websocket_client *client)
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition strings.h:80
#define AST_YESNO(x)
return Yes or No depending on the argument.
Definition strings.h:143
struct ast_tls_config * tls_cfg
Definition tcptls.h:135
struct ast_sockaddr remote_address
unsigned int secure
struct ast_tcptls_session_args * args
struct ast_tcptls_session_instance * ser
int ast_ssl_setup_client(struct ast_tls_config *cfg)
Set up an SSL client.
Definition tcptls.c:592
struct ast_tcptls_session_instance * ast_tcptls_start_tls(struct ast_tcptls_session_instance *tcptls_session)
Start TLS negotiation on an existing unsecured connection.
Definition tcptls.c:133
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:701
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:667

References ao2_cleanup, ao2_ref, websocket_client::args, ast_iostream_get_ssl(), ast_sockaddr_copy(), ast_ssl_setup_client(), ast_strlen_zero(), ast_tcptls_client_create(), ast_tcptls_client_start_timeout(), ast_tcptls_start_tls(), ast_trace, AST_YESNO, ast_websocket::client, ast_tls_config::enabled, LOG_ERROR, NULL, websocket_client::options, options, ast_websocket_client_options::proxy_host, ast_tcptls_session_instance::remote_address, ast_websocket::remote_address, S_OR, SCOPE_ENTER, SCOPE_EXIT_LOG_RTN_VALUE, SCOPE_EXIT_RTN_VALUE, ast_websocket::secure, websocket_client::ser, ast_tcptls_session_instance::stream, ast_websocket::stream, ast_tcptls_session_args::tls_cfg, websocket_client_handshake(), websocket_proxy_handshake(), WS_CLIENT_START_ERROR, WS_OK, and WS_TLS_ERROR.

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 1472 of file res_http_websocket.c.

1474{
1475 struct ast_websocket *ws = NULL;
1476
1477 ast_debug(2, "%s: Creating client\n", options->uri);
1478
1479 ws = ao2_alloc(sizeof(*ws), session_destroy_fn);
1480 if (!ws) {
1481 ast_log(LOG_ERROR, "Unable to allocate websocket\n");
1483 return NULL;
1484 }
1485
1486 if (!ast_uuid_generate_str(ws->session_id, sizeof(ws->session_id))) {
1487 ast_log(LOG_ERROR, "Unable to allocate websocket session_id\n");
1488 ao2_ref(ws, -1);
1490 return NULL;
1491 }
1492
1493 if (!(ws->client = ao2_alloc(
1494 sizeof(*ws->client), websocket_client_destroy))) {
1495 ast_log(LOG_ERROR, "Unable to allocate websocket client\n");
1496 ao2_ref(ws, -1);
1498 return NULL;
1499 }
1500 ws->client->ping_sched_timer = -1;
1501
1503 if (!ws->client->options) {
1504 ast_log(LOG_ERROR, "Unable to clone client options\n");
1505 ao2_ref(ws, -1);
1507 return NULL;
1508 }
1509
1510 if (!(ws->client->key = websocket_client_create_key())) {
1511 ao2_ref(ws, -1);
1513 return NULL;
1514 }
1515
1517 options->uri, &ws->client->host, &ws->client->resource_name,
1518 &ws->client->userinfo, 0)) {
1519 ao2_ref(ws, -1);
1521 return NULL;
1522 }
1523 ast_debug(2, "%s: host: %s resource: %s userinfo: %s\n", options->uri, ws->client->host,
1525 ws->client->userinfo);
1526
1528 && !ast_strlen_zero(options->username)
1529 && !ast_strlen_zero(options->password)) {
1530 ast_asprintf(&ws->client->userinfo, "%s:%s", options->username,
1531 options->password);
1532 }
1533
1534 if (!ast_strlen_zero(options->proxy_host)) {
1535 ast_debug(2, "%s: Proxy host: %s userinfo: %s\n", options->uri, ws->client->options->proxy_host,
1536 ws->client->proxy_userinfo);
1537
1539 && !ast_strlen_zero(options->proxy_username)
1540 && !ast_strlen_zero(options->proxy_password)) {
1541 ast_asprintf(&ws->client->proxy_userinfo, "%s:%s", options->proxy_username,
1542 options->proxy_password);
1543 }
1544
1545 }
1546
1548 ao2_ref(ws, -1);
1549 return NULL;
1550 }
1551
1552 ws->client->suppress_connection_msgs = options->suppress_connection_msgs;
1553 ws->client->args->suppress_connection_msgs = options->suppress_connection_msgs;
1554 ws->client->protocols = ast_strdup(options->protocols);
1555 ws->client->version = 13;
1556 ws->opcode = -1;
1558 ws->timeout = options->timeout;
1559
1560 return ws;
1561}
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition astmm.h:267
@ WS_KEY_ERROR
@ WS_URI_PARSE_ERROR
static void websocket_client_destroy(void *obj)
static int websocket_client_parse_uri(const char *uri, char **host, struct ast_str **path, char **userinfo, int proxy)
Parse the given uri into a path and remote address.
static struct ast_tcptls_session_args * websocket_client_args_create(struct ast_websocket *ws, struct ast_websocket_client_options *options, enum ast_websocket_result *result)
static char * websocket_client_create_key(void)
static struct ast_websocket_client_options * client_options_clone(struct ast_websocket_client_options *options)
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
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_debug, ast_log, ast_str_buffer(), ast_strdup, ast_strlen_zero(), ast_uuid_generate_str(), ast_websocket::client, client_options_clone(), DEFAULT_RECONSTRUCTION_CEILING, websocket_client::host, websocket_client::key, LOG_ERROR, NULL, ast_websocket::opcode, websocket_client::options, options, websocket_client::ping_sched_timer, websocket_client::protocols, ast_websocket_client_options::proxy_host, websocket_client::proxy_userinfo, 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 1371 of file res_http_websocket.c.

1372{
1373 static int encoded_size = CLIENT_KEY_SIZE * 2 * sizeof(char) + 1;
1374 /* key is randomly selected 16-byte base64 encoded value */
1375 unsigned char key[CLIENT_KEY_SIZE + sizeof(long) - 1];
1376 char *encoded = ast_malloc(encoded_size);
1377 long i = 0;
1378
1379 if (!encoded) {
1380 ast_log(LOG_ERROR, "Unable to allocate client websocket key\n");
1381 return NULL;
1382 }
1383
1384 while (i < CLIENT_KEY_SIZE) {
1385 long num = ast_random();
1386 memcpy(key + i, &num, sizeof(long));
1387 i += sizeof(long);
1388 }
1389
1390 ast_base64encode(encoded, key, CLIENT_KEY_SIZE, encoded_size);
1391 return encoded;
1392}
#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:404
long int ast_random(void)
Definition utils.c:2346

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

Definition at line 1394 of file res_http_websocket.c.

1395{
1396 struct websocket_client *client = obj;
1397 char *id = ast_strdupa(client->options->uri);
1398 SCOPE_ENTER(2, "%s: Client destructor %p\n", id, obj);
1399
1400 ao2_cleanup(client->options);
1401 ao2_cleanup(client->ser);
1402 ao2_cleanup(client->args);
1403
1404 ast_free(client->accept_protocol);
1405 ast_free(client->protocols);
1406 ast_free(client->key);
1407 ast_free(client->resource_name);
1408 ast_free(client->host);
1409 ast_free(client->userinfo);
1410 ast_free(client->proxy_userinfo);
1411
1412 SCOPE_EXIT_RTN("%s: Client destructor complete\n", id);
1413}

References websocket_client::accept_protocol, ao2_cleanup, websocket_client::args, ast_free, ast_strdupa, websocket_client::host, websocket_client::key, websocket_client::options, websocket_client::protocols, websocket_client::proxy_userinfo, websocket_client::resource_name, SCOPE_ENTER, SCOPE_EXIT_RTN, websocket_client::ser, ast_websocket_client_options::uri, and websocket_client::userinfo.

Referenced by websocket_client_create().

◆ websocket_client_handle_response_code()

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

Definition at line 1569 of file res_http_websocket.c.

1571{
1572 if (response_code <= 0) {
1573 return WS_INVALID_RESPONSE;
1574 }
1575
1576 switch (response_code) {
1577 case 101:
1578 if (!proxy) {
1579 return WS_OK;
1580 }
1581 break;
1582 case 200:
1583 if (proxy) {
1584 return WS_OK;
1585 }
1586 break;
1587 case 400:
1588 if (!client->suppress_connection_msgs) {
1589 ast_log(LOG_ERROR, "Received response 400 - Bad Request "
1590 "- from %s\n", client->host);
1591 }
1592 return WS_BAD_REQUEST;
1593 case 401:
1594 if (!client->suppress_connection_msgs) {
1595 ast_log(LOG_ERROR, "Received response 401 - Unauthorized "
1596 "- from %s\n", client->host);
1597 }
1598 return WS_UNAUTHORIZED;
1599 case 404:
1600 if (!client->suppress_connection_msgs) {
1601 ast_log(LOG_ERROR, "Received response 404 - Request URL not "
1602 "found - from %s\n", client->host);
1603 }
1604 return WS_URL_NOT_FOUND;
1605 }
1606
1607 if (!client->suppress_connection_msgs) {
1608 ast_log(LOG_ERROR, "Invalid HTTP response code %d from %s\n",
1609 response_code, proxy ? client->options->proxy_host : client->host);
1610 }
1611 return WS_INVALID_RESPONSE;
1612}
@ 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::options, ast_websocket_client_options::proxy_host, websocket_client::suppress_connection_msgs, WS_BAD_REQUEST, WS_INVALID_RESPONSE, WS_OK, 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 1788 of file res_http_websocket.c.

1790{
1791 size_t protocols_len = 0;
1792 struct ast_variable *auth_header = NULL;
1793 size_t res;
1794 SCOPE_ENTER(2, "%s: Handshaking with server\n", client->options->uri);
1795
1796 if (!ast_strlen_zero(client->userinfo)) {
1797 auth_header = ast_http_create_basic_auth_header(client->userinfo, NULL);
1798 if (!auth_header) {
1799 SCOPE_EXIT_LOG_RTN_VALUE(WS_ALLOCATE_ERROR, LOG_ERROR, "Unable to allocate client websocket userinfo\n");
1800 }
1801 }
1802
1803 protocols_len = client->protocols ? strlen(client->protocols) : 0;
1804
1806
1807 res = ast_iostream_printf(client->ser->stream,
1808 "GET /%s HTTP/1.1\r\n"
1809 "Sec-WebSocket-Version: %d\r\n"
1810 "Upgrade: websocket\r\n"
1811 "Connection: Upgrade\r\n"
1812 "Host: %s\r\n"
1815 "Sec-WebSocket-Key: %s\r\n"
1816 "\r\n",
1817 client->resource_name ? ast_str_buffer(client->resource_name) : "",
1818 client->version,
1819 client->host,
1820 print_optional_header(auth_header, "Authorization: ", auth_header->value),
1821 print_optional_header(protocols_len, "Sec-WebSocket-Protocol: ", client->protocols),
1822 client->key
1823 );
1824
1825 ast_variables_destroy(auth_header);
1826 if (res < 0) {
1828 SCOPE_EXIT_LOG_RTN_VALUE(WS_WRITE_ERROR, LOG_ERROR, "Failed to send handshake\n");
1829 }
1830
1831 /* wait for a response before doing anything else */
1834
1836}
struct ast_variable * ast_http_create_basic_auth_header(const char *userid, const char *password)
Create an HTTP authorization header.
Definition http.c:1726
@ WS_WRITE_ERROR
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
#define optional_header_spec
static enum ast_websocket_result websocket_client_handshake_get_response(struct websocket_client *client, int proxy)
#define print_optional_header(test, name, value)
static void websocket_client_start_handshake_timer(struct websocket_client *client)
static void websocket_client_stop_handshake_timer(struct websocket_client *client)

References ast_http_create_basic_auth_header(), ast_iostream_printf(), ast_str_buffer(), ast_strlen_zero(), ast_variables_destroy(), ast_websocket_result_to_str(), websocket_client::host, websocket_client::key, LOG_ERROR, NULL, optional_header_spec, websocket_client::options, print_optional_header, websocket_client::protocols, websocket_client::resource_name, SCOPE_ENTER, SCOPE_EXIT_LOG_RTN_VALUE, SCOPE_EXIT_RTN_VALUE, websocket_client::ser, ast_tcptls_session_instance::stream, ast_websocket_client_options::uri, websocket_client::userinfo, ast_variable::value, websocket_client::version, websocket_client_handshake_get_response(), websocket_client_start_handshake_timer(), websocket_client_stop_handshake_timer(), 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,
int  proxy 
)
static

Definition at line 1614 of file res_http_websocket.c.

1616{
1617 enum ast_websocket_result res;
1618 char buf[4096];
1619 char base64[64];
1620 int has_upgrade = 0;
1621 int has_connection = 0;
1622 int has_accept = 0;
1623 int has_protocol = 0;
1624 int status_code = 0;
1625 SCOPE_ENTER(2, "%s: Proxy? %s Proxy host: %s\n", client->options->uri, AST_YESNO(proxy),
1626 S_OR(client->options->proxy_host, "N/A"));
1627
1628 while (ast_iostream_gets(client->ser->stream, buf, sizeof(buf)) <= 0) {
1630 LOG_ERROR, "%s: %s waiting for HTTP status line", client->options->uri,
1631 (errno == EINTR || errno == EAGAIN) ? "Timeout" : "Error");
1632 }
1633
1634 status_code = ast_http_response_status_line(buf, "HTTP/1.1", 101);
1635 ast_trace(-1, "%s: Status code: %d\n", client->options->uri, status_code);
1636
1637 res = websocket_client_handle_response_code(client, status_code, proxy);
1638 if (res != WS_OK) {
1639 SCOPE_EXIT_RTN_VALUE(res, "%s: HTTP status line result: %d/%s", client->options->uri,
1640 res, ast_websocket_result_to_str(res));
1641 }
1642
1643 /* Ignoring line folding - assuming header field values are contained
1644 within a single line */
1645 while (1) {
1646 ssize_t len = ast_iostream_gets(client->ser->stream, buf, sizeof(buf));
1647 char *name, *value;
1648 int parsed;
1649
1650 if (len <= 0) {
1651 if (errno == EINTR || errno == EAGAIN) {
1653 LOG_ERROR, "%s: %s waiting for HTTP header", client->options->uri,
1654 (errno == EINTR || errno == EAGAIN) ? "Timeout" : "Error");
1655 } else {
1656 ast_trace(-1, "%s: Blank line received\n", client->options->uri);
1657 }
1658 break;
1659 }
1660
1661 parsed = ast_http_header_parse(buf, &name, &value);
1662 if (parsed < 0) {
1663 break;
1664 }
1665
1666 if (proxy || parsed > 0) {
1667 continue;
1668 }
1669
1670 if (!has_upgrade &&
1671 (has_upgrade = ast_http_header_match(
1672 name, "upgrade", value, "websocket")) < 0) {
1674 } else if (!has_connection &&
1675 (has_connection = ast_http_header_match(
1676 name, "connection", value, "upgrade")) < 0) {
1678 } else if (!has_accept &&
1679 (has_accept = ast_http_header_match(
1680 name, "sec-websocket-accept", value,
1682 client->key, base64, sizeof(base64)))) < 0) {
1684 } else if (!has_protocol &&
1685 (has_protocol = ast_http_header_match_in(
1686 name, "sec-websocket-protocol", value, client->protocols))) {
1687 if (has_protocol < 0) {
1689 }
1690 client->accept_protocol = ast_strdup(value);
1691 } else if (!strcasecmp(name, "sec-websocket-extensions")) {
1692 ast_log(LOG_ERROR, "Extensions received, but not "
1693 "supported by client\n");
1695 }
1696 }
1697
1698 if (status_code == 408) {
1700 } else {
1701 if (proxy) {
1702 res = WS_OK;
1703 } else {
1704 res = has_upgrade && has_connection && has_accept ?
1706 }
1707 }
1708
1709 SCOPE_EXIT_RTN_VALUE(res, "%s: Status code: %d\n", client->options->uri, status_code);
1710}
int ast_http_header_parse(char *buf, char **name, char **value)
Parse a header into the given name/value strings.
Definition http.c:1822
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:1860
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:1844
int ast_http_response_status_line(const char *buf, const char *version, int code)
Parse the http response status line.
Definition http.c:1770
@ 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:316
int errno
static enum ast_websocket_result websocket_client_handle_response_code(struct websocket_client *client, int response_code, int proxy)

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, ast_trace, ast_websocket_result_to_str(), AST_YESNO, base64, buf, ast_websocket::client, errno, websocket_client::key, len(), LOG_ERROR, name, websocket_client::options, websocket_client::protocols, ast_websocket_client_options::proxy_host, S_OR, SCOPE_ENTER, SCOPE_EXIT_LOG_RTN_VALUE, SCOPE_EXIT_RTN_VALUE, websocket_client::ser, ast_tcptls_session_instance::stream, ast_websocket_client_options::uri, value, websocket_client_handle_response_code(), websocket_combine_key(), WS_BAD_STATUS, WS_CLIENT_START_ERROR, WS_HEADER_MISMATCH, WS_HEADER_MISSING, WS_NOT_SUPPORTED, and WS_OK.

Referenced by websocket_client_handshake(), and websocket_proxy_handshake().

◆ websocket_client_parse_uri()

static int websocket_client_parse_uri ( const char *  uri,
char **  host,
struct ast_str **  path,
char **  userinfo,
int  proxy 
)
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 1284 of file res_http_websocket.c.

1286{
1287 struct ast_uri *parsed_uri = proxy ? ast_uri_parse_http(uri) : ast_uri_parse_websocket(uri);
1288
1289 if (!parsed_uri) {
1290 return -1;
1291 }
1292
1293 *host = ast_uri_make_host_with_port(parsed_uri);
1294 *userinfo = ast_strdup(ast_uri_user_info(parsed_uri));
1295 if (ast_uri_path(parsed_uri) || ast_uri_query(parsed_uri)) {
1296 *path = ast_str_create(64);
1297 if (!*path) {
1298 ao2_ref(parsed_uri, -1);
1299 return -1;
1300 }
1301
1302 if (ast_uri_path(parsed_uri)) {
1303 ast_str_set(path, 0, "%s", ast_uri_path(parsed_uri));
1304 }
1305
1306 if (ast_uri_query(parsed_uri)) {
1307 ast_str_append(path, 0, "?%s", ast_uri_query(parsed_uri));
1308 }
1309 }
1310
1311 ao2_ref(parsed_uri, -1);
1312 return 0;
1313}
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_http(const char *uri)
Parse the given http uri into a structure.
Definition uri.c:290
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_http(), 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_client_start_handshake_timer()

static void websocket_client_start_handshake_timer ( struct websocket_client client)
static

Definition at line 1712 of file res_http_websocket.c.

1713{
1714 /*
1715 * ast_iostream_gets (called above in websocket_client_handshake_get_response) is
1716 * a blocking call which means that if the TCP/TLS connection succeeds but the remote doesn't
1717 * actually respond to the proxy CONNECT (if proxy is configured) or GET requests, the process
1718 * can hang indefinitely and escalate to a deadlock. To get ast_iostream_gets to timeout,
1719 * we need to make the following 3 calls on the iostream.
1720 *
1721 * Since the write of the CONNECT and/or GET request is included in the timeout, we'll
1722 * double the timeout set in the websocket client's "connect_timeout" parameter to give
1723 * the server enough time to respond.
1724 */
1728}

References ast_iostream_nonblock(), ast_iostream_set_exclusive_input(), ast_iostream_set_timeout_sequence(), ast_tvnow(), ast_websocket::client, websocket_client::options, websocket_client::ser, ast_tcptls_session_instance::stream, and ast_websocket_client_options::timeout.

Referenced by websocket_client_handshake(), and websocket_proxy_handshake().

◆ websocket_client_stop_handshake_timer()

static void websocket_client_stop_handshake_timer ( struct websocket_client client)
static

Definition at line 1730 of file res_http_websocket.c.

1731{
1732 /*
1733 * Once the handshake is complete, we need to undo what we did in
1734 * websocket_client_start_handshake_timer.
1735 */
1739}
void ast_iostream_blocking(struct ast_iostream *stream)
Make an iostream blocking.
Definition iostream.c:109

References ast_iostream_blocking(), ast_iostream_set_exclusive_input(), ast_iostream_set_timeout_disable(), ast_websocket::client, websocket_client::ser, and ast_tcptls_session_instance::stream.

Referenced by websocket_client_handshake(), and websocket_proxy_handshake().

◆ websocket_close()

static int websocket_close ( struct ast_websocket session,
uint16_t  reason,
int  force 
)
static

Definition at line 421 of file res_http_websocket.c.

422{
424 /* The header is either 2 or 6 bytes and the
425 * reason code takes up another 2 bytes */
426 char frame[8] = { 0, };
427 int header_size, fsize, res = 0;
428 int fd = session->stream ? ast_iostream_get_fd(session->stream) : -1;
429 SCOPE_ENTER(2, "%s: Close requested. Reason: %s (%d) Closed by: %s Force: %s\n", WS_SESSION_REMOTE(session),
430 ast_websocket_status_to_str(reason), reason, closed_by_to_str(session->closed_by), AST_YESNO(force));
431
433
435 if (session->closing) {
437 SCOPE_EXIT_RTN_VALUE(0, "%s: Close already sent\n", WS_SESSION_REMOTE(session));
438 }
439
440 session->closing = 1;
441
442 if (!session->stream) {
444 SCOPE_EXIT_RTN_VALUE(-1, "%s: WebSocket stream already closed\n", WS_SESSION_REMOTE(session));
445 }
446
447 if (force) {
448 ast_trace(-1, "%s: Forcing close. Handle: %p FD: %d\n", WS_SESSION_REMOTE(session),
449 session->stream, fd);
451 session->stream = NULL;
453 SCOPE_EXIT_RTN_VALUE(-1, "%s: Forced close\n", WS_SESSION_REMOTE(session));
454 }
455
456 /* clients need space for an additional 4 byte masking key */
457 header_size = session->client ? 6 : 2;
458 fsize = header_size + 2;
459 session->close_sent = 1;
460
461 frame[0] = opcode | 0x80;
462 frame[1] = 2; /* The reason code is always 2 bytes */
463
464 /*
465 * If the remote initiated the close we should respond with the same
466 * reason code they sent.
467 */
468 if (session->closed_by == WS_CLOSED_BY_REMOTE) {
469 reason = session->close_status_code;
470 }
471
472 /* If no reason has been specified assume 1000 which is normal closure */
473 put_unaligned_uint16(&frame[header_size], htons(reason ? reason : 1000));
474
475 websocket_mask_payload(session, frame, &frame[header_size], 2);
476 ast_trace(-1, "%s: Writing %sCLOSE frame with reason %s (%d). fd: %d\n", WS_SESSION_REMOTE(session),
477 session->closed_by == WS_CLOSED_BY_REMOTE ? "reply " : "", ast_websocket_status_to_str(reason), reason, fd);
478
480 res = ast_iostream_write(session->stream, frame, fsize);
482
483 /*
484 * If the remote initiated the close or we failed to send,
485 * we can close the socket. If we just sent a CLOSE of our own, we need to wait
486 * until we get the close reply from the remote.
487 */
488 if (session->closed_by == WS_CLOSED_BY_REMOTE || res != fsize) {
489 ast_trace(-1, "%s: %s Closing socket. fd: %d\n",
490 session->closed_by == WS_CLOSED_BY_REMOTE ? "Wrote CLOSE reply." : "Writing CLOSE failed.",
492
494 session->stream = NULL;
496 SCOPE_EXIT_RTN_VALUE(0, "%s: Socket closed after %s\n", WS_SESSION_REMOTE(session),
497 session->closed_by == WS_CLOSED_BY_REMOTE ? "reply" : "write failure");
498 }
499
501 SCOPE_EXIT_RTN_VALUE(res == sizeof(frame), "%s: Close done\n", WS_SESSION_REMOTE(session));
502}
void ast_iostream_set_timeout_inactivity(struct ast_iostream *stream, int timeout)
Set the iostream inactivity timeout timer.
Definition iostream.c:127
static const char * closed_by_to_str(enum ws_closed_by closed_by)
static void ping_scheduler_cancel(struct ast_websocket *session)
const char *AST_OPTIONAL_API_NAME() ast_websocket_status_to_str(enum ast_websocket_status_code code)
Convert a websocket status code to a string.

References ao2_lock, ao2_unlock, ast_iostream_close(), ast_iostream_get_fd(), ast_iostream_set_timeout_disable(), ast_iostream_set_timeout_inactivity(), ast_iostream_write(), ast_trace, AST_WEBSOCKET_OPCODE_CLOSE, ast_websocket_status_to_str(), AST_YESNO, closed_by_to_str(), NULL, ping_scheduler_cancel(), put_unaligned_uint16(), SCOPE_ENTER, SCOPE_EXIT_RTN_VALUE, session, websocket_mask_payload(), WS_CLOSED_BY_REMOTE, and WS_SESSION_REMOTE.

Referenced by ast_websocket_close(), ast_websocket_write(), ping_scheduler_callback(), and websocket_handled_pong_or_close().

◆ websocket_combine_key()

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

Definition at line 954 of file res_http_websocket.c.

955{
956 char *combined;
957 unsigned combined_length = strlen(key) + strlen(WEBSOCKET_GUID) + 1;
958 uint8_t sha[20];
959
960 combined = ast_alloca(combined_length);
961 snprintf(combined, combined_length, "%s%s", key, WEBSOCKET_GUID);
962 ast_sha1_hash_uint(sha, combined);
963 ast_base64encode(res, (const unsigned char*)sha, 20, res_size);
964 return res;
965}
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 1197 of file res_http_websocket.c.

1198{
1199 int res;
1200
1201 ast_debug(1, "Entering WebSocket echo loop\n");
1202
1203 if (ast_fd_set_flags(ast_websocket_fd(session), O_NONBLOCK)) {
1204 goto end;
1205 }
1206
1207 while ((res = ast_websocket_wait_for_input(session, -1)) > 0) {
1208 char *payload;
1209 uint64_t payload_len;
1210 enum ast_websocket_opcode opcode;
1211 int fragmented;
1212
1213 if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) {
1214 /* We err on the side of caution and terminate the session if any error occurs */
1215 ast_log(LOG_WARNING, "Read failure during WebSocket echo loop\n");
1216 break;
1217 }
1218
1219 if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
1220 ast_websocket_write(session, opcode, payload, payload_len);
1221 } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
1222 break;
1223 } else {
1224 ast_debug(1, "Ignored WebSocket opcode %u\n", opcode);
1225 }
1226 }
1227
1228end:
1229 ast_debug(1, "Exiting WebSocket echo loop\n");
1231}
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:1079

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_handled_pong_or_close()

static int websocket_handled_pong_or_close ( struct ast_websocket session,
char *  payload,
uint64_t  payload_len,
enum ast_websocket_opcode  opcode 
)
static

Definition at line 504 of file res_http_websocket.c.

506{
507 SCOPE_ENTER(4, "%s: Opcode: %s\n", WS_SESSION_REMOTE(session), websocket_opcode2str(opcode));
508
509 if (opcode == AST_WEBSOCKET_OPCODE_PONG) {
510 /*
511 * If it's from our own PING, reset the missed count.
512 */
513 if (session->client->missed_pong_count
514 && payload_len == WS_PING_PAYLOAD_LEN
515 && strncmp(payload, WS_PING_PAYLOAD, payload_len) == 0) {
516 int mpc = session->client->missed_pong_count;
517
518 session->client->missed_pong_count = 0;
519 SCOPE_EXIT_RTN_VALUE(1, "%s: Received PONG from our own PING. Missed count was: %d. Cleared.\n",
521 } else {
522 SCOPE_EXIT_RTN_VALUE(0, "%s: Received PONG. Passing up to client.\n", WS_SESSION_REMOTE(session));
523
524 }
525 }
526
527 if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
528 if (payload_len >= 2) {
529 session->close_status_code = ntohs(get_unaligned_uint16(payload));
530 }
531 if (session->closed_by == WS_NOT_CLOSED) {
532 session->closed_by = WS_CLOSED_BY_REMOTE;
533 SCOPE_EXIT_RTN_VALUE(1, "%s: Handled CLOSE request by remote with reason %s (%d)\n", WS_SESSION_REMOTE(session),
534 ast_websocket_status_to_str(session->close_status_code), session->close_status_code);
535 }
536
537 ast_trace(-1, "%s: Received CLOSE response from remote with reason: %s (%d)\n",
539 session->close_status_code);
540 /*
541 * We got the close response so we can now clean up the socket.
542 */
543 websocket_close(session, session->close_status_code, 1);
544
545
546 SCOPE_EXIT_RTN_VALUE(1, "%s: Handled CLOSE\n", WS_SESSION_REMOTE(session));
547 }
548
549 SCOPE_EXIT_RTN_VALUE(0, "%s: Unhandled %s opcode\n", WS_SESSION_REMOTE(session), websocket_opcode2str(opcode));
550}

References ast_trace, AST_WEBSOCKET_OPCODE_CLOSE, AST_WEBSOCKET_OPCODE_PONG, ast_websocket_status_to_str(), get_unaligned_uint16(), SCOPE_ENTER, SCOPE_EXIT_RTN_VALUE, session, websocket_close(), websocket_opcode2str(), WS_CLOSED_BY_REMOTE, WS_NOT_CLOSED, WS_PING_PAYLOAD, WS_PING_PAYLOAD_LEN, and WS_SESSION_REMOTE.

Referenced by ast_websocket_read().

◆ 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 373 of file res_http_websocket.c.

374{
375 /* RFC 6455 5.1 - clients MUST mask frame data */
376 if (session->client) {
377 uint64_t i;
378 uint8_t mask_key_idx;
379 uint32_t mask_key = ast_random();
380 uint8_t length = frame[1] & 0x7f;
381 frame[1] |= 0x80; /* set mask bit to 1 */
382 /* The mask key octet position depends on the length */
383 mask_key_idx = length == 126 ? 4 : length == 127 ? 10 : 2;
384 put_unaligned_uint32(&frame[mask_key_idx], mask_key);
385 for (i = 0; i < payload_size; i++) {
386 payload[i] ^= ((char *)&mask_key)[i % 4];
387 }
388 }
389}
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_write(), and websocket_close().

◆ websocket_opcode2str()

static const char * websocket_opcode2str ( enum ast_websocket_opcode  opcode)
static

Definition at line 400 of file res_http_websocket.c.

401{
402 if (!ARRAY_IN_BOUNDS(opcode, opcode_map)) {
403 return "<unknown>";
404 }
405 return opcode_map[opcode];
406}
static const char * opcode_map[]

References ARRAY_IN_BOUNDS, and opcode_map.

Referenced by ast_websocket_read(), ast_websocket_write(), and websocket_handled_pong_or_close().

◆ websocket_proxy_handshake()

static enum ast_websocket_result websocket_proxy_handshake ( struct websocket_client client)
static

Definition at line 1747 of file res_http_websocket.c.

1749{
1750 struct ast_variable *auth_header = NULL;
1751 enum ast_websocket_result res = WS_OK;
1752 size_t bytes_written = 0;
1753 SCOPE_ENTER(2, "%s: Handshaking with proxy %s\n", client->options->uri, client->options->proxy_host);
1754
1755 if (!ast_strlen_zero(client->proxy_userinfo)) {
1757 if (!auth_header) {
1758 SCOPE_EXIT_LOG_RTN_VALUE(WS_ALLOCATE_ERROR, LOG_ERROR, "Unable to allocate client websocket userinfo\n");
1759 }
1760 }
1761
1763
1764 bytes_written = ast_iostream_printf(client->ser->stream,
1765 "CONNECT %s HTTP/1.1\r\n"
1766 "Host: %s\r\n"
1768 "Proxy-Connection: Keep-Alive\r\n"
1769 "\r\n",
1770 client->host,
1771 client->host,
1772 print_optional_header(auth_header, "Proxy-Authorization: ", auth_header->value)
1773 );
1774
1775 ast_variables_destroy(auth_header);
1776 if (bytes_written < 0) {
1778 SCOPE_EXIT_LOG_RTN_VALUE(WS_WRITE_ERROR, LOG_ERROR, "Failed to send handshake\n");
1779 }
1780
1781 /* wait for a response before doing anything else */
1784
1786}

References ast_http_create_basic_auth_header(), ast_iostream_printf(), ast_strlen_zero(), ast_variables_destroy(), ast_websocket_result_to_str(), websocket_client::host, LOG_ERROR, NULL, optional_header_spec, websocket_client::options, print_optional_header, ast_websocket_client_options::proxy_host, websocket_client::proxy_userinfo, SCOPE_ENTER, SCOPE_EXIT_LOG_RTN_VALUE, SCOPE_EXIT_RTN_VALUE, websocket_client::ser, ast_tcptls_session_instance::stream, ast_websocket_client_options::uri, ast_variable::value, websocket_client_handshake_get_response(), websocket_client_start_handshake_timer(), websocket_client_stop_handshake_timer(), WS_ALLOCATE_ERROR, WS_OK, and WS_WRITE_ERROR.

Referenced by websocket_client_connect().

◆ websocket_remove_protocol_internal()

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

Definition at line 1262 of file res_http_websocket.c.

1263{
1264 struct ast_websocket_server *ws_server = websocketuri.data;
1265 if (!ws_server) {
1266 return -1;
1267 }
1269}
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 226 of file res_http_websocket.c.

227{
228 RAII_VAR(struct ast_websocket_server *, server, NULL, ao2_cleanup);
229
230 server = ao2_alloc(sizeof(*server), websocket_server_dtor);
231 if (!server) {
232 return NULL;
233 }
234
237 if (!server->protocols) {
238 return NULL;
239 }
240
241 ao2_ref(server, +1);
242 return server;
243}
@ 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:981

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 219 of file res_http_websocket.c.

220{
221 struct ast_websocket_server *server = obj;
222 ao2_cleanup(server->protocols);
223 server->protocols = NULL;
224}

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 245 of file res_http_websocket.c.

246{
248}

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 735 of file res_http_websocket.c.

736{
737 ssize_t rlen;
738 int xlen = len;
739 char *rbuf = buf;
740 int sanity = 10;
741
742 ast_assert(len > 0);
743
744 if (!len) {
745 errno = EINVAL;
746 return -1;
747 }
748
750 if (!session->stream) {
752 errno = ECONNABORTED;
753 return -1;
754 }
755
756 for (;;) {
757 rlen = ast_iostream_read(session->stream, rbuf, xlen);
758 if (rlen != xlen) {
759 if (rlen == 0) {
760 ast_log(LOG_WARNING, "Web socket closed abruptly\n");
762 session->closing = 1;
764 return -1;
765 }
766
767 if (rlen < 0 && errno != EAGAIN) {
768 ast_log(LOG_ERROR, "Error reading from web socket: %s\n", strerror(errno));
770 session->closing = 1;
772 return -1;
773 }
774
775 if (!--sanity) {
776 ast_log(LOG_WARNING, "Websocket seems unresponsive, disconnecting ...\n");
778 session->closing = 1;
780 return -1;
781 }
782 }
783 if (rlen > 0) {
784 xlen = xlen - rlen;
785 rbuf = rbuf + rlen;
786 if (!xlen) {
787 break;
788 }
789 }
790 if (ast_iostream_wait_for_input(session->stream, 1000) < 0) {
791 ast_log(LOG_ERROR, "ast_iostream_wait_for_input returned err: %s\n", strerror(errno));
793 session->closing = 1;
795 return -1;
796 }
797 }
798
800 return 0;
801}
ssize_t ast_iostream_read(struct ast_iostream *stream, void *buffer, size_t count)
Read data from an iostream.
Definition iostream.c:289
#define ast_assert(a)
Definition utils.h:779

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 = ASTERISK_GPL_KEY , .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 2219 of file res_http_websocket.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 2219 of file res_http_websocket.c.

◆ closed_by_str

const char* closed_by_str[]
static
Initial value:
= {
[WS_NOT_CLOSED] = "not closed",
[WS_CLOSED_BY_REMOTE] = "remote",
[WS_CLOSED_BY_US] = "local"
}

Definition at line 153 of file res_http_websocket.c.

153 {
154 [WS_NOT_CLOSED] = "not closed",
155 [WS_CLOSED_BY_REMOTE] = "remote",
156 [WS_CLOSED_BY_US] = "local"
157};

Referenced by closed_by_to_str().

◆ opcode_map

const char* opcode_map[]
static

Definition at line 391 of file res_http_websocket.c.

391 {
392 [AST_WEBSOCKET_OPCODE_CONTINUATION] = "continuation",
393 [AST_WEBSOCKET_OPCODE_TEXT] = "text",
394 [AST_WEBSOCKET_OPCODE_BINARY] = "binary",
395 [AST_WEBSOCKET_OPCODE_CLOSE] = "close",
396 [AST_WEBSOCKET_OPCODE_PING] = "ping",
397 [AST_WEBSOCKET_OPCODE_PONG] = "pong",
398};

Referenced by websocket_opcode2str().

◆ ping_scheduler

struct ast_sched_context* ping_scheduler
static

◆ websocket_result_string_map

const char* websocket_result_string_map[]

Definition at line 2111 of file res_http_websocket.c.

2111 {
2112 [WS_OK] = "OK",
2113 [WS_ALLOCATE_ERROR] = "Allocation error",
2114 [WS_KEY_ERROR] = "Key error",
2115 [WS_URI_PARSE_ERROR] = "URI parse error",
2116 [WS_URI_RESOLVE_ERROR] = "URI resolve error",
2117 [WS_BAD_STATUS] = "Bad status line",
2118 [WS_INVALID_RESPONSE] = "Invalid response code",
2119 [WS_BAD_REQUEST] = "Bad request",
2120 [WS_URL_NOT_FOUND] = "URL not found",
2121 [WS_HEADER_MISMATCH] = "Header mismatch",
2122 [WS_HEADER_MISSING] = "Header missing",
2123 [WS_NOT_SUPPORTED] = "Not supported",
2124 [WS_WRITE_ERROR] = "Write error",
2125 [WS_CLIENT_START_ERROR] = "Client start error",
2126 [WS_UNAUTHORIZED] = "Unauthorized"
2127};

Referenced by ast_websocket_result_to_str().

◆ websocket_status_map

const struct status_map websocket_status_map[]
static

Definition at line 2143 of file res_http_websocket.c.

2143 {
2144 { AST_WEBSOCKET_STATUS_NORMAL, "Normal" },
2145 { AST_WEBSOCKET_STATUS_GOING_AWAY, "Going away" },
2146 { AST_WEBSOCKET_STATUS_PROTOCOL_ERROR, "Protocol error" },
2147 { AST_WEBSOCKET_STATUS_UNSUPPORTED_DATA, "Unsupported data" },
2148 { AST_WEBSOCKET_STATUS_RESERVED_1004, "reserved 1004" },
2149 { AST_WEBSOCKET_STATUS_RESERVED_1005, "reserved 1005" },
2150 { AST_WEBSOCKET_STATUS_RESERVED_1006, "reserved 1006" },
2151 { AST_WEBSOCKET_STATUS_INVALID_FRAME, "Invalid frame" },
2152 { AST_WEBSOCKET_STATUS_POLICY_VIOLATION, "Policy violation" },
2153 { AST_WEBSOCKET_STATUS_TOO_BIG, "Data too big" },
2154 { AST_WEBSOCKET_STATUS_MANDATORY_EXT, "Mandatory extension" },
2155 { AST_WEBSOCKET_STATUS_INTERNAL_ERROR, "Internal error" },
2156 { AST_WEBSOCKET_STATUS_RESERVED_1012, "reserved 1012" },
2157 { AST_WEBSOCKET_STATUS_RESERVED_1013, "reserved 1013" },
2158 { AST_WEBSOCKET_STATUS_BAD_GATEWAY, "Bad gateway" },
2159 { AST_WEBSOCKET_STATUS_RESERVED_1015, "reserved 1015" },
2160};
@ AST_WEBSOCKET_STATUS_RESERVED_1015
@ AST_WEBSOCKET_STATUS_BAD_GATEWAY
@ AST_WEBSOCKET_STATUS_UNSUPPORTED_DATA
@ AST_WEBSOCKET_STATUS_PROTOCOL_ERROR
@ AST_WEBSOCKET_STATUS_NORMAL
@ AST_WEBSOCKET_STATUS_MANDATORY_EXT
@ AST_WEBSOCKET_STATUS_TOO_BIG
@ AST_WEBSOCKET_STATUS_RESERVED_1006
@ AST_WEBSOCKET_STATUS_RESERVED_1004
@ AST_WEBSOCKET_STATUS_RESERVED_1013
@ AST_WEBSOCKET_STATUS_RESERVED_1005
@ AST_WEBSOCKET_STATUS_POLICY_VIOLATION
@ AST_WEBSOCKET_STATUS_INTERNAL_ERROR
@ AST_WEBSOCKET_STATUS_RESERVED_1012
@ AST_WEBSOCKET_STATUS_INVALID_FRAME

Referenced by ast_websocket_status_to_str().

◆ websocketuri

struct ast_http_uri websocketuri
static

Definition at line 1187 of file res_http_websocket.c.

1187 {
1189 .description = "Asterisk HTTP WebSocket",
1190 .uri = "ws",
1191 .has_subtree = 0,
1192 .data = NULL,
1193 .key = __FILE__,
1194};
#define AST_OPTIONAL_API_NAME(name)
Expands to the name of the implementation function.
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.

Referenced by ast_websocket_add_protocol2(), load_module(), unload_module(), websocket_add_protocol_internal(), and websocket_remove_protocol_internal().