/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2019 - 2021 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * included from libwebsockets.h * * This defines the Serialized Secure Streams framing, and the optional * lws_transport_mux framing. * * APIs are declared for lws_transport and binding those to the SSPC and proxy * sides in lws. */ #if defined(STANDALONE) struct lws_context_standalone; #define lws_context lws_context_standalone #endif #define LWSSSS_VERSION 1 typedef enum { /* * This is the Serialized Serure Streams framing. It's sufficient to * carry all SS API actions over a point-to-point bytestream between * an SSPC client and an SS oroxy, in both directions. * * These serialized streams may be multiplexed by the transport (eg, * for unix domain sockets transport, each SS opens its own UDS socket * to the proxy) or via lws_transport_mux framing encapsulation. * * * Framing for Proxy -> Client direction */ LWSSS_SER_RXPRE_RX_PAYLOAD = 0x55, /* * Proxied rx * * - 0: LWSSS_SER_RXPRE_RX_PAYLOAD * - 1: 2 byte MSB-first rest-of-frame length * - 3: 4-byte MSB-first flags * - 7: 4-byte MSB-first us between inbound read and wrote to client * - 11: 8-byte MSB-first us resolution unix time proxy wrote to client * - 17: (rideshare name len + rideshare name if flags & * LWSSS_FLAG_RIDESHARE) payload */ LWSSS_SER_RXPRE_CREATE_RESULT, /* * Proxied connection setup result * * - 0: LWSSS_SER_RXPRE_CREATE_RESULT * - 1: 2 byte MSB-first rest-of-frame length (usually 00, 03) * - 3: 1 byte result, 0 = success. On failure, proxy will close * connection. * - 4: 4 byte client dsh allocation recommended for stream type, * from policy (introduced in SSSv1) * - 8: 2 byte MSB-first initial tx credit * - 10: if present, comma-sep list of rideshare types from policy */ LWSSS_SER_RXPRE_CONNSTATE, /* * Proxied state (8 or 11 byte packet) * * - 0: LWSSS_SER_RXPRE_CONNSTATE * - 1: 00, 05 if state < 256, else 00, 08 * - 3: 1 byte state index if state < 256, else 4-byte MSB-first * state index * - 4 or 7: 4-byte MSB-first ordinal */ LWSSS_SER_RXPRE_TXCR_UPDATE, /* * Proxied tx credit * * - 0: LWSSS_SER_RXPRE_TXCR_UPDATE * - 1: 00, 04 * - 3: 4-byte MSB-first addition tx credit bytes */ LWSSS_SER_RXPRE_METADATA, /* * Proxied rx metadata * * - 0: LWSSS_SER_RXPRE_METADATA * - 1: 2-byte MSB-first rest-of-frame length * - 3: 1-byte metadata name length * - 4: metadata name * - ...: metadata value (for rest of packet) */ LWSSS_SER_RXPRE_TLSNEG_ENCLAVE_SIGN, /* reserved */ LWSSS_SER_RXPRE_PERF, /* * Proxied performance information * * - 0: LWSSS_SER_RXPRE_PERF * - 1: 2-byte MSB-first rest-of-frame length * - 3: ... performance JSON (for rest of packet) */ /* * Framing for Client -> Proxy direction */ LWSSS_SER_TXPRE_STREAMTYPE = 0xaa, /* * Proxied connection setup * * - 0: LWSSS_SER_TXPRE_STREAMTYPE * - 1: 2-byte MSB-first rest-of-frame length * - 3: 1-byte Client SSS protocol version (introduced in SSSv1) * - 4: 4-byte Client PID (introduced in SSSv1) * - 8: 4-byte MSB-first initial tx credit * - 12: the streamtype name with no NUL */ LWSSS_SER_TXPRE_ONWARD_CONNECT, /* * Proxied request for onward connection * * - 0: LWSSS_SER_TXPRE_ONWARD_CONNECT * - 1: 00, 00 */ LWSSS_SER_TXPRE_DESTROYING, /* * Proxied secure stream destroy * * - 0: LWSSS_SER_TXPRE_DESTROYING * - 1: 00, 00 */ LWSSS_SER_TXPRE_TX_PAYLOAD, /* * Proxied tx * * - 0: LWSSS_SER_TXPRE_TX_PAYLOAD * - 1: 2 byte MSB-first rest-of-frame length * - 3: 4-byte MSB-first flags * - 7: 4-byte MSB-first us between client requested write and wrote * to proxy * - 11: 8-byte MSB-first us resolution unix time client wrote to proxy * - 19: ...payload (for rest of packet) */ LWSSS_SER_TXPRE_METADATA, /* * Proxied metadata - sent when one metadata item set clientside * * - 0: LWSSS_SER_TXPRE_METADATA * - 1: 2-byte MSB-first rest-of-frame length * - 3: 1-byte metadata name length * - 4: metadata name * - ...: metadata value (for rest of packet) */ LWSSS_SER_TXPRE_TXCR_UPDATE, /* * TX credit management - sent when using tx credit apis, cf METADATA * * - 0: LWSSS_SER_TXPRE_TXCR_UPDATE * - 1: 2-byte MSB-first rest-of-frame length 00, 04 * - 3: 4-byte additional tx credit adjust value */ LWSSS_SER_TXPRE_TIMEOUT_UPDATE, /* * Stream timeout management - forwarded when user applying or * cancelling t.o. * * - 0: LWSSS_SER_TXPRE_TIMEOUT_UPDATE * - 1: 2-byte MSB-first rest-of-frame length 00, 04 * - 3: 4-byte MSB-first unsigned 32-bit timeout, * 0 = use policy, -1 = cancel */ LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT, /* * Passing up payload length hint * * - 0: LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT * - 1: 2-byte MSB-first rest-of-frame length 00, 04 * - 3: 4-byte MSB-first unsigned 32-bit payload length hint */ LWSSS_SER_TXPRE_TLSNEG_ENCLAVE_SIGNED, /* reserved */ LWSSS_SER_TXPRE_LINK_VALIDITY_PROBE, } lws_sss_cmds_t; /* SSPC serialization states */ typedef enum { LPCSPROX_WAIT_INITIAL_TX = 1, /* after connect, must send streamtype */ LPCSPROX_REPORTING_FAIL, /* stream creation failed, wait to to tell */ LPCSPROX_REPORTING_OK, /* stream creation succeeded, wait to to tell */ LPCSPROX_OPERATIONAL, /* ready for payloads */ LPCSPROX_DESTROYED, LPCSCLI_SENDING_INITIAL_TX, /* after connect, must send streamtype */ LPCSCLI_WAITING_CREATE_RESULT, /* wait to hear if proxy ss create OK */ LPCSCLI_LOCAL_CONNECTED, /* we are in touch with the proxy */ LPCSCLI_ONWARD_CONNECT, /* request onward ss connection */ LPCSCLI_OPERATIONAL, /* ready for payloads */ } lws_ss_conn_states_t; /* * Optional multiplexing layer * * Either side can: * * - open and close channels asynchronously * - send and receive transport-level (not mux channel) timed PINGs / PONGs * - send and receive data bound to an open mux channel * * PONGs are produced and sent automatically on recipt of a PING from the peer * The peer sends a PONGACK so the single transaction can validate connection * viability in both directions. */ enum { LWSSSS_LLM_CHANNEL_REQ = 0xf0, /**< * Either side proposes to open a new mux channel * * - 0: LWSSSS_LLM_CHANNEL_REQ * - 1: 1-byte mux channel index, client initiated: first free from * zero up, server initiated: first free from 0xff down */ LWSSSS_LLM_CHANNEL_ACK, /**< * Positive response to earlier LWSSSS_LLM_CHANNEL_REQ * * - 0: LWSSSS_LLM_CHANNEL_ACK * - 1: 1-byte mux channel index, from the reqyuest */ LWSSSS_LLM_CHANNEL_NACK, /**< * Negative response to earlier LWSSSS_LLM_CHANNEL_REQ. This also acts * as a FIN if one arrives on a channel unsolicited. * * - 0: LWSSSS_LLM_CHANNEL_NACK * - 1: 1-byte mux channel index, from the reqyuest */ LWSSSS_LLM_CHANNEL_CLOSE, /**< * Either side informs peer it is closing a mux channel * * - 0: LWSSSS_LLM_CHANNEL_CLOSE * - 1: 1-byte mux channel index */ LWSSSS_LLM_CHANNEL_CLOSE_ACK, /**< * Peer acknowledges closing a mux channel, so it can be reused * * - 0: LWSSSS_LLM_CHANNEL_CLOSE_ACK * - 1: 1-byte mux channel index */ LWSSSS_LLM_MUX, /**< * Encapsulate data on an open mux channel * * - 0: LWSSSS_LLM_MUX * - 1: 1-byte mux channel index * - 2: 2-byte MSB-first rest-of-frame length * - 4... mux payload */ LWSSSS_LLM_PING, /**< * Either side wants to validate communication on mux transport * * - 0: LWSSSS_LLM_PING * - 1: 8-byte MSB-first us resolution unix time this was issued */ LWSSSS_LLM_PONG, /**< * Either side responds to peer's PING. * * - 0: LWSSSS_LLM_PONG * - 1: 8-byte MSB-first us resolution unix time from PING * - 9: 8-byte MSB-first us resolution unix time this PONG sent */ LWSSSS_LLM_PONGACK, /**< * When the original PING sender receives a PONG, it immediately sends * a PINGACK, which is not replied to. This allows the other side to * also know the connection is valid in both directions, with only one * side needing to issue PINGs. * * It also synchronizes both sides' understanding of the transport * validity in one transaction. * * - 0: LWSSSS_LLM_PONGACK * - 1: 8-byte MSB-first us resolution unix time from PING */ LWSSSS_LLM_RESET_TRANSPORT, /**< * Either side can issue this to indicate they no longer trust the * transport link. They should close all their channels and enter a * state trying to resync using 3-way PINGs */ }; typedef void * lws_transport_priv_t; /* care - this is a pointer type already */ struct lws_transport_mux; struct lws_sss_proxy_conn; struct lws_transport_client_ops; struct lws_transport_proxy_ops; struct lws_sspc_handle; /* * These describe the path through different transport layers. Each has an * 'in' and 'onw' (onward) side that can be bound to different parts in lws. * SSPC and the SS Proxy code in lws each exposes one of these as terminals * for the "path" to handle the SS Serialization on each side. * * sspc-transport-wsi and proxy-transport-wsi expose possible endpoints for the * paths, so you can simply "wire SSPC and proxy up to a wsi transport". * * You can also create a lws_transport_mux_t and interpose it in the transport * path on each side, and produce your own custom lws_transport ops implementing * arbitrary transport support. */ typedef struct lws_txp_path_client { const struct lws_transport_client_ops *ops_in; lws_transport_priv_t priv_in; const struct lws_transport_client_ops *ops_onw; lws_transport_priv_t priv_onw; struct lws_transport_mux *mux; } lws_txp_path_client_t; typedef struct lws_txp_path_proxy { const struct lws_transport_proxy_ops *ops_in; lws_transport_priv_t priv_in; const struct lws_transport_proxy_ops *ops_onw; lws_transport_priv_t priv_onw; struct lws_transport_mux *mux; } lws_txp_path_proxy_t; /* * Operations for client-side transport */ typedef struct lws_transport_client_ops { const char *name; int (*event_retry_connect)(lws_txp_path_client_t *path, struct lws_sspc_handle *h); /**< Attempt to create a new connection / channel to the proxy */ lws_ss_state_return_t (*event_connect_disposition)( struct lws_sspc_handle *h, int disposition); /**< Connection attempt result, disposition 9 = success, else failed */ void (*req_write)(lws_transport_priv_t priv); /**< Request a write to the proxy on this channel */ int (*_write)(lws_transport_priv_t priv, uint8_t *buf, size_t len); /**< Write the requested data on the channel to the proxy *** MUST have * LWS_PRE usable behind buf */ lws_ss_state_return_t (*event_read)(lws_transport_priv_t priv, const uint8_t *buf, size_t len); /**< len bytes at buf have been received */ void (*lost_coherence)(lws_transport_priv_t priv); /**< report that the framing inside the mux channel is broken */ void (*_close)(lws_transport_priv_t priv); /**< Close the channel to the proxy */ void (*event_stream_up)(lws_transport_priv_t priv); /**< Called when a new channel to the proxy is acknowledged as up */ void (*event_client_up)(lws_transport_priv_t priv); /**< Called when a client channel is acknowledged as up */ lws_ss_state_return_t (*event_can_write)(struct lws_sspc_handle *h, size_t metadata_limit); /**< Called when possible to write on the transport, after req_write */ lws_ss_state_return_t (*event_closed)(lws_transport_priv_t priv /*struct lws_sspc_handle *h */); /**< we notice an onward proxy connection had closed */ uint32_t flags; /**< Used for DSH creation flags */ uint32_t dsh_splitat; } lws_transport_client_ops_t; /* * Operations for proxy-side transport */ typedef struct lws_transport_proxy_ops { const char *name; int (*init_proxy_server)(struct lws_context *context, const struct lws_transport_proxy_ops *txp_ops_inward, lws_transport_priv_t txp_priv_inward, lws_txp_path_proxy_t *txp_ppath, const void *aux, const char *bind, int port); /**< Instantiate a proxy transport... bind/port are as shown for wsi * transport, but may be overloaded to provide transport-specific init */ int (*destroy_proxy_server)(struct lws_context *context); lws_ss_state_return_t (*event_new_conn)(struct lws_context *cx, const struct lws_transport_proxy_ops *txp_ops_inward, lws_transport_priv_t txp_priv_inward, #if defined(LWS_WITH_SYS_FAULT_INJECTION) const lws_fi_ctx_t *fic, #endif struct lws_sss_proxy_conn **conn, lws_transport_priv_t txp_priv); /**< proxy has received a new connection from client */ void (*event_onward_bind)(lws_transport_priv_t priv, struct lws_ss_handle *h); /**< Called when the proxy creates an onward SS for a client channel */ void (*proxy_req_write)(lws_transport_priv_t priv); /**< Request a write to the proxy on this channel */ lws_ss_state_return_t (*event_proxy_can_write)( lws_transport_priv_t priv #if defined(LWS_WITH_SYS_FAULT_INJECTION) , const lws_fi_ctx_t *fic #endif ); /**< Transport can now be written on, after earlier proxy_req_write */ int (*proxy_write)(lws_transport_priv_t priv, uint8_t *buf, size_t *len); /**< Write the requested data on the channel to the proxy *** MUST have * LWS_PRE usable behind buf. May do partial writes, len is set on return * to actual length written*/ lws_ss_state_return_t (*event_close_conn)( struct lws_sss_proxy_conn *conn); /**< proxy sees an existing conn closes */ #if defined(LWS_WITH_SYS_FAULT_INJECTION) const lws_fi_ctx_t * (*fault_context)(lws_transport_priv_t priv); /**< Get the fault context relating to the proxy connection, if any */ #endif lws_ss_state_return_t (*close_conn)(struct lws_sss_proxy_conn *conn); /**< called to handle closure of underlying transport */ lws_ss_state_return_t (*proxy_read)(lws_transport_priv_t priv, const uint8_t *buf, size_t len); void (*event_client_up)(lws_transport_priv_t priv); /**< Called when the proxy has accepted a new client conn */ int (*proxy_check_write_more)(lws_transport_priv_t priv); /**< optional, allows checking if we can write again */ uint32_t flags; /* dsh flags */ } lws_transport_proxy_ops_t; /* lws_transport_mux parser states */ enum lwstmc_parser { LWSTMCPAR_CMD, LWSTMCPAR_CHIDX_DONE, LWSTMCPAR_CHIDX, LWSTMCPAR_PLENH, LWSTMCPAR_PLENL, LWSTMCPAR_PAY, LWSTMCPAR_T64_1, LWSTMCPAR_T64_2 }; /* lws_transport_mux channel definitions */ typedef uint8_t lws_mux_ch_idx_t; #define LWS_MUCH_RANGE 256 /* lws_transport mux states */ enum { /* lws_transport_mux_ch_t created */ LWSTMC_PENDING_CREATE_CHANNEL, /* waiting to send create channel */ LWSTMC_AWAITING_CREATE_CHANNEL_ACK, /* sent create ch, awaiting ack */ LWSTMC_PENDING_CREATE_CHANNEL_NACK, /* waiting to send create ch ack */ LWSTMC_PENDING_CREATE_CHANNEL_ACK, /* waiting to send create ch ack */ LWSTMC_OPERATIONAL, /* had ack, we are operational */ LWSTMC_PENDING_CLOSE_CHANNEL, /* waiting to send close channel */ LWSTMC_AWAITING_CLOSE_CHANNEL_ACK, /* sent close ch, awaiting ack */ LWSTMC_PENDING_CLOSE_CHANNEL_ACK, /* waiting to send close ch ack */ /* lws_transport_mux_ch_t destroyed */ }; #define LWS_TRANSPORT_MUXCH_MAGIC LWS_FOURCC('T', 'm', 'C', 'h') #define assert_is_tmch(_tm) lws_assert_fourcc(_tm->magic, LWS_TRANSPORT_MUXCH_MAGIC) typedef struct lws_transport_mux_ch { #if defined(_DEBUG) uint32_t magic; #endif lws_dll2_t list; lws_dll2_t list_pending_tx; lws_transport_priv_t priv; lws_sorted_usec_list_t sul; void *opaque; lws_mux_ch_idx_t ch_idx; uint8_t state; uint8_t server:1; } lws_transport_mux_ch_t; enum { /* states of the transport */ LWSTM_TRANSPORT_DOWN, LWSTM_OPERATIONAL, }; #define LWSTMINFO_SERVER (1 << 0) typedef struct lws_transport_info { uint32_t ping_interval_us; /**< us inbetween transport mux sending pings on transport */ uint32_t pong_grace_us; /**< us we should wait for pong before assuming transport down */ lws_txp_path_client_t txp_cpath; lws_txp_path_proxy_t txp_ppath; struct lws_transport_info *onward_txp_info; uint32_t flags; /* LWSTMINFO_.... */ } lws_transport_info_t; #define LWS_TRANSPORT_MUX_MAGIC LWS_FOURCC('I', 's', 'T', 'M') #define assert_is_tm(_tm) lws_assert_fourcc(_tm->magic, LWS_TRANSPORT_MUX_MAGIC) typedef struct lws_transport_mux { #if defined(_DEBUG) uint32_t magic; #endif struct lws_context *cx; lws_transport_info_t info; lws_sorted_usec_list_t sul_ping; void *txp_handle; void *txp_aux; uint64_t us_ping_in; uint64_t us_ping_out; uint64_t us_unixtime_peer; uint64_t us_unixtime_peer_loc; uint64_t mp_time; uint64_t mp_time1; enum lwstmc_parser mp_state; uint32_t mp_pay; /* remaining payload */ uint8_t mp_cmd; lws_mux_ch_idx_t mp_idx; uint8_t mp_ctr; uint32_t _open[LWS_MUCH_RANGE / 32]; uint32_t fin[LWS_MUCH_RANGE / 32]; lws_dll2_owner_t pending_tx; lws_dll2_owner_t owner; /* lws_mux_ch_t */ uint8_t link_state; uint8_t issue_ping:1; uint8_t issue_pong:1; uint8_t issue_pongack:1; uint8_t awaiting_pong:1; } lws_transport_mux_t; lws_transport_mux_t * lws_transport_mux_create(struct lws_context *cx, lws_transport_info_t *info, void *txp_handle); void lws_transport_mux_destroy(lws_transport_mux_t **tm); void lws_transport_mux_request_tx(lws_transport_mux_t *tm); #if defined(_DEBUG) void lws_transport_path_client_dump(lws_txp_path_client_t *path, const char *ctx); void lws_transport_path_proxy_dump(lws_txp_path_proxy_t *path, const char *ctx); #else #define lws_transport_path_client_dump(_a, _b) #define lws_transport_path_proxy_dump(_a, _b) #endif /* * Callback set used to customize parser and _pending apis */ typedef struct lws_txp_mux_parse_cbs { int (*payload)(lws_transport_mux_ch_t *tmc, const uint8_t *buf, size_t len); int (*ch_opens)(lws_transport_mux_ch_t *tmc, int determination); int (*ch_closes)(lws_transport_mux_ch_t *tmc); void (*txp_req_write)(lws_transport_mux_t *tm); int (*txp_can_write)(lws_transport_mux_ch_t *tmc); } lws_txp_mux_parse_cbs_t; int lws_transport_mux_rx_parse(lws_transport_mux_t *tm, const uint8_t *buf, size_t len, const lws_txp_mux_parse_cbs_t *cbs); int /* nonzero if the transport mux has filled buf and wants to write it */ lws_transport_mux_pending(lws_transport_mux_t *tm, uint8_t *buf, size_t *len, const lws_txp_mux_parse_cbs_t *cbs); extern const lws_transport_client_ops_t lws_transport_mux_client_ops; extern const lws_transport_proxy_ops_t lws_transport_mux_proxy_ops; extern const lws_transport_client_ops_t lws_txp_inside_sspc; extern const lws_transport_proxy_ops_t lws_txp_inside_proxy; #if defined(STANDALONE) #undef lws_context #endif