Asterisk - The Open Source Telephony Project GIT-master-c7a8271
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
Data Structures | Macros | Typedefs | Enumerations | Functions
http_websocket.h File Reference

Support for WebSocket connections within the Asterisk HTTP server and client WebSocket connections to a server. More...

#include "asterisk/http.h"
#include "asterisk/optional_api.h"
#include <errno.h>
Include dependency graph for http_websocket.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  ast_websocket_client_options
 Options used for a websocket client. More...
 
struct  ast_websocket_protocol
 A websocket protocol implementation. More...
 

Macros

#define AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT   100
 Default websocket write timeout, in ms. More...
 
#define AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR   "100"
 Default websocket write timeout, in ms (as a string) More...
 
#define AST_WEBSOCKET_PROTOCOL_VERSION   1
 Protocol version. This prevents dynamically loadable modules from registering if this struct is changed. More...
 

Typedefs

typedef void(* ast_websocket_callback) (struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
 Callback for when a new connection for a sub-protocol is established. More...
 
typedef int(* ast_websocket_pre_callback) (struct ast_tcptls_session_instance *ser, struct ast_variable *parameters, struct ast_variable *headers, const char *session_id)
 Callback from the HTTP request attempting to establish a websocket connection. More...
 

Enumerations

enum  ast_websocket_opcode {
  AST_WEBSOCKET_OPCODE_TEXT = 0x1 , AST_WEBSOCKET_OPCODE_BINARY = 0x2 , AST_WEBSOCKET_OPCODE_PING = 0x9 , AST_WEBSOCKET_OPCODE_PONG = 0xA ,
  AST_WEBSOCKET_OPCODE_CLOSE = 0x8 , AST_WEBSOCKET_OPCODE_CONTINUATION = 0x0
}
 WebSocket operation codes. More...
 
enum  ast_websocket_result {
  WS_OK = 0 , WS_ALLOCATE_ERROR , WS_KEY_ERROR , WS_URI_PARSE_ERROR ,
  WS_URI_RESOLVE_ERROR , WS_BAD_STATUS , WS_INVALID_RESPONSE , WS_BAD_REQUEST ,
  WS_URL_NOT_FOUND , WS_HEADER_MISMATCH , WS_HEADER_MISSING , WS_NOT_SUPPORTED ,
  WS_WRITE_ERROR , WS_CLIENT_START_ERROR , WS_UNAUTHORIZED
}
 Result code for a websocket client. More...
 
enum  ast_websocket_type {
  AST_WS_TYPE_CLIENT_PERSISTENT = (1 << 0) , AST_WS_TYPE_CLIENT_PER_CALL_CONFIG = (1 << 1) , AST_WS_TYPE_CLIENT_PER_CALL = (1 << 2) , AST_WS_TYPE_CLIENT = (1 << 3) ,
  AST_WS_TYPE_INBOUND = (1 << 4) , AST_WS_TYPE_SERVER = (1 << 5) , AST_WS_TYPE_ANY = (0xFFFFFFFF)
}
 WebSocket connection/configuration types. More...
 

Functions

int ast_websocket_add_protocol (const char *name, ast_websocket_callback callback)
 Add a sub-protocol handler to the default /ws server. More...
 
int ast_websocket_add_protocol2 (struct ast_websocket_protocol *protocol)
 Add a sub-protocol handler to the default /ws server. More...
 
const char * ast_websocket_client_accept_protocol (struct ast_websocket *ws)
 Retrieve the server accepted sub-protocol on the client. More...
 
struct ast_websocketast_websocket_client_create (const char *uri, const char *protocols, struct ast_tls_config *tls_cfg, enum ast_websocket_result *result)
 Create, and connect, a websocket client. More...
 
struct ast_websocketast_websocket_client_create_with_options (struct ast_websocket_client_options *options, enum ast_websocket_result *result)
 Create, and connect, a websocket client using given options. More...
 
int ast_websocket_close (struct ast_websocket *session, uint16_t reason)
 Close a WebSocket session by sending a message with the CLOSE opcode and an optional code. More...
 
int ast_websocket_fd (struct ast_websocket *session)
 Get the file descriptor for a WebSocket session. More...
 
int ast_websocket_is_secure (struct ast_websocket *session)
 Get whether the WebSocket session is using a secure transport or not. More...
 
struct ast_sockaddrast_websocket_local_address (struct ast_websocket *session)
 Get the local address for a WebSocket connection session. More...
 
int ast_websocket_read (struct ast_websocket *session, char **payload, uint64_t *payload_len, enum ast_websocket_opcode *opcode, int *fragmented)
 Read a WebSocket frame and handle it. More...
 
int ast_websocket_read_string (struct ast_websocket *ws, char **buf)
 Read a WebSocket frame containing string data. More...
 
void ast_websocket_reconstruct_disable (struct ast_websocket *session)
 Disable multi-frame reconstruction. More...
 
void ast_websocket_reconstruct_enable (struct ast_websocket *session, size_t bytes)
 Enable multi-frame reconstruction up to a certain number of bytes. More...
 
void ast_websocket_ref (struct ast_websocket *session)
 Increase the reference count for a WebSocket session. More...
 
struct ast_sockaddrast_websocket_remote_address (struct ast_websocket *session)
 Get the remote address for a WebSocket connected session. More...
 
int ast_websocket_remove_protocol (const char *name, ast_websocket_callback callback)
 Remove a sub-protocol handler from the default /ws server. More...
 
const char * ast_websocket_result_to_str (enum ast_websocket_result result)
 Convert a websocket result code to a string. More...
 
int ast_websocket_server_add_protocol (struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
 Add a sub-protocol handler to the given server. More...
 
int ast_websocket_server_add_protocol2 (struct ast_websocket_server *server, struct ast_websocket_protocol *protocol)
 Add a sub-protocol handler to the given server. More...
 
struct ast_websocket_serverast_websocket_server_create (void)
 Creates a ast_websocket_server. More...
 
int ast_websocket_server_remove_protocol (struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
 Remove a sub-protocol handler from the given server. More...
 
const char * ast_websocket_session_id (struct ast_websocket *session)
 Get the session ID for a WebSocket session. More...
 
int ast_websocket_set_nonblock (struct ast_websocket *session)
 Set the socket of a WebSocket session to be non-blocking. More...
 
int ast_websocket_set_timeout (struct ast_websocket *session, int timeout)
 Set the timeout on a non-blocking WebSocket session. More...
 
struct ast_websocket_protocolast_websocket_sub_protocol_alloc (const char *name)
 Allocate a websocket sub-protocol instance. More...
 
const char * ast_websocket_type_to_str (enum ast_websocket_type type)
 
void ast_websocket_unref (struct ast_websocket *session)
 Decrease the reference count for a WebSocket session. More...
 
int ast_websocket_uri_cb (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
 Callback suitable for use with a ast_http_uri. More...
 
int ast_websocket_wait_for_input (struct ast_websocket *session, int timeout)
 Wait for the WebSocket session to be ready to be read. More...
 
int ast_websocket_write (struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t payload_size)
 Construct and transmit a WebSocket frame. More...
 
int ast_websocket_write_string (struct ast_websocket *ws, const char *buf)
 Construct and transmit a WebSocket frame containing string data. More...
 

Detailed Description

Support for WebSocket connections within the Asterisk HTTP server and client WebSocket connections to a server.

Supported WebSocket versions in server implementation: Version 7 defined in specification http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07 Version 8 defined in specification http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10 Version 13 defined in specification http://tools.ietf.org/html/rfc6455 Supported WebSocket versions in client implementation: Version 13 defined in specification http://tools.ietf.org/html/rfc6455

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

Definition in file http_websocket.h.

Macro Definition Documentation

◆ AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT

#define AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT   100

Default websocket write timeout, in ms.

Definition at line 28 of file http_websocket.h.

◆ AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR

#define AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR   "100"

Default websocket write timeout, in ms (as a string)

Definition at line 31 of file http_websocket.h.

◆ AST_WEBSOCKET_PROTOCOL_VERSION

#define AST_WEBSOCKET_PROTOCOL_VERSION   1

Protocol version. This prevents dynamically loadable modules from registering if this struct is changed.

Definition at line 140 of file http_websocket.h.

Typedef Documentation

◆ ast_websocket_callback

typedef void(* ast_websocket_callback) (struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)

Callback for when a new connection for a sub-protocol is established.

Parameters
sessionA WebSocket session structure
parametersParameters extracted from the request URI
headersHeaders included in the request
Note
Once called the ownership of the session is transferred to the sub-protocol handler. It is responsible for closing and cleaning up.

Definition at line 121 of file http_websocket.h.

◆ ast_websocket_pre_callback

typedef int(* ast_websocket_pre_callback) (struct ast_tcptls_session_instance *ser, struct ast_variable *parameters, struct ast_variable *headers, const char *session_id)

Callback from the HTTP request attempting to establish a websocket connection.

This callback occurs when an HTTP request is made to establish a websocket connection. Implementers of ast_websocket_protocol can use this to deny a request, or to set up application specific data before invocation of ast_websocket_callback.

Parameters
serThe TCP/TLS session
parametersParameters extracted from the request URI
headersHeaders included in the request
session_idThe id of the current session.
Return values
0The session should be accepted
-1The session should be rejected. Note that the caller must send an error response using ast_http_error.
Since
13.5.0

Definition at line 108 of file http_websocket.h.

Enumeration Type Documentation

◆ ast_websocket_opcode

WebSocket operation codes.

Enumerator
AST_WEBSOCKET_OPCODE_TEXT 

Text frame

AST_WEBSOCKET_OPCODE_BINARY 

Binary frame

AST_WEBSOCKET_OPCODE_PING 

Request that the other side respond with a pong

AST_WEBSOCKET_OPCODE_PONG 

Response to a ping

AST_WEBSOCKET_OPCODE_CLOSE 

Connection is being closed

AST_WEBSOCKET_OPCODE_CONTINUATION 

Continuation of a previous frame

Definition at line 71 of file http_websocket.h.

71 {
72 AST_WEBSOCKET_OPCODE_TEXT = 0x1, /*!< Text frame */
73 AST_WEBSOCKET_OPCODE_BINARY = 0x2, /*!< Binary frame */
74 AST_WEBSOCKET_OPCODE_PING = 0x9, /*!< Request that the other side respond with a pong */
75 AST_WEBSOCKET_OPCODE_PONG = 0xA, /*!< Response to a ping */
76 AST_WEBSOCKET_OPCODE_CLOSE = 0x8, /*!< Connection is being closed */
77 AST_WEBSOCKET_OPCODE_CONTINUATION = 0x0, /*!< Continuation of a previous frame */
78};
@ AST_WEBSOCKET_OPCODE_PING
@ AST_WEBSOCKET_OPCODE_PONG
@ AST_WEBSOCKET_OPCODE_CONTINUATION
@ AST_WEBSOCKET_OPCODE_BINARY
@ AST_WEBSOCKET_OPCODE_CLOSE
@ AST_WEBSOCKET_OPCODE_TEXT

◆ ast_websocket_result

Result code for a websocket client.

Enumerator
WS_OK 
WS_ALLOCATE_ERROR 
WS_KEY_ERROR 
WS_URI_PARSE_ERROR 
WS_URI_RESOLVE_ERROR 
WS_BAD_STATUS 
WS_INVALID_RESPONSE 
WS_BAD_REQUEST 
WS_URL_NOT_FOUND 
WS_HEADER_MISMATCH 
WS_HEADER_MISSING 
WS_NOT_SUPPORTED 
WS_WRITE_ERROR 
WS_CLIENT_START_ERROR 
WS_UNAUTHORIZED 

Definition at line 419 of file http_websocket.h.

419 {
420 WS_OK = 0,
435};
@ WS_BAD_REQUEST
@ WS_URL_NOT_FOUND
@ WS_CLIENT_START_ERROR
@ WS_KEY_ERROR
@ WS_NOT_SUPPORTED
@ WS_ALLOCATE_ERROR
@ WS_HEADER_MISMATCH
@ WS_WRITE_ERROR
@ WS_UNAUTHORIZED
@ WS_URI_RESOLVE_ERROR
@ WS_OK
@ WS_INVALID_RESPONSE
@ WS_BAD_STATUS
@ WS_URI_PARSE_ERROR
@ WS_HEADER_MISSING

◆ ast_websocket_type

WebSocket connection/configuration types.

These may look like they overlap or are redundant, but they're shared by other modules like ari and chan_websocket and it didn't make sense to force them to define their own types.

Enumerator
AST_WS_TYPE_CLIENT_PERSISTENT 
AST_WS_TYPE_CLIENT_PER_CALL_CONFIG 
AST_WS_TYPE_CLIENT_PER_CALL 
AST_WS_TYPE_CLIENT 
AST_WS_TYPE_INBOUND 
AST_WS_TYPE_SERVER 
AST_WS_TYPE_ANY 

Definition at line 57 of file http_websocket.h.

57 {
61 AST_WS_TYPE_CLIENT = (1 << 3),
62 AST_WS_TYPE_INBOUND = (1 << 4),
63 AST_WS_TYPE_SERVER = (1 << 5),
64 AST_WS_TYPE_ANY = (0xFFFFFFFF),
65};
@ 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

Function Documentation

◆ ast_websocket_add_protocol()

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

1075{
1077}
static struct ast_channel * callback(struct ast_channelstorage_instance *driver, ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags)
static const char name[]
Definition: format_mp3.c:68
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_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 1079 of file res_http_websocket.c.

1080{
1081 struct ast_websocket_server *ws_server = websocketuri.data;
1082
1083 if (!ws_server) {
1084 return -1;
1085 }
1086
1087 if (ast_websocket_server_add_protocol2(ws_server, protocol)) {
1088 return -1;
1089 }
1090
1091 return 0;
1092}
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_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 1324 of file res_http_websocket.c.

1326{
1327 return ws->client->accept_protocol;
1328}
struct websocket_client * client

Referenced by AST_TEST_DEFINE().

◆ ast_websocket_client_create()

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

Create, and connect, a websocket client.

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

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

Definition at line 1521 of file res_http_websocket.c.

1524{
1526 .uri = uri,
1527 .protocols = protocols,
1528 .timeout = -1,
1529 .tls_cfg = tls_cfg,
1530 };
1531
1533}
static PGresult * result
Definition: cel_pgsql.c:84
struct ast_websocket *AST_OPTIONAL_API_NAME() ast_websocket_client_create_with_options(struct ast_websocket_client_options *options, enum ast_websocket_result *result)
Create, and connect, a websocket client using given options.
Options used for a websocket client.
struct ast_tls_config * tls_cfg
static struct test_options options

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

Referenced by AST_TEST_DEFINE().

◆ ast_websocket_client_create_with_options()

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

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

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

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

Definition at line 1535 of file res_http_websocket.c.

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

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

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

◆ ast_websocket_close()

int ast_websocket_close ( struct ast_websocket session,
uint16_t  reason 
)

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

Parameters
sessionPointer to the WebSocket session
reasonReason code for closing the session as defined in the RFC
Return values
0if successfully written
-1if error occurred

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

Definition at line 330 of file res_http_websocket.c.

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

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

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

◆ ast_websocket_fd()

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

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

References ast_iostream_get_fd(), and session.

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

◆ ast_websocket_is_secure()

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

504{
505 return session->secure;
506}

References session.

Referenced by transport_create().

◆ ast_websocket_local_address()

struct ast_sockaddr * 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 498 of file res_http_websocket.c.

499{
500 return &session->local_address;
501}

References session.

Referenced by transport_create().

◆ ast_websocket_read()

int ast_websocket_read ( struct ast_websocket session,
char **  payload,
uint64_t *  payload_len,
enum ast_websocket_opcode opcode,
int *  fragmented 
)

Read a WebSocket frame and handle it.

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

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

References ast_free, ast_log, ast_realloc, ast_websocket_close(), AST_WEBSOCKET_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, MAXIMUM_FRAME_SIZE, MIN_WS_HDR_SZ, ntohll(), NULL, ast_websocket::opcode, ast_websocket::payload, ast_websocket::payload_len, session, and ws_safe_read().

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

◆ ast_websocket_read_string()

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

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

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

Referenced by AST_TEST_DEFINE().

◆ ast_websocket_reconstruct_disable()

void ast_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 468 of file res_http_websocket.c.

469{
470 session->reconstruct = 0;
471}

References session.

◆ ast_websocket_reconstruct_enable()

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

464{
465 session->reconstruct = MIN(bytes, MAXIMUM_RECONSTRUCTION_CEILING);
466}
#define MAXIMUM_RECONSTRUCTION_CEILING
Maximum reconstruction size for multi-frame payload reconstruction.
#define MIN(a, b)
Definition: utils.h:231

References MAXIMUM_RECONSTRUCTION_CEILING, MIN, and session.

◆ ast_websocket_ref()

void ast_websocket_ref ( struct ast_websocket session)

Increase the reference count for a WebSocket session.

Parameters
sessionPointer to the WebSocket session

Definition at line 473 of file res_http_websocket.c.

474{
475 ao2_ref(session, +1);
476}

References ao2_ref, and session.

Referenced by transport_create().

◆ ast_websocket_remote_address()

struct ast_sockaddr * ast_websocket_remote_address ( struct ast_websocket session)

Get the remote address for a WebSocket connected session.

Returns
Remote address

Definition at line 493 of file res_http_websocket.c.

494{
495 return &session->remote_address;
496}

References session.

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

◆ ast_websocket_remove_protocol()

int ast_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 1103 of file res_http_websocket.c.

1104{
1106}
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_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 1632 of file res_http_websocket.c.

1634{
1636 return "unknown";
1637 }
1639}
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:687

References ARRAY_IN_BOUNDS, result, and websocket_result_string_map.

Referenced by ast_websocket_client_connect().

◆ ast_websocket_server_add_protocol()

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

233{
234 struct ast_websocket_protocol *protocol;
235
236 if (!server->protocols) {
237 return -1;
238 }
239
241 if (!protocol) {
242 return -1;
243 }
244 protocol->session_established = callback;
245
246 if (ast_websocket_server_add_protocol2(server, protocol)) {
247 ao2_ref(protocol, -1);
248 return -1;
249 }
250
251 return 0;
252}
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_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 254 of file res_http_websocket.c.

255{
256 struct ast_websocket_protocol *existing;
257
258 if (!server->protocols) {
259 return -1;
260 }
261
262 if (protocol->version != AST_WEBSOCKET_PROTOCOL_VERSION) {
263 ast_log(LOG_WARNING, "WebSocket could not register sub-protocol '%s': "
264 "expected version '%u', got version '%u'\n",
265 protocol->name, AST_WEBSOCKET_PROTOCOL_VERSION, protocol->version);
266 return -1;
267 }
268
269 ao2_lock(server->protocols);
270
271 /* Ensure a second protocol handler is not registered for the same protocol */
272 existing = ao2_find(server->protocols, protocol->name, OBJ_KEY | OBJ_NOLOCK);
273 if (existing) {
274 ao2_ref(existing, -1);
275 ao2_unlock(server->protocols);
276 return -1;
277 }
278
279 ao2_link_flags(server->protocols, protocol, OBJ_NOLOCK);
280 ao2_unlock(server->protocols);
281
282 ast_verb(5, "WebSocket registered sub-protocol '%s'\n", protocol->name);
283 ao2_ref(protocol, -1);
284
285 return 0;
286}
#define OBJ_KEY
Definition: astobj2.h:1151
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
Definition: astobj2.h:1554
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
@ OBJ_NOLOCK
Assume that the ao2_container is already locked.
Definition: astobj2.h:1063
#define AST_WEBSOCKET_PROTOCOL_VERSION
Protocol version. This prevents dynamically loadable modules from registering if this struct is chang...
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_log, ast_verb, 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(), and ast_websocket_server_add_protocol().

◆ ast_websocket_server_create()

struct ast_websocket_server * 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 189 of file res_http_websocket.c.

190{
192}
static struct ast_websocket_server * websocket_server_create_impl(void)

References websocket_server_create_impl().

Referenced by ari_websocket_load_module().

◆ ast_websocket_server_remove_protocol()

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

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

References ao2_find, ao2_ref, ao2_unlink, ast_verb, 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_websocket_session_id ( struct ast_websocket session)

Get the session ID for a WebSocket session.

Returns
session id

Definition at line 522 of file res_http_websocket.c.

523{
524 return session->session_id;
525}

References session.

Referenced by websocket_established_cb().

◆ ast_websocket_set_nonblock()

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

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

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

Referenced by session_update(), and websocket_cb().

◆ ast_websocket_set_timeout()

int ast_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 515 of file res_http_websocket.c.

516{
517 session->timeout = timeout;
518
519 return 0;
520}

References session, and ast_websocket::timeout.

Referenced by session_update(), and websocket_cb().

◆ ast_websocket_sub_protocol_alloc()

struct ast_websocket_protocol * 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 213 of file res_http_websocket.c.

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

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

Referenced by ari_websocket_load_module(), and ast_websocket_server_add_protocol().

◆ ast_websocket_type_to_str()

const char * ast_websocket_type_to_str ( enum ast_websocket_type  type)

Definition at line 106 of file res_http_websocket.c.

107{
108 switch (type) {
110 return "persistent";
112 return "per_call";
114 return "per_call_config";
116 return "client";
118 return "inbound";
120 return "server";
121 case AST_WS_TYPE_ANY:
122 return "any";
123 default:
124 return "unknown";
125 }
126}
static const char type[]
Definition: chan_ooh323.c:109

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

◆ ast_websocket_unref()

void ast_websocket_unref ( struct ast_websocket session)

Decrease the reference count for a WebSocket session.

Parameters
sessionPointer to the WebSocket session

Definition at line 478 of file res_http_websocket.c.

479{
481}
#define ao2_cleanup(obj)
Definition: astobj2.h:1934

References ao2_cleanup, and session.

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

◆ ast_websocket_uri_cb()

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

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

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

Referenced by ari_handle_websocket().

◆ ast_websocket_wait_for_input()

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

489{
490 return session->closing ? -1 : ast_iostream_wait_for_input(session->stream, timeout);
491}
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_websocket_write ( struct ast_websocket session,
enum ast_websocket_opcode  opcode,
char *  payload,
uint64_t  payload_size 
)

Construct and transmit a WebSocket frame.

Parameters
sessionPointer to the WebSocket session
opcodeWebSocket operation code to place in the frame
payloadOptional pointer to a payload to add to the frame
payload_sizeLength of the payload (0 if no payload)
Return values
0if successfully written
-1if error occurred

Construct and transmit a WebSocket frame.

Definition at line 396 of file res_http_websocket.c.

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

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

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

◆ ast_websocket_write_string()

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

1598{
1599 uint64_t len = strlen(buf);
1600
1601 ast_debug(3, "Writing websocket string of length %" PRIu64 "\n", len);
1602
1603 /* We do not pass strlen(buf) to ast_websocket_write() directly because the
1604 * size_t returned by strlen() may not require the same storage size
1605 * as the uint64_t that ast_websocket_write() uses. This normally
1606 * would not cause a problem, but since ast_websocket_write() uses
1607 * the optional API, this function call goes through a series of macros
1608 * that may cause a 32-bit to 64-bit conversion to go awry.
1609 */
1611 (char *)buf, len);
1612}
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(), session_write(), and speech_test_server_handle_request().