Asterisk - The Open Source Telephony Project  18.5.0
Data Structures | Macros | Functions | Variables
res_http_websocket.c File Reference

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

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

Go to the source code of this file.

Data Structures

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

Macros

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

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
int AST_OPTIONAL_API_NAME() ast_websocket_add_protocol (const char *name, ast_websocket_callback callback)
 
int AST_OPTIONAL_API_NAME() ast_websocket_add_protocol2 (struct ast_websocket_protocol *protocol)
 
const char *AST_OPTIONAL_API_NAME() ast_websocket_client_accept_protocol (struct ast_websocket *ws)
 
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)
 
int AST_OPTIONAL_API_NAME() ast_websocket_close (struct ast_websocket *session, uint16_t reason)
 Close function for websocket session. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_fd (struct ast_websocket *session)
 
int AST_OPTIONAL_API_NAME() ast_websocket_is_secure (struct ast_websocket *session)
 
struct ast_sockaddr *AST_OPTIONAL_API_NAME() ast_websocket_local_address (struct ast_websocket *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)
 
int AST_OPTIONAL_API_NAME() ast_websocket_read_string (struct ast_websocket *ws, char **buf)
 
void AST_OPTIONAL_API_NAME() ast_websocket_reconstruct_disable (struct ast_websocket *session)
 
void AST_OPTIONAL_API_NAME() ast_websocket_reconstruct_enable (struct ast_websocket *session, size_t bytes)
 
void AST_OPTIONAL_API_NAME() ast_websocket_ref (struct ast_websocket *session)
 
struct ast_sockaddr *AST_OPTIONAL_API_NAME() ast_websocket_remote_address (struct ast_websocket *session)
 
int AST_OPTIONAL_API_NAME() ast_websocket_remove_protocol (const char *name, ast_websocket_callback callback)
 
int AST_OPTIONAL_API_NAME() ast_websocket_server_add_protocol (struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
 
int AST_OPTIONAL_API_NAME() ast_websocket_server_add_protocol2 (struct ast_websocket_server *server, struct ast_websocket_protocol *protocol)
 
struct ast_websocket_server *AST_OPTIONAL_API_NAME() ast_websocket_server_create (void)
 
int AST_OPTIONAL_API_NAME() ast_websocket_server_remove_protocol (struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
 
const char *AST_OPTIONAL_API_NAME() ast_websocket_session_id (struct ast_websocket *session)
 
int AST_OPTIONAL_API_NAME() ast_websocket_set_nonblock (struct ast_websocket *session)
 
int AST_OPTIONAL_API_NAME() ast_websocket_set_timeout (struct ast_websocket *session, int timeout)
 
struct ast_websocket_protocol *AST_OPTIONAL_API_NAME() ast_websocket_sub_protocol_alloc (const char *name)
 
void AST_OPTIONAL_API_NAME() ast_websocket_unref (struct ast_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)
 
int AST_OPTIONAL_API_NAME() ast_websocket_wait_for_input (struct ast_websocket *session, int timeout)
 
int AST_OPTIONAL_API_NAME() ast_websocket_write (struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t payload_size)
 Write function for websocket traffic. More...
 
int AST_OPTIONAL_API_NAME() ast_websocket_write_string (struct ast_websocket *ws, const char *buf)
 
static int load_module (void)
 
static struct ast_websocket_protocolone_protocol (struct ast_websocket_server *server)
 If the server has exactly one configured protocol, return it. More...
 
static int protocol_cmp_fn (void *obj, void *arg, int flags)
 Comparison function for protocols. More...
 
static void protocol_destroy_fn (void *obj)
 Destructor function for protocols. More...
 
static int protocol_hash_fn (const void *obj, const int flags)
 Hashing function for protocols. More...
 
static void session_destroy_fn (void *obj)
 Destructor function for sessions. More...
 
static int unload_module (void)
 
static int websocket_add_protocol_internal (const char *name, ast_websocket_callback callback)
 
static void websocket_bad_request (struct ast_tcptls_session_instance *ser)
 
static struct ast_tcptls_session_argswebsocket_client_args_create (const char *host, struct ast_tls_config *tls_cfg, enum ast_websocket_result *result)
 
static void websocket_client_args_destroy (void *obj)
 
static enum ast_websocket_result websocket_client_connect (struct ast_websocket *ws)
 
static struct ast_websocketwebsocket_client_create (const char *uri, const char *protocols, struct ast_tls_config *tls_cfg, enum ast_websocket_result *result)
 
static char * websocket_client_create_key (void)
 
static void websocket_client_destroy (void *obj)
 
static enum ast_websocket_result websocket_client_handle_response_code (struct websocket_client *client, int response_code)
 
static enum ast_websocket_result websocket_client_handshake (struct websocket_client *client)
 
static enum ast_websocket_result websocket_client_handshake_get_response (struct websocket_client *client)
 
static int websocket_client_parse_uri (const char *uri, char **host, struct ast_str **path)
 Parse the given uri into a path and remote address. More...
 
static char * websocket_combine_key (const char *key, char *res, int res_size)
 
static void websocket_echo_callback (struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
 Simple echo implementation which echoes received text and binary frames. More...
 
static void websocket_mask_payload (struct ast_websocket *session, char *frame, char *payload, uint64_t payload_size)
 Perform payload masking for client sessions. More...
 
static const char * websocket_opcode2str (enum ast_websocket_opcode opcode)
 
static int websocket_remove_protocol_internal (const char *name, ast_websocket_callback callback)
 
static struct ast_websocket_serverwebsocket_server_create_impl (void)
 
static void websocket_server_dtor (void *obj)
 
static struct ast_websocket_serverwebsocket_server_internal_create (void)
 
static int ws_safe_read (struct ast_websocket *session, char *buf, size_t len, enum ast_websocket_opcode *opcode)
 

Variables

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

Detailed Description

WebSocket support for the Asterisk internal HTTP server.

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

Definition in file res_http_websocket.c.

Macro Definition Documentation

◆ AST_API_MODULE

#define AST_API_MODULE

Definition at line 41 of file res_http_websocket.c.

◆ CLIENT_KEY_SIZE

#define CLIENT_KEY_SIZE   16

Length of a websocket's client key.

Definition at line 48 of file res_http_websocket.c.

Referenced by websocket_client_create_key().

◆ DEFAULT_RECONSTRUCTION_CEILING

#define DEFAULT_RECONSTRUCTION_CEILING   MAXIMUM_FRAME_SIZE

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

Definition at line 71 of file res_http_websocket.c.

Referenced by ast_websocket_uri_cb(), and websocket_client_create().

◆ MAX_PROTOCOL_BUCKETS

#define MAX_PROTOCOL_BUCKETS   7

Number of buckets for registered protocols.

Definition at line 51 of file res_http_websocket.c.

Referenced by websocket_server_create_impl().

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

◆ MAXIMUM_FRAME_SIZE

#define MAXIMUM_FRAME_SIZE   65535

Size of the pre-determined buffer for WebSocket frames.

Definition at line 66 of file res_http_websocket.c.

Referenced by ast_websocket_read().

◆ MAXIMUM_RECONSTRUCTION_CEILING

#define MAXIMUM_RECONSTRUCTION_CEILING   MAXIMUM_FRAME_SIZE

Maximum reconstruction size for multi-frame payload reconstruction.

Definition at line 74 of file res_http_websocket.c.

Referenced by ast_websocket_reconstruct_enable().

◆ MIN_WS_HDR_SZ

#define MIN_WS_HDR_SZ   2

Definition at line 85 of file res_http_websocket.c.

Referenced by ast_websocket_read().

◆ WEBSOCKET_GUID

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

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

Definition at line 45 of file res_http_websocket.c.

Referenced by ast_websocket_uri_cb(), and websocket_combine_key().

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1529 of file res_http_websocket.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1529 of file res_http_websocket.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 1529 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 
)

Definition at line 1052 of file res_http_websocket.c.

References name, and websocket_add_protocol_internal().

Referenced by load_module(), and reload_config().

1053 {
1054  return websocket_add_protocol_internal(name, callback);
1055 }
static int websocket_add_protocol_internal(const char *name, ast_websocket_callback callback)
static const char name[]
Definition: cdr_mysql.c:74

◆ ast_websocket_add_protocol2()

int AST_OPTIONAL_API_NAME() ast_websocket_add_protocol2 ( struct ast_websocket_protocol protocol)

Definition at line 1057 of file res_http_websocket.c.

References ast_websocket_server_add_protocol2(), and ast_http_uri::data.

1058 {
1059  struct ast_websocket_server *ws_server = websocketuri.data;
1060 
1061  if (!ws_server) {
1062  return -1;
1063  }
1064 
1065  if (ast_websocket_server_add_protocol2(ws_server, protocol)) {
1066  return -1;
1067  }
1068 
1069  return 0;
1070 }
Structure for a WebSocket server.
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)
void * data
Definition: http.h:114

◆ ast_websocket_client_accept_protocol()

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

Definition at line 1270 of file res_http_websocket.c.

Referenced by __stub__ast_websocket_client_create(), and AST_TEST_DEFINE().

1271 {
1272  return ws->client->accept_protocol;
1273 }
struct websocket_client * client

◆ 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 
)

Definition at line 1421 of file res_http_websocket.c.

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

Referenced by AST_TEST_DEFINE(), and websocket_client_connect().

1423 {
1425  uri, protocols, tls_cfg, result);
1426 
1427  if (!ws) {
1428  return NULL;
1429  }
1430 
1431  if ((*result = websocket_client_connect(ws)) != WS_OK) {
1432  ao2_ref(ws, -1);
1433  return NULL;
1434  }
1435 
1436  return ws;
1437 }
#define NULL
Definition: resample.c:96
static enum ast_websocket_result websocket_client_connect(struct ast_websocket *ws)
static struct ast_websocket * websocket_client_create(const char *uri, const char *protocols, struct ast_tls_config *tls_cfg, enum ast_websocket_result *result)
#define ao2_ref(o, delta)
Definition: astobj2.h:464
Structure definition for session.
static PGresult * result
Definition: cel_pgsql.c:88

◆ ast_websocket_close()

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

Close function for websocket session.

Definition at line 308 of file res_http_websocket.c.

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, ast_websocket::client, ast_websocket::close_sent, ast_websocket::closing, NULL, ast_websocket::opcode, put_unaligned_uint16(), ast_websocket::remote_address, ast_websocket::stream, ast_websocket::timeout, and websocket_mask_payload().

Referenced by ast_websocket_read(), ast_websocket_write(), session_destroy_fn(), and ws_destroy().

309 {
311  /* The header is either 2 or 6 bytes and the
312  * reason code takes up another 2 bytes */
313  char frame[8] = { 0, };
314  int header_size, fsize, res;
315 
316  if (session->close_sent) {
317  return 0;
318  }
319 
320  /* clients need space for an additional 4 byte masking key */
321  header_size = session->client ? 6 : 2;
322  fsize = header_size + 2;
323 
324  frame[0] = opcode | 0x80;
325  frame[1] = 2; /* The reason code is always 2 bytes */
326 
327  /* If no reason has been specified assume 1000 which is normal closure */
328  put_unaligned_uint16(&frame[header_size], htons(reason ? reason : 1000));
329 
330  websocket_mask_payload(session, frame, &frame[header_size], 2);
331 
332  session->closing = 1;
333  session->close_sent = 1;
334 
335  ao2_lock(session);
337  res = ast_iostream_write(session->stream, frame, fsize);
339 
340  /* If an error occurred when trying to close this connection explicitly terminate it now.
341  * Doing so will cause the thread polling on it to wake up and terminate.
342  */
343  if (res != fsize) {
344  ast_iostream_close(session->stream);
345  session->stream = NULL;
346  ast_verb(2, "WebSocket connection %s '%s' forcefully closed due to fatal write error\n",
347  session->client ? "to" : "from", ast_sockaddr_stringify(&session->remote_address));
348  }
349 
350  ao2_unlock(session);
351  return res == sizeof(frame);
352 }
ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t count)
Write data to an iostream.
Definition: iostream.c:374
static void put_unaligned_uint16(void *p, unsigned short datum)
Definition: unaligned.h:65
void ast_iostream_set_timeout_inactivity(struct ast_iostream *stream, int timeout)
Set the iostream inactivity timeout timer.
Definition: iostream.c:121
#define ao2_unlock(a)
Definition: astobj2.h:730
unsigned int close_sent
#define NULL
Definition: resample.c:96
#define ast_verb(level,...)
Definition: logger.h:463
enum ast_websocket_opcode opcode
int ast_iostream_close(struct ast_iostream *stream)
Close an iostream.
Definition: iostream.c:528
#define ao2_lock(a)
Definition: astobj2.h:718
struct websocket_client * client
static void websocket_mask_payload(struct ast_websocket *session, char *frame, char *payload, uint64_t payload_size)
Perform payload masking for client sessions.
void ast_iostream_set_timeout_disable(struct ast_iostream *stream)
Disable the iostream timeout timer.
Definition: iostream.c:113
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:260
unsigned int closing
struct ast_sockaddr remote_address
struct ast_iostream * stream
ast_websocket_opcode
WebSocket operation codes.

◆ ast_websocket_fd()

int AST_OPTIONAL_API_NAME() ast_websocket_fd ( struct ast_websocket session)

Definition at line 461 of file res_http_websocket.c.

References ast_iostream_get_fd(), ast_websocket::closing, and ast_websocket::stream.

Referenced by ast_ari_websocket_session_read(), sip_prepare_socket(), sip_websocket_callback(), websocket_echo_callback(), and ws_destroy().

462 {
463  return session->closing ? -1 : ast_iostream_get_fd(session->stream);
464 }
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream's file descriptor.
Definition: iostream.c:84
unsigned int closing
struct ast_iostream * stream

◆ ast_websocket_is_secure()

int AST_OPTIONAL_API_NAME() ast_websocket_is_secure ( struct ast_websocket session)

Definition at line 481 of file res_http_websocket.c.

References ast_websocket::secure.

Referenced by sip_websocket_callback(), and transport_create().

482 {
483  return session->secure;
484 }
unsigned int secure

◆ ast_websocket_local_address()

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

Definition at line 476 of file res_http_websocket.c.

References ast_websocket::local_address.

Referenced by transport_create().

477 {
478  return &session->local_address;
479 }
struct ast_sockaddr local_address

◆ 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 
)

Definition at line 602 of file res_http_websocket.c.

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(), ast_websocket::buf, ast_websocket::close_status_code, ast_websocket::closing, 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, ast_websocket::reconstruct, and ws_safe_read().

Referenced by ast_ari_websocket_session_read(), ast_websocket_read_string(), sip_websocket_callback(), websocket_cb(), and websocket_echo_callback().

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

◆ ast_websocket_read_string()

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

Definition at line 1440 of file res_http_websocket.c.

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

Referenced by __stub__ast_websocket_read(), AST_TEST_DEFINE(), and ast_websocket_client_create().

1441 {
1442  char *payload;
1443  uint64_t payload_len;
1445  int fragmented = 1;
1446 
1447  while (fragmented) {
1448  if (ast_websocket_read(ws, &payload, &payload_len,
1449  &opcode, &fragmented)) {
1450  ast_log(LOG_ERROR, "Client WebSocket string read - "
1451  "error reading string data\n");
1452  return -1;
1453  }
1454 
1455  if (opcode == AST_WEBSOCKET_OPCODE_PING) {
1456  /* Try read again, we have sent pong already */
1457  fragmented = 1;
1458  continue;
1459  }
1460 
1461  if (opcode == AST_WEBSOCKET_OPCODE_CONTINUATION) {
1462  continue;
1463  }
1464 
1465  if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
1466  return -1;
1467  }
1468 
1469  if (opcode != AST_WEBSOCKET_OPCODE_TEXT) {
1470  ast_log(LOG_ERROR, "Client WebSocket string read - "
1471  "non string data received\n");
1472  return -1;
1473  }
1474  }
1475 
1476  if (!(*buf = ast_strndup(payload, payload_len))) {
1477  return -1;
1478  }
1479 
1480  return payload_len + 1;
1481 }
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)
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define ast_log
Definition: astobj2.c:42
enum ast_websocket_opcode opcode
#define LOG_ERROR
Definition: logger.h:285
#define ast_strndup(str, len)
A wrapper for strndup()
Definition: astmm.h:258
ast_websocket_opcode
WebSocket operation codes.

◆ ast_websocket_reconstruct_disable()

void AST_OPTIONAL_API_NAME() ast_websocket_reconstruct_disable ( struct ast_websocket session)

Definition at line 446 of file res_http_websocket.c.

References ast_websocket::reconstruct.

447 {
448  session->reconstruct = 0;
449 }

◆ ast_websocket_reconstruct_enable()

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

Definition at line 441 of file res_http_websocket.c.

References MAXIMUM_RECONSTRUCTION_CEILING, MIN, and ast_websocket::reconstruct.

442 {
444 }
#define MIN(a, b)
Definition: utils.h:226
#define MAXIMUM_RECONSTRUCTION_CEILING
Maximum reconstruction size for multi-frame payload reconstruction.

◆ ast_websocket_ref()

void AST_OPTIONAL_API_NAME() ast_websocket_ref ( struct ast_websocket session)

Definition at line 451 of file res_http_websocket.c.

References ao2_ref.

Referenced by copy_socket_data(), and transport_create().

452 {
453  ao2_ref(session, +1);
454 }
#define ao2_ref(o, delta)
Definition: astobj2.h:464

◆ ast_websocket_remote_address()

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

Definition at line 471 of file res_http_websocket.c.

References ast_websocket::remote_address.

Referenced by ast_ari_websocket_session_get_remote_addr(), sip_websocket_callback(), transport_create(), and transport_read().

472 {
473  return &session->remote_address;
474 }
struct ast_sockaddr remote_address

◆ ast_websocket_remove_protocol()

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

Definition at line 1081 of file res_http_websocket.c.

References name, and websocket_remove_protocol_internal().

Referenced by reload_config(), and unload_module().

1082 {
1083  return websocket_remove_protocol_internal(name, callback);
1084 }
static int websocket_remove_protocol_internal(const char *name, ast_websocket_callback callback)
static const char name[]
Definition: cdr_mysql.c:74

◆ 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 
)

Definition at line 210 of file res_http_websocket.c.

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

Referenced by websocket_add_protocol_internal().

211 {
212  struct ast_websocket_protocol *protocol;
213 
214  if (!server->protocols) {
215  return -1;
216  }
217 
219  if (!protocol) {
220  return -1;
221  }
222  protocol->session_established = callback;
223 
224  if (ast_websocket_server_add_protocol2(server, protocol)) {
225  ao2_ref(protocol, -1);
226  return -1;
227  }
228 
229  return 0;
230 }
A websocket protocol implementation.
struct ao2_container * protocols
#define ao2_ref(o, delta)
Definition: astobj2.h:464
int AST_OPTIONAL_API_NAME() ast_websocket_server_add_protocol2(struct ast_websocket_server *server, struct ast_websocket_protocol *protocol)
static const char name[]
Definition: cdr_mysql.c:74
ast_websocket_callback session_established
struct ast_websocket_protocol *AST_OPTIONAL_API_NAME() ast_websocket_sub_protocol_alloc(const char *name)

◆ 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 
)

Definition at line 232 of file res_http_websocket.c.

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 ast_websocket_add_protocol2(), ast_websocket_server_add_protocol(), and load_module().

233 {
234  struct ast_websocket_protocol *existing;
235 
236  if (!server->protocols) {
237  return -1;
238  }
239 
240  if (protocol->version != AST_WEBSOCKET_PROTOCOL_VERSION) {
241  ast_log(LOG_WARNING, "WebSocket could not register sub-protocol '%s': "
242  "expected version '%u', got version '%u'\n",
243  protocol->name, AST_WEBSOCKET_PROTOCOL_VERSION, protocol->version);
244  return -1;
245  }
246 
247  ao2_lock(server->protocols);
248 
249  /* Ensure a second protocol handler is not registered for the same protocol */
250  existing = ao2_find(server->protocols, protocol->name, OBJ_KEY | OBJ_NOLOCK);
251  if (existing) {
252  ao2_ref(existing, -1);
253  ao2_unlock(server->protocols);
254  return -1;
255  }
256 
257  ao2_link_flags(server->protocols, protocol, OBJ_NOLOCK);
258  ao2_unlock(server->protocols);
259 
260  ast_verb(2, "WebSocket registered sub-protocol '%s'\n", protocol->name);
261  ao2_ref(protocol, -1);
262 
263  return 0;
264 }
A websocket protocol implementation.
#define OBJ_KEY
Definition: astobj2.h:1155
#define LOG_WARNING
Definition: logger.h:274
Assume that the ao2_container is already locked.
Definition: astobj2.h:1067
struct ao2_container * protocols
#define ao2_link_flags(container, obj, flags)
Definition: astobj2.h:1572
#define ao2_unlock(a)
Definition: astobj2.h:730
#define ast_verb(level,...)
Definition: logger.h:463
#define ast_log
Definition: astobj2.c:42
char * name
Name of the protocol.
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_lock(a)
Definition: astobj2.h:718
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
#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.

◆ ast_websocket_server_create()

struct ast_websocket_server* AST_OPTIONAL_API_NAME() ast_websocket_server_create ( void  )

Definition at line 167 of file res_http_websocket.c.

References websocket_server_create_impl().

Referenced by load_module().

168 {
170 }
static struct ast_websocket_server * websocket_server_create_impl(void)

◆ 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 
)

Definition at line 266 of file res_http_websocket.c.

References ao2_find, ao2_ref, ao2_unlink, ast_verb, name, OBJ_KEY, ast_websocket_server::protocols, and ast_websocket_protocol::session_established.

Referenced by websocket_remove_protocol_internal().

267 {
268  struct ast_websocket_protocol *protocol;
269 
270  if (!(protocol = ao2_find(server->protocols, name, OBJ_KEY))) {
271  return -1;
272  }
273 
274  if (protocol->session_established != callback) {
275  ao2_ref(protocol, -1);
276  return -1;
277  }
278 
279  ao2_unlink(server->protocols, protocol);
280  ao2_ref(protocol, -1);
281 
282  ast_verb(2, "WebSocket unregistered sub-protocol '%s'\n", name);
283 
284  return 0;
285 }
A websocket protocol implementation.
#define OBJ_KEY
Definition: astobj2.h:1155
struct ao2_container * protocols
#define ast_verb(level,...)
Definition: logger.h:463
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_unlink(container, obj)
Definition: astobj2.h:1598
static const char name[]
Definition: cdr_mysql.c:74
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
ast_websocket_callback session_established

◆ ast_websocket_session_id()

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

Definition at line 500 of file res_http_websocket.c.

References ast_websocket::session_id.

Referenced by ast_ari_websocket_session_id().

501 {
502  return session->session_id;
503 }
char session_id[AST_UUID_STR_LEN]

◆ ast_websocket_set_nonblock()

int AST_OPTIONAL_API_NAME() ast_websocket_set_nonblock ( struct ast_websocket session)

Definition at line 486 of file res_http_websocket.c.

References ast_iostream_nonblock(), ast_iostream_set_exclusive_input(), and ast_websocket::stream.

Referenced by ast_ari_websocket_session_create(), sip_websocket_callback(), and websocket_cb().

487 {
488  ast_iostream_nonblock(session->stream);
490  return 0;
491 }
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:148
struct ast_iostream * stream
void ast_iostream_nonblock(struct ast_iostream *stream)
Make an iostream non-blocking.
Definition: iostream.c:103

◆ ast_websocket_set_timeout()

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

Definition at line 493 of file res_http_websocket.c.

References ast_websocket::timeout.

Referenced by ast_ari_websocket_session_create(), sip_websocket_callback(), and websocket_cb().

494 {
495  session->timeout = timeout;
496 
497  return 0;
498 }
static int timeout
Definition: cdr_mysql.c:86

◆ ast_websocket_sub_protocol_alloc()

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

Definition at line 191 of file res_http_websocket.c.

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 ast_websocket_server_add_protocol(), and load_module().

192 {
193  struct ast_websocket_protocol *protocol;
194 
195  protocol = ao2_alloc(sizeof(*protocol), protocol_destroy_fn);
196  if (!protocol) {
197  return NULL;
198  }
199 
200  protocol->name = ast_strdup(name);
201  if (!protocol->name) {
202  ao2_ref(protocol, -1);
203  return NULL;
204  }
206 
207  return protocol;
208 }
A websocket protocol implementation.
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define NULL
Definition: resample.c:96
char * name
Name of the protocol.
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
static const char name[]
Definition: cdr_mysql.c:74
static void protocol_destroy_fn(void *obj)
Destructor function for protocols.
#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.

◆ ast_websocket_unref()

void AST_OPTIONAL_API_NAME() ast_websocket_unref ( struct ast_websocket session)

◆ 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 
)

Definition at line 790 of file res_http_websocket.c.

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, ast_websocket::local_address, LOG_WARNING, method, ast_variable::name, ast_websocket_protocol::name, ast_variable::next, NULL, OBJ_KEY, one_protocol(), ast_websocket::opcode, ast_websocket_server::protocols, ast_websocket::reconstruct, ast_websocket::remote_address, ast_tcptls_session_instance::remote_address, SCOPED_MODULE_USE, ast_websocket::secure, ast_module_info::self, session, ast_websocket_protocol::session_attempted, session_destroy_fn(), ast_websocket_protocol::session_established, ast_websocket::session_id, ast_websocket::stream, ast_tcptls_session_instance::stream, strsep(), ast_websocket::timeout, 210693f3123d_create_cdr_table::upgrade(), ast_variable::value, version, websocket_bad_request(), websocket_combine_key(), and WEBSOCKET_GUID.

Referenced by ari_handle_websocket().

791 {
792  struct ast_variable *v;
793  const char *upgrade = NULL, *key = NULL, *key1 = NULL, *key2 = NULL, *protos = NULL;
794  char *requested_protocols = NULL, *protocol = NULL;
795  int version = 0, flags = 1;
796  struct ast_websocket_protocol *protocol_handler = NULL;
797  struct ast_websocket *session;
798  struct ast_websocket_server *server;
799 
801 
802  /* Upgrade requests are only permitted on GET methods */
803  if (method != AST_HTTP_GET) {
804  ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
805  return 0;
806  }
807 
808  server = urih->data;
809 
810  /* Get the minimum headers required to satisfy our needs */
811  for (v = headers; v; v = v->next) {
812  if (!strcasecmp(v->name, "Upgrade")) {
813  upgrade = v->value;
814  } else if (!strcasecmp(v->name, "Sec-WebSocket-Key")) {
815  key = v->value;
816  } else if (!strcasecmp(v->name, "Sec-WebSocket-Key1")) {
817  key1 = v->value;
818  } else if (!strcasecmp(v->name, "Sec-WebSocket-Key2")) {
819  key2 = v->value;
820  } else if (!strcasecmp(v->name, "Sec-WebSocket-Protocol")) {
821  protos = v->value;
822  } else if (!strcasecmp(v->name, "Sec-WebSocket-Version")) {
823  if (sscanf(v->value, "%30d", &version) != 1) {
824  version = 0;
825  }
826  }
827  }
828 
829  /* If this is not a websocket upgrade abort */
830  if (!upgrade || strcasecmp(upgrade, "websocket")) {
831  ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - did not request WebSocket\n",
833  ast_http_error(ser, 426, "Upgrade Required", NULL);
834  return 0;
835  } else if (ast_strlen_zero(protos)) {
836  /* If there's only a single protocol registered, and the
837  * client doesn't specify what protocol it's using, go ahead
838  * and accept the connection */
839  protocol_handler = one_protocol(server);
840  if (!protocol_handler) {
841  /* Multiple registered subprotocols; client must specify */
842  ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols requested\n",
845  return 0;
846  }
847  } else if (key1 && key2) {
848  /* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 and
849  * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -- not currently supported*/
850  ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '00/76' chosen\n",
853  return 0;
854  }
855 
856  if (!protocol_handler && protos) {
857  requested_protocols = ast_strdupa(protos);
858  /* Iterate through the requested protocols trying to find one that we have a handler for */
859  while (!protocol_handler && (protocol = strsep(&requested_protocols, ","))) {
860  protocol_handler = ao2_find(server->protocols, ast_strip(protocol), OBJ_KEY);
861  }
862  }
863 
864  /* If no protocol handler exists bump this back to the requester */
865  if (!protocol_handler) {
866  ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols out of '%s' supported\n",
867  ast_sockaddr_stringify(&ser->remote_address), protos);
869  return 0;
870  }
871 
872  /* Determine how to respond depending on the version */
873  if (version == 7 || version == 8 || version == 13) {
874  char base64[64];
875 
876  if (!key || strlen(key) + strlen(WEBSOCKET_GUID) + 1 > 8192) { /* no stack overflows please */
878  ao2_ref(protocol_handler, -1);
879  return 0;
880  }
881 
882  if (ast_http_body_discard(ser)) {
884  ao2_ref(protocol_handler, -1);
885  return 0;
886  }
887 
888  if (!(session = ao2_alloc(sizeof(*session) + AST_UUID_STR_LEN + 1, session_destroy_fn))) {
889  ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted\n",
892  ao2_ref(protocol_handler, -1);
893  return 0;
894  }
896 
897  /* Generate the session id */
898  if (!ast_uuid_generate_str(session->session_id, sizeof(session->session_id))) {
899  ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to generate a session id\n",
901  ast_http_error(ser, 500, "Internal Server Error", "Allocation failed");
902  ao2_ref(protocol_handler, -1);
903  return 0;
904  }
905 
906  if (protocol_handler->session_attempted
907  && protocol_handler->session_attempted(ser, get_vars, headers, session->session_id)) {
908  ast_debug(3, "WebSocket connection from '%s' rejected by protocol handler '%s'\n",
909  ast_sockaddr_stringify(&ser->remote_address), protocol_handler->name);
911  ao2_ref(protocol_handler, -1);
912  return 0;
913  }
914 
915  /* RFC 6455, Section 4.1:
916  *
917  * 6. If the response includes a |Sec-WebSocket-Protocol| header
918  * field and this header field indicates the use of a
919  * subprotocol that was not present in the client's handshake
920  * (the server has indicated a subprotocol not requested by
921  * the client), the client MUST _Fail the WebSocket
922  * Connection_.
923  */
924  if (protocol) {
926  "HTTP/1.1 101 Switching Protocols\r\n"
927  "Upgrade: %s\r\n"
928  "Connection: Upgrade\r\n"
929  "Sec-WebSocket-Accept: %s\r\n"
930  "Sec-WebSocket-Protocol: %s\r\n\r\n",
931  upgrade,
932  websocket_combine_key(key, base64, sizeof(base64)),
933  protocol);
934  } else {
936  "HTTP/1.1 101 Switching Protocols\r\n"
937  "Upgrade: %s\r\n"
938  "Connection: Upgrade\r\n"
939  "Sec-WebSocket-Accept: %s\r\n\r\n",
940  upgrade,
941  websocket_combine_key(key, base64, sizeof(base64)));
942  }
943  } else {
944 
945  /* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 or completely unknown */
946  ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '%d' chosen\n",
947  ast_sockaddr_stringify(&ser->remote_address), version ? version : 75);
949  ao2_ref(protocol_handler, -1);
950  return 0;
951  }
952 
953  /* Enable keepalive on all sessions so the underlying user does not have to */
954  if (setsockopt(ast_iostream_get_fd(ser->stream), SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
955  ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to enable keepalive\n",
958  ao2_ref(session, -1);
959  ao2_ref(protocol_handler, -1);
960  return 0;
961  }
962 
963  /* Get our local address for the connected socket */
965  ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to get local address\n",
968  ao2_ref(session, -1);
969  ao2_ref(protocol_handler, -1);
970  return 0;
971  }
972 
973  ast_verb(2, "WebSocket connection from '%s' for protocol '%s' accepted using version '%d'\n", ast_sockaddr_stringify(&ser->remote_address), protocol ? : "", version);
974 
975  /* Populate the session with all the needed details */
976  session->stream = ser->stream;
978  session->opcode = -1;
980  session->secure = ast_iostream_get_ssl(ser->stream) ? 1 : 0;
981 
982  /* Give up ownership of the socket and pass it to the protocol handler */
984  protocol_handler->session_established(session, get_vars, headers);
985  ao2_ref(protocol_handler, -1);
986 
987  /*
988  * By dropping the stream from the session the connection
989  * won't get closed when the HTTP server cleans up because we
990  * passed the connection to the protocol handler.
991  */
992  ser->stream = NULL;
993 
994  return 0;
995 }
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:148
struct ast_variable * next
static struct ast_websocket_protocol * one_protocol(struct ast_websocket_server *server)
If the server has exactly one configured protocol, return it.
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:648
A websocket protocol implementation.
#define AST_UUID_STR_LEN
Definition: uuid.h:27
#define WEBSOCKET_GUID
GUID used to compute the accept key, defined in the specifications.
int ast_http_body_discard(struct ast_tcptls_session_instance *ser)
Read and discard any unread HTTP request body.
Definition: http.c:1120
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:171
#define OBJ_KEY
Definition: astobj2.h:1155
#define LOG_WARNING
Definition: logger.h:274
Structure for a WebSocket server.
ast_websocket_pre_callback session_attempted
Callback called when a new session is attempted. Optional.
Structure for variables, used for configurations and for channel variables.
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream&#39;s file descriptor.
Definition: iostream.c:84
#define DEFAULT_RECONSTRUCTION_CEILING
Default reconstruction size for multi-frame payload reconstruction. If exceeded the next frame will s...
struct ao2_container * protocols
#define NULL
Definition: resample.c:96
#define ast_verb(level,...)
Definition: logger.h:463
#define ast_strlen_zero(foo)
Definition: strings.h:52
SSL * ast_iostream_get_ssl(struct ast_iostream *stream)
Get a pointer to an iostream&#39;s OpenSSL SSL structure.
Definition: iostream.c:108
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
enum ast_websocket_opcode opcode
char * name
Name of the protocol.
struct ast_module * self
Definition: module.h:342
static struct ast_mansession session
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
const char * method
Definition: res_pjsip.c:4335
ssize_t ast_iostream_printf(struct ast_iostream *stream, const char *format,...)
Write a formatted string to an iostream.
Definition: iostream.c:491
struct ast_sockaddr local_address
char session_id[AST_UUID_STR_LEN]
#define AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT
Default websocket write timeout, in ms.
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:260
static void session_destroy_fn(void *obj)
Destructor function for sessions.
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
static char version[AST_MAX_EXTENSION]
Definition: chan_ooh323.c:391
Structure definition for session.
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Definition: uuid.c:143
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
#define SCOPED_MODULE_USE(module)
Definition: module.h:665
struct ast_iostream * stream
Definition: tcptls.h:160
unsigned int secure
struct ast_sockaddr remote_address
char * strsep(char **str, const char *delims)
static void websocket_bad_request(struct ast_tcptls_session_instance *ser)
struct ast_iostream * stream
ast_websocket_callback session_established
int ast_getsockname(int sockfd, struct ast_sockaddr *addr)
Wrapper around getsockname(2) that uses struct ast_sockaddr.
Definition: netsock2.c:600
void * data
Definition: http.h:114
static char * websocket_combine_key(const char *key, char *res, int res_size)
static char base64[64]
Definition: main/utils.c:78
struct ast_sockaddr remote_address
Definition: tcptls.h:151

◆ ast_websocket_wait_for_input()

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

Definition at line 466 of file res_http_websocket.c.

References ast_iostream_wait_for_input(), ast_websocket::closing, ast_websocket::stream, and ast_websocket::timeout.

Referenced by websocket_cb(), and websocket_echo_callback().

467 {
468  return session->closing ? -1 : ast_iostream_wait_for_input(session->stream, timeout);
469 }
int ast_iostream_wait_for_input(struct ast_iostream *stream, int timeout)
Wait for input on the iostream&#39;s file descriptor.
Definition: iostream.c:89
static int timeout
Definition: cdr_mysql.c:86
unsigned int closing
struct ast_iostream * stream

◆ 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.

Definition at line 374 of file res_http_websocket.c.

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(), ast_websocket::client, ast_websocket::closing, frame_size, htonll(), ast_websocket::opcode, ast_websocket::payload, put_unaligned_uint16(), put_unaligned_uint64(), ast_websocket::stream, ast_websocket::timeout, websocket_mask_payload(), and websocket_opcode2str().

Referenced by ast_ari_events_event_websocket_ws_established_cb(), ast_websocket_read(), ast_websocket_write_string(), websocket_echo_callback(), and ws_send_msg().

375 {
376  size_t header_size = 2; /* The minimum size of a websocket frame is 2 bytes */
377  char *frame;
378  uint64_t length;
379  uint64_t frame_size;
380 
381  ast_debug(3, "Writing websocket %s frame, length %" PRIu64 "\n",
382  websocket_opcode2str(opcode), payload_size);
383 
384  if (payload_size < 126) {
385  length = payload_size;
386  } else if (payload_size < (1 << 16)) {
387  length = 126;
388  /* We need an additional 2 bytes to store the extended length */
389  header_size += 2;
390  } else {
391  length = 127;
392  /* We need an additional 8 bytes to store the really really extended length */
393  header_size += 8;
394  }
395 
396  if (session->client) {
397  /* Additional 4 bytes for the client masking key */
398  header_size += 4;
399  }
400 
401  frame_size = header_size + payload_size;
402 
403  frame = ast_alloca(frame_size + 1);
404  memset(frame, 0, frame_size + 1);
405 
406  frame[0] = opcode | 0x80;
407  frame[1] = length;
408 
409  /* Use the additional available bytes to store the length */
410  if (length == 126) {
411  put_unaligned_uint16(&frame[2], htons(payload_size));
412  } else if (length == 127) {
413  put_unaligned_uint64(&frame[2], htonll(payload_size));
414  }
415 
416  memcpy(&frame[header_size], payload, payload_size);
417 
418  websocket_mask_payload(session, frame, &frame[header_size], payload_size);
419 
420  ao2_lock(session);
421  if (session->closing) {
422  ao2_unlock(session);
423  return -1;
424  }
425 
427  if (ast_iostream_write(session->stream, frame, frame_size) != frame_size) {
428  ao2_unlock(session);
429  /* 1011 - server terminating connection due to not being able to fulfill the request */
430  ast_debug(1, "Closing WS with 1011 because we can't fulfill a write request\n");
431  ast_websocket_close(session, 1011);
432  return -1;
433  }
434 
436  ao2_unlock(session);
437 
438  return 0;
439 }
ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t count)
Write data to an iostream.
Definition: iostream.c:374
static const char * websocket_opcode2str(enum ast_websocket_opcode opcode)
static void put_unaligned_uint16(void *p, unsigned short datum)
Definition: unaligned.h:65
int AST_OPTIONAL_API_NAME() ast_websocket_close(struct ast_websocket *session, uint16_t reason)
Close function for websocket session.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define ao2_unlock(a)
Definition: astobj2.h:730
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
uint64_t htonll(uint64_t host64)
Definition: strcompat.c:390
#define ao2_lock(a)
Definition: astobj2.h:718
struct websocket_client * client
static void websocket_mask_payload(struct ast_websocket *session, char *frame, char *payload, uint64_t payload_size)
Perform payload masking for client sessions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
void ast_iostream_set_timeout_disable(struct ast_iostream *stream)
Disable the iostream timeout timer.
Definition: iostream.c:113
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:139
static void put_unaligned_uint64(void *p, uint64_t datum)
Definition: unaligned.h:51
unsigned int closing
struct ast_iostream * stream
static int frame_size[4]
Definition: format_g726.c:52

◆ ast_websocket_write_string()

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

Definition at line 1484 of file res_http_websocket.c.

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

Referenced by __sip_xmit(), __stub__ast_websocket_write(), ast_ari_websocket_session_write(), AST_TEST_DEFINE(), and ast_websocket_read_string().

1485 {
1486  uint64_t len = strlen(buf);
1487 
1488  ast_debug(3, "Writing websocket string of length %" PRIu64 "\n", len);
1489 
1490  /* We do not pass strlen(buf) to ast_websocket_write() directly because the
1491  * size_t returned by strlen() may not require the same storage size
1492  * as the uint64_t that ast_websocket_write() uses. This normally
1493  * would not cause a problem, but since ast_websocket_write() uses
1494  * the optional API, this function call goes through a series of macros
1495  * that may cause a 32-bit to 64-bit conversion to go awry.
1496  */
1498  (char *)buf, len);
1499 }
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
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.

◆ load_module()

static int load_module ( void  )
static

Definition at line 1501 of file res_http_websocket.c.

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

Referenced by unload_module().

1502 {
1504  if (!websocketuri.data) {
1505  return AST_MODULE_LOAD_DECLINE;
1506  }
1509 
1510  return 0;
1511 }
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:673
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_http_uri websocketuri
static int websocket_add_protocol_internal(const char *name, ast_websocket_callback callback)
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
void * data
Definition: http.h:114
static struct ast_websocket_server * websocket_server_internal_create(void)

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

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

Referenced by ast_websocket_uri_cb().

754 {
755  SCOPED_AO2LOCK(lock, server->protocols);
756 
757  if (ao2_container_count(server->protocols) != 1) {
758  return NULL;
759  }
760 
761  return ao2_callback(server->protocols, OBJ_NOLOCK, NULL, NULL);
762 }
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
Assume that the ao2_container is already locked.
Definition: astobj2.h:1067
struct ao2_container * protocols
#define NULL
Definition: resample.c:96
ast_mutex_t lock
Definition: app_meetme.c:1091
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:602

◆ protocol_cmp_fn()

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

Comparison function for protocols.

Definition at line 116 of file res_http_websocket.c.

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

Referenced by websocket_server_create_impl().

117 {
118  const struct ast_websocket_protocol *protocol1 = obj, *protocol2 = arg;
119  const char *protocol = arg;
120 
121  return !strcasecmp(protocol1->name, flags & OBJ_KEY ? protocol : protocol2->name) ? CMP_MATCH | CMP_STOP : 0;
122 }
A websocket protocol implementation.
#define OBJ_KEY
Definition: astobj2.h:1155
char * name
Name of the protocol.

◆ protocol_destroy_fn()

static void protocol_destroy_fn ( void *  obj)
static

Destructor function for protocols.

Definition at line 125 of file res_http_websocket.c.

References ast_free, and ast_websocket_protocol::name.

Referenced by ast_websocket_sub_protocol_alloc().

126 {
127  struct ast_websocket_protocol *protocol = obj;
128  ast_free(protocol->name);
129 }
A websocket protocol implementation.
char * name
Name of the protocol.
#define ast_free(a)
Definition: astmm.h:182

◆ protocol_hash_fn()

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

Hashing function for protocols.

Definition at line 107 of file res_http_websocket.c.

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

Referenced by websocket_server_create_impl().

108 {
109  const struct ast_websocket_protocol *protocol = obj;
110  const char *name = obj;
111 
112  return ast_str_case_hash(flags & OBJ_KEY ? name : protocol->name);
113 }
A websocket protocol implementation.
#define OBJ_KEY
Definition: astobj2.h:1155
char * name
Name of the protocol.
static const char name[]
Definition: cdr_mysql.c:74
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:1250

◆ session_destroy_fn()

static void session_destroy_fn ( void *  obj)
static

Destructor function for sessions.

Definition at line 173 of file res_http_websocket.c.

References ao2_cleanup, ast_free, ast_iostream_close(), ast_sockaddr_stringify(), ast_verb, ast_websocket_close(), ast_websocket::client, ast_websocket::close_status_code, NULL, ast_websocket::payload, ast_websocket::remote_address, session, and ast_websocket::stream.

Referenced by ast_websocket_uri_cb(), and websocket_client_create().

174 {
175  struct ast_websocket *session = obj;
176 
177  if (session->stream) {
178  ast_websocket_close(session, session->close_status_code);
179  if (session->stream) {
180  ast_iostream_close(session->stream);
181  session->stream = NULL;
182  ast_verb(2, "WebSocket connection %s '%s' closed\n", session->client ? "to" : "from",
184  }
185  }
186 
187  ao2_cleanup(session->client);
188  ast_free(session->payload);
189 }
int AST_OPTIONAL_API_NAME() ast_websocket_close(struct ast_websocket *session, uint16_t reason)
Close function for websocket session.
#define NULL
Definition: resample.c:96
#define ast_verb(level,...)
Definition: logger.h:463
int ast_iostream_close(struct ast_iostream *stream)
Close an iostream.
Definition: iostream.c:528
static struct ast_mansession session
struct websocket_client * client
uint16_t close_status_code
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:260
#define ast_free(a)
Definition: astmm.h:182
Structure definition for session.
struct ast_sockaddr remote_address
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
struct ast_iostream * stream

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1513 of file res_http_websocket.c.

References ao2_ref, ast_http_uri_unlink(), AST_MODFLAG_GLOBAL_SYMBOLS, AST_MODFLAG_LOAD_ORDER, AST_MODPRI_CHANNEL_DEPEND, AST_MODULE_INFO(), AST_MODULE_SUPPORT_CORE, ASTERISK_GPL_KEY, ast_http_uri::data, load_module(), NULL, websocket_echo_callback(), and websocket_remove_protocol_internal().

1514 {
1517  ao2_ref(websocketuri.data, -1);
1519 
1520  return 0;
1521 }
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:705
#define NULL
Definition: resample.c:96
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.
#define ao2_ref(o, delta)
Definition: astobj2.h:464
static int websocket_remove_protocol_internal(const char *name, ast_websocket_callback callback)
static struct ast_http_uri websocketuri
void * data
Definition: http.h:114

◆ websocket_add_protocol_internal()

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

Definition at line 1043 of file res_http_websocket.c.

References ast_websocket_server_add_protocol(), and ast_http_uri::data.

Referenced by ast_websocket_add_protocol(), and load_module().

1044 {
1045  struct ast_websocket_server *ws_server = websocketuri.data;
1046  if (!ws_server) {
1047  return -1;
1048  }
1049  return ast_websocket_server_add_protocol(ws_server, name, callback);
1050 }
Structure for a WebSocket server.
int AST_OPTIONAL_API_NAME() ast_websocket_server_add_protocol(struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)
static struct ast_http_uri websocketuri
static const char name[]
Definition: cdr_mysql.c:74
void * data
Definition: http.h:114

◆ websocket_bad_request()

static void websocket_bad_request ( struct ast_tcptls_session_instance ser)
static

Definition at line 777 of file res_http_websocket.c.

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

778 {
779  struct ast_str *http_header = ast_str_create(64);
780 
781  if (!http_header) {
783  ast_http_error(ser, 500, "Server Error", "Out of memory");
784  return;
785  }
786  ast_str_set(&http_header, 0, "Sec-WebSocket-Version: 7, 8, 13\r\n");
787  ast_http_send(ser, AST_HTTP_UNKNOWN, 400, "Bad Request", http_header, NULL, 0, 0);
788 }
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:648
#define NULL
Definition: resample.c:96
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:456
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:1065
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
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:836
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620

◆ websocket_client_args_create()

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

Definition at line 1139 of file res_http_websocket.c.

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

Referenced by websocket_client_create().

1142 {
1143  struct ast_sockaddr *addr;
1145  sizeof(*args), websocket_client_args_destroy);
1146 
1147  if (!args) {
1149  return NULL;
1150  }
1151 
1152  args->accept_fd = -1;
1153  args->tls_cfg = tls_cfg;
1154  args->name = "websocket client";
1155 
1156  if (!ast_sockaddr_resolve(&addr, host, 0, 0)) {
1157  ast_log(LOG_ERROR, "Unable to resolve address %s\n",
1158  host);
1159  ao2_ref(args, -1);
1161  return NULL;
1162  }
1163  ast_sockaddr_copy(&args->remote_address, addr);
1164  ast_free(addr);
1165  return args;
1166 }
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:171
static void websocket_client_args_destroy(void *obj)
arguments for the accepting thread
Definition: tcptls.h:129
const char * args
#define NULL
Definition: resample.c:96
Socket address structure.
Definition: netsock2.h:97
#define ast_log
Definition: astobj2.c:42
static char host[256]
Definition: muted.c:77
struct ast_sockaddr remote_address
Definition: tcptls.h:132
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define LOG_ERROR
Definition: logger.h:285
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
#define ast_free(a)
Definition: astmm.h:182
const char * name
Definition: tcptls.h:142
static PGresult * result
Definition: cel_pgsql.c:88
struct ast_tls_config * tls_cfg
Definition: tcptls.h:134
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

◆ websocket_client_args_destroy()

static void websocket_client_args_destroy ( void *  obj)
static

Definition at line 1123 of file res_http_websocket.c.

References args, ast_free, ast_ssl_teardown(), ast_tls_config::cafile, ast_tls_config::capath, ast_tls_config::certfile, ast_tls_config::cipher, ast_tls_config::pvtfile, and ast_tcptls_session_args::tls_cfg.

Referenced by websocket_client_args_create().

1124 {
1125  struct ast_tcptls_session_args *args = obj;
1126 
1127  if (args->tls_cfg) {
1128  ast_free(args->tls_cfg->certfile);
1129  ast_free(args->tls_cfg->pvtfile);
1130  ast_free(args->tls_cfg->cipher);
1131  ast_free(args->tls_cfg->cafile);
1132  ast_free(args->tls_cfg->capath);
1133 
1134  ast_ssl_teardown(args->tls_cfg);
1135  }
1136  ast_free(args->tls_cfg);
1137 }
char * pvtfile
Definition: tcptls.h:90
void ast_ssl_teardown(struct ast_tls_config *cfg)
free resources used by an SSL server
Definition: tcptls.c:575
arguments for the accepting thread
Definition: tcptls.h:129
const char * args
char * cafile
Definition: tcptls.h:92
#define ast_free(a)
Definition: astmm.h:182
char * certfile
Definition: tcptls.h:89
char * capath
Definition: tcptls.h:93
struct ast_tls_config * tls_cfg
Definition: tcptls.h:134
char * cipher
Definition: tcptls.h:91

◆ websocket_client_connect()

static enum ast_websocket_result websocket_client_connect ( struct ast_websocket ws)
static

Definition at line 1397 of file res_http_websocket.c.

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

Referenced by ast_websocket_client_create().

1398 {
1399  enum ast_websocket_result res;
1400  /* create and connect the client - note client_start
1401  releases the session instance on failure */
1402  if (!(ws->client->ser = ast_tcptls_client_start(
1404  return WS_CLIENT_START_ERROR;
1405  }
1406 
1407  if ((res = websocket_client_handshake(ws->client)) != WS_OK) {
1408  ao2_ref(ws->client->ser, -1);
1409  ws->client->ser = NULL;
1410  return res;
1411  }
1412 
1413  ws->stream = ws->client->ser->stream;
1414  ws->secure = ast_iostream_get_ssl(ws->stream) ? 1 : 0;
1415  ws->client->ser->stream = NULL;
1417  return WS_OK;
1418 }
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:171
ast_websocket_result
Result code for a websocket client.
struct ast_tcptls_session_instance * ser
static enum ast_websocket_result websocket_client_handshake(struct websocket_client *client)
struct ast_tcptls_session_args * args
#define NULL
Definition: resample.c:96
SSL * ast_iostream_get_ssl(struct ast_iostream *stream)
Get a pointer to an iostream&#39;s OpenSSL SSL structure.
Definition: iostream.c:108
#define ao2_ref(o, delta)
Definition: astobj2.h:464
struct websocket_client * client
struct ast_iostream * stream
Definition: tcptls.h:160
unsigned int secure
struct ast_sockaddr remote_address
struct ast_tcptls_session_instance * ast_tcptls_client_create(struct ast_tcptls_session_args *desc)
Definition: tcptls.c:615
struct ast_iostream * stream
struct ast_tcptls_session_instance * ast_tcptls_client_start(struct ast_tcptls_session_instance *tcptls_session)
attempts to connect and start tcptls session, on error the tcptls_session&#39;s ref count is decremented...
Definition: tcptls.c:585
struct ast_sockaddr remote_address
Definition: tcptls.h:151

◆ websocket_client_create()

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

Definition at line 1224 of file res_http_websocket.c.

References ao2_alloc, ao2_ref, websocket_client::args, ast_log, AST_OPTIONAL_API_NAME, ast_strdup, ast_websocket::client, DEFAULT_RECONSTRUCTION_CEILING, websocket_client::host, websocket_client::key, LOG_ERROR, NULL, ast_websocket::opcode, websocket_client::protocols, ast_websocket::reconstruct, websocket_client::resource_name, session_destroy_fn(), 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().

1227 {
1228  struct ast_websocket *ws = ao2_alloc(sizeof(*ws), session_destroy_fn);
1229 
1230  if (!ws) {
1231  ast_log(LOG_ERROR, "Unable to allocate websocket\n");
1233  return NULL;
1234  }
1235 
1236  if (!(ws->client = ao2_alloc(
1237  sizeof(*ws->client), websocket_client_destroy))) {
1238  ast_log(LOG_ERROR, "Unable to allocate websocket client\n");
1240  return NULL;
1241  }
1242 
1243  if (!(ws->client->key = websocket_client_create_key())) {
1244  ao2_ref(ws, -1);
1245  *result = WS_KEY_ERROR;
1246  return NULL;
1247  }
1248 
1250  uri, &ws->client->host, &ws->client->resource_name)) {
1251  ao2_ref(ws, -1);
1253  return NULL;
1254  }
1255 
1257  ws->client->host, tls_cfg, result))) {
1258  ao2_ref(ws, -1);
1259  return NULL;
1260  }
1261  ws->client->protocols = ast_strdup(protocols);
1262 
1263  ws->client->version = 13;
1264  ws->opcode = -1;
1266  return ws;
1267 }
static int websocket_client_parse_uri(const char *uri, char **host, struct ast_str **path)
Parse the given uri into a path and remote address.
struct ast_tcptls_session_args * args
#define DEFAULT_RECONSTRUCTION_CEILING
Default reconstruction size for multi-frame payload reconstruction. If exceeded the next frame will s...
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define NULL
Definition: resample.c:96
static char * websocket_client_create_key(void)
struct ast_str * resource_name
#define ast_log
Definition: astobj2.c:42
enum ast_websocket_opcode opcode
#define ao2_ref(o, delta)
Definition: astobj2.h:464
struct websocket_client * client
#define LOG_ERROR
Definition: logger.h:285
static void session_destroy_fn(void *obj)
Destructor function for sessions.
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
Structure definition for session.
static PGresult * result
Definition: cel_pgsql.c:88
static void websocket_client_destroy(void *obj)
static struct ast_tcptls_session_args * websocket_client_args_create(const char *host, struct ast_tls_config *tls_cfg, enum ast_websocket_result *result)

◆ websocket_client_create_key()

static char* websocket_client_create_key ( void  )
static

Definition at line 1168 of file res_http_websocket.c.

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

Referenced by websocket_client_create().

1169 {
1170  static int encoded_size = CLIENT_KEY_SIZE * 2 * sizeof(char) + 1;
1171  /* key is randomly selected 16-byte base64 encoded value */
1172  unsigned char key[CLIENT_KEY_SIZE + sizeof(long) - 1];
1173  char *encoded = ast_malloc(encoded_size);
1174  long i = 0;
1175 
1176  if (!encoded) {
1177  ast_log(LOG_ERROR, "Unable to allocate client websocket key\n");
1178  return NULL;
1179  }
1180 
1181  while (i < CLIENT_KEY_SIZE) {
1182  long num = ast_random();
1183  memcpy(key + i, &num, sizeof(long));
1184  i += sizeof(long);
1185  }
1186 
1187  ast_base64encode(encoded, key, CLIENT_KEY_SIZE, encoded_size);
1188  return encoded;
1189 }
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42
long int ast_random(void)
Definition: main/utils.c:2064
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
#define LOG_ERROR
Definition: logger.h:285
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
Definition: main/utils.c:404
#define CLIENT_KEY_SIZE
Length of a websocket&#39;s client key.

◆ websocket_client_destroy()

static void websocket_client_destroy ( void *  obj)
static

Definition at line 1210 of file res_http_websocket.c.

References websocket_client::accept_protocol, ao2_cleanup, websocket_client::args, ast_free, ast_websocket::client, websocket_client::host, websocket_client::key, websocket_client::protocols, websocket_client::resource_name, and websocket_client::ser.

Referenced by websocket_client_create().

1211 {
1212  struct websocket_client *client = obj;
1213 
1214  ao2_cleanup(client->ser);
1215  ao2_cleanup(client->args);
1216 
1217  ast_free(client->accept_protocol);
1218  ast_free(client->protocols);
1219  ast_free(client->key);
1220  ast_free(client->resource_name);
1221  ast_free(client->host);
1222 }
struct ast_tcptls_session_instance * ser
struct ast_tcptls_session_args * args
struct ast_str * resource_name
#define ast_free(a)
Definition: astmm.h:182
#define ao2_cleanup(obj)
Definition: astobj2.h:1958

◆ websocket_client_handle_response_code()

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

Definition at line 1275 of file res_http_websocket.c.

References ast_log, websocket_client::host, LOG_ERROR, WS_BAD_REQUEST, WS_INVALID_RESPONSE, and WS_URL_NOT_FOUND.

Referenced by websocket_client_handshake_get_response().

1277 {
1278  if (response_code <= 0) {
1279  return WS_INVALID_RESPONSE;
1280  }
1281 
1282  switch (response_code) {
1283  case 101:
1284  return 0;
1285  case 400:
1286  ast_log(LOG_ERROR, "Received response 400 - Bad Request "
1287  "- from %s\n", client->host);
1288  return WS_BAD_REQUEST;
1289  case 404:
1290  ast_log(LOG_ERROR, "Received response 404 - Request URL not "
1291  "found - from %s\n", client->host);
1292  return WS_URL_NOT_FOUND;
1293  }
1294 
1295  ast_log(LOG_ERROR, "Invalid HTTP response code %d from %s\n",
1296  response_code, client->host);
1297  return WS_INVALID_RESPONSE;
1298 }
#define ast_log
Definition: astobj2.c:42
#define LOG_ERROR
Definition: logger.h:285

◆ websocket_client_handshake()

static enum ast_websocket_result websocket_client_handshake ( struct websocket_client client)
static

Definition at line 1367 of file res_http_websocket.c.

References ast_iostream_printf(), ast_log, ast_str_buffer(), ast_strlen_zero, websocket_client::host, websocket_client::key, LOG_ERROR, websocket_client::protocols, websocket_client::resource_name, websocket_client::ser, ast_tcptls_session_instance::stream, websocket_client::version, websocket_client_handshake_get_response(), and WS_WRITE_ERROR.

Referenced by websocket_client_connect().

1369 {
1370  char protocols[100] = "";
1371 
1372  if (!ast_strlen_zero(client->protocols)) {
1373  sprintf(protocols, "Sec-WebSocket-Protocol: %s\r\n",
1374  client->protocols);
1375  }
1376 
1377  if (ast_iostream_printf(client->ser->stream,
1378  "GET /%s HTTP/1.1\r\n"
1379  "Sec-WebSocket-Version: %d\r\n"
1380  "Upgrade: websocket\r\n"
1381  "Connection: Upgrade\r\n"
1382  "Host: %s\r\n"
1383  "Sec-WebSocket-Key: %s\r\n"
1384  "%s\r\n",
1385  client->resource_name ? ast_str_buffer(client->resource_name) : "",
1386  client->version,
1387  client->host,
1388  client->key,
1389  protocols) < 0) {
1390  ast_log(LOG_ERROR, "Failed to send handshake.\n");
1391  return WS_WRITE_ERROR;
1392  }
1393  /* wait for a response before doing anything else */
1395 }
static enum ast_websocket_result websocket_client_handshake_get_response(struct websocket_client *client)
struct ast_tcptls_session_instance * ser
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
struct ast_str * resource_name
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_log
Definition: astobj2.c:42
ssize_t ast_iostream_printf(struct ast_iostream *stream, const char *format,...)
Write a formatted string to an iostream.
Definition: iostream.c:491
#define LOG_ERROR
Definition: logger.h:285
struct ast_iostream * stream
Definition: tcptls.h:160

◆ websocket_client_handshake_get_response()

static enum ast_websocket_result websocket_client_handshake_get_response ( struct websocket_client client)
static

Definition at line 1300 of file res_http_websocket.c.

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

Referenced by websocket_client_handshake().

1302 {
1303  enum ast_websocket_result res;
1304  char buf[4096];
1305  char base64[64];
1306  int has_upgrade = 0;
1307  int has_connection = 0;
1308  int has_accept = 0;
1309  int has_protocol = 0;
1310 
1311  if (ast_iostream_gets(client->ser->stream, buf, sizeof(buf)) <= 0) {
1312  ast_log(LOG_ERROR, "Unable to retrieve HTTP status line.");
1313  return WS_BAD_STATUS;
1314  }
1315 
1316  if ((res = websocket_client_handle_response_code(client,
1318  buf, "HTTP/1.1", 101))) != WS_OK) {
1319  return res;
1320  }
1321 
1322  /* Ignoring line folding - assuming header field values are contained
1323  within a single line */
1324  while (ast_iostream_gets(client->ser->stream, buf, sizeof(buf)) > 0) {
1325  char *name, *value;
1326  int parsed = ast_http_header_parse(buf, &name, &value);
1327 
1328  if (parsed < 0) {
1329  break;
1330  }
1331 
1332  if (parsed > 0) {
1333  continue;
1334  }
1335 
1336  if (!has_upgrade &&
1337  (has_upgrade = ast_http_header_match(
1338  name, "upgrade", value, "websocket")) < 0) {
1339  return WS_HEADER_MISMATCH;
1340  } else if (!has_connection &&
1341  (has_connection = ast_http_header_match(
1342  name, "connection", value, "upgrade")) < 0) {
1343  return WS_HEADER_MISMATCH;
1344  } else if (!has_accept &&
1345  (has_accept = ast_http_header_match(
1346  name, "sec-websocket-accept", value,
1348  client->key, base64, sizeof(base64)))) < 0) {
1349  return WS_HEADER_MISMATCH;
1350  } else if (!has_protocol &&
1351  (has_protocol = ast_http_header_match_in(
1352  name, "sec-websocket-protocol", value, client->protocols))) {
1353  if (has_protocol < 0) {
1354  return WS_HEADER_MISMATCH;
1355  }
1356  client->accept_protocol = ast_strdup(value);
1357  } else if (!strcasecmp(name, "sec-websocket-extensions")) {
1358  ast_log(LOG_ERROR, "Extensions received, but not "
1359  "supported by client\n");
1360  return WS_NOT_SUPPORTED;
1361  }
1362  }
1363  return has_upgrade && has_connection && has_accept ?
1365 }
ast_websocket_result
Result code for a websocket client.
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
struct ast_tcptls_session_instance * ser
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
int value
Definition: syslog.c:37
#define ast_log
Definition: astobj2.c:42
int ast_http_header_parse(char *buf, char **name, char **value)
Parse a header into the given name/value strings.
Definition: http.c:1688
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:1726
#define LOG_ERROR
Definition: logger.h:285
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:1710
static const char name[]
Definition: cdr_mysql.c:74
static enum ast_websocket_result websocket_client_handle_response_code(struct websocket_client *client, int response_code)
struct ast_iostream * stream
Definition: tcptls.h:160
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:300
static char * websocket_combine_key(const char *key, char *res, int res_size)
int ast_http_response_status_line(const char *buf, const char *version, int code)
Parse the http response status line.
Definition: http.c:1636
static char base64[64]
Definition: main/utils.c:78

◆ websocket_client_parse_uri()

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

References ao2_ref, ast_str_append(), ast_str_create, ast_str_set(), ast_uri_make_host_with_port(), ast_uri_parse_websocket(), ast_uri_path(), and ast_uri_query().

Referenced by websocket_client_create().

1094 {
1095  struct ast_uri *parsed_uri = ast_uri_parse_websocket(uri);
1096 
1097  if (!parsed_uri) {
1098  return -1;
1099  }
1100 
1101  *host = ast_uri_make_host_with_port(parsed_uri);
1102 
1103  if (ast_uri_path(parsed_uri) || ast_uri_query(parsed_uri)) {
1104  *path = ast_str_create(64);
1105  if (!*path) {
1106  ao2_ref(parsed_uri, -1);
1107  return -1;
1108  }
1109 
1110  if (ast_uri_path(parsed_uri)) {
1111  ast_str_set(path, 0, "%s", ast_uri_path(parsed_uri));
1112  }
1113 
1114  if (ast_uri_query(parsed_uri)) {
1115  ast_str_append(path, 0, "?%s", ast_uri_query(parsed_uri));
1116  }
1117  }
1118 
1119  ao2_ref(parsed_uri, -1);
1120  return 0;
1121 }
const char * ast_uri_path(const struct ast_uri *uri)
Retrieve the uri path.
Definition: uri.c:135
Stores parsed uri information.
Definition: uri.c:30
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:1091
const char * ast_uri_query(const struct ast_uri *uri)
Retrieve the uri query parameters.
Definition: uri.c:140
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:1065
static char host[256]
Definition: muted.c:77
#define ao2_ref(o, delta)
Definition: astobj2.h:464
char * ast_uri_make_host_with_port(const struct ast_uri *uri)
Retrieve a string of the host and port.
Definition: uri.c:300
char uri[0]
Definition: uri.c:44
struct ast_uri * ast_uri_parse_websocket(const char *uri)
Parse the given websocket uri into a structure.
Definition: uri.c:295
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620

◆ websocket_combine_key()

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

Definition at line 764 of file res_http_websocket.c.

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

Referenced by ast_websocket_uri_cb(), and websocket_client_handshake_get_response().

765 {
766  char *combined;
767  unsigned combined_length = strlen(key) + strlen(WEBSOCKET_GUID) + 1;
768  uint8_t sha[20];
769 
770  combined = ast_alloca(combined_length);
771  snprintf(combined, combined_length, "%s%s", key, WEBSOCKET_GUID);
772  ast_sha1_hash_uint(sha, combined);
773  ast_base64encode(res, (const unsigned char*)sha, 20, res_size);
774  return res;
775 }
#define WEBSOCKET_GUID
GUID used to compute the accept key, defined in the specifications.
void ast_sha1_hash_uint(uint8_t *digest, const char *input)
Produces SHA1 hash based on input string, stored in uint8_t array.
Definition: main/utils.c:282
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
Definition: main/utils.c:404

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

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, ast_websocket::opcode, ast_websocket::payload, and ast_websocket::payload_len.

Referenced by load_module(), and unload_module().

1008 {
1009  int res;
1010 
1011  ast_debug(1, "Entering WebSocket echo loop\n");
1012 
1013  if (ast_fd_set_flags(ast_websocket_fd(session), O_NONBLOCK)) {
1014  goto end;
1015  }
1016 
1017  while ((res = ast_websocket_wait_for_input(session, -1)) > 0) {
1018  char *payload;
1019  uint64_t payload_len;
1020  enum ast_websocket_opcode opcode;
1021  int fragmented;
1022 
1023  if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) {
1024  /* We err on the side of caution and terminate the session if any error occurs */
1025  ast_log(LOG_WARNING, "Read failure during WebSocket echo loop\n");
1026  break;
1027  }
1028 
1029  if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
1030  ast_websocket_write(session, opcode, payload, payload_len);
1031  } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
1032  break;
1033  } else {
1034  ast_debug(1, "Ignored WebSocket opcode %u\n", opcode);
1035  }
1036  }
1037 
1038 end:
1039  ast_debug(1, "Exiting WebSocket echo loop\n");
1040  ast_websocket_unref(session);
1041 }
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)
#define LOG_WARNING
Definition: logger.h:274
char * end
Definition: eagi_proxy.c:73
int AST_OPTIONAL_API_NAME() ast_websocket_fd(struct ast_websocket *session)
#define ast_fd_set_flags(fd, flags)
Set flags on the given file descriptor.
Definition: utils.h:1009
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
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_wait_for_input(struct ast_websocket *session, int timeout)
void AST_OPTIONAL_API_NAME() ast_websocket_unref(struct ast_websocket *session)
ast_websocket_opcode
WebSocket operation codes.

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

References ast_random(), ast_websocket::client, and put_unaligned_uint32().

Referenced by ast_websocket_close(), and ast_websocket_write().

289 {
290  /* RFC 6455 5.1 - clients MUST mask frame data */
291  if (session->client) {
292  uint64_t i;
293  uint8_t mask_key_idx;
294  uint32_t mask_key = ast_random();
295  uint8_t length = frame[1] & 0x7f;
296  frame[1] |= 0x80; /* set mask bit to 1 */
297  /* The mask key octet position depends on the length */
298  mask_key_idx = length == 126 ? 4 : length == 127 ? 10 : 2;
299  put_unaligned_uint32(&frame[mask_key_idx], mask_key);
300  for (i = 0; i < payload_size; i++) {
301  payload[i] ^= ((char *)&mask_key)[i % 4];
302  }
303  }
304 }
static void put_unaligned_uint32(void *p, unsigned int datum)
Definition: unaligned.h:58
long int ast_random(void)
Definition: main/utils.c:2064
struct websocket_client * client

◆ websocket_opcode2str()

static const char* websocket_opcode2str ( enum ast_websocket_opcode  opcode)
static

Definition at line 363 of file res_http_websocket.c.

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

Referenced by ast_websocket_write().

364 {
365  if (opcode < AST_WEBSOCKET_OPCODE_CONTINUATION ||
366  opcode > AST_WEBSOCKET_OPCODE_PONG) {
367  return "<unknown>";
368  } else {
369  return opcode_map[opcode];
370  }
371 }
static const char * opcode_map[]

◆ websocket_remove_protocol_internal()

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

Definition at line 1072 of file res_http_websocket.c.

References ast_websocket_server_remove_protocol(), and ast_http_uri::data.

Referenced by ast_websocket_remove_protocol(), and unload_module().

1073 {
1074  struct ast_websocket_server *ws_server = websocketuri.data;
1075  if (!ws_server) {
1076  return -1;
1077  }
1078  return ast_websocket_server_remove_protocol(ws_server, name, callback);
1079 }
Structure for a WebSocket server.
static struct ast_http_uri websocketuri
static const char name[]
Definition: cdr_mysql.c:74
void * data
Definition: http.h:114
int AST_OPTIONAL_API_NAME() ast_websocket_server_remove_protocol(struct ast_websocket_server *server, const char *name, ast_websocket_callback callback)

◆ websocket_server_create_impl()

static struct ast_websocket_server* websocket_server_create_impl ( void  )
static

Definition at line 143 of file res_http_websocket.c.

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

144 {
145  RAII_VAR(struct ast_websocket_server *, server, NULL, ao2_cleanup);
146 
147  server = ao2_alloc(sizeof(*server), websocket_server_dtor);
148  if (!server) {
149  return NULL;
150  }
151 
152  server->protocols = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
154  if (!server->protocols) {
155  return NULL;
156  }
157 
158  ao2_ref(server, +1);
159  return server;
160 }
Structure for a WebSocket server.
#define NULL
Definition: resample.c:96
static void websocket_server_dtor(void *obj)
static int protocol_hash_fn(const void *obj, const int flags)
Hashing function for protocols.
#define MAX_PROTOCOL_BUCKETS
Number of buckets for registered 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:911
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
static int protocol_cmp_fn(void *obj, void *arg, int flags)
Comparison function for protocols.
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
#define ao2_cleanup(obj)
Definition: astobj2.h:1958

◆ websocket_server_dtor()

static void websocket_server_dtor ( void *  obj)
static

Definition at line 136 of file res_http_websocket.c.

References ao2_cleanup, NULL, and ast_websocket_server::protocols.

Referenced by websocket_server_create_impl().

137 {
138  struct ast_websocket_server *server = obj;
139  ao2_cleanup(server->protocols);
140  server->protocols = NULL;
141 }
Structure for a WebSocket server.
struct ao2_container * protocols
#define NULL
Definition: resample.c:96
#define ao2_cleanup(obj)
Definition: astobj2.h:1958

◆ websocket_server_internal_create()

static struct ast_websocket_server* websocket_server_internal_create ( void  )
static

Definition at line 162 of file res_http_websocket.c.

References websocket_server_create_impl().

Referenced by load_module().

163 {
165 }
static struct ast_websocket_server * websocket_server_create_impl(void)

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

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

Referenced by ast_websocket_read().

535 {
536  ssize_t rlen;
537  int xlen = len;
538  char *rbuf = buf;
539  int sanity = 10;
540 
541  ast_assert(len > 0);
542 
543  if (!len) {
544  errno = EINVAL;
545  return -1;
546  }
547 
548  ao2_lock(session);
549  if (!session->stream) {
550  ao2_unlock(session);
551  errno = ECONNABORTED;
552  return -1;
553  }
554 
555  for (;;) {
556  rlen = ast_iostream_read(session->stream, rbuf, xlen);
557  if (rlen != xlen) {
558  if (rlen == 0) {
559  ast_log(LOG_WARNING, "Web socket closed abruptly\n");
560  *opcode = AST_WEBSOCKET_OPCODE_CLOSE;
561  session->closing = 1;
562  ao2_unlock(session);
563  return -1;
564  }
565 
566  if (rlen < 0 && errno != EAGAIN) {
567  ast_log(LOG_ERROR, "Error reading from web socket: %s\n", strerror(errno));
568  *opcode = AST_WEBSOCKET_OPCODE_CLOSE;
569  session->closing = 1;
570  ao2_unlock(session);
571  return -1;
572  }
573 
574  if (!--sanity) {
575  ast_log(LOG_WARNING, "Websocket seems unresponsive, disconnecting ...\n");
576  *opcode = AST_WEBSOCKET_OPCODE_CLOSE;
577  session->closing = 1;
578  ao2_unlock(session);
579  return -1;
580  }
581  }
582  if (rlen > 0) {
583  xlen = xlen - rlen;
584  rbuf = rbuf + rlen;
585  if (!xlen) {
586  break;
587  }
588  }
589  if (ast_iostream_wait_for_input(session->stream, 1000) < 0) {
590  ast_log(LOG_ERROR, "ast_iostream_wait_for_input returned err: %s\n", strerror(errno));
591  *opcode = AST_WEBSOCKET_OPCODE_CLOSE;
592  session->closing = 1;
593  ao2_unlock(session);
594  return -1;
595  }
596  }
597 
598  ao2_unlock(session);
599  return 0;
600 }
int ast_iostream_wait_for_input(struct ast_iostream *stream, int timeout)
Wait for input on the iostream&#39;s file descriptor.
Definition: iostream.c:89
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define LOG_WARNING
Definition: logger.h:274
#define ast_assert(a)
Definition: utils.h:695
#define ao2_unlock(a)
Definition: astobj2.h:730
ssize_t ast_iostream_read(struct ast_iostream *stream, void *buffer, size_t count)
Read data from an iostream.
Definition: iostream.c:273
#define ast_log
Definition: astobj2.c:42
#define ao2_lock(a)
Definition: astobj2.h:718
#define LOG_ERROR
Definition: logger.h:285
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int errno
unsigned int closing
struct ast_iostream * stream

Variable Documentation

◆ __mod_info

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

Definition at line 1529 of file res_http_websocket.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1529 of file res_http_websocket.c.

◆ opcode_map

const char* opcode_map[]
static

Definition at line 354 of file res_http_websocket.c.

Referenced by websocket_opcode2str().

◆ websocketuri

struct ast_http_uri websocketuri
static

Definition at line 997 of file res_http_websocket.c.