mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
client: secure streams
Secure Streams is an optional layer on top of lws that separates policy like endpoint selection and tls cert validation into a device JSON policy document. Code that wants to open a client connection just specifies a streamtype name, and no longer deals with details like the endpoint, the protocol (!) or anything else other than payloads and optionally generic metadata; the JSON policy contains all the details for each streamtype. h1, h2, ws and mqtt client connections are supported. Logical secure streams outlive any particular connection and supports "nailed-up" connectivity regardless of underlying connection stability.
This commit is contained in:
parent
9d099ba7be
commit
28ce32af64
79 changed files with 12428 additions and 44 deletions
|
@ -11,6 +11,8 @@ env:
|
|||
- LWS_METHOD=lwsws2 CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_WITH_LWS_DSH=1"
|
||||
- LWS_METHOD=default CMAKE_ARGS="-DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=mbedtls CMAKE_ARGS="-DLWS_WITH_MBEDTLS=1 -DLWS_WITH_HTTP2=1 -DLWS_WITH_LWSWS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_JOSE=1 -DCMAKE_BUILD_TYPE=DEBUG"
|
||||
- LWS_METHOD=ss CMAKE_ARGS="-DLWS_WITH_SECURE_STREAMS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=ss+mbedtls CMAKE_ARGS="-DLWS_WITH_MBEDTLS=1 -DLWS_WITH_SECURE_STREAMS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=noserver CMAKE_ARGS="-DLWS_WITHOUT_SERVER=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=noclient CMAKE_ARGS="-DLWS_WITHOUT_CLIENT=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=noext CMAKE_ARGS="-DLWS_WITHOUT_EXTENSIONS=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
|
|
136
CMakeLists.txt
136
CMakeLists.txt
|
@ -60,6 +60,13 @@ option(LWS_WITH_SYS_DHCP_CLIENT "Build in tiny DHCP client" OFF)
|
|||
option(LWS_WITH_HTTP_BASIC_AUTH "Support Basic Auth" ON)
|
||||
option(LWS_WITH_HTTP_UNCOMMON_HEADERS "Include less common http header support" ON)
|
||||
|
||||
#
|
||||
# Secure Streams
|
||||
#
|
||||
option(LWS_WITH_SECURE_STREAMS "Secure Streams protocol-agnostic API" OFF)
|
||||
option(LWS_WITH_SECURE_STREAMS_PROXY_API "Secure Streams support to work across processes" OFF)
|
||||
option(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM "Auth support for api.amazon.com" OFF)
|
||||
|
||||
#
|
||||
# TLS library options... all except mbedTLS are basically OpenSSL variants.
|
||||
#
|
||||
|
@ -245,6 +252,11 @@ if(LWS_WITH_DISTRO_RECOMMENDED)
|
|||
set(LWS_ROLE_MQTT 1)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
set(LWS_WITH_LWS_DSH 1)
|
||||
set(LWS_WITH_UNIX_SOCK 1)
|
||||
endif()
|
||||
|
||||
if (NOT LWS_WITH_NETWORK)
|
||||
set(LWS_ROLE_MQTT 0)
|
||||
set(LWS_ROLE_H1 0)
|
||||
|
@ -394,6 +406,10 @@ endif()
|
|||
|
||||
include_directories(include plugins lib/core lib/core-net lib/event-libs include/abstract lib/tls lib/roles lib/event-libs/libuv lib/event-libs/poll lib/event-libs/libevent lib/event-libs/glib lib/event-libs/libev lib/jose/jwe lib/jose/jws lib/jose lib/misc lib/roles/http lib/roles/http/compression lib/roles/h1 lib/roles/h2 lib/roles/ws lib/roles/cgi lib/roles/dbus lib/roles/raw-proxy lib/abstract lib/system/async-dns lib/roles/mqtt)
|
||||
|
||||
if (LWS_WITH_SECURE_STREAMS)
|
||||
set(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM 1)
|
||||
endif()
|
||||
|
||||
if (LWS_PLAT_FREERTOS)
|
||||
include_directories(lib/plat/freertos lib/plat/freertos/esp32)
|
||||
else()
|
||||
|
@ -752,14 +768,6 @@ if (LWS_WITHOUT_DAEMONIZE OR WIN32)
|
|||
set(LWS_NO_DAEMONIZE 1)
|
||||
endif()
|
||||
|
||||
if (LWS_WITHOUT_SERVER)
|
||||
set(LWS_WITHOUT_SERVER 1)
|
||||
endif()
|
||||
|
||||
if (LWS_WITHOUT_CLIENT)
|
||||
set(LWS_WITHOUT_CLIENT 1)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_LIBEV)
|
||||
set(LWS_WITH_LIBEV 1)
|
||||
endif()
|
||||
|
@ -790,11 +798,11 @@ endif()
|
|||
|
||||
set(LWS_WITH_CLIENT 1)
|
||||
if (LWS_WITHOUT_CLIENT)
|
||||
set(LWS_WITH_CLIENT 0)
|
||||
set(LWS_WITH_CLIENT)
|
||||
endif()
|
||||
set(LWS_WITH_SERVER 1)
|
||||
if (LWS_WITHOUT_SERVER)
|
||||
set(LWS_WITH_SERVER 0)
|
||||
set(LWS_WITH_SERVER)
|
||||
endif()
|
||||
|
||||
# using any abstract protocol enables LWS_WITH_ABSTRACT
|
||||
|
@ -1165,6 +1173,53 @@ if (LWS_WITH_NETWORK)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_SECURE_STREAMS)
|
||||
list(APPEND SOURCES
|
||||
lib/secure-streams/secure-streams.c
|
||||
lib/secure-streams/policy.c
|
||||
lib/secure-streams/system/fetch-policy/fetch-policy.c
|
||||
)
|
||||
if (LWS_ROLE_H1)
|
||||
list(APPEND SOURCES
|
||||
lib/secure-streams/protocols/ss-h1.c
|
||||
)
|
||||
endif()
|
||||
if (LWS_ROLE_H2)
|
||||
list(APPEND SOURCES
|
||||
lib/secure-streams/protocols/ss-h2.c
|
||||
)
|
||||
endif()
|
||||
if (LWS_ROLE_WS)
|
||||
list(APPEND SOURCES
|
||||
lib/secure-streams/protocols/ss-ws.c
|
||||
)
|
||||
endif()
|
||||
if (LWS_ROLE_MQTT)
|
||||
list(APPEND SOURCES
|
||||
lib/secure-streams/protocols/ss-mqtt.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
list(APPEND SOURCES
|
||||
lib/secure-streams/secure-streams-serialize.c
|
||||
lib/secure-streams/secure-streams-client.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
list(APPEND SOURCES
|
||||
lib/secure-streams/secure-streams-process.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM)
|
||||
list(APPEND SOURCES
|
||||
lib/secure-streams/system/auth-api.amazon.com/auth.c
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_STATS)
|
||||
list(APPEND SOURCES
|
||||
lib/core-net/stats.c
|
||||
|
@ -1300,7 +1355,11 @@ endif()
|
|||
|
||||
if (NOT LWS_WITHOUT_SERVER)
|
||||
list(APPEND SOURCES
|
||||
lib/core-net/server.c
|
||||
lib/core-net/server.c)
|
||||
endif()
|
||||
|
||||
if (NOT LWS_WITHOUT_SERVER OR LWS_WITH_SECURE_STREAMS_PROCESS_API)
|
||||
list(APPEND SOURCES
|
||||
lib/roles/listen/ops-listen.c)
|
||||
endif()
|
||||
|
||||
|
@ -1684,7 +1743,7 @@ if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG)
|
|||
endif()
|
||||
|
||||
if (UNIX AND NOT LWS_PLAT_FREERTOS)
|
||||
set(CMAKE_C_FLAGS "-Wall -Wsign-compare -Wuninitialized -Werror ${VISIBILITY_FLAG} -Wundef ${GCOV_FLAGS} ${CMAKE_C_FLAGS} ${ASAN_FLAGS}" )
|
||||
set(CMAKE_C_FLAGS "-Wall -Wsign-compare -Wstrict-aliasing -Wuninitialized -Werror ${VISIBILITY_FLAG} -Wundef ${GCOV_FLAGS} ${CMAKE_C_FLAGS} ${ASAN_FLAGS}" )
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "-Wall -Wsign-compare -Wuninitialized -Werror ${VISIBILITY_FLAG} ${GCOV_FLAGS} ${CMAKE_C_FLAGS}" )
|
||||
endif()
|
||||
|
@ -2666,6 +2725,59 @@ if (LWS_WITH_LWSWS)
|
|||
)
|
||||
endif (LWS_WITH_LWSWS)
|
||||
|
||||
# secure streams plugins
|
||||
|
||||
if (LWS_WITH_SECURE_STREAMS)
|
||||
#
|
||||
# Helper function for adding a secure stream plugin
|
||||
#
|
||||
macro(create_ss_plugin NAME S2 S3 S4 S5 S6)
|
||||
|
||||
set(SSP_SRCS)
|
||||
set(SSP_PUBLIC_HDR)
|
||||
set(SSP_HDR)
|
||||
|
||||
if ("${S2}" STREQUAL "")
|
||||
else()
|
||||
list(APPEND SSP_SRCS
|
||||
lib/secure-streams/plugins/${NAME}/${S2})
|
||||
endif()
|
||||
if ("${S3}" STREQUAL "")
|
||||
else()
|
||||
list(APPEND SSP_SRCS
|
||||
lib/secure-streams/plugins/${NAME}/${S3})
|
||||
endif()
|
||||
if ("${S4}" STREQUAL "")
|
||||
else()
|
||||
list(APPEND SSP_SRCS
|
||||
lib/secure-streams/plugins/${NAME}/${S4})
|
||||
endif()
|
||||
if ("${S5}" STREQUAL "")
|
||||
else()
|
||||
list(APPEND SSP_SRCS
|
||||
lib/secure-streams/plugins/${NAME}/${S5})
|
||||
endif()
|
||||
if ("${S6}" STREQUAL "")
|
||||
else()
|
||||
list(APPEND SSP_SRCS
|
||||
lib/secure-streams/plugins/${NAME}/${S6})
|
||||
endif()
|
||||
|
||||
source_group("Headers Private" FILES ${SSP_HDR})
|
||||
source_group("Sources" FILES ${SSP_SRCS})
|
||||
|
||||
add_library( ${NAME} STATIC
|
||||
${SSP_HDR} ${SSP_PUBLIC_HDR} ${SSP_SRCS} )
|
||||
|
||||
add_dependencies(${NAME} websockets_shared)
|
||||
list(APPEND SS_PLUGINS_LIST ${NAME})
|
||||
endmacro()
|
||||
|
||||
include_directories(lib/secure-streams)
|
||||
|
||||
create_ss_plugin(ssp-h1url "h1url.c" "" "" "" "")
|
||||
endif()
|
||||
|
||||
if (UNIX)
|
||||
|
||||
# Generate and install pkgconfig.
|
||||
|
|
20
README.md
20
README.md
|
@ -16,6 +16,26 @@ various scenarios, CC0-licensed (public domain) for cut-and-paste, allow you to
|
|||
News
|
||||
----
|
||||
|
||||
## Introducing Secure Streams client support
|
||||
|
||||
Secure Streams is an optional layer above lws (`-DLWS_WITH_SECURE_STREAMS=1`) that
|
||||
separates connectivity policy into a JSON document, which can be part of the
|
||||
firmware or fetched at boot time.
|
||||
|
||||
Code no longer deals with details like endpoint specification or tls cert stack used
|
||||
to validate the remote server, it's all specified in JSON, eg, see
|
||||
[this example](https://warmcat.com/policy/minimal-proxy.json). Even the protocol to use to talk to the
|
||||
server, between h1, h2, ws or MQTT, is specified in the policy JSON and the code
|
||||
itself just deals with payloads and optionally metadata, making it possible to
|
||||
switch endpoints, update certs and even switch communication protocols by just
|
||||
editing the JSON policy and leaving the code alone.
|
||||
|
||||
Logical Secure Stream connections outlive any underlying lws connection, and support
|
||||
"nailed-up" connection reacquisition and exponential backoff management.
|
||||
|
||||
See [./lib/secure-streams/README.md](https://libwebsockets.org/git/libwebsockets/tree/lib/secure-streams/README.md) and the related minimal examples
|
||||
for more details.
|
||||
|
||||
## mqtt client support
|
||||
|
||||
If you enable `-DLWS_ROLE_MQTT=1`, lws can now support QoS0 and QoS1 MQTT client
|
||||
|
|
|
@ -147,6 +147,9 @@
|
|||
#cmakedefine LWS_WITH_POLARSSL
|
||||
#cmakedefine LWS_WITH_POLL
|
||||
#cmakedefine LWS_WITH_RANGES
|
||||
#cmakedefine LWS_WITH_SECURE_STREAMS
|
||||
#cmakedefine LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM
|
||||
#cmakedefine LWS_WITH_SECURE_STREAMS_PROXY_API
|
||||
#cmakedefine LWS_WITH_SELFTESTS
|
||||
#cmakedefine LWS_WITH_SEQUENCER
|
||||
#cmakedefine LWS_WITH_SERVER_STATUS
|
||||
|
|
BIN
doc-assets/ss-explain.png
Normal file
BIN
doc-assets/ss-explain.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 375 KiB |
|
@ -576,6 +576,9 @@ struct lws;
|
|||
#include <libwebsockets/lws-fts.h>
|
||||
#include <libwebsockets/lws-diskcache.h>
|
||||
#include <libwebsockets/lws-sequencer.h>
|
||||
#include <libwebsockets/lws-secure-streams.h>
|
||||
#include <libwebsockets/lws-secure-streams-policy.h>
|
||||
#include <libwebsockets/lws-secure-streams-client.h>
|
||||
|
||||
#if !defined(LWS_PLAT_FREERTOS)
|
||||
#include <libwebsockets/abstract/abstract.h>
|
||||
|
|
|
@ -227,6 +227,8 @@
|
|||
#define lws_check_opt(c, f) ((((uint64_t)c) & ((uint64_t)f)) == ((uint64_t)f))
|
||||
|
||||
struct lws_plat_file_ops;
|
||||
struct lws_ss_policy;
|
||||
struct lws_ss_plugin;
|
||||
|
||||
typedef int (*lws_context_ready_cb_t)(struct lws_context *context);
|
||||
|
||||
|
@ -699,7 +701,7 @@ struct lws_context_creation_info {
|
|||
/**< VHOST: optional retry and idle policy to apply to this vhost.
|
||||
* Currently only the idle parts are applied to the connections.
|
||||
*/
|
||||
lws_state_notify_link_t **register_notifier_list;
|
||||
lws_state_notify_link_t * const *register_notifier_list;
|
||||
/**< CONTEXT: NULL, or pointer to an array of notifiers that should
|
||||
* be registered during context creation, so they can see state change
|
||||
* events from very early on. The array should end with a NULL. */
|
||||
|
@ -709,6 +711,26 @@ struct lws_context_creation_info {
|
|||
uint8_t udp_loss_sim_rx_pc;
|
||||
/**< CONTEXT: percentage of udp reads we actually received
|
||||
* to make disappear, in order to simulate and test udp retry flow */
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
const char *pss_policies_json; /**< CONTEXT: point to a string
|
||||
* containing a JSON description of the secure streams policies. Set
|
||||
* to NULL if not using Secure Streams. */
|
||||
const struct lws_ss_plugin **pss_plugins; /**< CONTEXT: point to an array
|
||||
* of pointers to plugin structs here, terminated with a NULL ptr.
|
||||
* Set to NULL if not using Secure Streams. */
|
||||
const char *ss_proxy_bind; /**< CONTEXT: NULL, or: ss_proxy_port == 0:
|
||||
* point to a string giving the Unix Domain Socket address to use (start
|
||||
* with @ for abstract namespace), ss_proxy_port nonzero: set the
|
||||
* network interface address (not name, it's ambiguous for ipv4/6) to
|
||||
* bind the tcp connection to the proxy to */
|
||||
const char *ss_proxy_address; /**< CONTEXT: NULL, or if ss_proxy_port
|
||||
* nonzero: the tcp address of the ss proxy to connect to */
|
||||
uint16_t ss_proxy_port; /* 0 = if connecting to ss proxy, do it via a
|
||||
* Unix Domain Socket, "+@proxy.ss.lws" if ss_proxy_bind is NULL else
|
||||
* the socket path given in ss_proxy_bind (start it with a + or +@);
|
||||
* nonzero means connect via a tcp socket to the tcp address in
|
||||
* ss_proxy_bind and the given port */
|
||||
#endif
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibility
|
||||
|
@ -718,7 +740,7 @@ struct lws_context_creation_info {
|
|||
* was not built against the newer headers.
|
||||
*/
|
||||
|
||||
void *_unused[4]; /**< dummy */
|
||||
void *_unused[2]; /**< dummy */
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
172
include/libwebsockets/lws-secure-streams-client.h
Normal file
172
include/libwebsockets/lws-secure-streams-client.h
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This is the headers for secure stream api variants that deal with clients in
|
||||
* different threads or even different processes.
|
||||
*
|
||||
* lws_ss_ when client is directly using the event loop
|
||||
* lws_sstc_ when client is in a different thread to the event loop
|
||||
* lws_sspc_ when client is in a different process to the event loop
|
||||
*
|
||||
* The client api is almost the same except the slightly diffent names.
|
||||
*/
|
||||
|
||||
/*
|
||||
* lws_sspc_ apis... different process
|
||||
*/
|
||||
|
||||
/*
|
||||
* Helper translation so user code written to lws_ss_ can be built for
|
||||
* lws_sspc_ in one step by #define LWS_SS_USE_SSPC before including
|
||||
*/
|
||||
|
||||
#if defined(LWS_SS_USE_SSPC)
|
||||
#define lws_ss_handle lws_sspc_handle
|
||||
#define lws_ss_create lws_sspc_create
|
||||
#define lws_ss_destroy lws_sspc_destroy
|
||||
#define lws_ss_request_tx lws_sspc_request_tx
|
||||
#define lws_ss_client_connect lws_sspc_client_connect
|
||||
#define lws_ss_get_sequencer lws_sspc_get_sequencer
|
||||
#define lws_ss_proxy_create lws_sspc_proxy_create
|
||||
#define lws_ss_get_context lws_sspc_get_context
|
||||
#define lws_ss_rideshare lws_sspc_rideshare
|
||||
#define lws_ss_set_metadata lws_sspc_set_metadata
|
||||
#define lws_ss_add_peer_tx_credit lws_sspc_add_peer_tx_credit
|
||||
#define lws_ss_get_est_peer_tx_credit lws_sspc_get_est_peer_tx_credit
|
||||
#endif
|
||||
|
||||
|
||||
struct lws_sspc_handle;
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
|
||||
void *opaque_user_data, struct lws_sspc_handle **ppss,
|
||||
struct lws_sequencer *seq_owner, const char **ppayload_fmt);
|
||||
|
||||
/**
|
||||
* lws_sspc_destroy() - Destroy secure stream
|
||||
*
|
||||
* \param ppss: pointer to lws_ss_t pointer to be destroyed
|
||||
*
|
||||
* Destroys the lws_ss_t pointed to by *ppss, and sets *ppss to NULL.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_sspc_destroy(struct lws_sspc_handle **ppss);
|
||||
|
||||
/**
|
||||
* lws_sspc_request_tx() - Schedule stream for tx
|
||||
*
|
||||
* \param pss: pointer to lws_ss_t representing stream that wants to transmit
|
||||
*
|
||||
* Schedules a write on the stream represented by \p pss. When it's possible to
|
||||
* write on this stream, the *tx callback will occur with an empty buffer for
|
||||
* the stream owner to fill in.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_sspc_request_tx(struct lws_sspc_handle *pss);
|
||||
|
||||
/**
|
||||
* lws_sspc_client_connect() - Attempt the client connect
|
||||
*
|
||||
* \param h: secure streams handle
|
||||
*
|
||||
* Starts the connection process for the secure stream. Returns 0 if OK or
|
||||
* nonzero if we have already failed.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_sspc_client_connect(struct lws_sspc_handle *h);
|
||||
|
||||
/**
|
||||
* lws_sspc_get_sequencer() - Return parent sequencer pointer if any
|
||||
*
|
||||
* \param h: secure streams handle
|
||||
*
|
||||
* Returns NULL if the secure stream is not associated with a sequencer.
|
||||
* Otherwise returns a pointer to the owning sequencer. You can use this to
|
||||
* identify which sequencer to direct messages to, from the secure stream
|
||||
* callback.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN struct lws_sequencer *
|
||||
lws_sspc_get_sequencer(struct lws_sspc_handle *h);
|
||||
|
||||
/**
|
||||
* lws_sspc_proxy_create() - Start a unix domain socket proxy for Secure Streams
|
||||
*
|
||||
* \param context: lws_context
|
||||
*
|
||||
* Creates a vhost that listens on an abstract namespace unix domain socket at
|
||||
* address "proxy.ss.lws". Client connections to this proxy to Secure Streams
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_sspc_proxy_create(struct lws_context *context);
|
||||
|
||||
/**
|
||||
* lws_ss_get_context() - convenience helper to recover the lws context
|
||||
*
|
||||
* \h: secure streams handle
|
||||
*
|
||||
* Returns the lws context. Dispenses with the need to pass a copy of it into
|
||||
* your secure streams handler.
|
||||
*/
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN struct lws_context *
|
||||
lws_sspc_get_context(struct lws_sspc_handle *h);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN const struct lws_protocols lws_sspc_protocols[];
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN const char *
|
||||
lws_sspc_rideshare(struct lws_sspc_handle *h);
|
||||
|
||||
|
||||
/**
|
||||
* lws_sspc_set_metadata() - allow user to bind external data to defined ss metadata
|
||||
*
|
||||
* \h: secure streams handle
|
||||
* \name: metadata name from the policy
|
||||
* \value: pointer to user-managed data to bind to name
|
||||
* \len: length of the user-managed data in value
|
||||
*
|
||||
* Binds user-managed data to the named metadata item from the ss policy.
|
||||
* If present, the metadata item is handled in a protocol-specific way using
|
||||
* the associated policy information. For example, in the policy
|
||||
*
|
||||
* "\"metadata\":" "["
|
||||
* "{\"uptag\":" "\"X-Upload-Tag:\"},"
|
||||
* "{\"ctype\":" "\"Content-Type:\"},"
|
||||
* "{\"xctype\":" "\"X-Content-Type:\"}"
|
||||
* "],"
|
||||
*
|
||||
* when the policy is using h1 is interpreted to add h1 headers of the given
|
||||
* name with the value of the metadata on the left.
|
||||
*
|
||||
* Return 0 if OK.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name,
|
||||
void *value, size_t len);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_sspc_add_peer_tx_credit(struct lws_sspc_handle *h, int32_t add);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_sspc_get_est_peer_tx_credit(struct lws_sspc_handle *h);
|
237
include/libwebsockets/lws-secure-streams-policy.h
Normal file
237
include/libwebsockets/lws-secure-streams-policy.h
Normal file
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
typedef int (*plugin_auth_status_cb)(struct lws_ss_handle *ss, int status);
|
||||
|
||||
/**
|
||||
* lws_ss_plugin_auth_t - api for an auth plugin
|
||||
*
|
||||
* Auth plugins create and sequence authenticated connections that can carry one
|
||||
* or more streams to an endpoint. That may involve other connections to other
|
||||
* places to eg, gather authenticated tokens and then make the real connection
|
||||
* using the tokens.
|
||||
*
|
||||
* The secure stream object contains members to record which auth plugin the
|
||||
* stream is bound to and an over-allocation of the secure stream object to
|
||||
* contain the plugin auth private data.
|
||||
*
|
||||
* The auth plugin controls the state of the stream connection via the status
|
||||
* callback, and handles retries.
|
||||
*
|
||||
* Network connections may require one kind of auth sequencing, and streams
|
||||
* inside those connections another kind of auth sequencing depending on their
|
||||
* role. So the secure stream object allows defining plugins for both kinds.
|
||||
*
|
||||
* Streams may disappear at any time and require reauth to bring a new one up.
|
||||
* The auth plugin sequencer will connect / reconnect either on demand, or from
|
||||
* the start and after any connectivity loss if any stream using the connection
|
||||
* has the LWSSSPOLF_NAILED_UP flag.
|
||||
*/
|
||||
|
||||
typedef struct lws_ss_plugin {
|
||||
struct lws_ss_plugin *next;
|
||||
const char *name; /**< auth plugin name */
|
||||
size_t alloc; /**< size of private allocation */
|
||||
|
||||
int (*create)(struct lws_ss_handle *ss, void *info,
|
||||
plugin_auth_status_cb status);
|
||||
/**< called when the auth plugin is instantiated
|
||||
and bound to the secure stream. status is
|
||||
called back with advisory information about
|
||||
the authenticated stream state as it
|
||||
proceeds */
|
||||
int (*destroy)(struct lws_ss_handle *ss);
|
||||
/**< called when the related secure stream is
|
||||
being destroyed, and anything the auth
|
||||
plugin is doing should also be destroyed */
|
||||
int (*munge)(struct lws_ss_handle *ss, char *path,
|
||||
size_t path_len);
|
||||
/**< if the plugin needs to munge transactions
|
||||
that have metadata outside the payload (eg,
|
||||
add http headers) this callback will give
|
||||
it the opportunity to do so */
|
||||
} lws_ss_plugin_t;
|
||||
|
||||
|
||||
typedef struct lws_ss_x509 {
|
||||
struct lws_ss_x509 *next;
|
||||
const char *vhost_name; /**< vhost name using cert ctx */
|
||||
const uint8_t *ca_der; /**< DER x.509 cert */
|
||||
size_t ca_der_len; /**< length of DER cert */
|
||||
} lws_ss_x509_t;
|
||||
|
||||
enum {
|
||||
LWSSSPOLF_OPPORTUNISTIC = (1 << 0),
|
||||
/**< the connection doesn't exist unless client asks to write */
|
||||
LWSSSPOLF_NAILED_UP = (1 << 1),
|
||||
/**< the connection tries to be connected the whole life of the ss */
|
||||
LWSSSPOLF_URGENT_TX = (1 << 2),
|
||||
/**< this connection carries critical tx data */
|
||||
LWSSSPOLF_URGENT_RX = (1 << 3),
|
||||
/**< this connection carries critical rx data */
|
||||
LWSSSPOLF_TLS = (1 << 4),
|
||||
/**< stream must be connected via a tls tunnel */
|
||||
LWSSSPOLF_LONG_POLL = (1 << 5),
|
||||
/**< stream used to receive async rx at arbitrary intervals */
|
||||
LWSSSPOLF_AUTH_BEARER = (1 << 6),
|
||||
/**< for http, use lws_system auth token 0 in authentication: bearer */
|
||||
LWSSSPOLF_HTTP_NO_CONTENT_LENGTH = (1 << 7),
|
||||
/**< don't add any content length even if we have it */
|
||||
LWSSSPOLF_QUIRK_NGHTTP2_END_STREAM = (1 << 8),
|
||||
/**< set the client flag LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM */
|
||||
LWSSSPOLF_H2_QUIRK_OVERFLOWS_TXCR = (1 << 9),
|
||||
/**< set the client flag LCCSCF_H2_QUIRK_OVERFLOWS_TXCR */
|
||||
LWSSSPOLF_H2_QUIRK_UNCLEAN_HPACK_STATE = (1 << 10),
|
||||
/**< HPACK decoder state does not end cleanly */
|
||||
LWSSSPOLF_HTTP_MULTIPART = (1 << 11),
|
||||
/**< indicates stream goes out as specifically a multipart mime POST
|
||||
* section... if the tx has LWSSS_FLAG_COALESCE_CONTINUES flag then more
|
||||
* multipart sections are expected. Without it, the multipart wrapper
|
||||
* is closed and the http transaction issue completed when this message
|
||||
* finishes. */
|
||||
LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED = (1 << 12),
|
||||
/**< set up lws_system client cert */
|
||||
LWSSSPOLF_LOCAL_SINK = (1 << 13),
|
||||
/**< expected to bind to a local sink only */
|
||||
};
|
||||
|
||||
typedef struct lws_ss_trust_store {
|
||||
struct lws_ss_trust_store *next;
|
||||
const char *name;
|
||||
|
||||
lws_ss_x509_t *ssx509[8];
|
||||
int count;
|
||||
} lws_ss_trust_store_t;
|
||||
|
||||
enum {
|
||||
LWSSSP_H1,
|
||||
LWSSSP_H2,
|
||||
LWSSSP_WS,
|
||||
|
||||
|
||||
LWSSS_HBI_AUTH = 0,
|
||||
LWSSS_HBI_DSN,
|
||||
LWSSS_HBI_FWV,
|
||||
LWSSS_HBI_TYPE,
|
||||
|
||||
_LWSSS_HBI_COUNT /* always last */
|
||||
};
|
||||
|
||||
typedef struct lws_ss_metadata {
|
||||
struct lws_ss_metadata *next;
|
||||
const char *name;
|
||||
void *value;
|
||||
size_t length;
|
||||
|
||||
uint8_t value_on_lws_heap; /* proxy does this */
|
||||
} lws_ss_metadata_t;
|
||||
|
||||
|
||||
/**
|
||||
* lws_ss_policy_t: policy database entry for a stream type
|
||||
*
|
||||
* Decides the system policy for how to implement connections of name
|
||||
* .streamtype.
|
||||
*
|
||||
* Streams may need one kind of auth sequencing for the network connection and
|
||||
* another kind of auth sequencing for the streams that are carried inside it,
|
||||
* this is the purpose of .nauth and .sauth. Both are optional and may be NULL.
|
||||
*
|
||||
* An array of these is set at context creation time, ending with one with a
|
||||
* NULL streamtype.
|
||||
*/
|
||||
typedef struct lws_ss_policy {
|
||||
struct lws_ss_policy *next;
|
||||
const char *streamtype; /**< stream type lhs to match on */
|
||||
|
||||
const char *endpoint; /**< DNS address to connect to */
|
||||
const char *rideshare_streamtype; /**< optional transport
|
||||
* on another, preexisting stream of this
|
||||
* streamtype name */
|
||||
const char *payload_fmt;
|
||||
const char *socks5_proxy;
|
||||
lws_ss_metadata_t *metadata; /* linked-list of metadata */
|
||||
|
||||
/* protocol-specific connection policy details */
|
||||
|
||||
union {
|
||||
|
||||
/* details for http-related protocols... */
|
||||
|
||||
struct {
|
||||
|
||||
/* common to all http-related protocols */
|
||||
|
||||
const char *method;
|
||||
const char *url;
|
||||
|
||||
const char *multipart_name;
|
||||
const char *multipart_filename;
|
||||
const char *multipart_content_type;
|
||||
|
||||
const char *blob_header[_LWSSS_HBI_COUNT];
|
||||
const char *auth_preamble;
|
||||
|
||||
union {
|
||||
// struct { /* LWSSSP_H1 */
|
||||
// } h1;
|
||||
// struct { /* LWSSSP_H2 */
|
||||
// } h2;
|
||||
struct { /* LWSSSP_WS */
|
||||
const char *subprotocol;
|
||||
uint8_t binary;
|
||||
/* false = TEXT, true = BINARY */
|
||||
} ws;
|
||||
} u;
|
||||
} http;
|
||||
|
||||
struct {
|
||||
const char *topic; /* stream sends on this topic */
|
||||
const char *subscribe; /* stream subscribes to this topic */
|
||||
uint8_t qos;
|
||||
} mqtt;
|
||||
|
||||
/* details for non-http related protocols... */
|
||||
} u;
|
||||
|
||||
const
|
||||
struct lws_ss_plugin *plugins[2]; /**< NULL or auth plugin */
|
||||
const void *plugins_info[2]; /**< plugin-specific data */
|
||||
|
||||
const lws_ss_trust_store_t *trust_store; /**< CA certs needed for conn
|
||||
validation, only set between policy parsing and vhost creation */
|
||||
|
||||
const lws_retry_bo_t *retry_bo; /**< retry policy to use */
|
||||
|
||||
uint32_t flags; /**< stream attribute flags */
|
||||
|
||||
uint16_t port; /**< endpoint port */
|
||||
|
||||
uint8_t metadata_count; /**< metadata count */
|
||||
uint8_t protocol; /**< protocol index */
|
||||
uint8_t client_cert; /**< which client cert to apply
|
||||
0 = none, 1+ = cc 0+ */
|
||||
} lws_ss_policy_t;
|
492
include/libwebsockets/lws-secure-streams.h
Normal file
492
include/libwebsockets/lws-secure-streams.h
Normal file
|
@ -0,0 +1,492 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*
|
||||
* Secure Streams is a *payload-only* client communication channel where all the
|
||||
* details about the connection are held in a systemwide policy database and
|
||||
* are keyed by the streamtype field... the user of the communication channel
|
||||
* does not know or manage the choice of endpoint, tls CA, or even wire
|
||||
* protocol. The advantage is he then does not have any dependency on any of
|
||||
* those and they can be changed just by changing the policy database without
|
||||
* touching the code using the stream.
|
||||
*
|
||||
* There are two ways secure streams interfaces to user code:
|
||||
*
|
||||
* 1) [Linux / RTOS] the natural, smallest interface is to call back to user
|
||||
* code that only operates directly from the lws event loop thread context
|
||||
* (direct callbacks from lws_ss_t)
|
||||
*
|
||||
* lws_thread( [user code] ---- lws )
|
||||
*
|
||||
* 2) [Linux] where the user code is in a different process and communicates
|
||||
* asynchronously via a proxy socket
|
||||
*
|
||||
* user_process{ [user code] | shim | socket-}------ lws_process{ lws }
|
||||
*
|
||||
* In the second, IPC, case, all packets are prepended by one or more bytes
|
||||
* indicating the packet type and serializing any associated data, known as
|
||||
* Serialized Secure Streams or SSS.
|
||||
*
|
||||
* Serialized Secure Streams
|
||||
* -------------------------
|
||||
*
|
||||
* On the transport, adjacent packets may be coalesced, that is, the original
|
||||
* packet sizes are lost and two or more packets are combined. For that reason
|
||||
* the serialization format always contains a 1-byte type and then a 2-byte
|
||||
* frame length.
|
||||
*
|
||||
* Client to proxy
|
||||
*
|
||||
* - Proxied connection setup
|
||||
*
|
||||
* - 0: LWSSS_SER_TXPRE_STREAMTYPE
|
||||
* - 1: 2-byte MSB-first rest-of-frame length
|
||||
* - 3: 4 byte MSB-first initial tx credit
|
||||
* - 7: the streamtype name with no NUL
|
||||
*
|
||||
* - 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
|
||||
* - 17: payload
|
||||
*
|
||||
* - Proxied secure stream destroy
|
||||
*
|
||||
* - 0: LWSSS_SER_TXPRE_DESTROYING
|
||||
* - 1: 00, 00
|
||||
*
|
||||
* - Proxied metadata - sent when one metadata item set clientside
|
||||
*
|
||||
* - 0: LWSSS_SER_TXPRE_METADATA
|
||||
* - 1: 2-byte MSB-first rest-of-frame length
|
||||
* - 2: 1-byte metadata name length
|
||||
* - 3: metadata name
|
||||
* - ...: metadata value (for rest of packet)
|
||||
*
|
||||
* Proxy to client
|
||||
*
|
||||
* - 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: 2 byte MSB-first initial tx credit
|
||||
* - 6: if present, comma-sep list of rideshare types from policy
|
||||
*
|
||||
* - 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
|
||||
*
|
||||
* - Proxied tx credit
|
||||
*
|
||||
* - 0: LWSSS_SER_RXPRE_TXCR_UPDATE
|
||||
* - 1: 00, 04
|
||||
* - 3: 4-byte MSB-first addition tx credit bytes
|
||||
*
|
||||
* - Proxied state
|
||||
*
|
||||
* - 0: LWSSS_SER_RXPRE_CONNSTATE
|
||||
* - 1: 00, 05
|
||||
* - 3: 1 byte state index
|
||||
* - 7: 4-byte MSB-first ordinal
|
||||
*
|
||||
*
|
||||
* Proxied tx may be read by the proxy but rejected due to lack of buffer space
|
||||
* at the proxy. For that reason, tx must be held at the sender until it has
|
||||
* been acknowledged or denied.
|
||||
*
|
||||
* Sinks
|
||||
* -----
|
||||
*
|
||||
* Sinks are logical "servers", you can register as a sink for a particular
|
||||
* streamtype by using the lws_ss_create() api with ssi->register_sink set to 1.
|
||||
*
|
||||
* For directly fulfilled Secure Streams, new streams of that streamtype bind
|
||||
* to the rx, tx and state handlers given when it was registered.
|
||||
*
|
||||
* - When new streams are created the registered sink handler for (*state) is
|
||||
* called with event LWSSSCS_SINK_JOIN and the new client stream handle in
|
||||
* the h_src parameter.
|
||||
*
|
||||
* - When the client stream sends something to the sink, it calls the sink's
|
||||
* (*rx) with the client stream's
|
||||
*/
|
||||
|
||||
#define LWS_SS_MTU 1540
|
||||
|
||||
struct lws_ss_handle;
|
||||
typedef uint32_t lws_ss_tx_ordinal_t;
|
||||
|
||||
/*
|
||||
* connection state events
|
||||
*/
|
||||
typedef enum {
|
||||
LWSSSCS_CREATING,
|
||||
LWSSSCS_DISCONNECTED,
|
||||
LWSSSCS_UNREACHABLE,
|
||||
LWSSSCS_AUTH_FAILED,
|
||||
LWSSSCS_CONNECTED,
|
||||
LWSSSCS_CONNECTING,
|
||||
LWSSSCS_DESTROYING,
|
||||
LWSSSCS_POLL,
|
||||
LWSSSCS_ALL_RETRIES_FAILED, /* all retries in bo policy failed */
|
||||
LWSSSCS_QOS_ACK_REMOTE, /* remote peer received and acked tx */
|
||||
LWSSSCS_QOS_NACK_REMOTE,
|
||||
LWSSSCS_QOS_ACK_LOCAL, /* local proxy accepted our tx */
|
||||
LWSSSCS_QOS_NACK_LOCAL, /* local proxy refused our tx */
|
||||
|
||||
LWSSSCS_SINK_JOIN, /* sinks get this when a new source
|
||||
* stream joins the sink */
|
||||
LWSSSCS_SINK_PART, /* sinks get this when a new source
|
||||
* stream leaves the sink */
|
||||
} lws_ss_constate_t;
|
||||
|
||||
enum {
|
||||
LWSSS_FLAG_SOM = (1 << 0),
|
||||
/* payload contains the start of new message */
|
||||
LWSSS_FLAG_EOM = (1 << 1),
|
||||
/* payload contains the end of message */
|
||||
LWSSS_FLAG_POLL = (1 << 2),
|
||||
/* Not a real transmit... poll for rx if protocol needs it */
|
||||
LWSSS_FLAG_RELATED_START = (1 << 3),
|
||||
/* Appears in a zero-length message indicating a message group of zero
|
||||
* or more messages is now starting. */
|
||||
LWSSS_FLAG_RELATED_END = (1 << 4),
|
||||
/* Appears in a zero-length message indicating a message group of zero
|
||||
* or more messages has now finished. */
|
||||
LWSSS_FLAG_RIDESHARE = (1 << 5),
|
||||
/* Serialized payload starts with non-default rideshare name length and
|
||||
* name string without NUL, then payload */
|
||||
|
||||
/*
|
||||
* In the case the secure stream is proxied across a process or thread
|
||||
* boundary, eg by proxying through a socket for IPC, metadata must be
|
||||
* carried in-band. A byte is prepended to each rx payload to
|
||||
* differentiate what it is.
|
||||
*
|
||||
* Secure streams where the user is called back directly does not need
|
||||
* any of this and only pure payloads are passed.
|
||||
*
|
||||
* rx (received by client) prepends for proxied connections
|
||||
*/
|
||||
|
||||
LWSSS_SER_RXPRE_RX_PAYLOAD = 0x55,
|
||||
LWSSS_SER_RXPRE_CREATE_RESULT,
|
||||
LWSSS_SER_RXPRE_CONNSTATE,
|
||||
LWSSS_SER_RXPRE_TXCR_UPDATE,
|
||||
LWSSS_SER_RXPRE_TLSNEG_ENCLAVE_SIGN,
|
||||
|
||||
/* tx (send by client) prepends for proxied connections */
|
||||
|
||||
LWSSS_SER_TXPRE_STREAMTYPE = 0xaa,
|
||||
LWSSS_SER_TXPRE_ONWARD_CONNECT,
|
||||
LWSSS_SER_TXPRE_DESTROYING,
|
||||
LWSSS_SER_TXPRE_TX_PAYLOAD,
|
||||
LWSSS_SER_TXPRE_METADATA,
|
||||
LWSSS_SER_TXPRE_TXCR_UPDATE,
|
||||
LWSSS_SER_TXPRE_TLSNEG_ENCLAVE_SIGNED,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LPCS_WAIT_INITIAL_TX = 1, /* after connect, must send streamtype */
|
||||
LPCS_REPORTING_FAIL, /* stream creation failed, wait to to tell */
|
||||
LPCS_REPORTING_OK, /* stream creation succeeded, wait to to tell */
|
||||
LPCS_OPERATIONAL, /* ready for payloads */
|
||||
LPCS_DESTROYED,
|
||||
|
||||
LPCS_SENDING_INITIAL_TX = 1, /* after connect, must send streamtype */
|
||||
LPCS_WAITING_CREATE_RESULT, /* wait to hear if proxy ss create OK */
|
||||
LPCS_LOCAL_CONNECTED, /* we are in touch with the proxy */
|
||||
LPCS_ONWARD_CONNECT, /* request onward ss connection */
|
||||
|
||||
} lws_ss_conn_states_t;
|
||||
|
||||
/**
|
||||
* lws_ss_info_t: information about stream to be created
|
||||
*
|
||||
* Prepare this struct with information about what the stream type is and how
|
||||
* the stream should interface with your code, and pass it to lws_ss_create()
|
||||
* to create the requested stream.
|
||||
*/
|
||||
|
||||
typedef struct lws_ss_info {
|
||||
const char *streamtype; /**< type of stream we want to create */
|
||||
size_t user_alloc; /**< size of user allocation */
|
||||
size_t handle_offset; /**< offset of handle stg in user_alloc type,
|
||||
set to offsetof(mytype, my_handle_member) */
|
||||
size_t opaque_user_data_offset;
|
||||
/**< offset of opaque user data ptr in user_alloc type, set to
|
||||
offsetof(mytype, opaque_ud_member) */
|
||||
|
||||
int (*rx)(void *userobj, const uint8_t *buf, size_t len,
|
||||
int flags);
|
||||
/**< callback with rx payload for this stream */
|
||||
int (*tx)(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags);
|
||||
/**< callback to send payload on this stream... 0 = send as set in
|
||||
* len and flags, 1 = do not send anything (ie, not even 0 len frame) */
|
||||
int (*state)(void *userobj, void *h_src /* ss handle type */,
|
||||
lws_ss_constate_t state, lws_ss_tx_ordinal_t ack);
|
||||
/**< advisory cb about state of stream and QoS status if applicable...
|
||||
* h_src is only used with sinks and LWSSSCS_SINK_JOIN/_PART events.
|
||||
* Return nonzero to indicate you want to destroy the stream. */
|
||||
int manual_initial_tx_credit;
|
||||
/**< 0 = manage any tx credit automatically, nonzero explicitly sets the
|
||||
* peer stream to have the given amount of tx credit, if the protocol
|
||||
* can support it. */
|
||||
char register_sink;
|
||||
/**< If set, we're not creating a specific stream, but registering
|
||||
* ourselves as the "sink" for .streamtype. It's analogous to saying
|
||||
* we want to be the many-to-one "server" for .streamtype; when other
|
||||
* streams are created with that streamtype, they should be forwarded
|
||||
* to this stream owner, where they join and part from the sink via
|
||||
* (*state) LWSSSCS_SINK_JOIN / _PART events, the new client handle
|
||||
* being provided in the h_src parameter.
|
||||
*/
|
||||
} lws_ss_info_t;
|
||||
|
||||
/**
|
||||
* lws_ss_create() - Create secure stream
|
||||
*
|
||||
* \param context: the lws context to create this inside
|
||||
* \param tsi: service thread index to create on (normally 0)
|
||||
* \param ssi: pointer to lws_ss_info_t filled in with info about desired stream
|
||||
* \param opaque_user_data: opaque data to set in the stream's user object
|
||||
* \param ppss: pointer to secure stream handle pointer set on exit
|
||||
* \param ppayload_fmt: NULL or pointer to a string ptr to take payload format
|
||||
* name from the policy
|
||||
*
|
||||
* Requests a new secure stream described by \p ssi be created. If successful,
|
||||
* the stream is created, its state callback called with LWSSSCS_CREATING, *ppss
|
||||
* is set to point to the handle, and it returns 0. If it failed, it returns
|
||||
* nonzero.
|
||||
*
|
||||
* Along with the opaque stream object, streams overallocate
|
||||
*
|
||||
* 1) a user data struct whose size is set in ssi
|
||||
* 2) nauth plugin instantiation data (size set in the plugin struct)
|
||||
* 3) sauth plugin instantiation data (size set in the plugin struct)
|
||||
* 4) space for a copy of the stream type name
|
||||
*
|
||||
* The user data struct is initialized to all zeros, then the .handle_offset and
|
||||
* .opaque_user_data_offset fields of the ssi are used to prepare the user data
|
||||
* struct with the ss handle that was created, and a copy of the
|
||||
* opaque_user_data pointer given as an argument.
|
||||
*
|
||||
* If you want to set up the stream with specific information, point to it in
|
||||
* opaque_user_data and use the copy of that pointer in your user data member
|
||||
* for it starting from the LWSSSCS_CREATING state call.
|
||||
*
|
||||
* Since different endpoints chosen by the policy may require different payload
|
||||
* formats, \p ppayload_fmt is set to point to the name of the needed payload
|
||||
* format from the policy database if non-NULL.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
|
||||
void *opaque_user_data, struct lws_ss_handle **ppss,
|
||||
struct lws_sequencer *seq_owner, const char **ppayload_fmt);
|
||||
|
||||
/**
|
||||
* lws_ss_destroy() - Destroy secure stream
|
||||
*
|
||||
* \param ppss: pointer to lws_ss_t pointer to be destroyed
|
||||
*
|
||||
* Destroys the lws_ss_t pointed to by *ppss, and sets *ppss to NULL.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_ss_destroy(struct lws_ss_handle **ppss);
|
||||
|
||||
/**
|
||||
* lws_ss_request_tx() - Schedule stream for tx
|
||||
*
|
||||
* \param pss: pointer to lws_ss_t representing stream that wants to transmit
|
||||
*
|
||||
* Schedules a write on the stream represented by \p pss. When it's possible to
|
||||
* write on this stream, the *tx callback will occur with an empty buffer for
|
||||
* the stream owner to fill in.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_ss_request_tx(struct lws_ss_handle *pss);
|
||||
|
||||
/**
|
||||
* lws_ss_request_tx() - Schedule stream for tx
|
||||
*
|
||||
* \param pss: pointer to lws_ss_t representing stream that wants to transmit
|
||||
* \param len: the length of the write in bytes
|
||||
*
|
||||
* Schedules a write on the stream represented by \p pss. When it's possible to
|
||||
* write on this stream, the *tx callback will occur with an empty buffer for
|
||||
* the stream owner to fill in.
|
||||
*
|
||||
* This api variant should be used when it's possible the payload will go out
|
||||
* over h1 with x-web-form-urlencoded or similar Content-Type.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_ss_request_tx_len(struct lws_ss_handle *pss, unsigned long len);
|
||||
|
||||
|
||||
/**
|
||||
* lws_ss_client_connect() - Attempt the client connect
|
||||
*
|
||||
* \param h: secure streams handle
|
||||
*
|
||||
* Starts the connection process for the secure stream. Returns 0 if OK or
|
||||
* nonzero if we have already failed.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_ss_client_connect(struct lws_ss_handle *h);
|
||||
|
||||
/**
|
||||
* lws_ss_get_sequencer() - Return parent sequencer pointer if any
|
||||
*
|
||||
* \param h: secure streams handle
|
||||
*
|
||||
* Returns NULL if the secure stream is not associated with a sequencer.
|
||||
* Otherwise returns a pointer to the owning sequencer. You can use this to
|
||||
* identify which sequencer to direct messages to, from the secure stream
|
||||
* callback.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN struct lws_sequencer *
|
||||
lws_ss_get_sequencer(struct lws_ss_handle *h);
|
||||
|
||||
/**
|
||||
* lws_ss_proxy_create() - Start a unix domain socket proxy for Secure Streams
|
||||
*
|
||||
* \param context: lws_context
|
||||
* \param bind: if port is 0, unix domain path with leading @ for abstract.
|
||||
* if port nonzero, NULL, or network interface to bind listen to
|
||||
* \param port: tcp port to listen on
|
||||
*
|
||||
* Creates a vhost that listens either on an abstract namespace unix domain
|
||||
* socket (port = 0) or a tcp listen socket (port nonzero). If bind is NULL
|
||||
* and port is 0, the abstract unix domain socket defaults to "proxy.ss.lws".
|
||||
*
|
||||
* Client connections to this proxy to Secure Streams are fulfilled using the
|
||||
* policy local to the proxy and the data passed between the client and the
|
||||
* proxy using serialized Secure Streams protocol.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_ss_proxy_create(struct lws_context *context, const char *bind, int port);
|
||||
|
||||
/**
|
||||
* lws_ss_state_name() - convenience helper to get a printable conn state name
|
||||
*
|
||||
* \param state: the connection state index
|
||||
*
|
||||
* Returns a printable name for the connection state index passed in.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN const char *
|
||||
lws_ss_state_name(int state);
|
||||
|
||||
/**
|
||||
* lws_ss_get_context() - convenience helper to recover the lws context
|
||||
*
|
||||
* \param h: secure streams handle
|
||||
*
|
||||
* Returns the lws context. Dispenses with the need to pass a copy of it into
|
||||
* your secure streams handler.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN struct lws_context *
|
||||
lws_ss_get_context(struct lws_ss_handle *h);
|
||||
|
||||
/**
|
||||
* lws_ss_rideshare() - find the current streamtype when types rideshare
|
||||
*
|
||||
* \param h: the stream handle
|
||||
*
|
||||
* Under some conditions, the payloads may be structured using protocol-
|
||||
* specific formatting, eg, http multipart mime. It's possible to map the
|
||||
* logical partitions in the payload to different stream types using
|
||||
* the policy "rideshare" feature.
|
||||
*
|
||||
* This api lets the callback code find out which rideshare stream type the
|
||||
* current payload chunk belongs to.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN const char *
|
||||
lws_ss_rideshare(struct lws_ss_handle *h);
|
||||
|
||||
|
||||
/**
|
||||
* lws_ss_set_metadata() - allow user to bind external data to defined ss metadata
|
||||
*
|
||||
* \param h: secure streams handle
|
||||
* \param name: metadata name from the policy
|
||||
* \param value: pointer to user-managed data to bind to name
|
||||
* \param len: length of the user-managed data in value
|
||||
*
|
||||
* Binds user-managed data to the named metadata item from the ss policy.
|
||||
* If present, the metadata item is handled in a protocol-specific way using
|
||||
* the associated policy information. For example, in the policy
|
||||
*
|
||||
* "\"metadata\":" "["
|
||||
* "{\"uptag\":" "\"X-Upload-Tag:\"},"
|
||||
* "{\"ctype\":" "\"Content-Type:\"},"
|
||||
* "{\"xctype\":" "\"\"}"
|
||||
* "],"
|
||||
*
|
||||
* when the policy is using h1 is interpreted to add h1 headers of the given
|
||||
* name with the value of the metadata on the left.
|
||||
*
|
||||
* Return 0 if OK or nonzero if, eg, metadata name does not exist on the
|
||||
* streamtype.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_ss_set_metadata(struct lws_ss_handle *h, const char *name,
|
||||
void *value, size_t len);
|
||||
|
||||
|
||||
/**
|
||||
* lws_ss_add_peer_tx_credit() - allow peer to transmit more to us
|
||||
*
|
||||
* \param h: secure streams handle
|
||||
* \param add: additional tx credit (signed)
|
||||
*
|
||||
* Indicate to remote peer that we can accept \p add bytes more payload being
|
||||
* sent to us.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_ss_add_peer_tx_credit(struct lws_ss_handle *h, int32_t add);
|
||||
|
||||
/**
|
||||
* lws_ss_get_est_peer_tx_credit() - get our current estimate of peer's tx credit
|
||||
*
|
||||
* \param h: secure streams handle
|
||||
*
|
||||
* Based on what credit we gave it, and what we have received, report our
|
||||
* estimate of peer's tx credit usable to transmit to us. This may be outdated
|
||||
* in that some or all of its credit may already have been expended by sending
|
||||
* stuff to us that is in flight already.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_ss_get_est_peer_tx_credit(struct lws_ss_handle *h);
|
|
@ -49,6 +49,14 @@ typedef enum {
|
|||
LWSSEQ_WSI_CONN_FAIL, /* wsi we bound to us has failed to connect */
|
||||
LWSSEQ_WSI_CONN_CLOSE, /* wsi we bound to us has closed */
|
||||
|
||||
|
||||
LWSSEQ_SS_STATE_BASE, /* secure streams owned by a sequencer provide
|
||||
* automatic messages about state changes on
|
||||
* the sequencer, passing the oridinal in the
|
||||
* event argument field. The message index is
|
||||
* LWSSEQ_SS_STATE_BASE + the enum from
|
||||
* lws_ss_constate_t */
|
||||
|
||||
LWSSEQ_USER_BASE = 100 /* define your events from here */
|
||||
} lws_seq_events_t;
|
||||
|
||||
|
|
|
@ -234,8 +234,9 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,
|
|||
const char *caller)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
struct lws *wsi1, *wsi2;
|
||||
const struct lws_protocols *pro;
|
||||
struct lws_context *context;
|
||||
struct lws *wsi1, *wsi2;
|
||||
int n, ccb;
|
||||
|
||||
lwsl_info("%s: %p: caller: %s\n", __func__, wsi, caller);
|
||||
|
@ -585,8 +586,15 @@ just_kill_connection:
|
|||
*/
|
||||
ccb = 1;
|
||||
|
||||
pro = wsi->protocol;
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
if (!ccb && (lwsi_state_PRE_CLOSE(wsi) & LWSIFS_NOT_EST) &&
|
||||
lwsi_role_client(wsi)) {
|
||||
lws_inform_client_conn_fail(wsi, "Closed before conn", 18);
|
||||
}
|
||||
#endif
|
||||
if (ccb) {
|
||||
const struct lws_protocols *pro = wsi->protocol;
|
||||
|
||||
if (!wsi->protocol && wsi->vhost && wsi->vhost->protocols)
|
||||
pro = &wsi->vhost->protocols[0];
|
||||
|
|
|
@ -313,8 +313,14 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
|
|||
i->uri_replace_to);
|
||||
#endif
|
||||
|
||||
if (i->method && (!strcmp(i->method, "RAW") ||
|
||||
!strcmp(i->method, "MQTT"))) {
|
||||
if (i->method && (!strcmp(i->method, "RAW") // ||
|
||||
// !strcmp(i->method, "MQTT")
|
||||
)) {
|
||||
|
||||
/*
|
||||
* Not for MQTT here, since we don't know if we will
|
||||
* pipeline it or not...
|
||||
*/
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
|
||||
|
|
|
@ -213,7 +213,7 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
|
|||
memset(&sin, 0, sizeof(sin));
|
||||
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
if (LWS_UNIX_SOCK_ENABLED(vhost)) {
|
||||
if (!port && LWS_UNIX_SOCK_ENABLED(vhost)) {
|
||||
v = (struct sockaddr *)&serv_unix;
|
||||
n = sizeof(struct sockaddr_un);
|
||||
memset(&serv_unix, 0, sizeof(serv_unix));
|
||||
|
@ -313,7 +313,7 @@ lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
|
|||
}
|
||||
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
if (LWS_UNIX_SOCK_ENABLED(vhost)) {
|
||||
if (!port && LWS_UNIX_SOCK_ENABLED(vhost)) {
|
||||
uid_t uid = vhost->context->uid;
|
||||
gid_t gid = vhost->context->gid;
|
||||
|
||||
|
|
|
@ -264,10 +264,10 @@ lws_write(struct lws *wsi, unsigned char *buf, size_t len,
|
|||
wsi->detlat.acc_size = m;
|
||||
wsi->detlat.type = LDLT_WRITE;
|
||||
if (wsi->detlat.earliest_write_req_pre_write)
|
||||
wsi->detlat.latencies[LAT_DUR_PROXY_RX_TO_ONWARD_TX] =
|
||||
wsi->detlat.latencies[LAT_DUR_PROXY_PROXY_REQ_TO_WRITE] =
|
||||
us - wsi->detlat.earliest_write_req_pre_write;
|
||||
else
|
||||
wsi->detlat.latencies[LAT_DUR_PROXY_RX_TO_ONWARD_TX] = 0;
|
||||
wsi->detlat.latencies[LAT_DUR_PROXY_PROXY_REQ_TO_WRITE] = 0;
|
||||
wsi->detlat.latencies[LAT_DUR_USERCB] = lws_now_usecs() - us;
|
||||
lws_det_lat_cb(wsi->context, &wsi->detlat);
|
||||
|
||||
|
|
|
@ -359,6 +359,15 @@ struct lws_context_per_thread {
|
|||
struct lws_dll2_owner seq_owner; /* list of lws_sequencer-s */
|
||||
lws_dll2_owner_t attach_owner; /* pending lws_attach */
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
lws_dll2_owner_t ss_owner;
|
||||
#endif
|
||||
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) || \
|
||||
defined(LWS_WITH_SECURE_STREAMS_THREAD_API)
|
||||
lws_dll2_owner_t ss_dsh_owner;
|
||||
lws_dll2_owner_t ss_client_owner;
|
||||
#endif
|
||||
|
||||
struct lws_dll2_owner pt_sul_owner;
|
||||
|
||||
#if defined (LWS_WITH_SEQUENCER)
|
||||
|
@ -588,9 +597,10 @@ struct lws_vhost {
|
|||
int log_fd;
|
||||
#endif
|
||||
|
||||
unsigned int allocated_vhost_protocols:1;
|
||||
unsigned int created_vhost_protocols:1;
|
||||
unsigned int being_destroyed:1;
|
||||
uint8_t allocated_vhost_protocols:1;
|
||||
uint8_t created_vhost_protocols:1;
|
||||
uint8_t being_destroyed:1;
|
||||
uint8_t from_ss_policy:1;
|
||||
|
||||
unsigned char default_protocol_index;
|
||||
unsigned char raw_protocol_index;
|
||||
|
@ -1150,6 +1160,9 @@ lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len);
|
|||
lws_usec_t
|
||||
__lws_seq_timeout_check(struct lws_context_per_thread *pt, lws_usec_t usnow);
|
||||
|
||||
lws_usec_t
|
||||
__lws_ss_timeout_check(struct lws_context_per_thread *pt, lws_usec_t usnow);
|
||||
|
||||
struct lws * LWS_WARN_UNUSED_RESULT
|
||||
lws_client_connect_2_dnsreq(struct lws *wsi);
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ lws_set_socks(struct lws_vhost *vhost, const char *socks)
|
|||
}
|
||||
}
|
||||
|
||||
lwsl_notice("%s: Connections via Socks5 %s:%u\n", __func__,
|
||||
lwsl_debug("%s: Connections via Socks5 %s:%u\n", __func__,
|
||||
vhost->socks_proxy_address, vhost->socks_proxy_port);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -71,6 +71,24 @@ const struct lws_protocols *available_abstract_protocols[] = {
|
|||
};
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
const struct lws_protocols *available_secstream_protocols[] = {
|
||||
#if defined(LWS_ROLE_H1)
|
||||
&protocol_secstream_h1,
|
||||
#endif
|
||||
#if defined(LWS_ROLE_H2)
|
||||
&protocol_secstream_h2,
|
||||
#endif
|
||||
#if defined(LWS_ROLE_WS)
|
||||
&protocol_secstream_ws,
|
||||
#endif
|
||||
#if defined(LWS_ROLE_MQTT)
|
||||
&protocol_secstream_mqtt,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
#endif
|
||||
|
||||
static const char * const mount_protocols[] = {
|
||||
"http://",
|
||||
"https://",
|
||||
|
@ -456,7 +474,7 @@ lws_create_vhost(struct lws_context *context,
|
|||
struct lws_plugin *plugin = context->plugin_list;
|
||||
#endif
|
||||
struct lws_protocols *lwsp;
|
||||
int m, f = !info->pvo, fx = 0, abs_pcol_count = 0;
|
||||
int m, f = !info->pvo, fx = 0, abs_pcol_count = 0, sec_pcol_count = 0;
|
||||
char buf[96];
|
||||
#if ((defined(LWS_CLIENT_HTTP_PROXYING) && defined(LWS_WITH_CLIENT)) \
|
||||
|| defined(LWS_WITH_SOCKS5)) && defined(LWS_HAVE_GETENV)
|
||||
|
@ -581,6 +599,9 @@ lws_create_vhost(struct lws_context *context,
|
|||
#if defined(LWS_WITH_ABSTRACT)
|
||||
abs_pcol_count = (int)LWS_ARRAY_SIZE(available_abstract_protocols) - 1;
|
||||
#endif
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
sec_pcol_count = (int)LWS_ARRAY_SIZE(available_secstream_protocols) - 1;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* give the vhost a unified list of protocols including:
|
||||
|
@ -592,7 +613,7 @@ lws_create_vhost(struct lws_context *context,
|
|||
*/
|
||||
lwsp = lws_zalloc(sizeof(struct lws_protocols) *
|
||||
(vh->count_protocols +
|
||||
abs_pcol_count +
|
||||
abs_pcol_count + sec_pcol_count +
|
||||
context->plugin_protocol_count +
|
||||
fx + 1),
|
||||
"vhost-specific plugin table");
|
||||
|
@ -633,6 +654,14 @@ lws_create_vhost(struct lws_context *context,
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
for (n = 0; n < sec_pcol_count; n++) {
|
||||
memcpy(&lwsp[m++], available_secstream_protocols[n],
|
||||
sizeof(*lwsp));
|
||||
vh->count_protocols++;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 3: For compatibility, all protocols enabled on vhost if only
|
||||
* the default vhost exists. Otherwise only vhosts who ask
|
||||
|
@ -1287,6 +1316,8 @@ lws_vhost_destroy(struct lws_vhost *vh)
|
|||
|
||||
lws_vhost_destroy1(vh);
|
||||
|
||||
lwsl_debug("%s: count_bound_wsi %d\n", __func__, vh->count_bound_wsi);
|
||||
|
||||
if (!vh->count_bound_wsi) {
|
||||
/*
|
||||
* After listen handoff, there are already no wsi bound to this
|
||||
|
@ -1406,6 +1437,7 @@ lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin)
|
|||
return ACTIVE_CONNS_QUEUED;
|
||||
}
|
||||
|
||||
#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT)
|
||||
if (wsi->mux.parent_wsi) {
|
||||
/*
|
||||
* We already decided...
|
||||
|
@ -1415,6 +1447,7 @@ lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin)
|
|||
|
||||
return ACTIVE_CONNS_MUXED;
|
||||
}
|
||||
#endif
|
||||
|
||||
lws_vhost_lock(wsi->vhost); /* ----------------------------------- { */
|
||||
|
||||
|
@ -1448,7 +1481,7 @@ lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin)
|
|||
* connection that it doesn't support pipelining...
|
||||
*/
|
||||
if (w->keepalive_rejected) {
|
||||
lwsl_info("defeating pipelining due to no "
|
||||
lwsl_notice("defeating pipelining due to no "
|
||||
"keepalive on server\n");
|
||||
goto solo;
|
||||
}
|
||||
|
|
|
@ -294,7 +294,7 @@ lws_validity_cb(lws_sorted_usec_list_t *sul)
|
|||
|
||||
/* schedule a protocol-dependent ping */
|
||||
|
||||
lwsl_info("%s: wsi %p: scheduling validity check\n", __func__, wsi);
|
||||
lwsl_notice("%s: wsi %p: scheduling validity check\n", __func__, wsi);
|
||||
|
||||
if (wsi->role_ops && wsi->role_ops->issue_keepalive)
|
||||
wsi->role_ops->issue_keepalive(wsi, 0);
|
||||
|
|
|
@ -114,7 +114,7 @@ lws_state_notify_protocol_init(struct lws_state_manager *mgr,
|
|||
lws_system_do_attach(&context->pt[n]);
|
||||
|
||||
#if defined(LWS_WITH_SYS_DHCP_CLIENT)
|
||||
if (current == LWS_SYSTATE_DHCP) {
|
||||
if (target == LWS_SYSTATE_DHCP) {
|
||||
/*
|
||||
* Don't let it past here until at least one iface has been
|
||||
* configured for operation with DHCP
|
||||
|
@ -125,6 +125,40 @@ lws_state_notify_protocol_init(struct lws_state_manager *mgr,
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM)
|
||||
/*
|
||||
* Skip this if we are running something without the policy for it
|
||||
*/
|
||||
if (target == LWS_SYSTATE_AUTH1 &&
|
||||
context->pss_policies &&
|
||||
!lws_system_blob_get_size(lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH,
|
||||
0))) {
|
||||
lwsl_info("%s: AUTH1 state triggering api.amazon.com auth\n", __func__);
|
||||
/*
|
||||
* Start trying to acquire it if it's not already in progress
|
||||
* returns nonzero if we determine it's not needed
|
||||
*/
|
||||
if (!lws_ss_sys_auth_api_amazon_com(context))
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
/*
|
||||
* Skip this if we are running something without the policy for it
|
||||
*/
|
||||
if (target == LWS_SYSTATE_POLICY_VALID &&
|
||||
context->pss_policies && !context->policy_updated) {
|
||||
/*
|
||||
* Start trying to acquire it if it's not already in progress
|
||||
* returns nonzero if we determine it's not needed
|
||||
*/
|
||||
if (!lws_ss_sys_fetch_policy(context))
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* protocol part */
|
||||
|
||||
if (context->protocol_init_done)
|
||||
|
@ -245,6 +279,15 @@ lws_create_context(const struct lws_context_creation_info *info)
|
|||
__func__, context->udp_loss_sim_tx_pc,
|
||||
context->udp_loss_sim_rx_pc);
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
context->ss_proxy_bind = info->ss_proxy_bind;
|
||||
context->ss_proxy_port = info->ss_proxy_port;
|
||||
context->ss_proxy_address = info->ss_proxy_address;
|
||||
lwsl_notice("%s: using ss proxy bind '%s', port %d, ads '%s'\n",
|
||||
__func__, context->ss_proxy_bind, context->ss_proxy_port,
|
||||
context->ss_proxy_address);
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_NETWORK)
|
||||
context->count_threads = count_threads;
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
|
@ -258,6 +301,11 @@ lws_create_context(const struct lws_context_creation_info *info)
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
context->pss_policies_json = info->pss_policies_json;
|
||||
context->pss_plugins = info->pss_plugins;
|
||||
#endif
|
||||
|
||||
/* if he gave us names, set the uid / gid */
|
||||
if (lws_plat_drop_app_privileges(context, 0))
|
||||
goto bail;
|
||||
|
@ -634,7 +682,6 @@ lws_create_context(const struct lws_context_creation_info *info)
|
|||
|
||||
context->user_space = info->user;
|
||||
|
||||
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
strcpy(context->canonical_hostname, "unknown");
|
||||
#if defined(LWS_WITH_NETWORK)
|
||||
|
@ -755,6 +802,33 @@ lws_create_context(const struct lws_context_creation_info *info)
|
|||
goto fail_clean_pipes;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
|
||||
if (context->pss_policies_json) {
|
||||
/*
|
||||
* You must create your context with the explicit vhosts flag
|
||||
* in order to use secure streams
|
||||
*/
|
||||
assert(lws_check_opt(info->options,
|
||||
LWS_SERVER_OPTION_EXPLICIT_VHOSTS));
|
||||
|
||||
if (lws_ss_policy_parse_begin(context))
|
||||
goto bail;
|
||||
|
||||
n = lws_ss_policy_parse(context,
|
||||
(uint8_t *)context->pss_policies_json,
|
||||
strlen(context->pss_policies_json));
|
||||
if (n != LEJP_CONTINUE && n < 0)
|
||||
goto bail;
|
||||
|
||||
if (lws_ss_policy_set(context, "hardcoded")) {
|
||||
lwsl_err("%s: policy set failed\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
} else
|
||||
lws_create_vhost(context, info);
|
||||
#endif
|
||||
|
||||
lws_context_init_extensions(info, context);
|
||||
|
||||
lwsl_info(" mem: per-conn: %5lu bytes + protocol rx buf\n",
|
||||
|
@ -859,9 +933,10 @@ static void
|
|||
lws_context_destroy3(struct lws_context *context)
|
||||
{
|
||||
struct lws_context **pcontext_finalize = context->pcontext_finalize;
|
||||
#if defined(LWS_WITH_NETWORK)
|
||||
int n;
|
||||
|
||||
#if defined(LWS_WITH_NETWORK)
|
||||
|
||||
lwsl_debug("%s\n", __func__);
|
||||
|
||||
for (n = 0; n < context->count_threads; n++) {
|
||||
|
@ -905,6 +980,10 @@ lws_context_destroy3(struct lws_context *context)
|
|||
compatible_close(context->latencies_fd);
|
||||
#endif
|
||||
|
||||
for (n = 0; n < LWS_SYSBLOB_TYPE_COUNT; n++)
|
||||
lws_system_blob_destroy(
|
||||
lws_system_get_blob(context, n, 0));
|
||||
|
||||
lws_free(context);
|
||||
lwsl_info("%s: ctx %p freed\n", __func__, context);
|
||||
|
||||
|
@ -921,6 +1000,7 @@ lws_context_destroy2(struct lws_context *context)
|
|||
{
|
||||
#if defined(LWS_WITH_NETWORK)
|
||||
struct lws_vhost *vh = NULL, *vh1;
|
||||
int n;
|
||||
#endif
|
||||
#if defined(LWS_WITH_PEER_LIMITS)
|
||||
uint32_t nu;
|
||||
|
@ -932,6 +1012,48 @@ lws_context_destroy2(struct lws_context *context)
|
|||
|
||||
context->being_destroyed2 = 1;
|
||||
#if defined(LWS_WITH_NETWORK)
|
||||
|
||||
/*
|
||||
* We're going to trash things like vhost-protocols
|
||||
* So we need to finish dealing with wsi close that
|
||||
* might make callbacks first
|
||||
*/
|
||||
for (n = 0; n < context->count_threads; n++) {
|
||||
struct lws_context_per_thread *pt = &context->pt[n];
|
||||
|
||||
(void)pt;
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
lws_dll2_foreach_safe(&pt->ss_owner, NULL, lws_ss_destroy_dll);
|
||||
if (context->ac_policy)
|
||||
lwsac_free(&context->ac_policy);
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
lws_dll2_foreach_safe(&pt->ss_client_owner, NULL, lws_sspc_destroy_dll);
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SEQUENCER)
|
||||
lws_seq_destroy_all_on_pt(pt);
|
||||
#endif
|
||||
LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) {
|
||||
if (ar->pt_init_destroy)
|
||||
ar->pt_init_destroy(context, NULL, pt, 1);
|
||||
} LWS_FOR_EVERY_AVAILABLE_ROLE_END;
|
||||
|
||||
#if defined(LWS_WITH_CGI)
|
||||
role_ops_cgi.pt_init_destroy(context, NULL, pt, 1);
|
||||
#endif
|
||||
|
||||
if (context->event_loop_ops->destroy_pt)
|
||||
context->event_loop_ops->destroy_pt(context, n);
|
||||
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
while (pt->http.ah_list)
|
||||
_lws_destroy_ah(pt, pt->http.ah_list);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* free all the per-vhost allocations
|
||||
*/
|
||||
|
@ -982,6 +1104,7 @@ lws_context_destroy2(struct lws_context *context)
|
|||
lws_check_deferred_free(context, 0, 1);
|
||||
#endif
|
||||
|
||||
|
||||
#if LWS_MAX_SMP > 1
|
||||
lws_mutex_refcount_destroy(&context->mr);
|
||||
#endif
|
||||
|
@ -1185,3 +1308,13 @@ out:
|
|||
context->inside_context_destroy = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct lws_context *
|
||||
lws_system_context_from_system_mgr(lws_state_manager_t *mgr)
|
||||
{
|
||||
#if defined(LWS_WITH_NETWORK)
|
||||
return lws_container_of(mgr, struct lws_context, mgr_system);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -197,6 +197,9 @@ struct lws;
|
|||
#if defined(LWS_WITH_NETWORK)
|
||||
#include "private-lib-event-libs.h"
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
#include "private-lib-secure-streams.h"
|
||||
#endif
|
||||
|
||||
struct lws_io_watcher {
|
||||
#ifdef LWS_WITH_LIBEV
|
||||
|
@ -357,6 +360,14 @@ struct lws_context {
|
|||
lws_async_dns_t async_dns;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM)
|
||||
void *pol_args;
|
||||
struct lws_ss_handle *hss_auth;
|
||||
struct lws_ss_handle *hss_fetch_policy;
|
||||
lws_sorted_usec_list_t sul_api_amazon_com;
|
||||
lws_sorted_usec_list_t sul_api_amazon_com_kick;
|
||||
#endif
|
||||
|
||||
lws_state_manager_t mgr_system;
|
||||
lws_state_notify_link_t protocols_notify;
|
||||
#if defined (LWS_WITH_SYS_DHCP_CLIENT)
|
||||
|
@ -397,6 +408,11 @@ struct lws_context {
|
|||
#endif
|
||||
#endif /* NETWORK */
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
const char *ss_proxy_bind;
|
||||
const char *ss_proxy_address;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_FILE_OPS)
|
||||
const struct lws_plat_file_ops *fops;
|
||||
#endif
|
||||
|
@ -425,6 +441,14 @@ struct lws_context {
|
|||
#endif
|
||||
|
||||
const lws_system_ops_t *system_ops;
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
const char *pss_policies_json;
|
||||
const lws_ss_policy_t *pss_policies;
|
||||
const lws_ss_plugin_t **pss_plugins;
|
||||
struct lwsac *ac_policy;
|
||||
#endif
|
||||
|
||||
void *external_baggage_free_on_destroy;
|
||||
const struct lws_token_limits *token_limits;
|
||||
void *user_space;
|
||||
|
@ -487,6 +511,7 @@ struct lws_context {
|
|||
unsigned int done_protocol_destroy_cb:1;
|
||||
unsigned int finalize_destroy_after_internal_loops_stopped:1;
|
||||
unsigned int max_fds_unrelated_to_ulimit:1;
|
||||
unsigned int policy_updated:1;
|
||||
|
||||
short count_threads;
|
||||
short plugin_protocol_count;
|
||||
|
@ -494,6 +519,9 @@ struct lws_context {
|
|||
short server_string_len;
|
||||
unsigned short ws_ping_pong_interval;
|
||||
unsigned short deprecation_pending_listen_close_count;
|
||||
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
uint16_t ss_proxy_port;
|
||||
#endif
|
||||
|
||||
uint8_t max_fi;
|
||||
uint8_t udp_loss_sim_tx_pc;
|
||||
|
|
|
@ -146,6 +146,7 @@ _lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size, char backfill)
|
|||
if (al >= alloc - hp)
|
||||
alloc = al + hp;
|
||||
|
||||
lwsl_debug("%s: alloc %d for %d\n", __func__, (int)alloc, (int)ensure);
|
||||
bf = malloc(alloc);
|
||||
if (!bf) {
|
||||
lwsl_err("%s: OOM trying to alloc %llud\n", __func__,
|
||||
|
|
|
@ -2611,7 +2611,7 @@ lws_h2_client_stream_long_poll_rxonly(struct lws *wsi)
|
|||
wsi->h2.long_poll = 1;
|
||||
wsi->h2.send_END_STREAM = 1;
|
||||
|
||||
lws_header_table_detach(wsi, 0);
|
||||
// lws_header_table_detach(wsi, 0);
|
||||
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
|
|
|
@ -200,6 +200,31 @@ send_hs:
|
|||
else {
|
||||
/* for a method = "RAW" connection, this makes us
|
||||
* established */
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
|
||||
|
||||
/* we can retry this... just cook the SSL BIO the first time */
|
||||
|
||||
if (lws_ssl_client_bio_create(wsi) < 0) {
|
||||
lwsl_err("%s: bio_create failed\n", __func__);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
//#if !defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
|
||||
n = lws_ssl_client_connect1(wsi);
|
||||
if (!n)
|
||||
return wsi;
|
||||
if (n < 0) {
|
||||
lwsl_err("%s: lws_ssl_client_connect1 failed\n", __func__);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
//#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
|
||||
|
|
|
@ -286,10 +286,10 @@ reset:
|
|||
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
#if defined(LWS_ROLE_MQTT)
|
||||
connect_via_info2:
|
||||
#endif
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED)
|
||||
if (!lws_http_client_connect_via_info2(wsi))
|
||||
/* our client connect has failed, the wsi
|
||||
|
@ -349,6 +349,7 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice)
|
|||
lws_peer_track_ah_detach(context, wsi->peer);
|
||||
#endif
|
||||
ah->wsi = NULL; /* no owner */
|
||||
wsi->http.ah = NULL;
|
||||
|
||||
pwsi = &pt->http.ah_wait_list;
|
||||
|
||||
|
|
|
@ -1900,8 +1900,8 @@ lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)
|
|||
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
||||
|
||||
n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
|
||||
if (wsi->role_ops->adoption_cb[lwsi_role_server(wsi)])
|
||||
n = wsi->role_ops->adoption_cb[lwsi_role_server(wsi)];
|
||||
if (wsi->role_ops->adoption_cb[0])
|
||||
n = wsi->role_ops->adoption_cb[0];
|
||||
|
||||
ipbuf[0] = '\0';
|
||||
#if !defined(LWS_PLAT_OPTEE)
|
||||
|
|
|
@ -1705,6 +1705,8 @@ lws_mqtt_client_send_publish(struct lws *wsi, lws_mqtt_publish_param_t *pub,
|
|||
|
||||
do_write:
|
||||
|
||||
// lwsl_hexdump_err(start, lws_ptr_diff(p, start));
|
||||
|
||||
if (lws_write(nwsi, start, lws_ptr_diff(p, start), LWS_WRITE_BINARY) !=
|
||||
lws_ptr_diff(p, start)) {
|
||||
lwsl_err("%s: write failed\n", __func__);
|
||||
|
|
398
lib/secure-streams/README.md
Normal file
398
lib/secure-streams/README.md
Normal file
|
@ -0,0 +1,398 @@
|
|||
# Secure Streams
|
||||
|
||||
Secure Streams is a client api that strictly separates payload from any metadata.
|
||||
That includes the endpoint address for the connection, the tls CA and even the
|
||||
protocol used to connect to the endpoint.
|
||||
|
||||
The user api just receives and transmits payload, and receives advisory connection
|
||||
state information.
|
||||
|
||||
The details about how the connections for different types of secure stream should
|
||||
be made are held in JSON "policy database" initially passed in to the context
|
||||
creation, but able to be updated from a remote copy.
|
||||
|
||||

|
||||
|
||||
# JSON Policy Database
|
||||
|
||||
Example JSON policy... formatting is shown for clarity but whitespace can be
|
||||
omitted in the actual policy.
|
||||
|
||||
Ordering is not critical in itself, but forward references are not allowed,
|
||||
things must be defined before they are allowed to be referenced later in the
|
||||
JSON.
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
"release": "01234567",
|
||||
"product": "myproduct",
|
||||
"schema-version": 1,
|
||||
"retry": [{
|
||||
"default": {
|
||||
"backoff": [1000, 2000, 3000, 5000, 10000],
|
||||
"conceal": 5,
|
||||
"jitterpc": 20
|
||||
}
|
||||
}],
|
||||
"certs": [{
|
||||
"isrg_root_x1": "MIIFazCCA1OgAw...AnX5iItreGCc="
|
||||
}, {
|
||||
"LEX3_isrg_root_x1": "MIIFjTCCA3WgAwIB...WEsikxqEt"
|
||||
}],
|
||||
"trust_stores": [{
|
||||
"le_via_isrg": ["isrg_root_x1", "LEX3_isrg_root_x1"]
|
||||
}],
|
||||
"s": [{
|
||||
"mintest": {
|
||||
"endpoint": "warmcat.com",
|
||||
"port": 4443,
|
||||
"protocol": "h1get",
|
||||
"aux": "index.html",
|
||||
"plugins": [],
|
||||
"tls": true,
|
||||
"opportunistic": true,
|
||||
"retry": "default",
|
||||
"tls_trust_store": "le_via_isrg"
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### `Release`
|
||||
|
||||
Identifies the policy version
|
||||
|
||||
### `Product`
|
||||
|
||||
Identifies the product the policy should apply to
|
||||
|
||||
### `Schema-version`
|
||||
|
||||
The minimum version of the policy parser required to parse this policy
|
||||
|
||||
### `via-socks5`
|
||||
|
||||
Optional redirect for Secure Streams client traffic through a socks5
|
||||
proxy given in the format `address:port`, eg, `127.0.0.1:12345`.
|
||||
|
||||
### `retry`
|
||||
|
||||
A list of backoff schemes referred to in the policy
|
||||
|
||||
### `backoff`
|
||||
|
||||
An array of ms delays for each retry in turn
|
||||
|
||||
### `conceal`
|
||||
|
||||
The number of retries to conceal from higher layers before giving errors. If
|
||||
this is larger than the number of times in the backoff array, then the last time
|
||||
is used for the extra delays
|
||||
|
||||
### `jitterpc`
|
||||
|
||||
Percentage of the delay times mentioned in the backoff array that may be
|
||||
randomly added to the figure from the array. For example with an array entry of
|
||||
1000ms, and jitterpc of 20%, actual delays will be chosen randomly from 1000ms
|
||||
through 1200ms. This is to stop retry storms triggered by a single event like
|
||||
an outage becoming synchronized into a DoS.
|
||||
|
||||
### `certs`
|
||||
|
||||
Certificates needed for validation should be listed here each with a name. The
|
||||
format is base64 DER, which is the same as the part of PEM that is inside the
|
||||
start and end lines.
|
||||
|
||||
### `trust_stores`
|
||||
|
||||
Chains of certificates given in the `certs` section may be named and described
|
||||
inside the `trust_stores` section. Each entry in `trust_stores` is created as
|
||||
a vhost + tls context with the given name. Stream types can later be associated
|
||||
with one of these to enforce validity checking of the remote server.
|
||||
|
||||
Entries should be named using "name" and the stack array defined using "stack"
|
||||
|
||||
### `s`
|
||||
|
||||
These are an array of policies for the supported stream type names.
|
||||
|
||||
### `endpoint`
|
||||
|
||||
The DNS address the secure stream should connect to
|
||||
|
||||
### `port`
|
||||
|
||||
The port number as an integer on the endpoint to connect to
|
||||
|
||||
### `protocol`
|
||||
|
||||
The wire protocol to connect to the endpoint with. Currently supported
|
||||
streamtypes are
|
||||
|
||||
|Wire protocol|Description|
|
||||
|---|---|
|
||||
|h1|http/1|
|
||||
|h2|http/2|
|
||||
|ws|http/1 Websockets|
|
||||
|mqtt|mqtt 3.1.1|
|
||||
|
||||
### `plugins`
|
||||
|
||||
Array of plugin names to apply to the stream, if any
|
||||
|
||||
### `tls`
|
||||
|
||||
Set to `true` to enforce the stream travelling in a tls tunnel
|
||||
|
||||
### `client cert`
|
||||
|
||||
Set if the stream needs to authenticate itself using a tls client certificate.
|
||||
Set to the certificate index counting from 0+. The certificates are managed
|
||||
using lws_sytstem blobs.
|
||||
|
||||
### `opportunistic`
|
||||
|
||||
Set to `true` if the connection may be left dropped except when in use
|
||||
|
||||
### `nailed_up`
|
||||
|
||||
Set to `true` to have lws retry if the connection carrying this stream should
|
||||
ever drop.
|
||||
|
||||
### `retry`
|
||||
|
||||
The name of the policy described in the `retry` section to apply to this
|
||||
connection for retry + backoff
|
||||
|
||||
### `tls_trust_store`
|
||||
|
||||
The name of the trust store described in the `trust_stores` section to apply
|
||||
to validate the remote server cert.
|
||||
|
||||
## http transport
|
||||
|
||||
### `http_method`
|
||||
|
||||
HTTP method to use with http-related protocols, like GET or POST.
|
||||
Not required for ws.
|
||||
|
||||
### `http_url`
|
||||
|
||||
Url path to use with http-related protocols
|
||||
|
||||
The URL path can include metatadata like this
|
||||
|
||||
"/mypath?whatever=${metadataname}"
|
||||
|
||||
${metadataname} will be replaced by the current value of the
|
||||
same metadata name. The metadata names must be listed in the
|
||||
"metadata": [ ] section.
|
||||
|
||||
### `http_auth_header`
|
||||
|
||||
The name of the header that takes the auth token, with a trailing ':', eg
|
||||
|
||||
```
|
||||
"http_auth_header": "authorization:"
|
||||
```
|
||||
|
||||
### `http_dsn_header`
|
||||
|
||||
The name of the header that takes the dsn token, with a trailing ':', eg
|
||||
|
||||
```
|
||||
"http_dsn_header": "x-dsn:"
|
||||
```
|
||||
|
||||
### `http_fwv_header`
|
||||
|
||||
The name of the header that takes the firmware version token, with a trailing ':', eg
|
||||
|
||||
```
|
||||
"http_fwv_header": "x-fw-version:"
|
||||
```
|
||||
|
||||
### `http_devtype_header`
|
||||
|
||||
The name of the header that takes the device type token, with a trailing ':', eg
|
||||
|
||||
```
|
||||
"http_devtype_header": "x-device-type:"
|
||||
```
|
||||
|
||||
### `http_auth_preamble`
|
||||
|
||||
An optional string that precedes the auth token, eg
|
||||
|
||||
```
|
||||
"http_auth_preamble": "bearer "
|
||||
```
|
||||
|
||||
### `auth_hexify`
|
||||
|
||||
Convert the auth token to hex ('A' -> "41") before transporting. Not necessary if the
|
||||
auth token is already in printable string format suitable for transport. Needed if the
|
||||
auth token is a chunk of 8-bit binary.
|
||||
|
||||
### `nghttp2_quirk_end_stream`
|
||||
|
||||
Set this to `true` if the peer server has the quirk it won't send a response until we have
|
||||
sent an `END_STREAM`, even though we have sent headers with `END_HEADERS`.
|
||||
|
||||
### `h2q_oflow_txcr`
|
||||
|
||||
Set this to `true` if the peer server has the quirk it sends an maximum initial tx credit
|
||||
of 0x7fffffff and then later increments it illegally.
|
||||
|
||||
### `http_multipart_name`
|
||||
|
||||
Indicates this stream goes out using multipart mime, and provides the name part of the
|
||||
multipart header
|
||||
|
||||
### `http_multipart_filename`
|
||||
|
||||
Indicates this stream goes out using multipart mime, and provides the filename part of the
|
||||
multipart header
|
||||
|
||||
### `http_multipart_content_type`
|
||||
|
||||
The `content-type` to mark up the multipart mime section with if present
|
||||
|
||||
### `http_www_form_urlencoded`
|
||||
|
||||
Indicate the data is sent in `x-www-form-urlencoded` form
|
||||
|
||||
### `rideshare`
|
||||
|
||||
For special cases where one logically separate stream travels with another when using this
|
||||
protocol. Eg, a single multipart mime transaction carries content from two or more streams.
|
||||
|
||||
## ws transport
|
||||
|
||||
### `ws_subprotocol`
|
||||
|
||||
Name of the ws subprotocol to use.
|
||||
|
||||
### `ws_binary`
|
||||
|
||||
Use if the ws messages are binary
|
||||
|
||||
## MQTT transport
|
||||
|
||||
### `mqtt_topic`
|
||||
|
||||
Set the topic this streamtype uses for writes
|
||||
|
||||
### `mqtt_subscribe`
|
||||
|
||||
Set the topic this streamtype subscribes to
|
||||
|
||||
### `mqtt qos`
|
||||
|
||||
Set the QOS level for this streamtype
|
||||
|
||||
## Loading and using updated remote policy
|
||||
|
||||
If the default, hardcoded policy includes a streamtype `fetch_policy`,
|
||||
during startup when lws_system reaches the POLICY state, lws will use
|
||||
a Secure Stream of type `fetch_policy` to download, parse and update
|
||||
the policy to use it.
|
||||
|
||||
The secure-streams-proxy minimal example shows how this is done and
|
||||
fetches its real policy from warmcat.com at startup using the built-in
|
||||
one.
|
||||
|
||||
## Stream serialization and proxying
|
||||
|
||||
By default Secure Streams expects to make the outgoing connection described in
|
||||
the policy in the same process / thread, this suits the case where all the
|
||||
participating clients are in the same statically-linked image.
|
||||
|
||||
In this case the `lws_ss_` apis are fulfilled locally by secure-streams.c and
|
||||
policy.c for policy lookups.
|
||||
|
||||
However it also supports serialization, where the SS api can be streamed over
|
||||
another transport such as a Unix Domain Socket connection. This suits the case
|
||||
where the clients are actually in different processes in, eg, Linux or Android.
|
||||
|
||||
In those cases, you run a proxy process (minimal-secure-streams-proxy) that
|
||||
listens on a Unix Domain Socket and is connected to by one or more other
|
||||
processes that pass their SS API activity to the proxy for fulfilment (or
|
||||
onward proxying).
|
||||
|
||||
In this case the proxy uses secure-streams.c and policy.c as before to fulfil
|
||||
the inbound proxy streams, but uses secure-streams-serialize.c to serialize and
|
||||
deserialize the proxied SS API activity. The proxy clients define
|
||||
LWS_SS_USE_SSPC either very early in their sources before the includes, or on
|
||||
the compiler commandline... this causes the lws_ss_ apis to be replaced at
|
||||
preprocessor time with lws_sspc_ equivalents. These serialize the api action
|
||||
and pass it to the proxy over a Unix Domain Socket for fulfilment, the results
|
||||
and state changes etc are streamed over the Unix Domain Socket and presented to
|
||||
the application exactly the same as if it was being fulfilled locally.
|
||||
|
||||
To demonstrate this, some minimal examples, eg, minimal-secure-streams and
|
||||
mimimal-secure-streams-avs build themselves both ways, once with direct SS API
|
||||
fulfilment and once with Unix Domain Socket proxying and -client appended on the
|
||||
executable name. To test the -client variants, run minimal-secure-streams-proxy
|
||||
on the same machine.
|
||||
|
||||
## Complicated scenarios with secure streams proxy
|
||||
|
||||
As mentioned above, Secure Streams has two modes, by default the application
|
||||
directly parses the policy and makes the outgoing connections itself.
|
||||
However when configured at cmake with
|
||||
|
||||
```
|
||||
-DLWS_WITH_SOCKS=1 -DLWS_WITH_SECURE_STREAMS=1 -DLWS_WITH_SECURE_STREAMS_PROXY_API=1 -DLWS_WITH_MINIMAL_EXAMPLES=1
|
||||
```
|
||||
|
||||
and define `LWS_SS_USE_SSPC` when building the application, applications forward
|
||||
their network requests to a local or remote SS proxy for fulfilment... and only
|
||||
the SS proxy has the system policy. By default, the SS proxy is on the local
|
||||
machine and is connected to via a Unix Domain Socket, but tcp links are also
|
||||
possible. (Note the proxied traffic is not encrypyed by default.)
|
||||
|
||||
Using the configuration above, the example SS applications are built two ways,
|
||||
once for direct connection fulfilment (eg, `./bin/lws-minimal-secure-streams`),
|
||||
and once with `LWS_SS_USE_SSPC` also defined so it connects via an SS proxy,
|
||||
(eg, `./bin/lws-minimal-secure-streams-client`).
|
||||
|
||||
## Testing an example scenario with SS Proxy and socks5 proxy
|
||||
|
||||
```
|
||||
[ SS application ] --- tcp --- [ socks 5 proxy ] --- tcp --- [ SS proxy ] --- internet
|
||||
```
|
||||
|
||||
In this scenario, everything is on localhost, the socks5 proxy listens on :1337 and
|
||||
the SS proxy listens on :1234. The SS application connects to the socks5
|
||||
proxy to get to the SS proxy, which then goes out to the internet
|
||||
|
||||
### 1 Start the SS proxy
|
||||
|
||||
Tell it to listen on lo interface on port 1234
|
||||
|
||||
```
|
||||
$ ./bin/lws-minimal-secure-streams-proxy -p 1234 -i lo
|
||||
```
|
||||
|
||||
### 2 Start the SOCKS5 proxy
|
||||
|
||||
```
|
||||
$ ssh -D 1337 -N -v localhost
|
||||
```
|
||||
|
||||
The -v makes connections to the proxy visible in the terminal for testing
|
||||
|
||||
### 3 Run the SS application
|
||||
|
||||
The application is told to make all connections via the socks5 proxy at
|
||||
127.0.0.1:1337, and to fulfil its SS connections via an SS proxy, binding
|
||||
connections to 127.0.0.1 (ipv4 lo interface, -1), to 127.0.0.1:1234 (-a/-p).
|
||||
|
||||
```
|
||||
socks_proxy=127.0.0.1:1337 ./bin/lws-minimal-secure-streams-client -p 1234 -i 127.0.0.1 -a 127.0.0.1
|
||||
```
|
||||
|
||||
You can confirm this goes through the ssh socks5 proxy to get to the SS proxy
|
||||
and fulfil the connection.
|
40
lib/secure-streams/plugins/ssp-h1url/h1url.c
Normal file
40
lib/secure-streams/plugins/ssp-h1url/h1url.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* ssp-h1url plugin
|
||||
*
|
||||
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* CC0 so it can be used as a template for your own secure streams plugins
|
||||
* licensed how you like.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
|
||||
static int
|
||||
ssp_h1url_create(struct lws_ss_handle *ss, void *info, plugin_auth_status_cb status)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ssp_h1url_destroy(struct lws_ss_handle *ss)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ssp_h1url_munge(struct lws_ss_handle *ss, char *path, size_t path_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this is the only exported symbol */
|
||||
const lws_ss_plugin_t ssp_h1url = {
|
||||
.name = "h1url",
|
||||
.alloc = 0,
|
||||
.create = ssp_h1url_create,
|
||||
.destroy = ssp_h1url_destroy,
|
||||
.munge = ssp_h1url_munge
|
||||
};
|
942
lib/secure-streams/policy.c
Normal file
942
lib/secure-streams/policy.c
Normal file
|
@ -0,0 +1,942 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
typedef struct backoffs {
|
||||
struct backoffs *next;
|
||||
const char *name;
|
||||
lws_retry_bo_t r;
|
||||
} backoff_t;
|
||||
|
||||
static const char * const lejp_tokens_policy[] = {
|
||||
"release",
|
||||
"product",
|
||||
"schema-version",
|
||||
"via-socks5",
|
||||
"retry[].*.backoff",
|
||||
"retry[].*.conceal",
|
||||
"retry[].*.jitterpc",
|
||||
"retry[].*.svalidping",
|
||||
"retry[].*.svalidhup",
|
||||
"retry[].*",
|
||||
"certs[].*",
|
||||
"trust_stores[].name",
|
||||
"trust_stores[].stack",
|
||||
"s[].*.endpoint",
|
||||
"s[].*.via-socks5",
|
||||
"s[].*.protocol",
|
||||
"s[].*.port",
|
||||
"s[].*.plugins",
|
||||
"s[].*.tls",
|
||||
"s[].*.client_cert",
|
||||
"s[].*.opportunistic",
|
||||
"s[].*.nailed_up",
|
||||
"s[].*.urgent_tx",
|
||||
"s[].*.urgent_rx",
|
||||
"s[].*.long_poll",
|
||||
"s[].*.retry",
|
||||
"s[].*.tls_trust_store",
|
||||
"s[].*.metadata",
|
||||
"s[].*.metadata[].*",
|
||||
|
||||
"s[].*.http_auth_header",
|
||||
"s[].*.http_dsn_header",
|
||||
"s[].*.http_fwv_header",
|
||||
"s[].*.http_devtype_header",
|
||||
|
||||
"s[].*.http_auth_preamble",
|
||||
|
||||
"s[].*.http_no_content_length",
|
||||
"s[].*.rideshare", /* streamtype name this rides shotgun with */
|
||||
"s[].*.payload_fmt",
|
||||
"s[].*.http_method",
|
||||
"s[].*.http_url",
|
||||
"s[].*.nghttp2_quirk_end_stream",
|
||||
"s[].*.h2q_oflow_txcr",
|
||||
"s[].*.http_multipart_name",
|
||||
"s[].*.http_multipart_filename",
|
||||
"s[].*.http_mime_content_type",
|
||||
"s[].*.http_www_form_urlencoded",
|
||||
"s[].*.ws_subprotocol",
|
||||
"s[].*.ws_binary",
|
||||
"s[].*.local_sink",
|
||||
"s[].*.mqtt_topic",
|
||||
"s[].*.mqtt_subscribe",
|
||||
"s[].*.mqtt_qos",
|
||||
"s[].*",
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LSSPPT_RELEASE,
|
||||
LSSPPT_PRODUCT,
|
||||
LSSPPT_SCHEMA_VERSION,
|
||||
LSSPPT_VIA_SOCKS5,
|
||||
LSSPPT_BACKOFF,
|
||||
LSSPPT_CONCEAL,
|
||||
LSSPPT_JITTERPC,
|
||||
LSSPPT_VALIDPING_S,
|
||||
LSSPPT_VALIDHUP_S,
|
||||
LSSPPT_RETRY,
|
||||
LSSPPT_CERTS,
|
||||
LSSPPT_TRUST_STORES_NAME,
|
||||
LSSPPT_TRUST_STORES_STACK,
|
||||
LSSPPT_ENDPOINT,
|
||||
LSSPPT_VH_VIA_SOCKS5,
|
||||
LSSPPT_PROTOCOL,
|
||||
LSSPPT_PORT,
|
||||
LSSPPT_PLUGINS,
|
||||
LSSPPT_TLS,
|
||||
LSSPPT_TLS_CLIENT_CERT,
|
||||
LSSPPT_OPPORTUNISTIC,
|
||||
LSSPPT_NAILED_UP,
|
||||
LSSPPT_URGENT_TX,
|
||||
LSSPPT_URGENT_RX,
|
||||
LSSPPT_LONG_POLL,
|
||||
LSSPPT_RETRYPTR,
|
||||
LSSPPT_TRUST,
|
||||
LSSPPT_METADATA,
|
||||
LSSPPT_METADATA_ITEM,
|
||||
|
||||
LSSPPT_HTTP_AUTH_HEADER,
|
||||
LSSPPT_HTTP_DSN_HEADER,
|
||||
LSSPPT_HTTP_FWV_HEADER,
|
||||
LSSPPT_HTTP_TYPE_HEADER,
|
||||
|
||||
LSSPPT_HTTP_AUTH_PREAMBLE,
|
||||
LSSPPT_HTTP_NO_CONTENT_LENGTH,
|
||||
LSSPPT_RIDESHARE,
|
||||
LSSPPT_PAYLOAD_FORMAT,
|
||||
LSSPPT_HTTP_METHOD,
|
||||
LSSPPT_HTTP_URL,
|
||||
LSSPPT_NGHTTP2_QUIRK_END_STREAM,
|
||||
LSSPPT_H2_QUIRK_OVERFLOWS_TXCR,
|
||||
LSSPPT_HTTP_MULTIPART_NAME,
|
||||
LSSPPT_HTTP_MULTIPART_FILENAME,
|
||||
LSSPPT_HTTP_MULTIPART_CONTENT_TYPE,
|
||||
LSSPPT_HTTP_WWW_FORM_URLENCODED,
|
||||
LSSPPT_WS_SUBPROTOCOL,
|
||||
LSSPPT_WS_BINARY,
|
||||
LSSPPT_LOCAL_SINK,
|
||||
LSSPPT_MQTT_TOPIC,
|
||||
LSSPPT_MQTT_SUBSCRIBE,
|
||||
LSSPPT_MQTT_QOS,
|
||||
LSSPPT_STREAMTYPES
|
||||
} policy_token_t;
|
||||
|
||||
union u {
|
||||
backoff_t *b;
|
||||
lws_ss_x509_t *x;
|
||||
lws_ss_trust_store_t *t;
|
||||
lws_ss_policy_t *p;
|
||||
};
|
||||
|
||||
enum {
|
||||
LTY_BACKOFF,
|
||||
LTY_X509,
|
||||
LTY_TRUSTSTORE,
|
||||
LTY_POLICY,
|
||||
|
||||
_LTY_COUNT /* always last */
|
||||
};
|
||||
|
||||
struct policy_cb_args {
|
||||
struct lejp_ctx jctx;
|
||||
struct lws_context *context;
|
||||
struct lwsac *ac;
|
||||
|
||||
const char *socks5_proxy;
|
||||
|
||||
struct lws_b64state b64;
|
||||
|
||||
union u heads[_LTY_COUNT];
|
||||
union u curr[_LTY_COUNT];
|
||||
|
||||
uint8_t *p;
|
||||
|
||||
int count;
|
||||
};
|
||||
|
||||
#define POL_AC_INITIAL 2048
|
||||
#define POL_AC_GRAIN 800
|
||||
#define MAX_CERT_TEMP 2048 /* used to discover actual cert size for realloc */
|
||||
|
||||
static uint8_t sizes[] = {
|
||||
sizeof(backoff_t),
|
||||
sizeof(lws_ss_x509_t),
|
||||
sizeof(lws_ss_trust_store_t),
|
||||
sizeof(lws_ss_policy_t),
|
||||
};
|
||||
|
||||
static const char *protonames[] = {
|
||||
"h1", /* LWSSSP_H1 */
|
||||
"h2", /* LWSSSP_H2 */
|
||||
"ws", /* LWSSSP_WS */
|
||||
"mqtt", /* LWSSSP_MQTT */
|
||||
};
|
||||
|
||||
lws_ss_metadata_t *
|
||||
lws_ss_policy_metadata(const lws_ss_policy_t *p, const char *name)
|
||||
{
|
||||
lws_ss_metadata_t *pmd = p->metadata;
|
||||
|
||||
while (pmd) {
|
||||
if (pmd->name && !strcmp(name, pmd->name))
|
||||
return pmd;
|
||||
pmd = pmd->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lws_ss_metadata_t *
|
||||
lws_ss_policy_metadata_index(const lws_ss_policy_t *p, size_t index)
|
||||
{
|
||||
lws_ss_metadata_t *pmd = p->metadata;
|
||||
|
||||
while (pmd) {
|
||||
if (pmd->length == index)
|
||||
return pmd;
|
||||
pmd = pmd->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_set_metadata(struct lws_ss_handle *h, const char *name,
|
||||
void *value, size_t len)
|
||||
{
|
||||
lws_ss_metadata_t *omd = lws_ss_policy_metadata(h->policy, name);
|
||||
|
||||
if (!omd) {
|
||||
lwsl_err("%s: unknown metadata %s\n", __func__, name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
h->metadata[omd->length].name = name;
|
||||
h->metadata[omd->length].value = value;
|
||||
h->metadata[omd->length].length = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
lws_ss_metadata_t *
|
||||
lws_ss_get_handle_metadata(struct lws_ss_handle *h, const char *name)
|
||||
{
|
||||
lws_ss_metadata_t *omd = lws_ss_policy_metadata(h->policy, name);
|
||||
|
||||
if (!omd)
|
||||
return NULL;
|
||||
|
||||
return &h->metadata[omd->length];
|
||||
}
|
||||
|
||||
static signed char
|
||||
lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
|
||||
{
|
||||
struct policy_cb_args *a = (struct policy_cb_args *)ctx->user;
|
||||
const lws_ss_plugin_t **pin;
|
||||
char **pp, dotstar[32], *q;
|
||||
lws_ss_trust_store_t *ts;
|
||||
lws_ss_metadata_t *pmd;
|
||||
lws_retry_bo_t *b;
|
||||
size_t inl, outl;
|
||||
lws_ss_x509_t *x;
|
||||
uint8_t *extant;
|
||||
backoff_t *bot;
|
||||
int n = -1;
|
||||
|
||||
lwsl_debug("%s: %d %d %s\n", __func__, reason, ctx->path_match - 1,
|
||||
ctx->path);
|
||||
|
||||
switch (ctx->path_match - 1) {
|
||||
case LSSPPT_RETRY:
|
||||
n = LTY_BACKOFF;
|
||||
break;
|
||||
case LSSPPT_CERTS:
|
||||
n = LTY_X509;
|
||||
break;
|
||||
case LSSPPT_TRUST_STORES_NAME:
|
||||
case LSSPPT_TRUST_STORES_STACK:
|
||||
n = LTY_TRUSTSTORE;
|
||||
break;
|
||||
case LSSPPT_STREAMTYPES:
|
||||
n = LTY_POLICY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (reason == LEJPCB_ARRAY_START &&
|
||||
(ctx->path_match - 1 == LSSPPT_PLUGINS ||
|
||||
ctx->path_match - 1 == LSSPPT_METADATA))
|
||||
a->count = 0;
|
||||
|
||||
if (reason == LEJPCB_ARRAY_END &&
|
||||
ctx->path_match - 1 == LSSPPT_TRUST_STORES_STACK && !a->count) {
|
||||
lwsl_err("%s: at least one cert required in trust store\n",
|
||||
__func__);
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if (reason == LEJPCB_OBJECT_END && a->p) {
|
||||
/*
|
||||
* Allocate a just-the-right-size buf for the cert DER now
|
||||
* we decoded it into the a->p temp buffer and know the exact
|
||||
* size
|
||||
*/
|
||||
a->curr[LTY_X509].x->ca_der = lws_malloc(a->count, "ssx509");
|
||||
if (!a->curr[LTY_X509].x->ca_der)
|
||||
goto oom;
|
||||
memcpy((uint8_t *)a->curr[LTY_X509].x->ca_der, a->p, a->count);
|
||||
a->curr[LTY_X509].x->ca_der_len = a->count;
|
||||
|
||||
/*
|
||||
* ... and then we can free the temp buffer
|
||||
*/
|
||||
lws_free_set_NULL(a->p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (reason == LEJPCB_PAIR_NAME && n != -1 && n != LTY_TRUSTSTORE) {
|
||||
/*
|
||||
* We do the pointers always as .b, all of the participating
|
||||
* structs begin with .next and .name
|
||||
*/
|
||||
a->curr[n].b = lwsac_use_zero(&a->ac, sizes[n], POL_AC_GRAIN);
|
||||
if (!a->curr[n].b)
|
||||
goto oom;
|
||||
|
||||
if (n == LTY_X509) {
|
||||
a->p = lws_malloc(MAX_CERT_TEMP, "cert temp");
|
||||
if (!a->p)
|
||||
goto oom;
|
||||
memset(&a->b64, 0, sizeof(a->b64));
|
||||
}
|
||||
|
||||
a->count = 0;
|
||||
a->curr[n].b->next = a->heads[n].b;
|
||||
a->heads[n].b = a->curr[n].b;
|
||||
pp = (char **)&a->curr[n].b->name;
|
||||
|
||||
goto string1;
|
||||
}
|
||||
|
||||
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
|
||||
return 0;
|
||||
|
||||
switch (ctx->path_match - 1) {
|
||||
|
||||
/* strings */
|
||||
|
||||
case LSSPPT_RELEASE:
|
||||
break;
|
||||
|
||||
case LSSPPT_PRODUCT:
|
||||
break;
|
||||
|
||||
case LSSPPT_SCHEMA_VERSION:
|
||||
break;
|
||||
|
||||
case LSSPPT_VIA_SOCKS5:
|
||||
/* the global / default proxy */
|
||||
pp = (char **)&a->socks5_proxy;
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_BACKOFF:
|
||||
b = &a->curr[LTY_BACKOFF].b->r;
|
||||
if (b->retry_ms_table_count == 8) {
|
||||
lwsl_err("%s: > 8 backoff levels\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
if (!b->retry_ms_table_count) {
|
||||
b->retry_ms_table = (uint32_t *)lwsac_use_zero(&a->ac,
|
||||
sizeof(uint32_t) * 8, POL_AC_GRAIN);
|
||||
if (!b->retry_ms_table)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
((uint32_t *)b->retry_ms_table)
|
||||
[b->retry_ms_table_count++] = atoi(ctx->buf);
|
||||
break;
|
||||
|
||||
case LSSPPT_CONCEAL:
|
||||
a->curr[LTY_BACKOFF].b->r.conceal_count = atoi(ctx->buf);
|
||||
break;
|
||||
|
||||
case LSSPPT_JITTERPC:
|
||||
a->curr[LTY_BACKOFF].b->r.jitter_percent = atoi(ctx->buf);
|
||||
break;
|
||||
|
||||
case LSSPPT_VALIDPING_S:
|
||||
a->curr[LTY_BACKOFF].b->r.secs_since_valid_ping = atoi(ctx->buf);
|
||||
break;
|
||||
|
||||
case LSSPPT_VALIDHUP_S:
|
||||
a->curr[LTY_BACKOFF].b->r.secs_since_valid_hangup = atoi(ctx->buf);
|
||||
break;
|
||||
|
||||
case LSSPPT_CERTS:
|
||||
if (a->count + ctx->npos >= MAX_CERT_TEMP) {
|
||||
lwsl_err("%s: cert too big\n", __func__);
|
||||
goto oom;
|
||||
}
|
||||
inl = ctx->npos;
|
||||
outl = MAX_CERT_TEMP - a->count;
|
||||
|
||||
lws_b64_decode_stateful(&a->b64, ctx->buf, &inl,
|
||||
a->p + a->count, &outl,
|
||||
reason == LEJPCB_VAL_STR_END);
|
||||
a->count += outl;
|
||||
if (inl != ctx->npos) {
|
||||
lwsl_err("%s: b64 decode fail\n", __func__);
|
||||
goto oom;
|
||||
}
|
||||
break;
|
||||
|
||||
case LSSPPT_TRUST_STORES_NAME:
|
||||
/*
|
||||
* We do the pointers always as .b, all of the participating
|
||||
* structs begin with .next and .name
|
||||
*/
|
||||
a->curr[LTY_TRUSTSTORE].b = lwsac_use_zero(&a->ac,
|
||||
sizes[LTY_TRUSTSTORE], POL_AC_GRAIN);
|
||||
if (!a->curr[LTY_TRUSTSTORE].b)
|
||||
goto oom;
|
||||
|
||||
a->count = 0;
|
||||
a->curr[LTY_TRUSTSTORE].b->next = a->heads[LTY_TRUSTSTORE].b;
|
||||
a->heads[LTY_TRUSTSTORE].b = a->curr[LTY_TRUSTSTORE].b;
|
||||
pp = (char **)&a->curr[LTY_TRUSTSTORE].b->name;
|
||||
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_TRUST_STORES_STACK:
|
||||
if (a->count >= (int)LWS_ARRAY_SIZE(
|
||||
a->curr[LTY_TRUSTSTORE].t->ssx509)) {
|
||||
lwsl_err("%s: trust store too big\n", __func__);
|
||||
goto oom;
|
||||
}
|
||||
lwsl_debug("%s: trust stores stack %.*s\n", __func__,
|
||||
ctx->npos, ctx->buf);
|
||||
x = a->heads[LTY_X509].x;
|
||||
while (x) {
|
||||
if (!strncmp(x->vhost_name, ctx->buf, ctx->npos)) {
|
||||
a->curr[LTY_TRUSTSTORE].t->ssx509[a->count++] = x;
|
||||
a->curr[LTY_TRUSTSTORE].t->count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
x = x->next;
|
||||
}
|
||||
lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar));
|
||||
lwsl_err("%s: unknown trust store entry %s\n", __func__,
|
||||
dotstar);
|
||||
goto oom;
|
||||
|
||||
case LSSPPT_ENDPOINT:
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->endpoint;
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_VH_VIA_SOCKS5:
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->socks5_proxy;
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_PORT:
|
||||
a->curr[LTY_POLICY].p->port = atoi(ctx->buf);
|
||||
break;
|
||||
|
||||
case LSSPPT_HTTP_METHOD:
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->u.http.method;
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_HTTP_URL:
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->u.http.url;
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_RIDESHARE:
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->rideshare_streamtype;
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_PAYLOAD_FORMAT:
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->payload_fmt;
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_PLUGINS:
|
||||
pin = a->context->pss_plugins;
|
||||
if (a->count ==
|
||||
(int)LWS_ARRAY_SIZE(a->curr[LTY_POLICY].p->plugins)) {
|
||||
lwsl_err("%s: too many plugins\n", __func__);
|
||||
|
||||
goto oom;
|
||||
}
|
||||
if (!pin)
|
||||
break;
|
||||
while (*pin) {
|
||||
if (!strncmp((*pin)->name, ctx->buf, ctx->npos)) {
|
||||
a->curr[LTY_POLICY].p->plugins[a->count++] = *pin;
|
||||
return 0;
|
||||
}
|
||||
pin++;
|
||||
}
|
||||
lwsl_err("%s: unknown plugin\n", __func__);
|
||||
goto oom;
|
||||
|
||||
case LSSPPT_TLS:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_TLS;
|
||||
break;
|
||||
|
||||
case LSSPPT_TLS_CLIENT_CERT:
|
||||
a->curr[LTY_POLICY].p->client_cert = atoi(ctx->buf) + 1;
|
||||
break;
|
||||
|
||||
case LSSPPT_OPPORTUNISTIC:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_OPPORTUNISTIC;
|
||||
break;
|
||||
case LSSPPT_NAILED_UP:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_NAILED_UP;
|
||||
break;
|
||||
case LSSPPT_URGENT_TX:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_URGENT_TX;
|
||||
break;
|
||||
case LSSPPT_URGENT_RX:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_URGENT_RX;
|
||||
break;
|
||||
case LSSPPT_LONG_POLL:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_LONG_POLL;
|
||||
break;
|
||||
case LSSPPT_HTTP_WWW_FORM_URLENCODED:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |=
|
||||
LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED;
|
||||
break;
|
||||
|
||||
case LSSPPT_RETRYPTR:
|
||||
bot = a->heads[LTY_BACKOFF].b;
|
||||
while (bot) {
|
||||
if (!strncmp(ctx->buf, bot->name, ctx->npos)) {
|
||||
a->curr[LTY_POLICY].p->retry_bo = &bot->r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
bot = bot->next;
|
||||
}
|
||||
lwsl_err("%s: unknown backoff scheme\n", __func__);
|
||||
|
||||
return -1;
|
||||
|
||||
case LSSPPT_TRUST:
|
||||
ts = a->heads[LTY_TRUSTSTORE].t;
|
||||
while (ts) {
|
||||
if (!strncmp(ctx->buf, ts->name, ctx->npos)) {
|
||||
a->curr[LTY_POLICY].p->trust_store = ts;
|
||||
return 0;
|
||||
}
|
||||
ts = ts->next;
|
||||
}
|
||||
lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar));
|
||||
lwsl_err("%s: unknown trust store name %s\n", __func__,
|
||||
dotstar);
|
||||
|
||||
return -1;
|
||||
|
||||
case LSSPPT_METADATA:
|
||||
break;
|
||||
|
||||
case LSSPPT_METADATA_ITEM:
|
||||
pmd = a->curr[LTY_POLICY].p->metadata;
|
||||
a->curr[LTY_POLICY].p->metadata = lwsac_use_zero(&a->ac,
|
||||
sizeof(lws_ss_metadata_t) + ctx->npos +
|
||||
(ctx->path_match_len - ctx->st[ctx->sp - 2].p + 1) + 2,
|
||||
POL_AC_GRAIN);
|
||||
a->curr[LTY_POLICY].p->metadata->next = pmd;
|
||||
|
||||
q = (char *)a->curr[LTY_POLICY].p->metadata +
|
||||
sizeof(lws_ss_metadata_t);
|
||||
a->curr[LTY_POLICY].p->metadata->name = q;
|
||||
memcpy(q, ctx->path + ctx->st[ctx->sp - 2].p + 1,
|
||||
ctx->path_match_len - ctx->st[ctx->sp - 2].p);
|
||||
|
||||
q += ctx->path_match_len - ctx->st[ctx->sp - 2].p;
|
||||
a->curr[LTY_POLICY].p->metadata->value = q;
|
||||
memcpy(q, ctx->buf, ctx->npos);
|
||||
|
||||
a->curr[LTY_POLICY].p->metadata->length = /* the index in handle->metadata */
|
||||
a->curr[LTY_POLICY].p->metadata_count++;
|
||||
break;
|
||||
|
||||
case LSSPPT_HTTP_AUTH_HEADER:
|
||||
case LSSPPT_HTTP_DSN_HEADER:
|
||||
case LSSPPT_HTTP_FWV_HEADER:
|
||||
case LSSPPT_HTTP_TYPE_HEADER:
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->u.http.blob_header[
|
||||
(ctx->path_match - 1) - LSSPPT_HTTP_AUTH_HEADER];
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_HTTP_AUTH_PREAMBLE:
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->u.http.auth_preamble;
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_HTTP_NO_CONTENT_LENGTH:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |=
|
||||
LWSSSPOLF_HTTP_NO_CONTENT_LENGTH;
|
||||
break;
|
||||
|
||||
case LSSPPT_NGHTTP2_QUIRK_END_STREAM:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |=
|
||||
LWSSSPOLF_QUIRK_NGHTTP2_END_STREAM;
|
||||
break;
|
||||
case LSSPPT_H2_QUIRK_OVERFLOWS_TXCR:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |=
|
||||
LWSSSPOLF_H2_QUIRK_OVERFLOWS_TXCR;
|
||||
break;
|
||||
case LSSPPT_HTTP_MULTIPART_NAME:
|
||||
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_HTTP_MULTIPART;
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->u.http.multipart_name;
|
||||
goto string2;
|
||||
case LSSPPT_HTTP_MULTIPART_FILENAME:
|
||||
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_HTTP_MULTIPART;
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->u.http.multipart_filename;
|
||||
goto string2;
|
||||
case LSSPPT_HTTP_MULTIPART_CONTENT_TYPE:
|
||||
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_HTTP_MULTIPART;
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->u.http.multipart_content_type;
|
||||
goto string2;
|
||||
case LSSPPT_WS_SUBPROTOCOL:
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->u.http.u.ws.subprotocol;
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_WS_BINARY:
|
||||
a->curr[LTY_POLICY].p->u.http.u.ws.binary =
|
||||
reason == LEJPCB_VAL_TRUE;
|
||||
break;
|
||||
case LSSPPT_LOCAL_SINK:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_LOCAL_SINK;
|
||||
break;
|
||||
|
||||
case LSSPPT_MQTT_TOPIC:
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->u.mqtt.topic;
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_MQTT_SUBSCRIBE:
|
||||
pp = (char **)&a->curr[LTY_POLICY].p->u.mqtt.subscribe;
|
||||
goto string2;
|
||||
|
||||
case LSSPPT_MQTT_QOS:
|
||||
a->curr[LTY_POLICY].p->u.mqtt.qos = atoi(ctx->buf);
|
||||
break;
|
||||
|
||||
case LSSPPT_PROTOCOL:
|
||||
a->curr[LTY_POLICY].p->protocol = 0xff;
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(protonames); n++)
|
||||
if (strlen(protonames[n]) == ctx->npos &&
|
||||
!strncmp(ctx->buf, protonames[n], ctx->npos))
|
||||
a->curr[LTY_POLICY].p->protocol = (uint8_t)n;
|
||||
|
||||
if (a->curr[LTY_POLICY].p->protocol != 0xff)
|
||||
break;
|
||||
lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar));
|
||||
lwsl_err("%s: unknown protocol name %s\n", __func__, dotstar);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
string2:
|
||||
/*
|
||||
* If we can do const string folding, reuse the existing string rather
|
||||
* than make a new entry
|
||||
*/
|
||||
extant = lwsac_scan_extant(a->ac, (uint8_t *)ctx->buf, ctx->npos, 1);
|
||||
if (extant) {
|
||||
*pp = (char *)extant;
|
||||
|
||||
return 0;
|
||||
}
|
||||
*pp = lwsac_use_backfill(&a->ac, ctx->npos + 1, POL_AC_GRAIN);
|
||||
if (!*pp)
|
||||
goto oom;
|
||||
memcpy(*pp, ctx->buf, ctx->npos);
|
||||
(*pp)[ctx->npos] = '\0';
|
||||
|
||||
return 0;
|
||||
|
||||
string1:
|
||||
n = ctx->st[ctx->sp].p;
|
||||
*pp = lwsac_use_backfill(&a->ac, ctx->path_match_len + 1 - n,
|
||||
POL_AC_GRAIN);
|
||||
if (!*pp)
|
||||
goto oom;
|
||||
memcpy(*pp, ctx->path + n, ctx->path_match_len - n);
|
||||
(*pp)[ctx->path_match_len - n] = '\0';
|
||||
|
||||
return 0;
|
||||
|
||||
oom:
|
||||
lwsl_err("%s: OOM\n", __func__);
|
||||
lws_free_set_NULL(a->p);
|
||||
lwsac_free(&a->ac);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_policy_parse_begin(struct lws_context *context)
|
||||
{
|
||||
struct policy_cb_args *args;
|
||||
char *p;
|
||||
|
||||
args = lws_zalloc(sizeof(struct policy_cb_args), __func__);
|
||||
if (!args) {
|
||||
lwsl_err("%s: OOM\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
context->pol_args = args;
|
||||
args->context = context;
|
||||
p = lwsac_use(&args->ac, 1, POL_AC_INITIAL);
|
||||
if (!p) {
|
||||
lwsl_err("%s: OOM\n", __func__);
|
||||
lws_free_set_NULL(context->pol_args);
|
||||
|
||||
return -1;
|
||||
}
|
||||
*p = 0;
|
||||
lejp_construct(&args->jctx, lws_ss_policy_parser_cb, args,
|
||||
lejp_tokens_policy, LWS_ARRAY_SIZE(lejp_tokens_policy));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_policy_parse_abandon(struct lws_context *context)
|
||||
{
|
||||
struct policy_cb_args *args = (struct policy_cb_args *)context->pol_args;
|
||||
|
||||
lejp_destruct(&args->jctx);
|
||||
lws_free_set_NULL(context->pol_args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_policy_parse(struct lws_context *context, const uint8_t *buf, size_t len)
|
||||
{
|
||||
struct policy_cb_args *args = (struct policy_cb_args *)context->pol_args;
|
||||
int m;
|
||||
|
||||
m = (int)(signed char)lejp_parse(&args->jctx, buf, len);
|
||||
if (m == LEJP_CONTINUE || m >= 0)
|
||||
return m;
|
||||
|
||||
lwsl_err("%s: parse failed: %d: %s\n", __func__, m,
|
||||
lejp_error_to_string(m));
|
||||
lws_ss_policy_parse_abandon(context);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_policy_set(struct lws_context *context, const char *name)
|
||||
{
|
||||
struct policy_cb_args *args = (struct policy_cb_args *)context->pol_args;
|
||||
lws_ss_trust_store_t *ts;
|
||||
struct lws_vhost *v;
|
||||
lws_ss_x509_t *x;
|
||||
char buf[16];
|
||||
int m, ret = 0;
|
||||
|
||||
/*
|
||||
* Parsing seems to have succeeded, and we're going to use the new
|
||||
* policy that's laid out in args->ac
|
||||
*/
|
||||
|
||||
lejp_destruct(&args->jctx);
|
||||
|
||||
if (context->ac_policy) {
|
||||
|
||||
/*
|
||||
* So this is a bit fun-filled, we already had a policy in
|
||||
* force, perhaps it was the default policy that's just good for
|
||||
* fetching the real policy, and we're doing that now.
|
||||
*
|
||||
* We can destroy all the policy-related direct allocations
|
||||
* easily because they're cleanly in a single lwsac...
|
||||
*/
|
||||
lwsac_free(&context->ac_policy);
|
||||
|
||||
/*
|
||||
* ...but when we did the trust stores, we created vhosts for
|
||||
* each. We need to destroy those now too, and recreate new
|
||||
* ones from the new policy, perhaps with different X.509s.
|
||||
*/
|
||||
|
||||
v = context->vhost_list;
|
||||
while (v) {
|
||||
if (v->from_ss_policy) {
|
||||
struct lws_vhost *vh = v->vhost_next;
|
||||
lwsl_debug("%s: destroying vh %p\n", __func__, v);
|
||||
lws_vhost_destroy(v);
|
||||
v = vh;
|
||||
continue;
|
||||
}
|
||||
v = v->vhost_next;
|
||||
}
|
||||
|
||||
lws_check_deferred_free(context, 0, 1);
|
||||
}
|
||||
|
||||
context->pss_policies = args->heads[LTY_POLICY].p;
|
||||
context->ac_policy = args->ac;
|
||||
|
||||
lws_humanize(buf, sizeof(buf), lwsac_total_alloc(args->ac),
|
||||
humanize_schema_si_bytes);
|
||||
if (lwsac_total_alloc(args->ac))
|
||||
m = (int)((lwsac_total_overhead(args->ac) * 100) /
|
||||
lwsac_total_alloc(args->ac));
|
||||
else
|
||||
m = 0;
|
||||
|
||||
lwsl_notice("%s: %s, pad %d%c: %s\n", __func__, buf, m, '%', name);
|
||||
|
||||
/* Create vhosts for each type of trust store */
|
||||
|
||||
ts = args->heads[LTY_TRUSTSTORE].t;
|
||||
while (ts) {
|
||||
struct lws_context_creation_info i;
|
||||
|
||||
memset(&i, 0, sizeof(i));
|
||||
|
||||
/*
|
||||
* We get called from context creation... instantiates
|
||||
* vhosts with client tls contexts set up for each unique CA.
|
||||
*
|
||||
* Create the vhost with the first (mandatory) entry in the
|
||||
* trust store...
|
||||
*/
|
||||
|
||||
v = lws_get_vhost_by_name(context, ts->name);
|
||||
if (!v) {
|
||||
int n;
|
||||
|
||||
i.options = context->options;
|
||||
i.vhost_name = ts->name;
|
||||
lwsl_debug("%s: %s\n", __func__, i.vhost_name);
|
||||
i.client_ssl_ca_mem = ts->ssx509[0]->ca_der;
|
||||
i.client_ssl_ca_mem_len = ts->ssx509[0]->ca_der_len;
|
||||
i.port = CONTEXT_PORT_NO_LISTEN;
|
||||
lwsl_info("%s: %s trust store initial '%s'\n", __func__,
|
||||
ts->name, ts->ssx509[0]->vhost_name);
|
||||
|
||||
v = lws_create_vhost(context, &i);
|
||||
if (!v) {
|
||||
lwsl_err("%s: failed to create vhost %s\n",
|
||||
__func__, ts->name);
|
||||
ret = 1;
|
||||
} else
|
||||
v->from_ss_policy = 1;
|
||||
|
||||
for (n = 1; v && n < ts->count; n++) {
|
||||
lwsl_info("%s: add '%s' to trust store\n",
|
||||
__func__, ts->ssx509[n]->vhost_name);
|
||||
if (lws_tls_client_vhost_extra_cert_mem(v,
|
||||
ts->ssx509[n]->ca_der,
|
||||
ts->ssx509[n]->ca_der_len)) {
|
||||
lwsl_err("%s: add extra cert failed\n",
|
||||
__func__);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ts = ts->next;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_SOCKS5)
|
||||
|
||||
/*
|
||||
* ... we need to go through every vhost updating its understanding of
|
||||
* which socks5 proxy to use...
|
||||
*/
|
||||
|
||||
v = context->vhost_list;
|
||||
while (v) {
|
||||
lws_set_socks(v, args->socks5_proxy);
|
||||
v = v->vhost_next;
|
||||
}
|
||||
if (context->vhost_system)
|
||||
lws_set_socks(context->vhost_system, args->socks5_proxy);
|
||||
|
||||
if (args->socks5_proxy)
|
||||
lwsl_notice("%s: global socks5 proxy: %s\n", __func__,
|
||||
args->socks5_proxy);
|
||||
#endif
|
||||
|
||||
/* now we processed the x.509 CAs, we can free all of our originals */
|
||||
|
||||
x = args->heads[LTY_X509].x;
|
||||
while (x) {
|
||||
/*
|
||||
* Free all the DER buffers now they have been parsed into
|
||||
* tls library X.509 objects
|
||||
*/
|
||||
lws_free((void *)x->ca_der);
|
||||
x->ca_der = NULL;
|
||||
x = x->next;
|
||||
}
|
||||
|
||||
/* and we can discard the parsing args object now, invalidating args */
|
||||
|
||||
lws_free_set_NULL(context->pol_args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const lws_ss_policy_t *
|
||||
lws_ss_policy_lookup(const struct lws_context *context, const char *streamtype)
|
||||
{
|
||||
const lws_ss_policy_t *p = context->pss_policies;
|
||||
|
||||
if (!streamtype)
|
||||
return NULL;
|
||||
|
||||
while (p) {
|
||||
if (!strcmp(p->streamtype, streamtype))
|
||||
return p;
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
347
lib/secure-streams/private-lib-secure-streams.h
Normal file
347
lib/secure-streams/private-lib-secure-streams.h
Normal file
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Secure Stream state
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
SSSEQ_IDLE,
|
||||
SSSEQ_TRY_CONNECT,
|
||||
SSSEQ_TRY_CONNECT_NAUTH,
|
||||
SSSEQ_TRY_CONNECT_SAUTH,
|
||||
SSSEQ_RECONNECT_WAIT,
|
||||
SSSEQ_DO_RETRY,
|
||||
SSSEQ_CONNECTED,
|
||||
} lws_ss_seq_state_t;
|
||||
|
||||
|
||||
/**
|
||||
* lws_ss_handle_t: publicly-opaque secure stream object implementation
|
||||
*/
|
||||
|
||||
typedef struct lws_ss_handle {
|
||||
lws_ss_info_t info; /**< copy of stream creation info */
|
||||
struct lws_dll2 list; /**< pt lists active ss */
|
||||
struct lws_dll2 to_list; /**< pt lists ss with pending to-s */
|
||||
|
||||
struct lws_dll2_owner src_list; /**< sink's list of bound sources */
|
||||
|
||||
struct lws_context *context; /**< lws context we are created on */
|
||||
const lws_ss_policy_t *policy; /**< system policy for stream */
|
||||
|
||||
struct lws_sequencer *seq; /**< owning sequencer if any */
|
||||
struct lws *wsi; /**< the stream wsi if any */
|
||||
|
||||
void *nauthi; /**< the nauth plugin instance data */
|
||||
void *sauthi; /**< the sauth plugin instance data */
|
||||
|
||||
lws_ss_metadata_t *metadata;
|
||||
const lws_ss_policy_t *rideshare;
|
||||
|
||||
struct lws_ss_handle *h_sink; /**< sink we are bound to, or NULL */
|
||||
void *sink_obj;/**< sink's private object representing us */
|
||||
|
||||
lws_sorted_usec_list_t sul;
|
||||
lws_ss_tx_ordinal_t txord;
|
||||
|
||||
/* protocol-specific connection helpers */
|
||||
|
||||
union {
|
||||
|
||||
/* ...for http-related protocols... */
|
||||
|
||||
struct {
|
||||
|
||||
/* common to all http-related protocols */
|
||||
|
||||
/* incoming multipart parsing */
|
||||
|
||||
char boundary[24]; /* --boundary from headers */
|
||||
uint8_t boundary_len; /* length of --boundary */
|
||||
uint8_t boundary_seq; /* current match amount */
|
||||
uint8_t boundary_dashes; /* check for -- after */
|
||||
uint8_t boundary_post; /* swallow post CRLF */
|
||||
|
||||
uint8_t som:1; /* SOM has been sent */
|
||||
uint8_t any:1; /* any content has been sent */
|
||||
|
||||
|
||||
uint8_t good_respcode:1; /* 200 type response code */
|
||||
|
||||
union {
|
||||
struct { /* LWSSSP_H1 */
|
||||
} h1;
|
||||
struct { /* LWSSSP_H2 */
|
||||
} h2;
|
||||
struct { /* LWSSSP_WS */
|
||||
} ws;
|
||||
} u;
|
||||
} http;
|
||||
|
||||
/* details for non-http related protocols... */
|
||||
#if defined(LWS_ROLE_MQTT)
|
||||
struct {
|
||||
lws_mqtt_topic_elem_t topic_qos;
|
||||
lws_mqtt_topic_elem_t sub_top;
|
||||
lws_mqtt_subscribe_param_t sub_info;
|
||||
} mqtt;
|
||||
#endif
|
||||
} u;
|
||||
|
||||
unsigned long writeable_len;
|
||||
|
||||
lws_ss_constate_t connstate;/**< public connection state */
|
||||
lws_ss_seq_state_t seqstate; /**< private connection state */
|
||||
|
||||
uint16_t retry; /**< retry / backoff tracking */
|
||||
int16_t temp16;
|
||||
|
||||
uint8_t tsi; /**< service thread idx, usually 0 */
|
||||
uint8_t subseq; /**< emulate SOM tracking */
|
||||
uint8_t txn_ok; /**< 1 = transaction was OK */
|
||||
|
||||
uint8_t hanging_som:1;
|
||||
uint8_t inside_msg:1;
|
||||
uint8_t being_serialized:1; /* we are not the consumer */
|
||||
} lws_ss_handle_t;
|
||||
|
||||
/* connection helper that doesn't need to hang around after connection starts */
|
||||
|
||||
union lws_ss_contemp {
|
||||
#if defined(LWS_ROLE_MQTT)
|
||||
lws_mqtt_client_connect_param_t ccp;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* When allocating the opaque handle, we overallocate for:
|
||||
*
|
||||
* 1) policy->nauth_plugin->alloc (.nauthi) if any
|
||||
* 2) policy->sauth_plugin->alloc (.sauthi) if any
|
||||
* 3) copy of creation info stream type pointed to by info.streamtype... this
|
||||
* may be arbitrarily long and since it may be coming from socket ipc and be
|
||||
* temporary at creation time, we need a place for the copy to stay in scope
|
||||
* 4) copy of info->streamtype contents
|
||||
*/
|
||||
|
||||
|
||||
/* the user object allocation is immediately after the ss object allocation */
|
||||
#define ss_to_userobj(ss) ((void *)&(ss)[1])
|
||||
|
||||
/*
|
||||
* serialization parser state
|
||||
*/
|
||||
|
||||
enum {
|
||||
KIND_C_TO_P,
|
||||
KIND_SS_TO_P,
|
||||
};
|
||||
|
||||
struct lws_ss_serialization_parser {
|
||||
char streamtype[32];
|
||||
char rideshare[32];
|
||||
char metadata_name[32];
|
||||
|
||||
uint64_t ust_pwait;
|
||||
|
||||
lws_ss_metadata_t *ssmd;
|
||||
|
||||
int ps;
|
||||
int ctr;
|
||||
|
||||
uint32_t usd_phandling;
|
||||
uint32_t flags;
|
||||
int32_t temp32;
|
||||
|
||||
int32_t txcr_out;
|
||||
int32_t txcr_in;
|
||||
uint16_t rem;
|
||||
|
||||
uint8_t type;
|
||||
uint8_t frag1;
|
||||
uint8_t slen;
|
||||
uint8_t rsl_pos;
|
||||
uint8_t rsl_idx;
|
||||
};
|
||||
|
||||
/*
|
||||
* Unlike locally-fulfilled SS, SSS doesn't have to hold metadata on client side
|
||||
* but pass it through to the proxy. The client side doesn't know the real
|
||||
* metadata names that are available in the policy (since it's hardcoded in code
|
||||
* no point passing them back to the client from the policy). Because of that,
|
||||
* it doesn't know how many to allocate when we create the sspc_handle either.
|
||||
*
|
||||
* So we use a linked-list of changed-but-not-yet-proxied metadata allocated
|
||||
* on the heap and items removed as they are proxied out. Anything on the list
|
||||
* is sent to the proxy before any requested tx is handled.
|
||||
*
|
||||
* This is also used to queue tx credit changes
|
||||
*/
|
||||
|
||||
typedef struct lws_sspc_metadata {
|
||||
lws_dll2_t list;
|
||||
char name[32]; /* empty string, then actually TCXR */
|
||||
size_t len;
|
||||
int tx_cr_adjust;
|
||||
|
||||
/* the value of length .len is overallocated after this */
|
||||
} lws_sspc_metadata_t;
|
||||
|
||||
|
||||
typedef struct lws_sspc_handle {
|
||||
char rideshare_list[128];
|
||||
lws_ss_info_t ssi;
|
||||
lws_sorted_usec_list_t sul_retry;
|
||||
|
||||
struct lws_ss_serialization_parser parser;
|
||||
|
||||
lws_dll2_owner_t metadata_owner;
|
||||
|
||||
struct lws_dll2 client_list;
|
||||
struct lws_tx_credit txc;
|
||||
|
||||
struct lws *cwsi;
|
||||
|
||||
struct lws_dsh *dsh;
|
||||
struct lws_context *context;
|
||||
|
||||
lws_usec_t us_earliest_write_req;
|
||||
|
||||
lws_ss_conn_states_t state;
|
||||
|
||||
int16_t temp16;
|
||||
|
||||
uint32_t ord;
|
||||
|
||||
uint8_t rideshare_ofs[4];
|
||||
uint8_t conn_req;
|
||||
uint8_t rsidx;
|
||||
|
||||
uint8_t destroying:1;
|
||||
} lws_sspc_handle_t;
|
||||
|
||||
int
|
||||
lws_ss_deserialize_parse(struct lws_ss_serialization_parser *par,
|
||||
struct lws_context *context,
|
||||
struct lws_dsh *dsh, const uint8_t *cp, size_t len,
|
||||
lws_ss_conn_states_t *state, void *parconn,
|
||||
lws_ss_handle_t **pss, lws_ss_info_t *ssi, char client);
|
||||
int
|
||||
lws_ss_serialize_rx_payload(struct lws_dsh *dsh, const uint8_t *buf,
|
||||
size_t len, int flags, const char *rsp);
|
||||
int
|
||||
lws_ss_deserialize_tx_payload(struct lws_dsh *dsh, struct lws *wsi,
|
||||
lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags);
|
||||
int
|
||||
lws_ss_serialize_state(struct lws_dsh *dsh, lws_ss_constate_t state,
|
||||
lws_ss_tx_ordinal_t ack);
|
||||
|
||||
void
|
||||
lws_ss_serialize_state_transition(lws_ss_conn_states_t *state, int new_state);
|
||||
|
||||
const lws_ss_policy_t *
|
||||
lws_ss_policy_lookup(const struct lws_context *context, const char *streamtype);
|
||||
|
||||
/* can be used as a cb from lws_dll2_foreach_safe() to destroy ss */
|
||||
int
|
||||
lws_ss_destroy_dll(struct lws_dll2 *d, void *user);
|
||||
|
||||
int
|
||||
lws_sspc_destroy_dll(struct lws_dll2 *d, void *user);
|
||||
|
||||
|
||||
int
|
||||
lws_ss_policy_parse_begin(struct lws_context *context);
|
||||
|
||||
int
|
||||
lws_ss_policy_parse(struct lws_context *context, const uint8_t *buf, size_t len);
|
||||
|
||||
int
|
||||
lws_ss_policy_set(struct lws_context *context, const char *name);
|
||||
|
||||
int
|
||||
lws_ss_policy_parse_abandon(struct lws_context *context);
|
||||
|
||||
int
|
||||
lws_ss_sys_fetch_policy(struct lws_context *context);
|
||||
|
||||
int
|
||||
lws_ss_event_helper(lws_ss_handle_t *h, lws_ss_constate_t cs);
|
||||
|
||||
int
|
||||
lws_ss_backoff(lws_ss_handle_t *h);
|
||||
|
||||
int
|
||||
lws_ss_set_timeout_us(lws_ss_handle_t *h, lws_usec_t us);
|
||||
|
||||
void
|
||||
ss_proxy_onward_txcr(void *userobj, int bump);
|
||||
|
||||
int
|
||||
lws_ss_serialize_txcr(struct lws_dsh *dsh, int txcr);
|
||||
|
||||
int
|
||||
lws_ss_sys_auth_api_amazon_com(struct lws_context *context);
|
||||
|
||||
lws_ss_metadata_t *
|
||||
lws_ss_get_handle_metadata(struct lws_ss_handle *h, const char *name);
|
||||
lws_ss_metadata_t *
|
||||
lws_ss_policy_metadata_index(const lws_ss_policy_t *p, size_t index);
|
||||
|
||||
lws_ss_metadata_t *
|
||||
lws_ss_policy_metadata(const lws_ss_policy_t *p, const char *name);
|
||||
|
||||
int
|
||||
lws_ss_exp_cb_metadata(void *priv, const char *name, char *out, size_t *pos,
|
||||
size_t olen, size_t *exp_ofs);
|
||||
|
||||
typedef int (* const secstream_protocol_connect_munge_t)(lws_ss_handle_t *h,
|
||||
char *buf, size_t len, struct lws_client_connect_info *i,
|
||||
union lws_ss_contemp *ct);
|
||||
|
||||
typedef int (* const secstream_protocol_add_txcr_t)(lws_ss_handle_t *h, int add);
|
||||
|
||||
typedef int (* const secstream_protocol_get_txcr_t)(lws_ss_handle_t *h);
|
||||
|
||||
struct ss_pcols {
|
||||
const char *name;
|
||||
const char *alpn;
|
||||
const char *protocol_name;
|
||||
const secstream_protocol_connect_munge_t munge;
|
||||
const secstream_protocol_add_txcr_t tx_cr_add;
|
||||
const secstream_protocol_get_txcr_t tx_cr_est;
|
||||
};
|
||||
|
||||
extern const struct ss_pcols ss_pcol_h1;
|
||||
extern const struct ss_pcols ss_pcol_h2;
|
||||
extern const struct ss_pcols ss_pcol_ws;
|
||||
extern const struct ss_pcols ss_pcol_mqtt;
|
||||
|
||||
extern const struct lws_protocols protocol_secstream_h1;
|
||||
extern const struct lws_protocols protocol_secstream_h2;
|
||||
extern const struct lws_protocols protocol_secstream_ws;
|
||||
extern const struct lws_protocols protocol_secstream_mqtt;
|
||||
|
38
lib/secure-streams/protocols/README.md
Normal file
38
lib/secure-streams/protocols/README.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Lws Protocol bindings for Secure Streams
|
||||
|
||||
This directory contains the code wiring up normal lws protocols
|
||||
to Secure Streams.
|
||||
|
||||
## The lws_protocols callback
|
||||
|
||||
This is the normal lws struct lws_protocols callback that handles events and
|
||||
traffic on the lws protocol being supported.
|
||||
|
||||
The various events and traffic are converted into calls using the Secure
|
||||
Streams api, and Secure Streams events.
|
||||
|
||||
## The connect_munge helper
|
||||
|
||||
Different protocols have different semantics in the arguments to the client
|
||||
connect function, this protocol-specific helper is called to munge the
|
||||
connect_info struct to match the details of the protocol selected.
|
||||
|
||||
The `ss->policy->aux` string is used to hold protocol-specific information
|
||||
passed in the from the policy, eg, the URL path or websockets subprotocol
|
||||
name.
|
||||
|
||||
## The (library-private) ss_pcols export
|
||||
|
||||
Each protocol binding exports two things to other parts of lws (they
|
||||
are not exported to user code)
|
||||
|
||||
- a struct lws_protocols, including a pointer to the callback
|
||||
|
||||
- a struct ss_pcols describing how secure_streams should use, including
|
||||
a pointer to the related connect_munge helper.
|
||||
|
||||
In ./lib/core-net/vhost.c, enabled protocols are added to vhost protcols
|
||||
lists so they may be used. And in ./lib/secure-streams/secure-streams.c,
|
||||
enabled struct ss_pcols are listed and checked for matches when the user
|
||||
creates a new Secure Stream.
|
||||
|
571
lib/secure-streams/protocols/ss-h1.c
Normal file
571
lib/secure-streams/protocols/ss-h1.c
Normal file
|
@ -0,0 +1,571 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This is the glue that wires up h1 to Secure Streams.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
#if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
|
||||
static int
|
||||
ss_http_multipart_parser(lws_ss_handle_t *h, void *in, size_t len)
|
||||
{
|
||||
uint8_t *q = (uint8_t *)in;
|
||||
int pending_issue = 0, n = 0;
|
||||
|
||||
/* let's stick it in the boundary state machine first */
|
||||
while (n < (int)len) {
|
||||
if (h->u.http.boundary_seq != h->u.http.boundary_len) {
|
||||
if (q[n] == h->u.http.boundary[h->u.http.boundary_seq])
|
||||
h->u.http.boundary_seq++;
|
||||
else {
|
||||
h->u.http.boundary_seq = 0;
|
||||
h->u.http.boundary_dashes = 0;
|
||||
h->u.http.boundary_post = 0;
|
||||
}
|
||||
goto around;
|
||||
}
|
||||
|
||||
/*
|
||||
* We already matched the boundary string, now we're
|
||||
* looking if there's a -- afterwards
|
||||
*/
|
||||
if (h->u.http.boundary_dashes < 2) {
|
||||
if (q[n] == '-') {
|
||||
h->u.http.boundary_dashes++;
|
||||
goto around;
|
||||
}
|
||||
/* there was no final -- ... */
|
||||
}
|
||||
|
||||
if (h->u.http.boundary_dashes == 2) {
|
||||
/*
|
||||
* It's an EOM boundary: issue pending + multipart EOP
|
||||
*/
|
||||
lwsl_debug("%s: seen EOP, n %d pi %d\n",
|
||||
__func__, n, pending_issue);
|
||||
/*
|
||||
* It's possible we already started the decode before
|
||||
* the end of the last packet. Then there is no
|
||||
* remainder to send.
|
||||
*/
|
||||
if (n >= pending_issue + h->u.http.boundary_len +
|
||||
(h->u.http.any ? 2 : 0) + 1)
|
||||
h->info.rx(ss_to_userobj(h),
|
||||
&q[pending_issue],
|
||||
n - pending_issue -
|
||||
h->u.http.boundary_len - 1 -
|
||||
(h->u.http.any ? 2 : 0) /* crlf */,
|
||||
(!h->u.http.som ? LWSSS_FLAG_SOM : 0) |
|
||||
LWSSS_FLAG_EOM | LWSSS_FLAG_RELATED_END);
|
||||
|
||||
/*
|
||||
* Peer may not END_STREAM us
|
||||
*/
|
||||
return 0;
|
||||
//return -1;
|
||||
}
|
||||
|
||||
/* how about --boundaryCRLF */
|
||||
|
||||
if (h->u.http.boundary_post < 2) {
|
||||
if ((!h->u.http.boundary_post && q[n] == '\x0d') ||
|
||||
(h->u.http.boundary_post && q[n] == '\x0a')) {
|
||||
h->u.http.boundary_post++;
|
||||
goto around;
|
||||
}
|
||||
/* there was no final CRLF ... it's wrong */
|
||||
|
||||
return -1;
|
||||
}
|
||||
if (h->u.http.boundary_post != 2)
|
||||
goto around;
|
||||
|
||||
/*
|
||||
* We have a starting "--boundaryCRLF" or intermediate
|
||||
* "CRLF--boundaryCRLF" boundary
|
||||
*/
|
||||
lwsl_debug("%s: b_post = 2 (pi %d)\n", __func__, pending_issue);
|
||||
h->u.http.boundary_seq = 0;
|
||||
h->u.http.boundary_post = 0;
|
||||
|
||||
if (n >= pending_issue && (h->u.http.any || !h->u.http.som)) {
|
||||
/* Intermediate... do the EOM */
|
||||
lwsl_debug("%s: seen interm EOP n %d pi %d\n", __func__,
|
||||
n, pending_issue);
|
||||
/*
|
||||
* It's possible we already started the decode before
|
||||
* the end of the last packet. Then there is no
|
||||
* remainder to send.
|
||||
*/
|
||||
if (n >= pending_issue + h->u.http.boundary_len +
|
||||
(h->u.http.any ? 2 : 0))
|
||||
h->info.rx(ss_to_userobj(h), &q[pending_issue],
|
||||
n - pending_issue -
|
||||
h->u.http.boundary_len -
|
||||
(h->u.http.any ? 2 /* crlf */ : 0),
|
||||
(!h->u.http.som ? LWSSS_FLAG_SOM : 0) |
|
||||
LWSSS_FLAG_EOM);
|
||||
}
|
||||
|
||||
/* Next message starts after this boundary */
|
||||
|
||||
pending_issue = n;
|
||||
h->u.http.som = 0;
|
||||
|
||||
around:
|
||||
n++;
|
||||
}
|
||||
|
||||
if (pending_issue != n) {
|
||||
h->info.rx(ss_to_userobj(h), &q[pending_issue], n - pending_issue,
|
||||
(!h->u.http.som ? LWSSS_FLAG_SOM : 0));
|
||||
h->u.http.any = 1;
|
||||
h->u.http.som = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const uint8_t blob_idx[] = {
|
||||
LWS_SYSBLOB_TYPE_AUTH,
|
||||
LWS_SYSBLOB_TYPE_DEVICE_SERIAL,
|
||||
LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION,
|
||||
LWS_SYSBLOB_TYPE_DEVICE_TYPE,
|
||||
};
|
||||
|
||||
int
|
||||
secstream_h1(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len)
|
||||
{
|
||||
lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi);
|
||||
uint8_t buf[LWS_PRE + 1520], *p = &buf[LWS_PRE],
|
||||
*end = &buf[sizeof(buf) - 1];
|
||||
int f = 0, m, status;
|
||||
size_t buflen;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
/* because we are protocols[0] ... */
|
||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
||||
assert(h);
|
||||
assert(h->policy);
|
||||
lwsl_info("%s: h: %p, %s CLIENT_CONNECTION_ERROR: %s\n", __func__,
|
||||
h, h->policy->streamtype, in ? (char *)in : "(null)");
|
||||
lws_ss_event_helper(h, LWSSSCS_UNREACHABLE);
|
||||
h->wsi = NULL;
|
||||
lws_ss_backoff(h);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
|
||||
if (!h)
|
||||
break;
|
||||
lwsl_info("%s: h: %p, %s LWS_CALLBACK_CLOSED_CLIENT_HTTP\n",
|
||||
__func__, h,
|
||||
h->policy ? h->policy->streamtype : "no policy");
|
||||
h->wsi = NULL;
|
||||
//bad = status != 200;
|
||||
//lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
|
||||
if (h->policy && !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) &&
|
||||
!h->txn_ok && !wsi->context->being_destroyed)
|
||||
lws_ss_backoff(h);
|
||||
if (lws_ss_event_helper(h, LWSSSCS_DISCONNECTED))
|
||||
lws_ss_destroy(&h);
|
||||
break;
|
||||
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
|
||||
status = lws_http_client_http_response(wsi);
|
||||
lwsl_info("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: %d\n", __func__, status);
|
||||
// if (!status)
|
||||
/* it's just telling use we connected / joined the nwsi */
|
||||
// break;
|
||||
h->u.http.good_respcode = (status >= 200 && status < 300);
|
||||
// lwsl_err("%s: good resp %d %d\n", __func__, status, h->u.http.good_respcode);
|
||||
|
||||
if (h->u.http.good_respcode)
|
||||
lwsl_info("%s: Connected streamtype %s, %d\n", __func__,
|
||||
h->policy->streamtype, status);
|
||||
else
|
||||
lwsl_warn("%s: Connected streamtype %s, BAD %d\n", __func__,
|
||||
h->policy->streamtype, status);
|
||||
|
||||
h->hanging_som = 0;
|
||||
|
||||
h->retry = 0;
|
||||
h->seqstate = SSSEQ_CONNECTED;
|
||||
lws_ss_set_timeout_us(h, LWS_SET_TIMER_USEC_CANCEL);
|
||||
lws_ss_event_helper(h, LWSSSCS_CONNECTED);
|
||||
|
||||
/*
|
||||
* Since it's an http transaction we initiated... this is
|
||||
* proof of connection validity
|
||||
*/
|
||||
lws_validity_confirmed(wsi);
|
||||
|
||||
#if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
|
||||
|
||||
if (lws_hdr_copy(wsi, (char *)buf, sizeof(buf),
|
||||
WSI_TOKEN_HTTP_CONTENT_TYPE) > 0 &&
|
||||
/* multipart/form-data;
|
||||
* boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
|
||||
|
||||
(!strncmp((char *)buf, "multipart/form-data", 19) ||
|
||||
!strncmp((char *)buf, "multipart/related", 17))) {
|
||||
struct lws_tokenize ts;
|
||||
lws_tokenize_elem e;
|
||||
|
||||
// puts((const char *)buf);
|
||||
|
||||
memset(&ts, 0, sizeof(ts));
|
||||
ts.start = (char *)buf;
|
||||
ts.len = strlen(ts.start);
|
||||
ts.flags = LWS_TOKENIZE_F_RFC7230_DELIMS |
|
||||
LWS_TOKENIZE_F_SLASH_NONTERM |
|
||||
LWS_TOKENIZE_F_MINUS_NONTERM;
|
||||
|
||||
h->u.http.boundary[0] = '\0';
|
||||
do {
|
||||
e = lws_tokenize(&ts);
|
||||
if (e == LWS_TOKZE_TOKEN_NAME_EQUALS &&
|
||||
!strncmp(ts.token, "boundary", 8) &&
|
||||
ts.token_len == 8) {
|
||||
e = lws_tokenize(&ts);
|
||||
if (e != LWS_TOKZE_TOKEN)
|
||||
goto malformed;
|
||||
h->u.http.boundary[0] = '\x0d';
|
||||
h->u.http.boundary[1] = '\x0a';
|
||||
h->u.http.boundary[2] = '-';
|
||||
h->u.http.boundary[3] = '-';
|
||||
lws_strnncpy(h->u.http.boundary + 4,
|
||||
ts.token, ts.token_len,
|
||||
sizeof(h->u.http.boundary) - 4);
|
||||
h->u.http.boundary_len = ts.token_len + 4;
|
||||
h->u.http.boundary_seq = 2;
|
||||
h->u.http.boundary_dashes = 0;
|
||||
}
|
||||
} while (e > 0);
|
||||
lwsl_info("%s: multipart boundary '%s' len %d\n", __func__,
|
||||
h->u.http.boundary, h->u.http.boundary_len);
|
||||
|
||||
/* inform the ss that a related message group begins */
|
||||
|
||||
if (h->u.http.boundary[0])
|
||||
h->info.rx(ss_to_userobj(h), NULL, 0,
|
||||
LWSSS_FLAG_RELATED_START);
|
||||
|
||||
// lws_header_table_detach(wsi, 0);
|
||||
}
|
||||
break;
|
||||
malformed:
|
||||
lwsl_notice("%s: malformed multipart header\n", __func__);
|
||||
return -1;
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
||||
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
|
||||
if (h->writeable_len)
|
||||
wsi->http.writeable_len = h->writeable_len;
|
||||
|
||||
{
|
||||
uint8_t **p = (uint8_t **)in, *end = (*p) + len,
|
||||
*oin = *(uint8_t **)in;
|
||||
|
||||
/*
|
||||
* blob-based headers
|
||||
*/
|
||||
|
||||
for (m = 0; m < _LWSSS_HBI_COUNT; m++) {
|
||||
int o = 0, n;
|
||||
|
||||
if (!h->policy->u.http.blob_header[m])
|
||||
continue;
|
||||
|
||||
if (m == LWSSS_HBI_AUTH &&
|
||||
h->policy->u.http.auth_preamble)
|
||||
o = lws_snprintf((char *)buf, sizeof(buf), "%s",
|
||||
h->policy->u.http.auth_preamble);
|
||||
|
||||
if (o > (int)sizeof(buf) - 2)
|
||||
return -1;
|
||||
|
||||
buflen = sizeof(buf) - o - 2;
|
||||
n = lws_system_blob_get(
|
||||
lws_system_get_blob(wsi->context, blob_idx[m], 0),
|
||||
buf + o, &buflen, 0);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
buf[o + buflen] = '\0';
|
||||
lwsl_debug("%s: adding blob %d: %s\n", __func__, m, buf);
|
||||
|
||||
if (lws_add_http_header_by_name(wsi,
|
||||
(uint8_t *)h->policy->u.http.blob_header[m],
|
||||
buf, buflen + o, p, end))
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* metadata-based headers
|
||||
*/
|
||||
|
||||
for (m = 0; m < h->policy->metadata_count; m++) {
|
||||
lws_ss_metadata_t *polmd;
|
||||
|
||||
/* has to have a header string listed */
|
||||
if (!h->metadata[m].value)
|
||||
continue;
|
||||
|
||||
polmd = lws_ss_policy_metadata_index(h->policy, m);
|
||||
|
||||
assert(polmd);
|
||||
/* has to have a value */
|
||||
if (polmd->value && ((uint8_t *)polmd->value)[0]) {
|
||||
if (lws_add_http_header_by_name(wsi,
|
||||
polmd->value,
|
||||
h->metadata[m].value,
|
||||
h->metadata[m].length, p, end))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Content-length on POST if we have the length information
|
||||
*/
|
||||
|
||||
if (!strcmp(h->policy->u.http.method, "POST") &&
|
||||
wsi->http.writeable_len) {
|
||||
if (!(h->policy->flags &
|
||||
LWSSSPOLF_HTTP_NO_CONTENT_LENGTH)) {
|
||||
int n = lws_snprintf((char *)buf, 20, "%u",
|
||||
(unsigned int)wsi->http.writeable_len);
|
||||
if (lws_add_http_header_by_token(wsi,
|
||||
WSI_TOKEN_HTTP_CONTENT_LENGTH,
|
||||
buf, n, p, end))
|
||||
return -1;
|
||||
}
|
||||
lws_client_http_body_pending(wsi, 1);
|
||||
}
|
||||
|
||||
(void)oin;
|
||||
// if (*p != oin)
|
||||
// lwsl_hexdump_notice(oin, lws_ptr_diff(*p, oin));
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
/* chunks of chunked content, with header removed */
|
||||
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
|
||||
lwsl_debug("%s: RECEIVE_CLIENT_HTTP_READ: read %d\n",
|
||||
__func__, (int)len);
|
||||
if (!h)
|
||||
return 0;
|
||||
|
||||
#if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
|
||||
if (h->u.http.boundary[0])
|
||||
return ss_http_multipart_parser(h, in, len);
|
||||
#endif
|
||||
|
||||
if (!h->subseq) {
|
||||
f |= LWSSS_FLAG_SOM;
|
||||
h->hanging_som = 1;
|
||||
h->subseq = 1;
|
||||
}
|
||||
|
||||
// lwsl_notice("%s: HTTP_READ: client side sent len %d fl 0x%x\n",
|
||||
// __func__, (int)len, (int)f);
|
||||
|
||||
h->info.rx(ss_to_userobj(h), (const uint8_t *)in, len, f);
|
||||
|
||||
return 0; /* don't passthru */
|
||||
|
||||
/* uninterpreted http content */
|
||||
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
|
||||
{
|
||||
char *px = (char *)buf + LWS_PRE; /* guarantees LWS_PRE */
|
||||
int lenx = sizeof(buf) - LWS_PRE;
|
||||
|
||||
if (lws_http_client_read(wsi, &px, &lenx) < 0)
|
||||
return -1;
|
||||
}
|
||||
lws_set_timeout(wsi, 99, 30);
|
||||
|
||||
return 0; /* don't passthru */
|
||||
|
||||
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
|
||||
lwsl_debug("%s: LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n", __func__);
|
||||
if (h->hanging_som)
|
||||
h->info.rx(ss_to_userobj(h), NULL, 0, LWSSS_FLAG_EOM);
|
||||
|
||||
wsi->http.writeable_len = h->writeable_len = 0;
|
||||
|
||||
if (h->u.http.good_respcode)
|
||||
lws_ss_event_helper(h, LWSSSCS_QOS_ACK_REMOTE);
|
||||
else
|
||||
lws_ss_event_helper(h, LWSSSCS_QOS_NACK_REMOTE);
|
||||
|
||||
h->wsi = NULL;
|
||||
h->txn_ok = 1;
|
||||
//bad = status != 200;
|
||||
lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:
|
||||
lwsl_info("%s: LWS_CALLBACK_CLIENT_HTTP_WRITEABLE\n", __func__);
|
||||
if (!h)
|
||||
return 0;
|
||||
|
||||
if (!h->rideshare)
|
||||
h->rideshare = h->policy;
|
||||
|
||||
#if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
|
||||
if (!h->inside_msg && h->rideshare->u.http.multipart_name)
|
||||
lws_client_http_multipart(wsi,
|
||||
h->rideshare->u.http.multipart_name,
|
||||
h->rideshare->u.http.multipart_filename,
|
||||
h->rideshare->u.http.multipart_content_type,
|
||||
(char **)&p, (char *)end);
|
||||
|
||||
buflen = lws_ptr_diff(end, p);
|
||||
if (h->policy->u.http.multipart_name)
|
||||
buflen -= 24; /* allow space for end of multipart */
|
||||
|
||||
#endif
|
||||
|
||||
if (h->info.tx(ss_to_userobj(h), h->txord++, p, &buflen, &f)) {
|
||||
/* don't want to send anything */
|
||||
lwsl_debug("%s: dont want to write\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lwsl_info("%s: WRITEABLE: user tx says len %d fl 0x%x\n",
|
||||
__func__, (int)buflen, (int)f);
|
||||
|
||||
p += buflen;
|
||||
|
||||
if (f & LWSSS_FLAG_EOM) {
|
||||
#if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
|
||||
/* end of rideshares */
|
||||
if (!h->rideshare->rideshare_streamtype) {
|
||||
lws_client_http_body_pending(wsi, 0);
|
||||
if (h->rideshare->u.http.multipart_name)
|
||||
lws_client_http_multipart(wsi, NULL, NULL, NULL,
|
||||
(char **)&p, (char *)end);
|
||||
} else {
|
||||
#endif
|
||||
h->rideshare = lws_ss_policy_lookup(wsi->context,
|
||||
h->rideshare->rideshare_streamtype);
|
||||
lws_callback_on_writable(wsi);
|
||||
#if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
|
||||
}
|
||||
#endif
|
||||
|
||||
h->inside_msg = 0;
|
||||
} else {
|
||||
/* otherwise we can spin with zero length writes */
|
||||
if (!f && !lws_ptr_diff(p, buf + LWS_PRE))
|
||||
break;
|
||||
h->inside_msg = 1;
|
||||
lws_callback_on_writable(wsi);
|
||||
}
|
||||
|
||||
lwsl_info("%s: lws_write %d %d\n", __func__,
|
||||
lws_ptr_diff(p, buf + LWS_PRE), f);
|
||||
|
||||
if (lws_write(wsi, buf + LWS_PRE, lws_ptr_diff(p, buf + LWS_PRE),
|
||||
LWS_WRITE_HTTP) != (int)lws_ptr_diff(p, buf + LWS_PRE)) {
|
||||
lwsl_err("%s: write failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lws_set_timeout(wsi, 0, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return lws_callback_http_dummy(wsi, reason, user, in, len);
|
||||
}
|
||||
|
||||
const struct lws_protocols protocol_secstream_h1 = {
|
||||
"lws-secstream-h1",
|
||||
secstream_h1,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Munge connect info according to protocol-specific considerations... this
|
||||
* usually means interpreting aux in a protocol-specific way and using the
|
||||
* pieces at connection setup time, eg, http url pieces.
|
||||
*
|
||||
* len bytes of buf can be used for things with scope until after the actual
|
||||
* connect.
|
||||
*/
|
||||
|
||||
static int
|
||||
secstream_connect_munge_h1(lws_ss_handle_t *h, char *buf, size_t len,
|
||||
struct lws_client_connect_info *i,
|
||||
union lws_ss_contemp *ct)
|
||||
{
|
||||
size_t used_in, used_out;
|
||||
lws_strexp_t exp;
|
||||
|
||||
if (!h->policy->u.http.url)
|
||||
return 0;
|
||||
|
||||
#if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
|
||||
if (h->policy->flags & LWSSSPOLF_HTTP_MULTIPART)
|
||||
i->ssl_connection |= LCCSCF_HTTP_MULTIPART_MIME;
|
||||
|
||||
if (h->policy->flags & LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED)
|
||||
i->ssl_connection |= LCCSCF_HTTP_X_WWW_FORM_URLENCODED;
|
||||
#endif
|
||||
|
||||
/* protocol aux is the path part */
|
||||
|
||||
i->path = buf;
|
||||
buf[0] = '/';
|
||||
|
||||
lws_strexp_init(&exp, (void *)h, lws_ss_exp_cb_metadata, buf + 1, len - 1);
|
||||
|
||||
if (lws_strexp_expand(&exp, h->policy->u.http.url,
|
||||
strlen(h->policy->u.http.url),
|
||||
&used_in, &used_out) != LSTRX_DONE)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const struct ss_pcols ss_pcol_h1 = {
|
||||
"h1",
|
||||
"http/1.1",
|
||||
"lws-secstream-h1",
|
||||
secstream_connect_munge_h1,
|
||||
NULL
|
||||
};
|
183
lib/secure-streams/protocols/ss-h2.c
Normal file
183
lib/secure-streams/protocols/ss-h2.c
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
extern int
|
||||
secstream_h1(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len);
|
||||
|
||||
static int
|
||||
secstream_h2(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len)
|
||||
{
|
||||
lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi);
|
||||
int n;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
if (h->being_serialized) {
|
||||
/*
|
||||
* We are the proxy-side SS for a remote client... we
|
||||
* need to inform the client about the initial tx credit
|
||||
* to write to it that the remote h2 server set up
|
||||
*/
|
||||
lwsl_info("%s: reporting initial tx cr from server %d\n",
|
||||
__func__, wsi->txc.tx_cr);
|
||||
ss_proxy_onward_txcr((void *)&h[1], wsi->txc.tx_cr);
|
||||
}
|
||||
#endif
|
||||
|
||||
n = secstream_h1(wsi, reason, user, in, len);
|
||||
|
||||
if (!n && (h->policy->flags & LWSSSPOLF_LONG_POLL)) {
|
||||
lwsl_notice("%s: h2 client %p entering LONG_POLL\n",
|
||||
__func__, wsi);
|
||||
lws_h2_client_stream_long_poll_rxonly(wsi);
|
||||
}
|
||||
return n;
|
||||
|
||||
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
|
||||
// lwsl_err("%s: h2 COMPLETED_CLIENT_HTTP\n", __func__);
|
||||
h->info.rx(ss_to_userobj(h), NULL, 0, LWSSS_FLAG_EOM);
|
||||
h->wsi = NULL;
|
||||
h->txn_ok = 1;
|
||||
//bad = status != 200;
|
||||
lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_WSI_TX_CREDIT_GET:
|
||||
/*
|
||||
* The peer has sent us additional tx credit...
|
||||
*/
|
||||
lwsl_info("%s: LWS_CALLBACK_WSI_TX_CREDIT_GET: %d\n",
|
||||
__func__, (int32_t)len);
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
if (h->being_serialized)
|
||||
/* we are the proxy-side SS for a remote client */
|
||||
ss_proxy_onward_txcr((void *)&h[1], (int)len);
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return secstream_h1(wsi, reason, user, in, len);
|
||||
}
|
||||
|
||||
const struct lws_protocols protocol_secstream_h2 = {
|
||||
"lws-secstream-h2",
|
||||
secstream_h2,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Munge connect info according to protocol-specific considerations... this
|
||||
* usually means interpreting aux in a protocol-specific way and using the
|
||||
* pieces at connection setup time, eg, http url pieces.
|
||||
*
|
||||
* len bytes of buf can be used for things with scope until after the actual
|
||||
* connect.
|
||||
*/
|
||||
|
||||
int
|
||||
secstream_connect_munge_h2(lws_ss_handle_t *h, char *buf, size_t len,
|
||||
struct lws_client_connect_info *i,
|
||||
union lws_ss_contemp *ct)
|
||||
{
|
||||
if (h->policy->flags & LWSSSPOLF_QUIRK_NGHTTP2_END_STREAM)
|
||||
i->ssl_connection |= LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM;
|
||||
|
||||
if (h->policy->flags & LWSSSPOLF_H2_QUIRK_OVERFLOWS_TXCR)
|
||||
i->ssl_connection |= LCCSCF_H2_QUIRK_OVERFLOWS_TXCR;
|
||||
|
||||
if (h->policy->flags & LWSSSPOLF_HTTP_MULTIPART)
|
||||
i->ssl_connection |= LCCSCF_HTTP_MULTIPART_MIME;
|
||||
|
||||
if (h->policy->flags & LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED)
|
||||
i->ssl_connection |= LCCSCF_HTTP_X_WWW_FORM_URLENCODED;
|
||||
|
||||
i->ssl_connection |= LCCSCF_PIPELINE;
|
||||
|
||||
i->alpn = "h2";
|
||||
|
||||
/* initial peer tx credit */
|
||||
|
||||
if (h->info.manual_initial_tx_credit) {
|
||||
i->ssl_connection |= LCCSCF_H2_MANUAL_RXFLOW;
|
||||
i->manual_initial_tx_credit = h->info.manual_initial_tx_credit;
|
||||
lwsl_info("%s: initial txcr %d\n", __func__,
|
||||
i->manual_initial_tx_credit);
|
||||
}
|
||||
|
||||
if (!h->policy->u.http.url)
|
||||
return 0;
|
||||
|
||||
/* protocol aux is the path part */
|
||||
|
||||
i->path = buf;
|
||||
lws_snprintf(buf, len, "/%s", h->policy->u.http.url);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
secstream_tx_credit_add_h2(lws_ss_handle_t *h, int add)
|
||||
{
|
||||
lwsl_info("%s: h %p: add %d\n", __func__, h, add);
|
||||
if (h->wsi)
|
||||
return lws_h2_update_peer_txcredit(h->wsi, LWS_H2_STREAM_SID, add);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
secstream_tx_credit_est_h2(lws_ss_handle_t *h)
|
||||
{
|
||||
if (h->wsi) {
|
||||
lwsl_info("%s: h %p: est %d\n", __func__, h,
|
||||
lws_h2_get_peer_txcredit_estimate(h->wsi));
|
||||
|
||||
return lws_h2_get_peer_txcredit_estimate(h->wsi);
|
||||
}
|
||||
|
||||
lwsl_info("%s: h %p: Unknown (0)\n", __func__, h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct ss_pcols ss_pcol_h2 = {
|
||||
"h2",
|
||||
NULL,
|
||||
"lws-secstream-h2",
|
||||
secstream_connect_munge_h2,
|
||||
secstream_tx_credit_add_h2,
|
||||
secstream_tx_credit_est_h2
|
||||
};
|
248
lib/secure-streams/protocols/ss-mqtt.c
Normal file
248
lib/secure-streams/protocols/ss-mqtt.c
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
static int
|
||||
secstream_mqtt(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len)
|
||||
{
|
||||
lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi);
|
||||
lws_mqtt_publish_param_t mqpp, *pmqpp;
|
||||
uint8_t buf[LWS_PRE + 1400];
|
||||
size_t buflen;
|
||||
int f = 0;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
/* because we are protocols[0] ... */
|
||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
||||
lwsl_info("%s: CLIENT_CONNECTION_ERROR: %s\n", __func__,
|
||||
in ? (char *)in : "(null)");
|
||||
if (!h)
|
||||
break;
|
||||
lws_ss_event_helper(h, LWSSSCS_UNREACHABLE);
|
||||
h->wsi = NULL;
|
||||
lws_ss_backoff(h);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_MQTT_CLIENT_CLOSED:
|
||||
if (!h)
|
||||
break;
|
||||
f = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED);
|
||||
if (h->wsi)
|
||||
lws_set_opaque_user_data(h->wsi, NULL);
|
||||
h->wsi = NULL;
|
||||
if (f) {
|
||||
lws_ss_destroy(&h);
|
||||
break;
|
||||
}
|
||||
|
||||
if (h->policy && !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) &&
|
||||
!h->txn_ok && !wsi->context->being_destroyed)
|
||||
lws_ss_backoff(h);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_MQTT_CLIENT_ESTABLISHED:
|
||||
/*
|
||||
* Make sure the handle wsi points to the stream wsi not the
|
||||
* original nwsi, in the case it was migrated
|
||||
*/
|
||||
h->wsi = wsi;
|
||||
h->retry = 0;
|
||||
h->seqstate = SSSEQ_CONNECTED;
|
||||
lws_ss_set_timeout_us(h, LWS_SET_TIMER_USEC_CANCEL);
|
||||
lws_ss_event_helper(h, LWSSSCS_CONNECTED);
|
||||
if (h->policy->u.mqtt.topic)
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_MQTT_CLIENT_RX:
|
||||
// lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: read %d\n", (int)len);
|
||||
if (!h)
|
||||
return 0;
|
||||
|
||||
pmqpp = (lws_mqtt_publish_param_t *)in;
|
||||
|
||||
f = 0;
|
||||
if (!pmqpp->payload_pos)
|
||||
f |= LWSSS_FLAG_SOM;
|
||||
if (pmqpp->payload_pos + len == pmqpp->payload_len)
|
||||
f |= LWSSS_FLAG_EOM;
|
||||
|
||||
h->subseq = 1;
|
||||
|
||||
h->info.rx(ss_to_userobj(h), (const uint8_t *)pmqpp->payload,
|
||||
len, f);
|
||||
|
||||
return 0; /* don't passthru */
|
||||
|
||||
case LWS_CALLBACK_MQTT_SUBSCRIBED:
|
||||
wsi->mqtt->done_subscribe = 1;
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_MQTT_ACK:
|
||||
lws_ss_event_helper(h, LWSSSCS_QOS_ACK_REMOTE);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_MQTT_CLIENT_WRITEABLE:
|
||||
if (!h)
|
||||
return 0;
|
||||
lwsl_notice("%s: ss %p: WRITEABLE\n", __func__, h);
|
||||
|
||||
if (h->seqstate != SSSEQ_CONNECTED) {
|
||||
lwsl_warn("%s: seqstate %d\n", __func__, h->seqstate);
|
||||
break;
|
||||
}
|
||||
|
||||
if (h->policy->u.mqtt.subscribe && !wsi->mqtt->done_subscribe) {
|
||||
|
||||
/*
|
||||
* The policy says to subscribe to something, and we
|
||||
* haven't done it yet
|
||||
*/
|
||||
|
||||
lwsl_warn("%s: subscribing %s\n", __func__, h->policy->u.mqtt.subscribe);
|
||||
|
||||
memset(&h->u.mqtt.sub_top, 0, sizeof(h->u.mqtt.sub_top));
|
||||
h->u.mqtt.sub_top.name = h->policy->u.mqtt.subscribe;
|
||||
h->u.mqtt.sub_top.qos = h->policy->u.mqtt.qos;
|
||||
memset(&h->u.mqtt.sub_info, 0, sizeof(h->u.mqtt.sub_info));
|
||||
h->u.mqtt.sub_info.num_topics = 1;
|
||||
h->u.mqtt.sub_info.topic = &h->u.mqtt.sub_top;
|
||||
|
||||
if (lws_mqtt_client_send_subcribe(wsi, &h->u.mqtt.sub_info)) {
|
||||
lwsl_notice("%s: unable to subscribe", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
buflen = sizeof(buf) - LWS_PRE;
|
||||
if (h->info.tx(ss_to_userobj(h), h->txord++, buf + LWS_PRE,
|
||||
&buflen, &f))
|
||||
/* don't want to send anything */
|
||||
return 0;
|
||||
|
||||
memset(&mqpp, 0, sizeof(mqpp));
|
||||
mqpp.topic = (char *)h->policy->u.mqtt.topic;
|
||||
mqpp.topic_len = strlen(mqpp.topic);
|
||||
mqpp.packet_id = h->txord - 1;
|
||||
mqpp.payload = buf + LWS_PRE;
|
||||
if (h->writeable_len)
|
||||
mqpp.payload_len = h->writeable_len;
|
||||
else
|
||||
mqpp.payload_len = buflen;
|
||||
|
||||
lwsl_notice("%s: payload len %d\n", __func__, (int)mqpp.payload_len);
|
||||
|
||||
mqpp.qos = h->policy->u.mqtt.qos;
|
||||
|
||||
if (lws_mqtt_client_send_publish(wsi, &mqpp,
|
||||
(const char *)buf + LWS_PRE, buflen,
|
||||
f & LWSSS_FLAG_EOM)) {
|
||||
lwsl_notice("%s: failed to publish\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return lws_callback_http_dummy(wsi, reason, user, in, len);
|
||||
}
|
||||
|
||||
const struct lws_protocols protocol_secstream_mqtt = {
|
||||
"lws-secstream-mqtt",
|
||||
secstream_mqtt,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
/*
|
||||
* Munge connect info according to protocol-specific considerations... this
|
||||
* usually means interpreting aux in a protocol-specific way and using the
|
||||
* pieces at connection setup time, eg, http url pieces.
|
||||
*
|
||||
* len bytes of buf can be used for things with scope until after the actual
|
||||
* connect.
|
||||
*
|
||||
* For ws, protocol aux is <url path>;<ws subprotocol name>
|
||||
*/
|
||||
|
||||
static int
|
||||
secstream_connect_munge_mqtt(lws_ss_handle_t *h, char *buf, size_t len,
|
||||
struct lws_client_connect_info *i,
|
||||
union lws_ss_contemp *ct)
|
||||
{
|
||||
memset(&ct->ccp, 0, sizeof(ct->ccp));
|
||||
|
||||
ct->ccp.client_id = "lwsMqttClient";
|
||||
ct->ccp.keep_alive = 60;
|
||||
ct->ccp.clean_start = 1;
|
||||
ct->ccp.will_param.topic = "good/bye";
|
||||
ct->ccp.will_param.message = "sign-off";
|
||||
ct->ccp.will_param.qos = 0;
|
||||
ct->ccp.will_param.retain = 0;
|
||||
|
||||
lwsl_notice("%s\n", __func__);
|
||||
|
||||
h->u.mqtt.topic_qos.name = h->policy->u.mqtt.subscribe;
|
||||
h->u.mqtt.topic_qos.qos = h->policy->u.mqtt.qos;
|
||||
|
||||
i->method = "MQTT";
|
||||
i->mqtt_cp = &ct->ccp;
|
||||
|
||||
i->alpn = "x-amzn-mqtt-ca";
|
||||
|
||||
/* share connections where possible */
|
||||
i->ssl_connection |= LCCSCF_PIPELINE;
|
||||
|
||||
/*
|
||||
if (!h->policy->u.http.url)
|
||||
return 0;
|
||||
|
||||
// protocol aux is the path part ; ws subprotocol name
|
||||
|
||||
i->path = NULL;
|
||||
lws_snprintf(buf, len, "/%s", h->policy->u.mqtt.topic);
|
||||
|
||||
// i->protocol = h->policy->u.mqtt.u.ws.subprotocol;
|
||||
|
||||
lwsl_notice("%s: url %s, ws subprotocol %s\n", __func__, buf, i->protocol);
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct ss_pcols ss_pcol_mqtt = {
|
||||
"MQTT",
|
||||
"x-amzn-mqtt-ca", //"mqtt/3.1.1",
|
||||
"lws-secstream-mqtt",
|
||||
secstream_connect_munge_mqtt
|
||||
};
|
165
lib/secure-streams/protocols/ss-ws.c
Normal file
165
lib/secure-streams/protocols/ss-ws.c
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
static int
|
||||
secstream_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len)
|
||||
{
|
||||
lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi);
|
||||
uint8_t buf[LWS_PRE + 1400];
|
||||
size_t buflen;
|
||||
int f = 0, f1;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
/* because we are protocols[0] ... */
|
||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
||||
lwsl_info("%s: CLIENT_CONNECTION_ERROR: %s\n", __func__,
|
||||
in ? (char *)in : "(null)");
|
||||
if (!h)
|
||||
break;
|
||||
lws_ss_event_helper(h, LWSSSCS_UNREACHABLE);
|
||||
h->wsi = NULL;
|
||||
lws_ss_backoff(h);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_CLOSED:
|
||||
if (!h)
|
||||
break;
|
||||
f = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED);
|
||||
if (h->wsi)
|
||||
lws_set_opaque_user_data(h->wsi, NULL);
|
||||
h->wsi = NULL;
|
||||
|
||||
if (f) {
|
||||
lws_ss_destroy(&h);
|
||||
break;
|
||||
}
|
||||
|
||||
if (h->policy && !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) &&
|
||||
!h->txn_ok && !wsi->context->being_destroyed)
|
||||
lws_ss_backoff(h);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
||||
h->retry = 0;
|
||||
h->seqstate = SSSEQ_CONNECTED;
|
||||
lws_ss_set_timeout_us(h, LWS_SET_TIMER_USEC_CANCEL);
|
||||
lws_ss_event_helper(h, LWSSSCS_CONNECTED);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_RECEIVE:
|
||||
// lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: read %d\n", (int)len);
|
||||
if (!h)
|
||||
return 0;
|
||||
if (lws_is_first_fragment(wsi))
|
||||
f |= LWSSS_FLAG_SOM;
|
||||
if (lws_is_final_fragment(wsi))
|
||||
f |= LWSSS_FLAG_EOM;
|
||||
// lws_frame_is_binary(wsi);
|
||||
|
||||
h->subseq = 1;
|
||||
|
||||
h->info.rx(ss_to_userobj(h), (const uint8_t *)in, len, f);
|
||||
|
||||
return 0; /* don't passthru */
|
||||
|
||||
case LWS_CALLBACK_CLIENT_WRITEABLE:
|
||||
if (!h)
|
||||
return 0;
|
||||
// lwsl_notice("%s: ss %p: WRITEABLE\n", __func__, h);
|
||||
|
||||
if (h->seqstate != SSSEQ_CONNECTED) {
|
||||
lwsl_warn("%s: seqstate %d\n", __func__, h->seqstate);
|
||||
break;
|
||||
}
|
||||
|
||||
buflen = sizeof(buf) - LWS_PRE;
|
||||
if (h->info.tx(ss_to_userobj(h), h->txord++, buf + LWS_PRE,
|
||||
&buflen, &f))
|
||||
/* don't want to send anything */
|
||||
return 0;
|
||||
|
||||
f1 = lws_write_ws_flags(LWS_WRITE_BINARY,
|
||||
!!(f & LWSSS_FLAG_SOM),
|
||||
!!(f & LWSSS_FLAG_EOM));
|
||||
|
||||
if (lws_write(wsi, buf + LWS_PRE, buflen, f1) != (int)buflen) {
|
||||
lwsl_err("%s: write failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return lws_callback_http_dummy(wsi, reason, user, in, len);
|
||||
}
|
||||
|
||||
const struct lws_protocols protocol_secstream_ws = {
|
||||
"lws-secstream-ws",
|
||||
secstream_ws,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
/*
|
||||
* Munge connect info according to protocol-specific considerations... this
|
||||
* usually means interpreting aux in a protocol-specific way and using the
|
||||
* pieces at connection setup time, eg, http url pieces.
|
||||
*
|
||||
* len bytes of buf can be used for things with scope until after the actual
|
||||
* connect.
|
||||
*
|
||||
* For ws, protocol aux is <url path>;<ws subprotocol name>
|
||||
*/
|
||||
|
||||
static int
|
||||
secstream_connect_munge_ws(lws_ss_handle_t *h, char *buf, size_t len,
|
||||
struct lws_client_connect_info *i,
|
||||
union lws_ss_contemp *ct)
|
||||
{
|
||||
lwsl_notice("%s\n", __func__);
|
||||
|
||||
if (!h->policy->u.http.url)
|
||||
return 0;
|
||||
|
||||
/* protocol aux is the path part ; ws subprotocol name */
|
||||
|
||||
i->path = h->policy->u.http.url;
|
||||
lws_snprintf(buf, len, "/%s", h->policy->u.http.url);
|
||||
|
||||
i->protocol = h->policy->u.http.u.ws.subprotocol;
|
||||
|
||||
lwsl_notice("%s: url %s, ws subprotocol %s\n", __func__, buf, i->protocol);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct ss_pcols ss_pcol_ws = {
|
||||
"ws", "http/1.1", "lws-secstream-ws", secstream_connect_munge_ws
|
||||
};
|
574
lib/secure-streams/secure-streams-client.c
Normal file
574
lib/secure-streams/secure-streams-client.c
Normal file
|
@ -0,0 +1,574 @@
|
|||
/*
|
||||
* lws-minimal-secure-streams-client
|
||||
*
|
||||
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
*
|
||||
* This client does not perform any INET networking... instead it opens a unix
|
||||
* domain socket on a proxy that is listening for it, and that creates the
|
||||
* actual secure stream connection.
|
||||
*
|
||||
* We are able to use the usual secure streams api in the client process, with
|
||||
* payloads and connection state information proxied over the unix domain
|
||||
* socket and fulfilled in the proxy process.
|
||||
*
|
||||
* The public client helper pieces are built as part of lws
|
||||
*/
|
||||
#include <private-lib-core.h>
|
||||
|
||||
static void
|
||||
lws_sspc_sul_retry_cb(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
lws_sspc_handle_t *h = lws_container_of(sul, lws_sspc_handle_t, sul_retry);
|
||||
static struct lws_client_connect_info i;
|
||||
|
||||
/*
|
||||
* We may have started up before the system proxy, so be prepared with
|
||||
* a sul to retry at 1Hz
|
||||
*/
|
||||
|
||||
memset(&i, 0, sizeof i);
|
||||
i.context = h->context;
|
||||
if (h->context->ss_proxy_port) { /* tcp */
|
||||
i.address = h->context->ss_proxy_address;
|
||||
i.port = h->context->ss_proxy_port;
|
||||
i.iface = h->context->ss_proxy_bind;
|
||||
} else {
|
||||
if (h->context->ss_proxy_bind)
|
||||
i.address = h->context->ss_proxy_bind;
|
||||
else
|
||||
i.address = "+@proxy.ss.lws";
|
||||
}
|
||||
i.host = i.address;
|
||||
i.origin = i.address;
|
||||
i.method = "RAW";
|
||||
i.protocol = lws_sspc_protocols[0].name;
|
||||
i.local_protocol_name = lws_sspc_protocols[0].name;
|
||||
i.path = "";
|
||||
i.pwsi = &h->cwsi;
|
||||
i.opaque_user_data = (void *)h;
|
||||
|
||||
if (!lws_client_connect_via_info(&i)) {
|
||||
lws_sul_schedule(h->context, 0, &h->sul_retry,
|
||||
lws_sspc_sul_retry_cb, LWS_US_PER_SEC);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
lws_sspc_serialize_metadata(lws_sspc_metadata_t *md, uint8_t *p)
|
||||
{
|
||||
int n, txc;
|
||||
|
||||
if (md->name[0] == '\0') {
|
||||
|
||||
lwsl_info("%s: sending tx credit update %d\n", __func__,
|
||||
md->tx_cr_adjust);
|
||||
|
||||
p[0] = LWSSS_SER_TXPRE_TXCR_UPDATE;
|
||||
lws_ser_wu16be(&p[1], 4);
|
||||
lws_ser_wu32be(&p[3], md->tx_cr_adjust);
|
||||
|
||||
n = 7;
|
||||
|
||||
} else {
|
||||
|
||||
lwsl_info("%s: sending metadata\n", __func__);
|
||||
|
||||
p[0] = LWSSS_SER_TXPRE_METADATA;
|
||||
txc = strlen(md->name);
|
||||
n = txc + 1 + md->len;
|
||||
lws_ser_wu16be(&p[1], n);
|
||||
p[3] = txc;
|
||||
memcpy(&p[4], md->name, txc);
|
||||
memcpy(&p[4 + txc], &md[1], md->len);
|
||||
n = 4 + txc + md->len;
|
||||
}
|
||||
|
||||
lws_dll2_remove(&md->list);
|
||||
lws_free(md);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
lws_sspc_handle_t *h = (lws_sspc_handle_t *)lws_get_opaque_user_data(wsi);
|
||||
uint8_t s[32], pkt[LWS_PRE + 1400], *p = pkt + LWS_PRE;
|
||||
void *m = (void *)((uint8_t *)&h[1]);
|
||||
const uint8_t *cp;
|
||||
lws_usec_t us;
|
||||
int flags, n;
|
||||
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_PROTOCOL_INIT:
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_PROTOCOL_DESTROY:
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
||||
lwsl_warn("%s: CONNECTION_ERROR\n", __func__);
|
||||
lws_set_opaque_user_data(wsi, NULL);
|
||||
h->cwsi = NULL;
|
||||
lws_sul_schedule(h->context, 0, &h->sul_retry,
|
||||
lws_sspc_sul_retry_cb, LWS_US_PER_SEC);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_CONNECTED:
|
||||
if (!h)
|
||||
return -1;
|
||||
lwsl_info("%s: CONNECTED (%s)\n", __func__, h->ssi.streamtype);
|
||||
|
||||
h->state = LPCS_SENDING_INITIAL_TX;
|
||||
h->dsh = lws_dsh_create(NULL, (LWS_PRE + LWS_SS_MTU) * 160, 1);
|
||||
if (!h->dsh)
|
||||
return -1;
|
||||
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, 3);
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_CLOSE:
|
||||
/*
|
||||
* our ss proxy Unix Domain socket has closed...
|
||||
*/
|
||||
lwsl_notice("%s: LWS_CALLBACK_RAW_CLOSE: proxy conn down\n", __func__);
|
||||
h->cwsi = NULL;
|
||||
//lws_sspc_destroy(&h);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_RX:
|
||||
lwsl_info("%s: RAW_RX: rx %d\n", __func__, (int)len);
|
||||
|
||||
if (!h || !h->cwsi) {
|
||||
lwsl_err("%s: rx with bad conn state\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lws_ss_deserialize_parse(&h->parser, lws_get_context(wsi),
|
||||
h->dsh, in, len, &h->state, h,
|
||||
(lws_ss_handle_t **)m, &h->ssi, 1))
|
||||
return -1;
|
||||
|
||||
if (wsi && h->state == LPCS_LOCAL_CONNECTED)
|
||||
lws_set_timeout(wsi, 0, 0);
|
||||
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_WRITEABLE:
|
||||
|
||||
/*
|
||||
* We can transmit something to the proxy...
|
||||
*/
|
||||
|
||||
if (!h)
|
||||
break;
|
||||
|
||||
lwsl_info("%s: WRITEABLE %p: (%s) state %d\n", __func__, wsi,
|
||||
h->ssi.streamtype, h->state);
|
||||
|
||||
n = 0;
|
||||
cp = s;
|
||||
s[1] = 0;
|
||||
switch (h->state) {
|
||||
case LPCS_SENDING_INITIAL_TX:
|
||||
n = strlen(h->ssi.streamtype) + 4;
|
||||
|
||||
s[0] = LWSSS_SER_TXPRE_STREAMTYPE;
|
||||
lws_ser_wu16be(&s[1], n);
|
||||
lws_ser_wu32be(&s[3], h->txc.peer_tx_cr_est);
|
||||
//h->txcr_out = txc;
|
||||
lws_strncpy((char *)&s[7], h->ssi.streamtype, sizeof(s) - 7);
|
||||
n += 3;
|
||||
h->state = LPCS_WAITING_CREATE_RESULT;
|
||||
break;
|
||||
|
||||
case LPCS_LOCAL_CONNECTED:
|
||||
if (!h->conn_req)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Do we need to prioritize sending any metadata
|
||||
* changes?
|
||||
*/
|
||||
|
||||
if (h->metadata_owner.count) {
|
||||
lws_sspc_metadata_t *md = lws_container_of(
|
||||
lws_dll2_get_tail(&h->metadata_owner),
|
||||
lws_sspc_metadata_t, list);
|
||||
|
||||
cp = p;
|
||||
n = lws_sspc_serialize_metadata(md, p);
|
||||
|
||||
/* in case anything else to write */
|
||||
lws_callback_on_writable(h->cwsi);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
h->conn_req = 0;
|
||||
s[0] = LWSSS_SER_TXPRE_ONWARD_CONNECT;
|
||||
s[1] = 0;
|
||||
s[2] = 0;
|
||||
n = 3;
|
||||
break;
|
||||
|
||||
case LPCS_OPERATIONAL:
|
||||
|
||||
/*
|
||||
* Do we want to adjust the peer's ability to write
|
||||
* to us?
|
||||
*/
|
||||
|
||||
/*
|
||||
* Do we need to prioritize sending any metadata
|
||||
* changes?
|
||||
*/
|
||||
|
||||
if (h->metadata_owner.count) {
|
||||
lws_sspc_metadata_t *md = lws_container_of(
|
||||
lws_dll2_get_tail(&h->metadata_owner),
|
||||
lws_sspc_metadata_t, list);
|
||||
|
||||
cp = p;
|
||||
n = lws_sspc_serialize_metadata(md, p);
|
||||
|
||||
/* in case anything else to write */
|
||||
lws_callback_on_writable(h->cwsi);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* we can't write anything if we don't have credit */
|
||||
if (h->txc.tx_cr <= 0) {
|
||||
lwsl_notice("%s: WRITEABLE / OPERATIONAL:"
|
||||
" lack credit (%d)\n", __func__,
|
||||
h->txc.tx_cr);
|
||||
break;
|
||||
}
|
||||
|
||||
len = sizeof(pkt) - LWS_PRE - 19;
|
||||
flags = 0;
|
||||
if (h->ssi.tx(m, h->ord++, pkt + LWS_PRE + 19, &len, &flags))
|
||||
break;
|
||||
|
||||
h->txc.tx_cr -= len;
|
||||
|
||||
cp = p;
|
||||
n = len + 19;
|
||||
us = lws_now_usecs();
|
||||
p[0] = LWSSS_SER_TXPRE_TX_PAYLOAD;
|
||||
lws_ser_wu16be(&p[1], len + 19 - 3);
|
||||
lws_ser_wu32be(&p[3], flags);
|
||||
/* time spent here waiting to send this */
|
||||
lws_ser_wu32be(&p[7], us - h->us_earliest_write_req);
|
||||
/* ust that the client write happened */
|
||||
lws_ser_wu64be(&p[11], us);
|
||||
h->us_earliest_write_req = 0;
|
||||
|
||||
if (flags & LWSSS_FLAG_EOM)
|
||||
if (h->rsidx + 1 < (int)LWS_ARRAY_SIZE(h->rideshare_ofs) &&
|
||||
h->rideshare_ofs[h->rsidx + 1])
|
||||
h->rsidx++;
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!n)
|
||||
break;
|
||||
|
||||
// lwsl_hexdump_notice(cp, n);
|
||||
|
||||
n = lws_write(wsi, (uint8_t *)cp, n, LWS_WRITE_RAW);
|
||||
if (n < 0) {
|
||||
lwsl_notice("%s: WRITEABLE: %d\n", __func__, n);
|
||||
|
||||
goto hangup;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return lws_callback_http_dummy(wsi, reason, user, in, len);
|
||||
|
||||
hangup:
|
||||
lwsl_warn("hangup\n");
|
||||
/* hang up on him */
|
||||
return -1;
|
||||
}
|
||||
|
||||
const struct lws_protocols lws_sspc_protocols[] = {
|
||||
{
|
||||
"ssproxy-protocol",
|
||||
callback_sspc_client,
|
||||
0,
|
||||
2048, 2048, NULL, 0
|
||||
},
|
||||
{ NULL, NULL, 0, 0, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int
|
||||
lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
|
||||
void *opaque_user_data, lws_sspc_handle_t **ppss,
|
||||
struct lws_sequencer *seq_owner, const char **ppayload_fmt)
|
||||
{
|
||||
lws_sspc_handle_t *h;
|
||||
uint8_t *ua;
|
||||
char *p;
|
||||
|
||||
lwsl_notice("%s: streamtype %s\n", __func__, ssi->streamtype);
|
||||
|
||||
/* allocate the handle (including ssi), the user alloc,
|
||||
* and the streamname */
|
||||
|
||||
h = malloc(sizeof(lws_sspc_handle_t) + ssi->user_alloc +
|
||||
strlen(ssi->streamtype) + 1);
|
||||
memset(h, 0, sizeof(*h));
|
||||
memcpy(&h->ssi, ssi, sizeof(*ssi));
|
||||
ua = (uint8_t *)&h[1];
|
||||
memset(ua, 0, ssi->user_alloc);
|
||||
p = (char *)ua + ssi->user_alloc;
|
||||
memcpy(p, ssi->streamtype, strlen(ssi->streamtype) + 1);
|
||||
h->ssi.streamtype = (const char *)p;
|
||||
h->context = context;
|
||||
if (!ssi->manual_initial_tx_credit)
|
||||
h->txc.peer_tx_cr_est = 500000000;
|
||||
else
|
||||
h->txc.peer_tx_cr_est = ssi->manual_initial_tx_credit;
|
||||
|
||||
lws_dll2_add_head(&h->client_list, &context->pt[tsi].ss_client_owner);
|
||||
|
||||
/* fill in the things the real api does for the caller */
|
||||
|
||||
*((void **)(ua + ssi->opaque_user_data_offset)) = opaque_user_data;
|
||||
*((void **)(ua + ssi->handle_offset)) = h;
|
||||
|
||||
if (ppss)
|
||||
*ppss = h;
|
||||
|
||||
/* try the actual connect */
|
||||
|
||||
lws_sspc_sul_retry_cb(&h->sul_retry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* used on context destroy when iterating listed lws_ss on a pt */
|
||||
|
||||
int
|
||||
lws_sspc_destroy_dll(struct lws_dll2 *d, void *user)
|
||||
{
|
||||
lws_sspc_handle_t *h = lws_container_of(d, lws_sspc_handle_t, client_list);
|
||||
|
||||
lws_sspc_destroy(&h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lws_sspc_destroy(lws_sspc_handle_t **ph)
|
||||
{
|
||||
lws_sspc_handle_t *h;
|
||||
void *m;
|
||||
|
||||
lwsl_debug("%s\n", __func__);
|
||||
|
||||
if (!*ph)
|
||||
return;
|
||||
|
||||
h = *ph;
|
||||
m = (void *)((uint8_t *)&h[1]);
|
||||
|
||||
if (h->destroying)
|
||||
return;
|
||||
|
||||
h->destroying = 1;
|
||||
|
||||
lws_sul_schedule(h->context, 0, &h->sul_retry, NULL,
|
||||
LWS_SET_TIMER_USEC_CANCEL);
|
||||
lws_dll2_remove(&h->client_list);
|
||||
|
||||
if (h->dsh)
|
||||
lws_dsh_destroy(&h->dsh);
|
||||
if (h->cwsi) {
|
||||
struct lws *wsi = h->cwsi;
|
||||
h->cwsi = NULL;
|
||||
lws_set_timeout(wsi, 1, LWS_TO_KILL_SYNC);
|
||||
}
|
||||
|
||||
/* clean out any pending metadata changes that didn't make it */
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
|
||||
lws_dll2_get_head(&(*ph)->metadata_owner)) {
|
||||
lws_sspc_metadata_t *md =
|
||||
lws_container_of(d, lws_sspc_metadata_t, list);
|
||||
|
||||
lws_dll2_remove(&md->list);
|
||||
lws_free(md);
|
||||
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
|
||||
h->ssi.state(m, NULL, LWSSSCS_DESTROYING, 0);
|
||||
*ph = NULL;
|
||||
free(h);
|
||||
}
|
||||
|
||||
void
|
||||
lws_sspc_request_tx(lws_sspc_handle_t *h)
|
||||
{
|
||||
if (!h || !h->cwsi)
|
||||
return;
|
||||
|
||||
if (!h->us_earliest_write_req)
|
||||
h->us_earliest_write_req = lws_now_usecs();
|
||||
|
||||
lws_callback_on_writable(h->cwsi);
|
||||
}
|
||||
|
||||
int
|
||||
lws_sspc_client_connect(lws_sspc_handle_t *h)
|
||||
{
|
||||
if (!h || h->state == LPCS_OPERATIONAL)
|
||||
return 0;
|
||||
|
||||
assert(h->state == LPCS_LOCAL_CONNECTED);
|
||||
h->conn_req = 1;
|
||||
if (h->cwsi)
|
||||
lws_callback_on_writable(h->cwsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws_context *
|
||||
lws_sspc_get_context(struct lws_sspc_handle *h)
|
||||
{
|
||||
return h->context;
|
||||
}
|
||||
|
||||
const char *
|
||||
lws_sspc_rideshare(struct lws_sspc_handle *h)
|
||||
{
|
||||
/*
|
||||
* ...the serialized RX rideshare name if any...
|
||||
*/
|
||||
|
||||
if (h->parser.rideshare[0]) {
|
||||
lwsl_info("%s: parser %s\n", __func__, h->parser.rideshare);
|
||||
return h->parser.rideshare;
|
||||
}
|
||||
|
||||
/*
|
||||
* The tx rideshare index
|
||||
*/
|
||||
|
||||
if (h->rideshare_list[0]) {
|
||||
lwsl_info("%s: tx list %s\n", __func__,
|
||||
&h->rideshare_list[h->rideshare_ofs[h->rsidx]]);
|
||||
return &h->rideshare_list[h->rideshare_ofs[h->rsidx]];
|
||||
}
|
||||
|
||||
/*
|
||||
* ... otherwise default to our stream type name
|
||||
*/
|
||||
|
||||
lwsl_info("%s: def %s\n", __func__, h->ssi.streamtype);
|
||||
|
||||
return h->ssi.streamtype;
|
||||
}
|
||||
|
||||
static int
|
||||
_lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name,
|
||||
void *value, size_t len, int tx_cr_adjust)
|
||||
{
|
||||
lws_sspc_metadata_t *md;
|
||||
|
||||
/*
|
||||
* Are we replacing a pending metadata of the same name? It's not
|
||||
* efficient to do this but user code can do what it likes... let's
|
||||
* optimize away the old one.
|
||||
*
|
||||
* Tx credit adjust always has name ""
|
||||
*/
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
|
||||
lws_dll2_get_head(&h->metadata_owner)) {
|
||||
md = lws_container_of(d, lws_sspc_metadata_t, list);
|
||||
|
||||
if (!strcmp(name, md->name)) {
|
||||
lws_dll2_remove(&md->list);
|
||||
lws_free(md);
|
||||
break;
|
||||
}
|
||||
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
|
||||
/*
|
||||
* We have to stash the metadata and pass it to the proxy
|
||||
*/
|
||||
|
||||
md = lws_malloc(sizeof(*md) + len, "set metadata");
|
||||
if (!md) {
|
||||
lwsl_err("%s: OOM\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(md, 0, sizeof(*md));
|
||||
|
||||
md->tx_cr_adjust = tx_cr_adjust;
|
||||
h->txc.peer_tx_cr_est += tx_cr_adjust;
|
||||
|
||||
lws_strncpy(md->name, name, sizeof(md->name));
|
||||
md->len = len;
|
||||
if (len)
|
||||
memcpy(&md[1], value, len);
|
||||
|
||||
lws_dll2_add_tail(&md->list, &h->metadata_owner);
|
||||
|
||||
if (len) {
|
||||
lwsl_info("%s: set metadata %s\n", __func__, name);
|
||||
lwsl_hexdump_info(value, len);
|
||||
} else
|
||||
lwsl_info("%s: serializing tx cr adj %d\n", __func__,
|
||||
(int)tx_cr_adjust);
|
||||
|
||||
if (h->cwsi)
|
||||
lws_callback_on_writable(h->cwsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name,
|
||||
void *value, size_t len)
|
||||
{
|
||||
return _lws_sspc_set_metadata(h, name, value, len, 0);
|
||||
}
|
||||
|
||||
int
|
||||
lws_sspc_add_peer_tx_credit(struct lws_sspc_handle *h, int32_t bump)
|
||||
{
|
||||
lwsl_notice("%s: %d\n", __func__, bump);
|
||||
return _lws_sspc_set_metadata(h, "", NULL, 0, (int)bump);
|
||||
}
|
||||
|
||||
int
|
||||
lws_sspc_get_est_peer_tx_credit(struct lws_sspc_handle *h)
|
||||
{
|
||||
return h->txc.peer_tx_cr_est;
|
||||
}
|
533
lib/secure-streams/secure-streams-process.c
Normal file
533
lib/secure-streams/secure-streams-process.c
Normal file
|
@ -0,0 +1,533 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* When the user code is in a different process, a non-tls unix domain socket
|
||||
* proxy is used to asynchronusly transfer buffers in each direction via the
|
||||
* network stack, without explicit IPC
|
||||
*
|
||||
* user_process{ [user code] | shim | socket-}------ lws_process{ lws }
|
||||
*
|
||||
* Lws exposes a listening unix domain socket in this case, the user processes
|
||||
* connect to it and pass just info.streamtype in an initial tx packet. All
|
||||
* packets are prepended by a 1-byte type field when used in this mode. See
|
||||
* lws-secure-streams.h for documentation and definitions.
|
||||
*
|
||||
* Proxying in either direction can face the situation it cannot send the onward
|
||||
* packet immediately and is subject to separating the write request from the
|
||||
* write action. To make the best use of memory, a single preallocated buffer
|
||||
* stashes pending packets in all four directions (c->p, p->c, p->ss, ss->p).
|
||||
* This allows it to adapt to different traffic patterns without wasted areas
|
||||
* dedicated to traffic that isn't coming in a particular application.
|
||||
*
|
||||
* A shim is provided to monitor the process' unix domain socket and regenerate
|
||||
* the secure sockets api there with callbacks happening in the process thread
|
||||
* context.
|
||||
*
|
||||
* This file implements the listening unix domain socket proxy... this code is
|
||||
* only going to run on a Linux-class device with its implications about memory
|
||||
* availability.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
/*
|
||||
* Because both sides of the connection share the conn, we allocate it
|
||||
* during accepted adoption, and both sides point to it.
|
||||
*
|
||||
* The last one of the accepted side and the onward side to close frees it.
|
||||
*/
|
||||
|
||||
struct conn {
|
||||
struct lws_ss_serialization_parser parser;
|
||||
|
||||
lws_dsh_t *dsh; /* unified buffer for both sides */
|
||||
struct lws *wsi; /* the client side */
|
||||
lws_ss_handle_t *ss; /* the onward, ss side */
|
||||
|
||||
lws_ss_conn_states_t state;
|
||||
};
|
||||
|
||||
struct raw_pss {
|
||||
struct conn *conn;
|
||||
};
|
||||
|
||||
/*
|
||||
* Proxy - onward secure-stream handler
|
||||
*/
|
||||
|
||||
typedef struct ss_proxy_onward {
|
||||
lws_ss_handle_t *ss;
|
||||
struct conn *conn;
|
||||
} ss_proxy_t;
|
||||
|
||||
|
||||
/* secure streams payload interface */
|
||||
|
||||
static int
|
||||
ss_proxy_onward_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
ss_proxy_t *m = (ss_proxy_t *)userobj;
|
||||
const char *rsp = NULL;
|
||||
int n;
|
||||
|
||||
/*
|
||||
* The onward secure stream connection has received something.
|
||||
*/
|
||||
|
||||
if (m->ss->rideshare != m->ss->policy && m->ss->rideshare) {
|
||||
rsp = m->ss->rideshare->streamtype;
|
||||
flags |= LWSSS_FLAG_RIDESHARE;
|
||||
}
|
||||
|
||||
n = lws_ss_serialize_rx_payload(m->conn->dsh, buf, len, flags, rsp);
|
||||
if (n)
|
||||
return n;
|
||||
|
||||
if (m->conn->wsi) /* if possible, request client conn write */
|
||||
lws_callback_on_writable(m->conn->wsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* we are transmitting buffered payload originally from the client on to the ss
|
||||
*/
|
||||
|
||||
static int
|
||||
ss_proxy_onward_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags)
|
||||
{
|
||||
ss_proxy_t *m = (ss_proxy_t *)userobj;
|
||||
void *p;
|
||||
size_t si;
|
||||
|
||||
if (!m->conn->ss || m->conn->state != LPCS_OPERATIONAL) {
|
||||
lwsl_notice("%s: ss not ready\n", __func__);
|
||||
*len = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The onward secure stream says that we could send something to it
|
||||
* (by putting it in buf, and setting *len and *flags)
|
||||
*/
|
||||
|
||||
if (lws_ss_deserialize_tx_payload(m->conn->dsh, m->ss->wsi,
|
||||
ord, buf, len, flags))
|
||||
return 1;
|
||||
|
||||
if (!lws_dsh_get_head(m->conn->dsh, KIND_C_TO_P, (void **)&p, &si))
|
||||
lws_ss_request_tx(m->conn->ss);
|
||||
|
||||
if (!*len && !*flags)
|
||||
return 1; /* we don't actually want to send anything */
|
||||
|
||||
lwsl_info("%s: onward tx %d fl 0x%x\n", __func__, (int)*len, *flags);
|
||||
|
||||
#if 0
|
||||
{
|
||||
int ff = open("/tmp/z", O_RDWR | O_CREAT | O_APPEND, 0666);
|
||||
if (ff == -1)
|
||||
lwsl_err("%s: errno %d\n", __func__, errno);
|
||||
write(ff, buf, *len);
|
||||
close(ff);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_proxy_onward_state(void *userobj, void *sh,
|
||||
lws_ss_constate_t state, lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
ss_proxy_t *m = (ss_proxy_t *)userobj;
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
break;
|
||||
|
||||
case LWSSSCS_DESTROYING:
|
||||
if (!m->conn)
|
||||
break;
|
||||
if (!m->conn->wsi) {
|
||||
/*
|
||||
* Our onward secure stream is closing and our client
|
||||
* connection has already gone away... destroy the conn.
|
||||
*/
|
||||
lwsl_info("%s: Destroying conn\n", __func__);
|
||||
lws_dsh_destroy(&m->conn->dsh);
|
||||
free(m->conn);
|
||||
m->conn = NULL;
|
||||
return 0;
|
||||
} else
|
||||
lwsl_info("%s: ss DESTROYING, wsi up\n", __func__);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!m->conn) {
|
||||
lwsl_warn("%s: dropping state due to conn not up\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
lws_ss_serialize_state(m->conn->dsh, state, ack);
|
||||
|
||||
if (m->conn->wsi) /* if possible, request client conn write */
|
||||
lws_callback_on_writable(m->conn->wsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ss_proxy_onward_txcr(void *userobj, int bump)
|
||||
{
|
||||
ss_proxy_t *m = (ss_proxy_t *)userobj;
|
||||
|
||||
if (!m->conn)
|
||||
return;
|
||||
|
||||
lws_ss_serialize_txcr(m->conn->dsh, bump);
|
||||
|
||||
if (m->conn->wsi) /* if possible, request client conn write */
|
||||
lws_callback_on_writable(m->conn->wsi);
|
||||
}
|
||||
|
||||
/*
|
||||
* Client - Proxy connection on unix domain socket
|
||||
*/
|
||||
|
||||
static int
|
||||
callback_ss_proxy(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
struct raw_pss *pss = (struct raw_pss *)user;
|
||||
const lws_ss_policy_t *rsp;
|
||||
struct conn *conn = NULL;
|
||||
lws_ss_info_t ssi;
|
||||
const uint8_t *cp;
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
lws_usec_t us;
|
||||
#endif
|
||||
char s[128];
|
||||
uint8_t *p;
|
||||
size_t si;
|
||||
char pay;
|
||||
int n;
|
||||
|
||||
if (pss)
|
||||
conn = pss->conn;
|
||||
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_PROTOCOL_INIT:
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_PROTOCOL_DESTROY:
|
||||
break;
|
||||
|
||||
/* callbacks related to raw socket descriptor "accepted side" */
|
||||
|
||||
case LWS_CALLBACK_RAW_ADOPT:
|
||||
lwsl_info("LWS_CALLBACK_RAW_ADOPT\n");
|
||||
if (!pss)
|
||||
return -1;
|
||||
pss->conn = malloc(sizeof(struct conn));
|
||||
if (!pss->conn)
|
||||
return -1;
|
||||
memset(pss->conn, 0, sizeof(*pss->conn));
|
||||
|
||||
pss->conn->dsh = lws_dsh_create(&pt->ss_dsh_owner,
|
||||
LWS_SS_MTU * 160, 2);
|
||||
if (!pss->conn->dsh) {
|
||||
free(pss->conn);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
pss->conn->wsi = wsi;
|
||||
pss->conn->state = LPCS_WAIT_INITIAL_TX;
|
||||
|
||||
/*
|
||||
* Client is expected to follow the unix domain socket
|
||||
* acceptance up rapidly with an initial tx containing the
|
||||
* streamtype name. We can't create the stream until then.
|
||||
*/
|
||||
lws_set_timeout(wsi,
|
||||
PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, 3);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_CLOSE:
|
||||
lwsl_info("LWS_CALLBACK_RAW_CLOSE:\n");
|
||||
|
||||
/*
|
||||
* the client unix domain socket connection has closed...
|
||||
* eg, client has exited or otherwise has definitively finished
|
||||
* with the proxying and onward connection
|
||||
*/
|
||||
|
||||
if (!conn)
|
||||
break;
|
||||
|
||||
if (conn->ss) {
|
||||
lwsl_info("%s: destroying ss\n", __func__);
|
||||
/* sever relationship with ss about to be deleted */
|
||||
lws_set_opaque_user_data(wsi, NULL);
|
||||
|
||||
conn->wsi = NULL;
|
||||
|
||||
|
||||
lws_ss_destroy(&conn->ss);
|
||||
/* conn may have gone */
|
||||
break;
|
||||
}
|
||||
|
||||
if (conn->state == LPCS_DESTROYED || !conn->ss) {
|
||||
/*
|
||||
* There's no onward secure stream and our client
|
||||
* connection is closing. Destroy the conn.
|
||||
*/
|
||||
lws_dsh_destroy(&conn->dsh);
|
||||
free(conn);
|
||||
pss->conn = NULL;
|
||||
} else
|
||||
lwsl_debug("%s: CLOSE; ss=%p\n", __func__, conn->ss);
|
||||
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_RX:
|
||||
lwsl_info("%s: RX: rx %d\n", __func__, (int)len);
|
||||
|
||||
if (!conn || !conn->wsi) {
|
||||
lwsl_err("%s: rx with bad conn state\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// lwsl_hexdump_info(in, len);
|
||||
|
||||
if (conn->state == LPCS_WAIT_INITIAL_TX) {
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.user_alloc = sizeof(ss_proxy_t);
|
||||
ssi.handle_offset = offsetof(ss_proxy_t, ss);
|
||||
ssi.opaque_user_data_offset =
|
||||
offsetof(ss_proxy_t, conn);
|
||||
ssi.rx = ss_proxy_onward_rx;
|
||||
ssi.tx = ss_proxy_onward_tx;
|
||||
ssi.state = ss_proxy_onward_state;
|
||||
}
|
||||
|
||||
if (lws_ss_deserialize_parse(&conn->parser,
|
||||
lws_get_context(wsi), conn->dsh, in, len,
|
||||
&conn->state, conn, &conn->ss, &ssi, 0)) {
|
||||
lwsl_err("%s: RAW_RX: deserialize_parse fail\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conn->state == LPCS_REPORTING_FAIL ||
|
||||
conn->state == LPCS_REPORTING_OK)
|
||||
lws_callback_on_writable(conn->wsi);
|
||||
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_WRITEABLE:
|
||||
// lwsl_notice("LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE\n");
|
||||
|
||||
/*
|
||||
* We can transmit something back to the client from the dsh
|
||||
* of stuff we received on its behalf from the ss
|
||||
*/
|
||||
|
||||
if (!conn || !conn->wsi)
|
||||
break;
|
||||
|
||||
n = 0;
|
||||
pay = 0;
|
||||
s[3] = 0;
|
||||
cp = (const uint8_t *)s;
|
||||
switch (conn->state) {
|
||||
case LPCS_REPORTING_FAIL:
|
||||
s[3] = 1;
|
||||
/* fallthru */
|
||||
case LPCS_REPORTING_OK:
|
||||
s[0] = LWSSS_SER_RXPRE_CREATE_RESULT;
|
||||
s[1] = 0;
|
||||
s[2] = 1;
|
||||
|
||||
n = 4;
|
||||
|
||||
/*
|
||||
* If there's rideshare sequencing, it's added after the
|
||||
* first 4 bytes or the create result, comma-separated
|
||||
*/
|
||||
|
||||
rsp = conn->ss->policy;
|
||||
|
||||
while (rsp) {
|
||||
if (n != 4 && n < (int)sizeof(s) - 2)
|
||||
s[n++] = ',';
|
||||
n += lws_snprintf(&s[n], sizeof(s) - n,
|
||||
"%s", rsp->streamtype);
|
||||
rsp = lws_ss_policy_lookup(wsi->context,
|
||||
rsp->rideshare_streamtype);
|
||||
}
|
||||
s[2] = n - 3;
|
||||
conn->state = LPCS_OPERATIONAL;
|
||||
lws_set_timeout(wsi, 0, 0);
|
||||
break;
|
||||
case LPCS_OPERATIONAL:
|
||||
if (lws_dsh_get_head(conn->dsh, KIND_SS_TO_P,
|
||||
(void **)&p, &si))
|
||||
break;
|
||||
cp = p;
|
||||
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
if (cp[0] == LWSSS_SER_RXPRE_RX_PAYLOAD &&
|
||||
wsi->context->detailed_latency_cb) {
|
||||
|
||||
/*
|
||||
* we're fulfilling rx that came in on ss
|
||||
* by sending it back out to the client on
|
||||
* the Unix Domain Socket
|
||||
*
|
||||
* + 7 u32 write will compute latency here...
|
||||
* + 11 u32 ust we received from ss
|
||||
*
|
||||
* lws_write will report it and fill in
|
||||
* LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE
|
||||
*/
|
||||
|
||||
us = lws_now_usecs();
|
||||
lws_ser_wu32be(&p[7], us -
|
||||
lws_ser_ru64be(&p[11]));
|
||||
lws_ser_wu64be(&p[11], us);
|
||||
|
||||
wsi->detlat.acc_size =
|
||||
wsi->detlat.req_size = si - 19;
|
||||
/* time proxy held it */
|
||||
wsi->detlat.latencies[
|
||||
LAT_DUR_PROXY_RX_TO_ONWARD_TX] =
|
||||
lws_ser_ru32be(&p[7]);
|
||||
}
|
||||
#endif
|
||||
|
||||
pay = 1;
|
||||
n = (int)si;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
again:
|
||||
if (!n)
|
||||
break;
|
||||
|
||||
n = lws_write(wsi, (uint8_t *)cp, n, LWS_WRITE_RAW);
|
||||
if (n < 0) {
|
||||
lwsl_info("%s: WRITEABLE: %d\n", __func__, n);
|
||||
|
||||
goto hangup;
|
||||
}
|
||||
|
||||
switch (conn->state) {
|
||||
case LPCS_REPORTING_FAIL:
|
||||
goto hangup;
|
||||
case LPCS_OPERATIONAL:
|
||||
if (pay)
|
||||
lws_dsh_free((void **)&p);
|
||||
if (!lws_dsh_get_head(conn->dsh, KIND_SS_TO_P,
|
||||
(void **)&p, &si)) {
|
||||
if (!lws_send_pipe_choked(wsi)) {
|
||||
cp = p;
|
||||
pay = 1;
|
||||
n = (int)si;
|
||||
goto again;
|
||||
}
|
||||
lws_callback_on_writable(wsi);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return lws_callback_http_dummy(wsi, reason, user, in, len);
|
||||
|
||||
hangup:
|
||||
//lws_ss_destroy(&conn->ss);
|
||||
//conn->state = LPCS_DESTROYED;
|
||||
|
||||
/* hang up on him */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const struct lws_protocols protocols[] = {
|
||||
{
|
||||
"ssproxy-protocol",
|
||||
callback_ss_proxy,
|
||||
sizeof(struct raw_pss),
|
||||
2048, 2048, NULL, 0
|
||||
},
|
||||
{ NULL, NULL, 0, 0, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* called from create_context()
|
||||
*/
|
||||
|
||||
int
|
||||
lws_ss_proxy_create(struct lws_context *context, const char *bind, int port)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
info.vhost_name = "ssproxy";
|
||||
info.options = LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG;
|
||||
info.port = port;
|
||||
if (!port) {
|
||||
if (!bind)
|
||||
bind = "@proxy.ss.lws";
|
||||
info.options |= LWS_SERVER_OPTION_UNIX_SOCK;
|
||||
}
|
||||
info.iface = bind;
|
||||
info.unix_socket_perms = "root:root";
|
||||
info.listen_accept_role = "raw-skt";
|
||||
info.listen_accept_protocol = "ssproxy-protocol";
|
||||
info.protocols = protocols;
|
||||
|
||||
if (!lws_create_vhost(context, &info)) {
|
||||
lwsl_err("%s: Failed to create ss proxy vhost\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
926
lib/secure-streams/secure-streams-serialize.c
Normal file
926
lib/secure-streams/secure-streams-serialize.c
Normal file
|
@ -0,0 +1,926 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* In the case Secure Streams protocol needs to pass through a buffer,
|
||||
* or a streamed connection, the protocol metadata must be serialized. This
|
||||
* file provides internal apis to perform the serialization and deserialization
|
||||
* in and out of an lws_dsh fifo-type buffer.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
typedef enum {
|
||||
RPAR_TYPE,
|
||||
RPAR_LEN_MSB,
|
||||
RPAR_LEN_LSB,
|
||||
|
||||
RPAR_FLAG_B3,
|
||||
RPAR_FLAG_B2,
|
||||
RPAR_FLAG_B1,
|
||||
RPAR_FLAG_B0,
|
||||
|
||||
RPAR_LATA3,
|
||||
RPAR_LATA2,
|
||||
RPAR_LATA1,
|
||||
RPAR_LATA0,
|
||||
|
||||
RPAR_LATB7,
|
||||
RPAR_LATB6,
|
||||
RPAR_LATB5,
|
||||
RPAR_LATB4,
|
||||
RPAR_LATB3,
|
||||
RPAR_LATB2,
|
||||
RPAR_LATB1,
|
||||
RPAR_LATB0,
|
||||
|
||||
RPAR_RIDESHARE_LEN,
|
||||
RPAR_RIDESHARE,
|
||||
|
||||
RPAR_RESULT_CREATION_RIDESHARE,
|
||||
|
||||
RPAR_METADATA_NAMELEN,
|
||||
RPAR_METADATA_NAME,
|
||||
RPAR_METADATA_VALUE,
|
||||
|
||||
RPAR_PAYLOAD,
|
||||
|
||||
RPAR_RX_TXCR_UPDATE,
|
||||
|
||||
RPAR_STREAMTYPE,
|
||||
RPAR_INITTXC0,
|
||||
|
||||
RPAR_TXCR0,
|
||||
|
||||
RPAR_RESULT_CREATION,
|
||||
|
||||
RPAR_STATEINDEX,
|
||||
RPAR_ORD3,
|
||||
RPAR_ORD2,
|
||||
RPAR_ORD1,
|
||||
RPAR_ORD0,
|
||||
} rx_parser_t;
|
||||
|
||||
#if defined(_DEBUG)
|
||||
static const char *sn[] = {
|
||||
"unset",
|
||||
|
||||
"LPCS_WAIT_INITIAL_TX",
|
||||
"LPCS_REPORTING_FAIL",
|
||||
"LPCS_REPORTING_OK",
|
||||
"LPCS_OPERATIONAL",
|
||||
"LPCS_DESTROYED",
|
||||
|
||||
"LPCS_SENDING_INITIAL_TX",
|
||||
"LPCS_WAITING_CREATE_RESULT",
|
||||
"LPCS_LOCAL_CONNECTED",
|
||||
"LPCS_ONWARD_CONNECT",
|
||||
};
|
||||
#endif
|
||||
|
||||
void
|
||||
lws_ss_serialize_state_transition(lws_ss_conn_states_t *state, int new_state)
|
||||
{
|
||||
#if defined(_DEBUG)
|
||||
lwsl_info("%s: %s -> %s\n", __func__, sn[*state], sn[new_state]);
|
||||
#endif
|
||||
*state = new_state;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* event loop received something and is queueing it for the foreign side of
|
||||
* the dsh to consume later as serialized rx
|
||||
*/
|
||||
|
||||
int
|
||||
lws_ss_serialize_rx_payload(struct lws_dsh *dsh, const uint8_t *buf,
|
||||
size_t len, int flags, const char *rsp)
|
||||
{
|
||||
lws_usec_t us = lws_now_usecs();
|
||||
uint8_t pre[128];
|
||||
int est = 19, l = 0;
|
||||
|
||||
if (flags & LWSSS_FLAG_RIDESHARE) {
|
||||
/*
|
||||
* We should have the rideshare name if we have been told it's
|
||||
* on a non-default rideshare
|
||||
*/
|
||||
assert(rsp);
|
||||
l = strlen(rsp);
|
||||
est += 1 + l;
|
||||
} else
|
||||
assert(!rsp);
|
||||
|
||||
// lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags);
|
||||
// lwsl_hexdump_info(buf, len);
|
||||
|
||||
pre[0] = LWSSS_SER_RXPRE_RX_PAYLOAD;
|
||||
lws_ser_wu16be(&pre[1], len + est - 3);
|
||||
lws_ser_wu32be(&pre[3], flags);
|
||||
lws_ser_wu32be(&pre[7], 0); /* write will compute latency here... */
|
||||
lws_ser_wu64be(&pre[11], us); /* ... and set this to the write time */
|
||||
|
||||
/*
|
||||
* If we are on a non-default rideshare, append the non-default name to
|
||||
* the headers of the payload part, 1-byte length first
|
||||
*/
|
||||
|
||||
if (flags & LWSSS_FLAG_RIDESHARE) {
|
||||
pre[19] = (uint8_t)l;
|
||||
memcpy(&pre[20], rsp, l);
|
||||
}
|
||||
|
||||
if (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, est, buf, len)) {
|
||||
lwsl_err("%s: unable to alloc in dsh 1\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* event loop is consuming dsh-buffered, already-serialized tx from the
|
||||
* foreign side
|
||||
*/
|
||||
|
||||
int
|
||||
lws_ss_deserialize_tx_payload(struct lws_dsh *dsh, struct lws *wsi,
|
||||
lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags)
|
||||
{
|
||||
uint8_t *p;
|
||||
size_t si;
|
||||
|
||||
if (lws_dsh_get_head(dsh, KIND_C_TO_P, (void **)&p, &si)) {
|
||||
*len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The packet in the dsh has a proxying serialization header, process
|
||||
* and strip it so we just forward the payload
|
||||
*/
|
||||
|
||||
if (*len <= si - 23 || si < 23) {
|
||||
/*
|
||||
* What comes out of the dsh needs to fit in the tx buffer
|
||||
*/
|
||||
lwsl_err("%s: *len = %d, si = %d\n", __func__, (int)*len, (int)si);
|
||||
assert(0);
|
||||
return 1;
|
||||
}
|
||||
if (p[0] != LWSSS_SER_TXPRE_TX_PAYLOAD) {
|
||||
assert(0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*len = lws_ser_ru16be(&p[1]) - (23 - 3);
|
||||
assert(*len == si - 23);
|
||||
|
||||
memcpy(buf, p + 23, si - 23);
|
||||
|
||||
*flags = lws_ser_ru32be(&p[3]);
|
||||
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
if (wsi && wsi->context->detailed_latency_cb) {
|
||||
/*
|
||||
* use the proxied latency information to compute the client
|
||||
* and our delays, and apply to wsi.
|
||||
*
|
||||
* + 7 u32 us held at client before written
|
||||
* +11 u32 us taken for transit to proxy
|
||||
* +15 u64 ustime when proxy got packet from client
|
||||
*/
|
||||
lws_usec_t us = lws_now_usecs();
|
||||
|
||||
wsi->detlat.acc_size = wsi->detlat.req_size = si - 23;
|
||||
wsi->detlat.latencies[LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE] =
|
||||
lws_ser_ru32be(&p[7]);
|
||||
wsi->detlat.latencies[LAT_DUR_PROXY_CLIENT_WRITE_TO_PROXY_RX] =
|
||||
lws_ser_ru32be(&p[11]);
|
||||
wsi->detlat.latencies[LAT_DUR_PROXY_RX_TO_ONWARD_TX] =
|
||||
us - lws_ser_ru64be(&p[15]);
|
||||
|
||||
wsi->detlat.latencies[LAT_DUR_USERCB] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// lwsl_user("%s: len %d, flags: %d\n", __func__, (int)*len, *flags);
|
||||
// lwsl_hexdump_info(buf, *len);
|
||||
|
||||
lws_dsh_free((void **)&p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* event loop side is issuing state, serialize and put it in the dbuf for
|
||||
* the foreign side to consume later
|
||||
*/
|
||||
|
||||
int
|
||||
lws_ss_serialize_state(struct lws_dsh *dsh, lws_ss_constate_t state,
|
||||
lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
uint8_t pre[8];
|
||||
|
||||
lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
|
||||
(unsigned int)ack);
|
||||
|
||||
pre[0] = LWSSS_SER_RXPRE_CONNSTATE;
|
||||
pre[1] = 0;
|
||||
pre[2] = 5;
|
||||
pre[3] = (uint8_t)state;
|
||||
lws_ser_wu32be(&pre[4], ack);
|
||||
|
||||
if (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, 8, NULL, 0)) {
|
||||
lwsl_err("%s: unable to alloc in dsh 2\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* event loop side was told about remote peer tx credit window update, serialize
|
||||
* and put it in the dbuf for the foreign side to consume later
|
||||
*/
|
||||
|
||||
int
|
||||
lws_ss_serialize_txcr(struct lws_dsh *dsh, int txcr)
|
||||
{
|
||||
uint8_t pre[7];
|
||||
|
||||
lwsl_info("%s: %d\n", __func__, txcr);
|
||||
|
||||
pre[0] = LWSSS_SER_RXPRE_TXCR_UPDATE;
|
||||
pre[1] = 0;
|
||||
pre[2] = 4;
|
||||
lws_ser_wu32be(&pre[3], txcr);
|
||||
|
||||
if (lws_dsh_alloc_tail(dsh, KIND_SS_TO_P, pre, 7, NULL, 0)) {
|
||||
lwsl_err("%s: unable to alloc in dsh 2\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* event loop side is consuming serialized data from the client via dsh, parse
|
||||
* it using a bytewise parser for the serialization header(s)...
|
||||
* it's possibly coalesced
|
||||
*/
|
||||
|
||||
int
|
||||
lws_ss_deserialize_parse(struct lws_ss_serialization_parser *par,
|
||||
struct lws_context *context,
|
||||
struct lws_dsh *dsh, const uint8_t *cp, size_t len,
|
||||
lws_ss_conn_states_t *state, void *parconn,
|
||||
lws_ss_handle_t **pss, lws_ss_info_t *ssi, char client)
|
||||
{
|
||||
lws_ss_metadata_t *pm;
|
||||
lws_sspc_handle_t *h;
|
||||
uint8_t pre[23];
|
||||
lws_usec_t us;
|
||||
uint32_t flags;
|
||||
uint8_t *p;
|
||||
int n;
|
||||
|
||||
while (len--) {
|
||||
switch (par->ps) {
|
||||
case RPAR_TYPE:
|
||||
par->type = *cp++;
|
||||
par->ps++;
|
||||
break;
|
||||
|
||||
case RPAR_LEN_MSB: /* this is remaining frame length */
|
||||
par->rem = (*cp++) << 8;
|
||||
par->ps++;
|
||||
break;
|
||||
|
||||
case RPAR_LEN_LSB:
|
||||
par->rem |= *cp++;
|
||||
switch (par->type) {
|
||||
|
||||
/* event loop side */
|
||||
|
||||
case LWSSS_SER_TXPRE_TX_PAYLOAD:
|
||||
if (client)
|
||||
goto hangup;
|
||||
if (*state != LPCS_OPERATIONAL)
|
||||
goto hangup;
|
||||
par->ps = RPAR_FLAG_B3;
|
||||
break;
|
||||
|
||||
case LWSSS_SER_TXPRE_DESTROYING:
|
||||
if (client)
|
||||
goto hangup;
|
||||
par->ps = RPAR_TYPE;
|
||||
lwsl_notice("%s: DESTROYING\n", __func__);
|
||||
goto hangup;
|
||||
|
||||
case LWSSS_SER_TXPRE_ONWARD_CONNECT:
|
||||
if (client)
|
||||
goto hangup;
|
||||
if (*state != LPCS_OPERATIONAL)
|
||||
goto hangup;
|
||||
par->ps = RPAR_TYPE;
|
||||
if (*pss)
|
||||
lws_ss_client_connect(*pss);
|
||||
break;
|
||||
|
||||
case LWSSS_SER_TXPRE_STREAMTYPE:
|
||||
if (client)
|
||||
goto hangup;
|
||||
if (*state != LPCS_WAIT_INITIAL_TX)
|
||||
goto hangup;
|
||||
if (par->rem < 4)
|
||||
goto hangup;
|
||||
par->ctr = 0;
|
||||
par->ps = RPAR_INITTXC0;
|
||||
break;
|
||||
|
||||
case LWSSS_SER_TXPRE_METADATA:
|
||||
if (client)
|
||||
goto hangup;
|
||||
if (par->rem < 3)
|
||||
goto hangup;
|
||||
par->ctr = 0;
|
||||
par->ps = RPAR_METADATA_NAMELEN;
|
||||
break;
|
||||
|
||||
case LWSSS_SER_TXPRE_TXCR_UPDATE:
|
||||
par->ps = RPAR_TXCR0;
|
||||
par->ctr = 0;
|
||||
break;
|
||||
|
||||
/* client side */
|
||||
|
||||
case LWSSS_SER_RXPRE_RX_PAYLOAD:
|
||||
if (!client)
|
||||
goto hangup;
|
||||
if (*state != LPCS_OPERATIONAL &&
|
||||
*state != LPCS_LOCAL_CONNECTED) {
|
||||
lwsl_err("rx in state %d\n", *state);
|
||||
goto hangup;
|
||||
}
|
||||
par->rideshare[0] = '\0';
|
||||
par->ps = RPAR_FLAG_B3;
|
||||
break;
|
||||
|
||||
case LWSSS_SER_RXPRE_CREATE_RESULT:
|
||||
if (!client)
|
||||
goto hangup;
|
||||
if (*state != LPCS_WAITING_CREATE_RESULT) {
|
||||
lwsl_err("a2\n");
|
||||
goto hangup;
|
||||
}
|
||||
if (par->rem < 1) {
|
||||
lwsl_err("a3\n");
|
||||
goto hangup;
|
||||
}
|
||||
par->ps = RPAR_RESULT_CREATION;
|
||||
break;
|
||||
|
||||
case LWSSS_SER_RXPRE_CONNSTATE:
|
||||
if (!client)
|
||||
goto hangup;
|
||||
if (*state != LPCS_LOCAL_CONNECTED &&
|
||||
*state != LPCS_OPERATIONAL) {
|
||||
lwsl_err("a4\n");
|
||||
goto hangup;
|
||||
}
|
||||
if (par->rem < 4) {
|
||||
lwsl_err("a5\n");
|
||||
goto hangup;
|
||||
}
|
||||
par->ps = RPAR_STATEINDEX;
|
||||
break;
|
||||
|
||||
case LWSSS_SER_RXPRE_TXCR_UPDATE:
|
||||
par->ctr = 0;
|
||||
par->ps = RPAR_RX_TXCR_UPDATE;
|
||||
break;
|
||||
|
||||
default:
|
||||
lwsl_notice("%s: bad type 0x%x\n", __func__,
|
||||
par->type);
|
||||
goto hangup;
|
||||
}
|
||||
break;
|
||||
|
||||
case RPAR_FLAG_B3:
|
||||
case RPAR_FLAG_B2:
|
||||
case RPAR_FLAG_B1:
|
||||
case RPAR_FLAG_B0:
|
||||
par->flags <<= 8;
|
||||
par->flags |= *cp++;
|
||||
par->ps++;
|
||||
if (!par->rem--)
|
||||
goto hangup;
|
||||
break;
|
||||
|
||||
case RPAR_LATA3:
|
||||
case RPAR_LATA2:
|
||||
case RPAR_LATA1:
|
||||
case RPAR_LATA0:
|
||||
par->usd_phandling <<= 8;
|
||||
par->usd_phandling |= *cp++;
|
||||
par->ps++;
|
||||
if (!par->rem--)
|
||||
goto hangup;
|
||||
break;
|
||||
|
||||
case RPAR_LATB7:
|
||||
case RPAR_LATB6:
|
||||
case RPAR_LATB5:
|
||||
case RPAR_LATB4:
|
||||
case RPAR_LATB3:
|
||||
case RPAR_LATB2:
|
||||
case RPAR_LATB1:
|
||||
case RPAR_LATB0:
|
||||
par->ust_pwait <<= 8;
|
||||
par->ust_pwait |= *cp++;
|
||||
par->ps++;
|
||||
par->frag1 = 1;
|
||||
if (!par->rem--)
|
||||
goto hangup;
|
||||
|
||||
if (par->ps == RPAR_RIDESHARE_LEN &&
|
||||
!(par->flags & LWSSS_FLAG_RIDESHARE))
|
||||
par->ps = RPAR_PAYLOAD;
|
||||
|
||||
if (par->rem)
|
||||
break;
|
||||
|
||||
/* fallthru - handle 0-length payload */
|
||||
|
||||
if (!(par->flags & LWSSS_FLAG_RIDESHARE))
|
||||
goto payload_ff;
|
||||
goto hangup;
|
||||
|
||||
/*
|
||||
* Inbound rideshare info is provided on the RX packet
|
||||
* itself
|
||||
*/
|
||||
|
||||
case RPAR_RIDESHARE_LEN:
|
||||
par->slen = *cp++;
|
||||
par->ctr = 0;
|
||||
par->ps++;
|
||||
if (par->rem-- < par->slen)
|
||||
goto hangup;
|
||||
break;
|
||||
|
||||
case RPAR_RIDESHARE:
|
||||
par->rideshare[par->ctr++] = *cp++;
|
||||
if (!par->rem--)
|
||||
goto hangup;
|
||||
if (par->ctr != par->slen)
|
||||
break;
|
||||
par->ps = RPAR_PAYLOAD;
|
||||
if (par->rem)
|
||||
break;
|
||||
|
||||
/* fallthru - handle 0-length payload */
|
||||
|
||||
case RPAR_PAYLOAD:
|
||||
payload_ff:
|
||||
n = (int)len + 1;
|
||||
if (n > par->rem)
|
||||
n = par->rem;
|
||||
if (n > 1380)
|
||||
n = 1380;
|
||||
|
||||
/* deal with refragmented SOM / EOM flags */
|
||||
|
||||
flags = par->flags & LWSSS_FLAG_RELATED_START;
|
||||
if (par->frag1)
|
||||
flags |= par->flags &
|
||||
(LWSSS_FLAG_SOM | LWSSS_FLAG_POLL);
|
||||
|
||||
if (par->rem == n)
|
||||
flags |= par->flags & (LWSSS_FLAG_EOM |
|
||||
LWSSS_FLAG_RELATED_END);
|
||||
|
||||
par->frag1 = 0;
|
||||
us = lws_now_usecs();
|
||||
|
||||
if (!client) {
|
||||
/*
|
||||
* Proxy - we received some serialized tx from
|
||||
* the client.
|
||||
*
|
||||
* The header for buffering private to the
|
||||
* proxy is 23 bytes vs 19 to hold the
|
||||
* current time when it was buffered
|
||||
*/
|
||||
|
||||
lwsl_info("%s: C2P RX: len %d\n", __func__, (int)n);
|
||||
|
||||
p = pre;
|
||||
pre[0] = LWSSS_SER_TXPRE_TX_PAYLOAD;
|
||||
lws_ser_wu16be(&p[1], n + 23 - 3);
|
||||
lws_ser_wu32be(&p[3], par->flags);
|
||||
/* us held at client before written */
|
||||
lws_ser_wu32be(&p[7], par->usd_phandling);
|
||||
/* us taken for transit to proxy */
|
||||
lws_ser_wu32be(&p[11], us - par->ust_pwait);
|
||||
/* time used later to find proxy hold time */
|
||||
lws_ser_wu64be(&p[15], us);
|
||||
|
||||
if (lws_dsh_alloc_tail(dsh, KIND_C_TO_P, pre,
|
||||
23, cp, n)) {
|
||||
lwsl_err("%s: unable to alloc in dsh 3\n",
|
||||
__func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
lws_ss_request_tx(*pss);
|
||||
} else {
|
||||
|
||||
/*
|
||||
* Client receives some RX from proxy
|
||||
*
|
||||
* Pass whatever payload we have to ss user
|
||||
*/
|
||||
|
||||
lwsl_info("%s: P2C RX: len %d\n", __func__, (int)n);
|
||||
|
||||
h = lws_container_of(par, lws_sspc_handle_t, parser);
|
||||
h->txc.peer_tx_cr_est -= n;
|
||||
|
||||
ssi->rx((void *)pss, (uint8_t *)cp, n, flags);
|
||||
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
if (lws_det_lat_active(context)) {
|
||||
lws_detlat_t d;
|
||||
|
||||
d.type = LDLT_READ;
|
||||
d.acc_size = d.req_size = n;
|
||||
d.latencies[LAT_DUR_USERCB] =
|
||||
lws_now_usecs() - us;
|
||||
d.latencies[LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE] =
|
||||
par->usd_phandling;
|
||||
d.latencies[LAT_DUR_PROXY_CLIENT_WRITE_TO_PROXY_RX] =
|
||||
us - par->ust_pwait;
|
||||
|
||||
lws_det_lat_cb(context, &d);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (n) {
|
||||
cp += n;
|
||||
par->rem -= n;
|
||||
len = (len + 1) - n;
|
||||
}
|
||||
if (!par->rem)
|
||||
par->ps = RPAR_TYPE;
|
||||
break;
|
||||
|
||||
case RPAR_RX_TXCR_UPDATE:
|
||||
if (!--par->rem && par->ctr != 3)
|
||||
goto hangup;
|
||||
|
||||
par->temp32 = (par->temp32 << 8) | *cp++;
|
||||
if (++par->ctr < 4)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Proxy is telling us remote endpoint is allowing us
|
||||
* par->temp32 more bytes tx credit to write to it
|
||||
*/
|
||||
|
||||
h = lws_container_of(par, lws_sspc_handle_t, parser);
|
||||
h->txc.tx_cr += par->temp32;
|
||||
lwsl_info("%s: RX_PEER_TXCR: %d\n", __func__, par->temp32);
|
||||
lws_sspc_request_tx(h); /* in case something waiting */
|
||||
par->ctr = 0;
|
||||
par->ps = RPAR_TYPE;
|
||||
break;
|
||||
|
||||
case RPAR_INITTXC0:
|
||||
if (!--par->rem)
|
||||
goto hangup;
|
||||
|
||||
par->temp32 = (par->temp32 << 8) | *cp++;
|
||||
if (++par->ctr < 4)
|
||||
break;
|
||||
|
||||
par->txcr_out = par->temp32;
|
||||
par->ctr = 0;
|
||||
par->ps = RPAR_STREAMTYPE;
|
||||
break;
|
||||
|
||||
/*
|
||||
* These are the client adjusting our / the remote peer ability
|
||||
* to send back to him. He's sending a signed u32 BE
|
||||
*/
|
||||
|
||||
case RPAR_TXCR0:
|
||||
|
||||
par->temp32 = (par->temp32 << 8) | *cp++;
|
||||
if (++par->ctr < 4) {
|
||||
if (!--par->rem)
|
||||
goto hangup;
|
||||
break;
|
||||
}
|
||||
|
||||
if (--par->rem)
|
||||
goto hangup;
|
||||
|
||||
if (!client) {
|
||||
/*
|
||||
* We're the proxy, being told by the client
|
||||
* that it wants to allow more tx from the peer
|
||||
* on the onward connection towards it.
|
||||
*/
|
||||
#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT)
|
||||
if ((*pss)->wsi) {
|
||||
lws_wsi_tx_credit((*pss)->wsi,
|
||||
LWSTXCR_PEER_TO_US,
|
||||
par->temp32);
|
||||
lwsl_notice("%s: proxy RX_PEER_TXCR: +%d (est %d)\n",
|
||||
__func__, par->temp32,
|
||||
(*pss)->wsi->txc.peer_tx_cr_est);
|
||||
lws_ss_request_tx(*pss);
|
||||
} else
|
||||
#endif
|
||||
lwsl_info("%s: dropping TXCR\n", __func__);
|
||||
} else {
|
||||
/*
|
||||
* We're the client, being told by the proxy
|
||||
* about tx credit being given to us from the
|
||||
* remote peer, allowing the client to write to
|
||||
* it.
|
||||
*/
|
||||
h = lws_container_of(par, lws_sspc_handle_t, parser);
|
||||
h->txc.tx_cr += par->temp32;
|
||||
lwsl_info("%s: client RX_PEER_TXCR: %d\n",
|
||||
__func__, par->temp32);
|
||||
lws_sspc_request_tx(h); /* in case something waiting */
|
||||
}
|
||||
par->ps = RPAR_TYPE;
|
||||
break;
|
||||
|
||||
case RPAR_METADATA_NAMELEN:
|
||||
if (!--par->rem)
|
||||
goto hangup;
|
||||
par->slen = *cp++;
|
||||
if (par->slen >= sizeof(par->metadata_name) - 1)
|
||||
goto hangup;
|
||||
par->ctr = 0;
|
||||
par->ps++;
|
||||
break;
|
||||
|
||||
case RPAR_METADATA_NAME:
|
||||
if (!--par->rem)
|
||||
goto hangup;
|
||||
par->metadata_name[par->ctr++] = *cp++;
|
||||
if (par->ctr != par->slen)
|
||||
break;
|
||||
par->ps = RPAR_METADATA_VALUE;
|
||||
|
||||
/* only non-client side can receive these */
|
||||
|
||||
/*
|
||||
* This is the policy's metadata list for the given
|
||||
* name
|
||||
*/
|
||||
pm = lws_ss_policy_metadata((*pss)->policy,
|
||||
par->metadata_name);
|
||||
if (!pm) {
|
||||
lwsl_err("%s: metadata %s not in proxy policy\n",
|
||||
__func__, par->metadata_name);
|
||||
|
||||
goto hangup;
|
||||
}
|
||||
|
||||
par->ssmd = &(*pss)->metadata[pm->length];
|
||||
|
||||
if (par->ssmd->value_on_lws_heap)
|
||||
lws_free_set_NULL(par->ssmd->value);
|
||||
par->ssmd->value_on_lws_heap = 0;
|
||||
|
||||
par->ssmd->value = lws_malloc(par->rem + 1, "metadata");
|
||||
if (!par->ssmd->value) {
|
||||
lwsl_err("%s: OOM mdv\n", __func__);
|
||||
goto hangup;
|
||||
}
|
||||
par->ssmd->length = par->rem;
|
||||
/* mark it as needing cleanup */
|
||||
par->ssmd->value_on_lws_heap = 1;
|
||||
par->ctr = 0;
|
||||
break;
|
||||
|
||||
case RPAR_METADATA_VALUE:
|
||||
((uint8_t *)(par->ssmd->value))[par->ctr++] = *cp++;
|
||||
if (--par->rem)
|
||||
break;
|
||||
|
||||
/* we think we got all the value */
|
||||
lwsl_info("%s: RPAR_METADATA_VALUE for %s (len %d)\n",
|
||||
__func__, par->ssmd->name,
|
||||
(int)par->ssmd->length);
|
||||
lwsl_hexdump_info(par->ssmd->value, par->ssmd->length);
|
||||
par->ps = RPAR_TYPE;
|
||||
break;
|
||||
|
||||
case RPAR_STREAMTYPE:
|
||||
if (client)
|
||||
goto hangup;
|
||||
if (par->ctr == sizeof(par->streamtype) - 1)
|
||||
goto hangup;
|
||||
|
||||
/*
|
||||
* We're the proxy, creating an SS on behalf of a
|
||||
* client
|
||||
*/
|
||||
|
||||
par->streamtype[par->ctr++] = *cp++;
|
||||
if (--par->rem)
|
||||
break;
|
||||
|
||||
par->ps = RPAR_TYPE;
|
||||
par->streamtype[par->ctr] = '\0';
|
||||
lwsl_notice("%s: creating proxied ss '%s', txcr %d\n",
|
||||
__func__, par->streamtype, par->txcr_out);
|
||||
|
||||
ssi->streamtype = par->streamtype;
|
||||
if (par->txcr_out)
|
||||
ssi->manual_initial_tx_credit = par->txcr_out;
|
||||
|
||||
if (lws_ss_create(context, 0, ssi, parconn, pss, NULL, NULL)) {
|
||||
/*
|
||||
* We're unable to create the onward secure
|
||||
* stream he asked for... schedule a chance to
|
||||
* inform him
|
||||
*/
|
||||
lwsl_err("%s: create '%s' fail\n",
|
||||
__func__, par->streamtype);
|
||||
*state = LPCS_REPORTING_FAIL;
|
||||
} else {
|
||||
lwsl_debug("%s: create '%s' OK\n",
|
||||
__func__, par->streamtype);
|
||||
*state = LPCS_REPORTING_OK;
|
||||
}
|
||||
|
||||
if (*pss) {
|
||||
(*pss)->being_serialized = 1;
|
||||
lwsl_notice("%s: Created SS initial credit %d\n",
|
||||
__func__, par->txcr_out);
|
||||
(*pss)->info.manual_initial_tx_credit = par->txcr_out;
|
||||
}
|
||||
|
||||
/* parent needs to schedule write on client conn */
|
||||
break;
|
||||
|
||||
/* clientside states */
|
||||
|
||||
case RPAR_RESULT_CREATION:
|
||||
if (*cp++) {
|
||||
lwsl_err("%s: stream creation failed\n",
|
||||
__func__);
|
||||
goto hangup;
|
||||
}
|
||||
|
||||
lws_ss_serialize_state_transition(state,
|
||||
LPCS_LOCAL_CONNECTED);
|
||||
h = lws_container_of(par, lws_sspc_handle_t, parser);
|
||||
if (h->cwsi)
|
||||
lws_callback_on_writable(h->cwsi);
|
||||
|
||||
/*
|
||||
* This is telling us that the streamtype could be (and
|
||||
* was) created at the proxy. It's not telling us that
|
||||
* the onward peer connection could be connected.
|
||||
*
|
||||
* We'll get a proxied state() coming later that informs
|
||||
* us about the situation with that.
|
||||
*/
|
||||
|
||||
par->rsl_pos = 0;
|
||||
par->rsl_idx = 0;
|
||||
h = lws_container_of(par, lws_sspc_handle_t, parser);
|
||||
memset(&h->rideshare_ofs[0], 0, sizeof(h->rideshare_ofs[0]));
|
||||
h->rideshare_list[0] = '\0';
|
||||
h->rsidx = 0;
|
||||
|
||||
if (!--par->rem)
|
||||
par->ps = RPAR_TYPE;
|
||||
else {
|
||||
par->ps = RPAR_RESULT_CREATION_RIDESHARE;
|
||||
if (par->rem >= sizeof(h->rideshare_list))
|
||||
goto hangup;
|
||||
}
|
||||
break;
|
||||
|
||||
case RPAR_RESULT_CREATION_RIDESHARE:
|
||||
h = lws_container_of(par, lws_sspc_handle_t, parser);
|
||||
if (*cp == ',') {
|
||||
cp++;
|
||||
h->rideshare_list[par->rsl_pos++] = '\0';
|
||||
if (par->rsl_idx == LWS_ARRAY_SIZE(h->rideshare_ofs))
|
||||
goto hangup;
|
||||
h->rideshare_ofs[++par->rsl_idx] = par->rsl_pos;
|
||||
} else
|
||||
h->rideshare_list[par->rsl_pos++] = *cp++;
|
||||
if (!--par->rem)
|
||||
par->ps = RPAR_TYPE;
|
||||
break;
|
||||
|
||||
case RPAR_STATEINDEX:
|
||||
par->ctr = *cp++;
|
||||
par->ps = RPAR_ORD3;
|
||||
break;
|
||||
|
||||
case RPAR_ORD3:
|
||||
par->flags = (*cp++) << 24;
|
||||
par->ps++;
|
||||
break;
|
||||
|
||||
case RPAR_ORD2:
|
||||
par->flags |= (*cp++) << 16;
|
||||
par->ps++;
|
||||
break;
|
||||
|
||||
case RPAR_ORD1:
|
||||
par->flags |= (*cp++) << 8;
|
||||
par->ps++;
|
||||
break;
|
||||
|
||||
case RPAR_ORD0:
|
||||
par->flags |= *cp++;
|
||||
par->ps++;
|
||||
par->ps = RPAR_TYPE;
|
||||
|
||||
/*
|
||||
* we received a proxied state change
|
||||
*/
|
||||
|
||||
switch (par->ctr) {
|
||||
case LWSSSCS_DISCONNECTED:
|
||||
case LWSSSCS_UNREACHABLE:
|
||||
case LWSSSCS_AUTH_FAILED:
|
||||
lws_ss_serialize_state_transition(state,
|
||||
LPCS_LOCAL_CONNECTED);
|
||||
break;
|
||||
case LWSSSCS_CONNECTED:
|
||||
lwsl_info("%s: CONNECTED %s\n", __func__,
|
||||
ssi->streamtype);
|
||||
lws_ss_serialize_state_transition(state,
|
||||
LPCS_OPERATIONAL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (par->ctr < 0 || par->ctr > 9)
|
||||
goto hangup;
|
||||
|
||||
#if defined(_DEBUG)
|
||||
lwsl_info("%s: forwarding proxied state %s\n",
|
||||
__func__, sn[par->ctr]);
|
||||
#endif
|
||||
if (ssi->state((void *)pss, NULL, par->ctr, par->flags))
|
||||
goto hangup;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
goto hangup;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
hangup:
|
||||
return -1;
|
||||
}
|
569
lib/secure-streams/secure-streams.c
Normal file
569
lib/secure-streams/secure-streams.c
Normal file
|
@ -0,0 +1,569 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
static const struct ss_pcols *ss_pcols[] = {
|
||||
#if defined(LWS_ROLE_H1)
|
||||
&ss_pcol_h1, /* LWSSSP_H1 */
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#if defined(LWS_ROLE_H2)
|
||||
&ss_pcol_h2, /* LWSSSP_H2 */
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#if defined(LWS_ROLE_WS)
|
||||
&ss_pcol_ws, /* LWSSSP_WS */
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#if defined(LWS_ROLE_MQTT)
|
||||
&ss_pcol_mqtt, /* LWSSSP_MQTT */
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const char *state_names[] = {
|
||||
"LWSSSCS_CREATING",
|
||||
"LWSSSCS_DISCONNECTED",
|
||||
"LWSSSCS_UNREACHABLE",
|
||||
"LWSSSCS_AUTH_FAILED",
|
||||
"LWSSSCS_CONNECTED",
|
||||
"LWSSSCS_CONNECTING",
|
||||
"LWSSSCS_DESTROYING",
|
||||
"LWSSSCS_POLL",
|
||||
"LWSSSCS_ALL_RETRIES_FAILED",
|
||||
"LWSSSCS_QOS_ACK_REMOTE",
|
||||
"LWSSSCS_QOS_NACK_REMOTE",
|
||||
"LWSSSCS_QOS_ACK_LOCAL",
|
||||
"LWSSSCS_QOS_NACK_LOCAL",
|
||||
};
|
||||
|
||||
const char *
|
||||
lws_ss_state_name(int state)
|
||||
{
|
||||
if (state >= (int)LWS_ARRAY_SIZE(state_names))
|
||||
return "unknown";
|
||||
|
||||
return state_names[state];
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_event_helper(lws_ss_handle_t *h, lws_ss_constate_t cs)
|
||||
{
|
||||
if (!h)
|
||||
return 0;
|
||||
|
||||
#if defined(LWS_WITH_SEQUENCER)
|
||||
/*
|
||||
* A parent sequencer for the ss is optional, if we have one, keep it
|
||||
* informed of state changes on the ss connection
|
||||
*/
|
||||
if (h->seq && cs != LWSSSCS_DESTROYING)
|
||||
lws_seq_queue_event(h->seq, LWSSEQ_SS_STATE_BASE + cs,
|
||||
(void *)h, NULL);
|
||||
#endif
|
||||
|
||||
if (h->h_sink &&h->h_sink->info.state(h->sink_obj, h->h_sink, cs, 0))
|
||||
return 1;
|
||||
|
||||
return h->info.state(ss_to_userobj(h), NULL, cs, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_ss_timeout_sul_check_cb(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
lws_ss_handle_t *h = lws_container_of(sul, lws_ss_handle_t, sul);
|
||||
|
||||
lwsl_err("%s: retrying ss h %p after backoff\n", __func__, h);
|
||||
/* we want to retry... */
|
||||
h->seqstate = SSSEQ_DO_RETRY;
|
||||
|
||||
lws_ss_request_tx(h);
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_exp_cb_metadata(void *priv, const char *name, char *out, size_t *pos,
|
||||
size_t olen, size_t *exp_ofs)
|
||||
{
|
||||
lws_ss_handle_t *h = (lws_ss_handle_t *)priv;
|
||||
const char *replace = NULL;
|
||||
size_t total, budget;
|
||||
lws_ss_metadata_t *md = lws_ss_policy_metadata(h->policy, name);
|
||||
|
||||
if (!md) {
|
||||
lwsl_err("%s: Unknown metadata %s\n", __func__, name);
|
||||
|
||||
return LSTRX_FATAL_NAME_UNKNOWN;
|
||||
}
|
||||
|
||||
lwsl_info("%s %s %d\n", __func__, name, (int)md->length);
|
||||
|
||||
replace = h->metadata[md->length].value;
|
||||
total = h->metadata[md->length].length;
|
||||
// lwsl_hexdump_err(replace, total);
|
||||
|
||||
budget = olen - *pos;
|
||||
total -= *exp_ofs;
|
||||
if (total < budget)
|
||||
budget = total;
|
||||
|
||||
memcpy(out + *pos, replace + (*exp_ofs), budget);
|
||||
*exp_ofs += budget;
|
||||
*pos += budget;
|
||||
|
||||
if (budget == total)
|
||||
return LSTRX_DONE;
|
||||
|
||||
return LSTRX_FILLED_OUT;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_set_timeout_us(lws_ss_handle_t *h, lws_usec_t us)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &h->context->pt[h->tsi];
|
||||
|
||||
h->sul.cb = lws_ss_timeout_sul_check_cb;
|
||||
__lws_sul_insert(&pt->pt_sul_owner, &h->sul, us);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_backoff(lws_ss_handle_t *h)
|
||||
{
|
||||
uint64_t ms;
|
||||
char conceal;
|
||||
|
||||
if (h->seqstate == SSSEQ_RECONNECT_WAIT)
|
||||
return 0;
|
||||
|
||||
/* figure out what we should do about another retry */
|
||||
|
||||
lwsl_info("%s: ss %p: retry backoff after failure\n", __func__, h);
|
||||
ms = lws_retry_get_delay_ms(h->context, h->policy->retry_bo,
|
||||
&h->retry, &conceal);
|
||||
if (!conceal) {
|
||||
lwsl_info("%s: ss %p: abandon conn attempt \n",__func__, h);
|
||||
h->seqstate = SSSEQ_IDLE;
|
||||
lws_ss_event_helper(h, LWSSSCS_ALL_RETRIES_FAILED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
h->seqstate = SSSEQ_RECONNECT_WAIT;
|
||||
lws_ss_set_timeout_us(h, ms * LWS_US_PER_MS);
|
||||
|
||||
lwsl_info("%s: ss %p: retry wait %"PRIu64"ms\n", __func__, h, ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_client_connect(lws_ss_handle_t *h)
|
||||
{
|
||||
struct lws_client_connect_info i;
|
||||
const struct ss_pcols *ssp;
|
||||
union lws_ss_contemp ct;
|
||||
char path[128];
|
||||
|
||||
if (!h->policy) {
|
||||
lwsl_err("%s: ss with no policy\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are already bound to a sink?
|
||||
*/
|
||||
|
||||
if (h->h_sink)
|
||||
return 0;
|
||||
|
||||
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
|
||||
i.context = h->context;
|
||||
|
||||
if (h->policy->flags & LWSSSPOLF_TLS) {
|
||||
lwsl_info("%s: using tls\n", __func__);
|
||||
i.ssl_connection = LCCSCF_USE_SSL;
|
||||
|
||||
if (!h->policy->trust_store) {
|
||||
lwsl_err("%s: tls required but no policy trust store\n",
|
||||
__func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
i.vhost = lws_get_vhost_by_name(h->context,
|
||||
h->policy->trust_store->name);
|
||||
if (!i.vhost) {
|
||||
lwsl_err("%s: missing vh for policy ca\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
i.address = h->policy->endpoint;
|
||||
i.port = h->policy->port;
|
||||
i.host = i.address;
|
||||
i.origin = i.address;
|
||||
i.opaque_user_data = h;
|
||||
i.seq = h->seq;
|
||||
i.retry_and_idle_policy = h->policy->retry_bo;
|
||||
i.sys_tls_client_cert = h->policy->client_cert;
|
||||
|
||||
i.path = "";
|
||||
|
||||
ssp = ss_pcols[(int)h->policy->protocol];
|
||||
if (!ssp) {
|
||||
lwsl_err("%s: unsupported protocol\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
i.alpn = ssp->alpn;
|
||||
|
||||
/*
|
||||
* For http, we can get the method from the http object, override in
|
||||
* the protocol-specific munge callback below if not http
|
||||
*/
|
||||
i.method = h->policy->u.http.method;
|
||||
i.protocol = ssp->protocol_name; /* lws protocol name */
|
||||
i.local_protocol_name = i.protocol;
|
||||
|
||||
ssp->munge(h, path, sizeof(path), &i, &ct);
|
||||
|
||||
i.pwsi = &h->wsi;
|
||||
|
||||
if (h->policy->plugins[0] && h->policy->plugins[0]->munge)
|
||||
h->policy->plugins[0]->munge(h, path, sizeof(path));
|
||||
|
||||
lwsl_info("%s: connecting %s, '%s' '%s' %s\n", __func__, i.method,
|
||||
i.alpn, i.address, i.path);
|
||||
|
||||
h->txn_ok = 0;
|
||||
if (lws_ss_event_helper(h, LWSSSCS_CONNECTING))
|
||||
return -1;
|
||||
|
||||
if (!lws_client_connect_via_info(&i)) {
|
||||
lws_ss_event_helper(h, LWSSSCS_UNREACHABLE);
|
||||
lws_ss_backoff(h);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Public API
|
||||
*/
|
||||
|
||||
/*
|
||||
* Create either a stream or a sink
|
||||
*/
|
||||
|
||||
int
|
||||
lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
|
||||
void *opaque_user_data, lws_ss_handle_t **ppss,
|
||||
struct lws_sequencer *seq_owner, const char **ppayload_fmt)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
const lws_ss_policy_t *pol;
|
||||
lws_ss_metadata_t *smd;
|
||||
lws_ss_handle_t *h;
|
||||
size_t size;
|
||||
void **v;
|
||||
char *p;
|
||||
int n;
|
||||
|
||||
pol = lws_ss_policy_lookup(context, ssi->streamtype);
|
||||
if (!pol) {
|
||||
lwsl_err("%s: unknown stream type %s\n", __func__,
|
||||
ssi->streamtype);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ssi->register_sink) {
|
||||
/*
|
||||
* This can register a secure streams sink as well as normal
|
||||
* secure streams connections. If that's what's happening,
|
||||
* confirm the policy agrees that this streamtype should be
|
||||
* directed to a sink.
|
||||
*/
|
||||
if (!(pol->flags & LWSSSPOLF_LOCAL_SINK)) {
|
||||
/*
|
||||
* Caller wanted to create a sink for this streamtype,
|
||||
* but the policy does not agree the streamtype should
|
||||
* be routed to a local sink.
|
||||
*/
|
||||
lwsl_err("%s: %s policy does not allow local sink\n",
|
||||
__func__, ssi->streamtype);
|
||||
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
|
||||
if (!(pol->flags & LWSSSPOLF_LOCAL_SINK)) {
|
||||
|
||||
}
|
||||
// lws_dll2_foreach_safe(&pt->ss_owner, NULL, lws_ss_destroy_dll);
|
||||
}
|
||||
|
||||
/*
|
||||
* We overallocate and point to things in the overallocation...
|
||||
*
|
||||
* 1) the user_alloc from the stream info
|
||||
* 2) network auth plugin instantiation data
|
||||
* 3) stream auth plugin instantiation data
|
||||
* 4) as many metadata pointer structs as the policy tells
|
||||
* 5) the streamtype name (length is not aligned)
|
||||
*
|
||||
* ... when we come to destroy it, just one free to do.
|
||||
*/
|
||||
|
||||
size = sizeof(*h) + ssi->user_alloc + strlen(ssi->streamtype) + 1;
|
||||
if (pol->plugins[0])
|
||||
size += pol->plugins[0]->alloc;
|
||||
if (pol->plugins[1])
|
||||
size += pol->plugins[1]->alloc;
|
||||
size += pol->metadata_count * sizeof(lws_ss_metadata_t);
|
||||
|
||||
h = lws_zalloc(size, __func__);
|
||||
if (!h)
|
||||
return 2;
|
||||
|
||||
h->info = *ssi;
|
||||
h->policy = pol;
|
||||
h->context = context;
|
||||
h->tsi = tsi;
|
||||
h->seq = seq_owner;
|
||||
|
||||
/* start of overallocated area */
|
||||
p = (char *)&h[1];
|
||||
|
||||
/* set the handle pointer in the user data struct */
|
||||
v = (void **)(p + ssi->handle_offset);
|
||||
*v = h;
|
||||
|
||||
/* set the opaque user data in the user data struct */
|
||||
v = (void **)(p + ssi->opaque_user_data_offset);
|
||||
*v = opaque_user_data;
|
||||
|
||||
p += ssi->user_alloc;
|
||||
|
||||
if (pol->plugins[0]) {
|
||||
h->nauthi = p;
|
||||
p += pol->plugins[0]->alloc;
|
||||
}
|
||||
if (pol->plugins[1]) {
|
||||
h->sauthi = p;
|
||||
p += pol->plugins[1]->alloc;
|
||||
}
|
||||
|
||||
if (pol->metadata_count) {
|
||||
h->metadata = (lws_ss_metadata_t *)p;
|
||||
p += pol->metadata_count * sizeof(lws_ss_metadata_t);
|
||||
|
||||
lwsl_info("%s: %s metadata count %d\n", __func__,
|
||||
pol->streamtype, pol->metadata_count);
|
||||
}
|
||||
|
||||
smd = pol->metadata;
|
||||
for (n = 0; n < pol->metadata_count; n++) {
|
||||
h->metadata[n].name = smd->name;
|
||||
if (n + 1 == pol->metadata_count)
|
||||
h->metadata[n].next = NULL;
|
||||
else
|
||||
h->metadata[n].next = &h->metadata[n + 1];
|
||||
smd = smd->next;
|
||||
}
|
||||
|
||||
memcpy(p, ssi->streamtype, strlen(ssi->streamtype) + 1);
|
||||
h->info.streamtype = p;
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
lws_dll2_add_head(&h->list, &pt->ss_owner);
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
if (ppss)
|
||||
*ppss = h;
|
||||
|
||||
if (ppayload_fmt)
|
||||
*ppayload_fmt = pol->payload_fmt;
|
||||
|
||||
if (ssi->register_sink) {
|
||||
/*
|
||||
*
|
||||
*/
|
||||
}
|
||||
|
||||
lws_ss_event_helper(h, LWSSSCS_CREATING);
|
||||
|
||||
if (!ssi->register_sink && (h->policy->flags & LWSSSPOLF_NAILED_UP))
|
||||
if (lws_ss_client_connect(h))
|
||||
lws_ss_backoff(h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_ss_destroy(lws_ss_handle_t **ppss)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
lws_ss_handle_t *h = *ppss;
|
||||
lws_ss_metadata_t *pmd;
|
||||
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
if (h->wsi) {
|
||||
/*
|
||||
* Don't let the wsi point to us any more,
|
||||
* we (the ss object bound to the wsi) are going away now
|
||||
*/
|
||||
// lws_set_opaque_user_data(h->wsi, NULL);
|
||||
lws_set_timeout(h->wsi, 1, LWS_TO_KILL_SYNC);
|
||||
}
|
||||
|
||||
pt = &h->context->pt[h->tsi];
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
*ppss = NULL;
|
||||
lws_dll2_remove(&h->list);
|
||||
lws_dll2_remove(&h->to_list);
|
||||
lws_ss_event_helper(h, LWSSSCS_DESTROYING);
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
/* in proxy case, metadata value on heap may need cleaning up */
|
||||
|
||||
pmd = h->metadata;
|
||||
while (pmd) {
|
||||
lwsl_info("%s: pmd %p\n", __func__, pmd);
|
||||
if (pmd->value_on_lws_heap)
|
||||
lws_free_set_NULL(pmd->value);
|
||||
pmd = pmd->next;
|
||||
}
|
||||
|
||||
lws_sul_schedule(h->context, 0, &h->sul, NULL, LWS_SET_TIMER_USEC_CANCEL);
|
||||
|
||||
lws_free_set_NULL(h);
|
||||
}
|
||||
|
||||
void
|
||||
lws_ss_request_tx(lws_ss_handle_t *h)
|
||||
{
|
||||
lwsl_info("%s: wsi %p\n", __func__, h->wsi);
|
||||
|
||||
if (h->wsi) {
|
||||
lws_callback_on_writable(h->wsi);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (h->seqstate != SSSEQ_IDLE &&
|
||||
h->seqstate != SSSEQ_DO_RETRY)
|
||||
return;
|
||||
|
||||
h->seqstate = SSSEQ_TRY_CONNECT;
|
||||
lws_ss_event_helper(h, LWSSSCS_POLL);
|
||||
|
||||
if (lws_ss_client_connect(h))
|
||||
lws_ss_backoff(h);
|
||||
}
|
||||
|
||||
void
|
||||
lws_ss_request_tx_len(lws_ss_handle_t *h, unsigned long len)
|
||||
{
|
||||
if (h->wsi)
|
||||
h->wsi->http.writeable_len = len;
|
||||
else
|
||||
h->writeable_len = len;
|
||||
lws_ss_request_tx(h);
|
||||
}
|
||||
|
||||
/*
|
||||
* private helpers
|
||||
*/
|
||||
|
||||
/* used on context destroy when iterating listed lws_ss on a pt */
|
||||
|
||||
int
|
||||
lws_ss_destroy_dll(struct lws_dll2 *d, void *user)
|
||||
{
|
||||
lws_ss_handle_t *h = lws_container_of(d, lws_ss_handle_t, list);
|
||||
|
||||
lws_ss_destroy(&h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws_sequencer *
|
||||
lws_ss_get_sequencer(lws_ss_handle_t *h)
|
||||
{
|
||||
return h->seq;
|
||||
}
|
||||
|
||||
struct lws_context *
|
||||
lws_ss_get_context(struct lws_ss_handle *h)
|
||||
{
|
||||
return h->context;
|
||||
}
|
||||
|
||||
const char *
|
||||
lws_ss_rideshare(struct lws_ss_handle *h)
|
||||
{
|
||||
if (!h->rideshare)
|
||||
return h->policy->streamtype;
|
||||
|
||||
return h->rideshare->streamtype;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_add_peer_tx_credit(struct lws_ss_handle *h, int32_t bump)
|
||||
{
|
||||
const struct ss_pcols *ssp;
|
||||
|
||||
ssp = ss_pcols[(int)h->policy->protocol];
|
||||
|
||||
if (h->wsi && ssp && ssp->tx_cr_add)
|
||||
return ssp->tx_cr_add(h, bump);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_get_est_peer_tx_credit(struct lws_ss_handle *h)
|
||||
{
|
||||
const struct ss_pcols *ssp;
|
||||
|
||||
ssp = ss_pcols[(int)h->policy->protocol];
|
||||
|
||||
if (h->wsi && ssp && ssp->tx_cr_add)
|
||||
return ssp->tx_cr_est(h);
|
||||
|
||||
return 0;
|
||||
}
|
276
lib/secure-streams/system/auth-api.amazon.com/auth.c
Normal file
276
lib/secure-streams/system/auth-api.amazon.com/auth.c
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* LWA auth support for Secure Streams
|
||||
*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
typedef struct ss_api_amazon_auth {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
struct lejp_ctx jctx;
|
||||
lws_sorted_usec_list_t sul;
|
||||
size_t pos;
|
||||
int expires_secs;
|
||||
} ss_api_amazon_auth_t;
|
||||
|
||||
static const char * const lejp_tokens_lwa[] = {
|
||||
"access_token",
|
||||
"expires_in",
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LSSPPT_ACCESS_TOKEN,
|
||||
LSSPPT_EXPIRES_IN,
|
||||
} lejp_tokens_t;
|
||||
|
||||
enum {
|
||||
AUTH_IDX_LWA,
|
||||
AUTH_IDX_ROOT,
|
||||
};
|
||||
|
||||
static void
|
||||
lws_ss_sys_auth_api_amazon_com_kick(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
struct lws_context *context = lws_container_of(sul, struct lws_context,
|
||||
sul_api_amazon_com_kick);
|
||||
|
||||
lws_state_transition_steps(&context->mgr_system,
|
||||
LWS_SYSTATE_OPERATIONAL);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_ss_sys_auth_api_amazon_com_renew(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
struct lws_context *context = lws_container_of(sul, struct lws_context,
|
||||
sul_api_amazon_com);
|
||||
|
||||
/* if nothing is there to intercept anything, go all the way */
|
||||
lws_state_transition_steps(&context->mgr_system,
|
||||
LWS_SYSTATE_OPERATIONAL);
|
||||
}
|
||||
|
||||
static signed char
|
||||
auth_api_amazon_com_parser_cb(struct lejp_ctx *ctx, char reason)
|
||||
{
|
||||
ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)ctx->user;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
|
||||
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
|
||||
return 0;
|
||||
|
||||
switch (ctx->path_match - 1) {
|
||||
case LSSPPT_ACCESS_TOKEN:
|
||||
if (!ctx->npos)
|
||||
break;
|
||||
if (lws_system_blob_heap_append(lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH,
|
||||
AUTH_IDX_LWA),
|
||||
(const uint8_t *)ctx->buf,
|
||||
ctx->npos)) {
|
||||
lwsl_err("%s: unable to store auth token\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case LSSPPT_EXPIRES_IN:
|
||||
m->expires_secs = atoi(ctx->buf);
|
||||
lws_sul_schedule(context, 0, &context->sul_api_amazon_com,
|
||||
lws_ss_sys_auth_api_amazon_com_renew,
|
||||
(uint64_t)m->expires_secs * LWS_US_PER_SEC);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* secure streams payload interface */
|
||||
|
||||
static int
|
||||
ss_api_amazon_auth_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
lws_system_blob_t *ab;
|
||||
size_t total;
|
||||
int n;
|
||||
|
||||
ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_LWA);
|
||||
|
||||
if (buf) {
|
||||
if (flags & LWSSS_FLAG_SOM) {
|
||||
lejp_construct(&m->jctx, auth_api_amazon_com_parser_cb,
|
||||
m, lejp_tokens_lwa,
|
||||
LWS_ARRAY_SIZE(lejp_tokens_lwa));
|
||||
lws_system_blob_heap_empty(ab);
|
||||
}
|
||||
|
||||
n = (int)(signed char)lejp_parse(&m->jctx, buf, len);
|
||||
if (n < 0) {
|
||||
lejp_destruct(&m->jctx);
|
||||
lws_system_blob_destroy(
|
||||
lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH,
|
||||
AUTH_IDX_LWA));
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (!(flags & LWSSS_FLAG_EOM))
|
||||
return 0;
|
||||
|
||||
/* we should have the auth token now */
|
||||
|
||||
total = lws_system_blob_get_size(ab);
|
||||
lwsl_notice("%s: acquired %u-byte api.amazon.com auth token, exp %ds\n",
|
||||
__func__, (unsigned int)total, m->expires_secs);
|
||||
|
||||
lejp_destruct(&m->jctx);
|
||||
|
||||
/* we move the system state at auth connection close */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_api_amazon_auth_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags)
|
||||
{
|
||||
ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
lws_system_blob_t *ab;
|
||||
size_t total;
|
||||
int n;
|
||||
|
||||
/*
|
||||
* We send out auth slot AUTH_IDX_ROOT, it's the LWA user / device
|
||||
* identity token
|
||||
*/
|
||||
|
||||
ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_ROOT);
|
||||
total = lws_system_blob_get_size(ab);
|
||||
|
||||
n = lws_system_blob_get(ab, buf, len, m->pos);
|
||||
if (n < 0)
|
||||
return 1;
|
||||
|
||||
if (!m->pos)
|
||||
*flags |= LWSSS_FLAG_SOM;
|
||||
|
||||
m->pos += *len;
|
||||
|
||||
if (m->pos == total) {
|
||||
*flags |= LWSSS_FLAG_EOM;
|
||||
m->pos = 0; /* for next time */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_api_amazon_auth_state(void *userobj, void *sh, lws_ss_constate_t state,
|
||||
lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
size_t s;
|
||||
|
||||
lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
|
||||
(unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
case LWSSSCS_CONNECTING:
|
||||
s = lws_system_blob_get_size(
|
||||
lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH,
|
||||
AUTH_IDX_ROOT));
|
||||
lws_ss_request_tx_len(m->ss, s);
|
||||
m->pos = 0;
|
||||
break;
|
||||
|
||||
case LWSSSCS_DISCONNECTED:
|
||||
/*
|
||||
* We defer moving the system state forward until we have
|
||||
* closed our connection + tls for the auth action... this is
|
||||
* because on small systems, we need that memory recovered
|
||||
* before we can make another connection subsequently.
|
||||
*
|
||||
* At this point, we're ultimately being called from within
|
||||
* the wsi close process, the tls tunnel is not freed yet.
|
||||
* Use a sul to actually do it next time around the event loop
|
||||
* when the close process for the auth wsi has completed and
|
||||
* the related tls is already freed.
|
||||
*/
|
||||
s = lws_system_blob_get_size(
|
||||
lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH,
|
||||
AUTH_IDX_LWA));
|
||||
|
||||
if (s)
|
||||
lws_sul_schedule(context, 0,
|
||||
&context->sul_api_amazon_com_kick,
|
||||
lws_ss_sys_auth_api_amazon_com_kick, 1);
|
||||
break;
|
||||
|
||||
case LWSSSCS_DESTROYING:
|
||||
lws_sul_schedule(context, 0, &context->sul_api_amazon_com,
|
||||
NULL, LWS_SET_TIMER_USEC_CANCEL);
|
||||
lws_system_blob_destroy(
|
||||
lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH,
|
||||
AUTH_IDX_LWA));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_sys_auth_api_amazon_com(struct lws_context *context)
|
||||
{
|
||||
lws_ss_info_t ssi;
|
||||
|
||||
if (context->hss_auth) /* already exists */
|
||||
return 0;
|
||||
|
||||
/* We're making an outgoing secure stream ourselves */
|
||||
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.handle_offset = offsetof(ss_api_amazon_auth_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(ss_api_amazon_auth_t, opaque_data);
|
||||
ssi.rx = ss_api_amazon_auth_rx;
|
||||
ssi.tx = ss_api_amazon_auth_tx;
|
||||
ssi.state = ss_api_amazon_auth_state;
|
||||
ssi.user_alloc = sizeof(ss_api_amazon_auth_t);
|
||||
ssi.streamtype = "api_amazon_com_auth";
|
||||
|
||||
if (lws_ss_create(context, 0, &ssi, context, &context->hss_auth,
|
||||
NULL, NULL)) {
|
||||
lwsl_info("%s: Create LWA auth ss failed (policy?)\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
159
lib/secure-streams/system/fetch-policy/fetch-policy.c
Normal file
159
lib/secure-streams/system/fetch-policy/fetch-policy.c
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Policy fetching for Secure Streams
|
||||
*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
typedef struct ss_fetch_policy {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
|
||||
lws_sorted_usec_list_t sul;
|
||||
|
||||
uint8_t partway;
|
||||
} ss_fetch_policy_t;
|
||||
|
||||
/* secure streams payload interface */
|
||||
|
||||
static int
|
||||
ss_fetch_policy_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
ss_fetch_policy_t *m = (ss_fetch_policy_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
|
||||
if (flags & LWSSS_FLAG_SOM) {
|
||||
if (lws_ss_policy_parse_begin(context))
|
||||
return 1;
|
||||
m->partway = 1;
|
||||
}
|
||||
|
||||
if (len && lws_ss_policy_parse(context, buf, len) < 0)
|
||||
return 1;
|
||||
|
||||
if (flags & LWSSS_FLAG_EOM)
|
||||
m->partway = 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_fetch_policy_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
policy_set(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
ss_fetch_policy_t *m = lws_container_of(sul, ss_fetch_policy_t, sul);
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
|
||||
/*
|
||||
* We get called if the policy parse was successful, just after the
|
||||
* ss connection close that was using the vhost from the old policy
|
||||
*/
|
||||
|
||||
if (lws_ss_policy_set(context, "updated"))
|
||||
lwsl_err("%s: policy set failed\n", __func__);
|
||||
else {
|
||||
context->policy_updated = 1;
|
||||
lws_state_transition_steps(&context->mgr_system,
|
||||
LWS_SYSTATE_OPERATIONAL);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ss_fetch_policy_state(void *userobj, void *sh, lws_ss_constate_t state,
|
||||
lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
ss_fetch_policy_t *m = (ss_fetch_policy_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
|
||||
lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
|
||||
(unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
lws_ss_request_tx(m->ss);
|
||||
break;
|
||||
case LWSSSCS_CONNECTING:
|
||||
break;
|
||||
|
||||
case LWSSSCS_DISCONNECTED:
|
||||
lwsl_info("%s: DISCONNECTED\n", __func__);
|
||||
switch (m->partway) {
|
||||
case 1:
|
||||
lws_ss_policy_parse_abandon(context);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
lws_sul_schedule(context, 0, &m->sul, policy_set, 1);
|
||||
break;
|
||||
}
|
||||
m->partway = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_sys_fetch_policy(struct lws_context *context)
|
||||
{
|
||||
lws_ss_info_t ssi;
|
||||
|
||||
if (context->hss_fetch_policy) /* already exists */
|
||||
return 0;
|
||||
|
||||
/* We're making an outgoing secure stream ourselves */
|
||||
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.handle_offset = offsetof(ss_fetch_policy_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(ss_fetch_policy_t, opaque_data);
|
||||
ssi.rx = ss_fetch_policy_rx;
|
||||
ssi.tx = ss_fetch_policy_tx;
|
||||
ssi.state = ss_fetch_policy_state;
|
||||
ssi.user_alloc = sizeof(ss_fetch_policy_t);
|
||||
ssi.streamtype = "fetch_policy";
|
||||
|
||||
if (lws_ss_create(context, 0, &ssi, context, &context->hss_fetch_policy,
|
||||
NULL, NULL)) {
|
||||
/*
|
||||
* If there's no fetch_policy streamtype, it can just be we're
|
||||
* running on a proxied client with no policy of its own,
|
||||
* it's OK.
|
||||
*/
|
||||
lwsl_info("%s: Create LWA auth ss failed (policy?)\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -340,14 +340,17 @@ do_cb:
|
|||
|
||||
if (p + 14 > e)
|
||||
return -1;
|
||||
|
||||
/* it should have exactly reached rrpaylen */
|
||||
#if 0
|
||||
/* it should have exactly reached rrpaylen if only one
|
||||
* CNAME, else somewhere in the middle */
|
||||
|
||||
if (p != pay + rrpaylen) {
|
||||
lwsl_err("%s: cname name bad len %d\n", __func__, rrpaylen);
|
||||
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
lwsl_notice("%s: recursing looking for %s\n", __func__, stack[stp].name);
|
||||
|
||||
lwsl_info("%s: recursing looking for %s\n", __func__,
|
||||
stack[stp].name);
|
||||
|
|
|
@ -134,7 +134,7 @@ callback_ntpc(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
if (v)
|
||||
break;
|
||||
|
||||
lwsl_debug("%s: LWS_CALLBACK_PROTOCOL_INIT\n", __func__);
|
||||
lwsl_debug("%s: LWS_CALLBACK_PROTOCOL_INIT:\n", __func__);
|
||||
lws_protocol_vh_priv_zalloc(wsi->vhost, wsi->protocol,
|
||||
sizeof(*v));
|
||||
v = (struct vhd_ntpc *)lws_protocol_vh_priv_get(wsi->vhost,
|
||||
|
|
|
@ -56,6 +56,8 @@ lws_system_blob_heap_append(lws_system_blob_t *b, const uint8_t *buf, size_t len
|
|||
{
|
||||
assert(!b->is_direct);
|
||||
|
||||
lwsl_debug("%s: blob %p\n", __func__, b);
|
||||
|
||||
if (lws_buflist_append_segment(&b->u.bl, buf, len) < 0)
|
||||
return -1;
|
||||
|
||||
|
@ -126,6 +128,7 @@ lws_system_blob_destroy(lws_system_blob_t *b)
|
|||
{
|
||||
if (!b)
|
||||
return;
|
||||
lwsl_info("%s: blob %p\n", __func__, b);
|
||||
if (!b->is_direct)
|
||||
lws_buflist_destroy_all_segments(&b->u.bl);
|
||||
}
|
||||
|
|
|
@ -60,6 +60,8 @@ int lws_ssl_get_error(struct lws *wsi, int n)
|
|||
lwsl_debug("%s: %p %d -> %d (errno %d)\n", __func__, wsi->tls.ssl, n, m,
|
||||
errno);
|
||||
|
||||
assert (errno != 9);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ dbus-server|Minimal examples showing how to integrate DBUS into lws event loop
|
|||
http-client|Minimal examples providing an http client
|
||||
http-server|Minimal examples providing an http server
|
||||
raw|Minimal examples related to adopting raw file or socket descriptors into the event loop
|
||||
secure-streams|Minimal examples related to the Secure Streams client api
|
||||
ws-client|Minimal examples providing a ws client
|
||||
ws-server|Minimal examples providing a ws server (and an http server)
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ drain_cb(lws_sorted_usec_list_t *sul)
|
|||
{
|
||||
struct pss *pss = lws_container_of(sul, struct pss, sul);
|
||||
|
||||
lws_h2_update_peer_txcredit(pss->wsi, LWS_H2_STREAM_SID, each);
|
||||
lws_wsi_tx_credit(pss->wsi, LWSTXCR_PEER_TO_US, each);
|
||||
|
||||
lws_sul_schedule(lws_get_context(pss->wsi), 0, &pss->sul, drain_cb,
|
||||
250 * LWS_US_PER_MS);
|
||||
|
|
14
minimal-examples/secure-streams/README.md
Normal file
14
minimal-examples/secure-streams/README.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Secure Streams
|
||||
|
||||
Secure Streams is a client API that strictly decouples the policy for connections
|
||||
from the payloads. The user code only deals with the stream type name and payloads,
|
||||
a policy database set at `lws_context` creation time decides all policy about the
|
||||
connection, including the endpoint, tls CA, and even the wire protocol.
|
||||
|
||||
|name|demonstrates|
|
||||
---|---
|
||||
minimal-secure-streams|Minimal secure streams client / proxy example
|
||||
minimal-secure-streams-tx|Proxy used for client-tx test below
|
||||
minimal-secure-streams-client-tx|Secure streams client showing tx and rx
|
||||
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-minimal-secure-streams-alexa)
|
||||
set(SRCS main.c alexa.c audio.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_ROLE_H1 1 requirements)
|
||||
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
|
||||
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
|
||||
require_lws_config(LWS_WITH_ALSA 1 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared asound pv_porcupine mpg123)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets asound pv_porcupine mpg123)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
add_compile_options(-DLWS_SS_USE_SSPC)
|
||||
|
||||
add_executable(${SAMP}-client ${SRCS})
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP}-client websockets_shared asound pv_porcupine mpg123)
|
||||
add_dependencies(${SAMP}-client websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP}-client websockets asound pv_porcupine mpg123)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endif()
|
|
@ -0,0 +1,77 @@
|
|||
# lws secure streams alexa
|
||||
|
||||
This demonstrates AVS Alexa usage using secure streams. It connects to AVS,
|
||||
uses your linux computer's microphone to wait for the 'alexa' wakeword, sends
|
||||
the utterance to AVS and plays back the result.
|
||||
|
||||
## build
|
||||
|
||||
There are some special build considerations:
|
||||
|
||||
1) Build lws with cmake options `-DLWS_WITH_ALSA=1 -DLWS_WITH_SECURE_STREAMS=1`
|
||||
|
||||
2) Install distro build dependency packages:
|
||||
|
||||
|Dependency|Ubuntu package|Fedora Package|
|
||||
|---|---|---|
|
||||
|libasound|libasound2-dev|alsa-lib-devel|
|
||||
|mpg123|libmpg123-dev|mpg123-devel|
|
||||
|
||||
3) Clone Picovoice Porcupine Apache-licensed demo version from here
|
||||
|
||||
https://github.com/Picovoice/porcupine
|
||||
|
||||
It provides binary libs for wakeword detection on various platforms. Copy
|
||||
the headers and binary lib to your build context, eg, for native x86_64
|
||||
|
||||
```
|
||||
$ sudo cp ./include/* /usr/include
|
||||
$ sudo cp ./lib/linux/x86_64/libpv_porcupine.* /usr/lib
|
||||
$ sudo ldconfig
|
||||
```
|
||||
|
||||
Enter the minimal example dir for secure-streams-alexa and make the sample
|
||||
|
||||
```
|
||||
$ cd ./minimal-examples/secure-streams/minimal-secure-streams-alexa
|
||||
$ cmake .
|
||||
$ make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
```
|
||||
$ ./lws-minimal-secure-streams-alexa
|
||||
[2019/10/16 16:22:01:1097] U: LWS secure streams - Alex voice test [-d<verb>]
|
||||
[2019/10/16 16:22:01:1115] N: lws_create_context: creating Secure Streams policy
|
||||
[2019/10/16 16:22:01:1115] N: lwsac_use: alloc 1532 for 1
|
||||
[2019/10/16 16:22:01:1119] N: lwsac_use: alloc 288 for 168
|
||||
[2019/10/16 16:22:01:1119] N: lws_ss_policy_set: policy lwsac size: 1.796KiB, pad 11%
|
||||
[2019/10/16 16:22:02:4114] N: lws_ss_client_connect: connecting 0 api.amazon.com /auth/o2/token
|
||||
[2019/10/16 16:22:02:8686] N: auth_api_amazon_com_parser_cb: expires in 3600
|
||||
[2019/10/16 16:22:02:8686] N: ss_api_amazon_auth_rx: acquired 656-byte api.amazon.com auth token
|
||||
[2019/10/16 16:22:02:8754] N: lws_ss_client_connect: connecting 1 alexa.na.gateway.devices.a2z.com /v20160207/directives
|
||||
[2019/10/16 16:22:02:3182] N: secstream_h2: h2 client entering LONG_POLL
|
||||
[2019/10/16 16:22:02:3183] U: Connected to Alexa... speak "Alexa, ..."
|
||||
[2019/10/16 16:22:06:9380] W: ************* Wakeword
|
||||
[2019/10/16 16:22:06:9380] N: avs_query_start:
|
||||
[2019/10/16 16:22:06:9381] N: lws_ss_client_connect: connecting 1 alexa.na.gateway.devices.a2z.com /v20160207/events
|
||||
[2019/10/16 16:22:06:9381] N: lws_vhost_active_conns: just join h2 directly
|
||||
[2019/10/16 16:22:06:9384] N: metadata done
|
||||
[2019/10/16 16:22:06:1524] N: est: 42 1
|
||||
[2019/10/16 16:22:06:3723] N: est: 108 1
|
||||
[2019/10/16 16:22:07:5914] N: est: 352 1
|
||||
[2019/10/16 16:22:07:8112] N: est: 4284 1
|
||||
[2019/10/16 16:22:07:0300] N: est: 3369 1
|
||||
[2019/10/16 16:22:07:2325] N: est: 577 1
|
||||
[2019/10/16 16:22:08:4519] N: est: 9 1
|
||||
[2019/10/16 16:22:08:6716] N: est: 3 1
|
||||
[2019/10/16 16:22:08:6718] N: est: 11 1
|
||||
[2019/10/16 16:22:08:8915] N: est: 10 1
|
||||
[2019/10/16 16:22:08:8915] W: callback_audio: ended capture
|
||||
[2019/10/16 16:22:09:0993] N: identified reply...
|
||||
^C[2019/10/16 16:22:14:3067] U: Disconnected from Alexa
|
||||
[2019/10/16 16:22:14:3123] U: Completed
|
||||
$
|
||||
|
||||
```
|
|
@ -0,0 +1,678 @@
|
|||
/*
|
||||
* lws-minimal-secure-streams-alexa
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <mpg123.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
struct lws_ss_handle *hss_avs_event, *hss_avs_sync;
|
||||
|
||||
/* this is the type for the long poll event channel */
|
||||
|
||||
typedef struct ss_avs_event {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
|
||||
struct lejp_ctx jctx;
|
||||
} ss_avs_event_t;
|
||||
|
||||
enum {
|
||||
LAMP3STATE_IDLE,
|
||||
LAMP3STATE_SPOOLING,
|
||||
LAMP3STATE_DRAINING,
|
||||
};
|
||||
|
||||
/* this is the type for the utterance metadata (and audio rideshares) */
|
||||
|
||||
typedef struct ss_avs_metadata {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
|
||||
struct lws_buflist *dribble; /* next mp3 data while draining last */
|
||||
|
||||
struct lejp_ctx jctx;
|
||||
size_t pos;
|
||||
size_t mp3_in;
|
||||
mpg123_handle *mh;
|
||||
|
||||
lws_sorted_usec_list_t sul;
|
||||
|
||||
uint8_t stash_eom[16];
|
||||
|
||||
uint8_t se_head;
|
||||
uint8_t se_tail;
|
||||
|
||||
char mp3_state;
|
||||
char first_mp3;
|
||||
uint8_t mp3_mime_match;
|
||||
uint8_t seen;
|
||||
uint8_t inside_mp3;
|
||||
|
||||
} ss_avs_metadata_t;
|
||||
|
||||
/*
|
||||
* The remote server only seems to give us a budget of 10s to consume the
|
||||
* results, after that it doesn't drop the stream, but doesn't send us anything
|
||||
* further on it.
|
||||
*
|
||||
* This makes it impossible to optimize buffering for incoming mp3 since we
|
||||
* have to go ahead and take it before the 10s is up.
|
||||
*/
|
||||
|
||||
#define MAX_MP3_IN_BUFFERING_BYTES 32768
|
||||
|
||||
/*
|
||||
* Structure of JSON metadata for utterance handling
|
||||
*/
|
||||
|
||||
static const char *metadata = "{"
|
||||
"\"event\": {"
|
||||
"\"header\": {"
|
||||
"\"namespace\": \"SpeechRecognizer\","
|
||||
"\"name\": \"Recognize\","
|
||||
"\"messageId\": \"message-123\","
|
||||
"\"dialogRequestId\": \"dialog-request-321\""
|
||||
"},"
|
||||
"\"payload\": {"
|
||||
"\"profile\":" "\"CLOSE_TALK\","
|
||||
"\"format\":" "\"AUDIO_L16_RATE_16000_CHANNELS_1\""
|
||||
"}"
|
||||
"}"
|
||||
"}";
|
||||
|
||||
/*
|
||||
* avs metadata
|
||||
*/
|
||||
|
||||
static void
|
||||
use_buffer_250ms(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
ss_avs_metadata_t *m = lws_container_of(sul, ss_avs_metadata_t, sul);
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
int est = lws_ss_get_est_peer_tx_credit(m->ss);
|
||||
|
||||
lwsl_notice("%s: est txcr %d\n", __func__, est);
|
||||
|
||||
if (est < MAX_MP3_IN_BUFFERING_BYTES - (MAX_MP3_IN_BUFFERING_BYTES / 4)) {
|
||||
lwsl_notice(" adding %d\n", MAX_MP3_IN_BUFFERING_BYTES / 4);
|
||||
lws_ss_add_peer_tx_credit(m->ss, MAX_MP3_IN_BUFFERING_BYTES / 4);
|
||||
}
|
||||
|
||||
lws_sul_schedule(context, 0, &m->sul, use_buffer_250ms,
|
||||
250 * LWS_US_PER_MS);
|
||||
}
|
||||
|
||||
static const char *mp3_mimetype = "application/octet-stream",
|
||||
*match2 = "\x0d\x0a\x0d\x0a";
|
||||
|
||||
static int
|
||||
ss_avs_mp3_open(ss_avs_metadata_t *m)
|
||||
{
|
||||
int r;
|
||||
|
||||
lwsl_notice("%s\n", __func__);
|
||||
|
||||
m->first_mp3 = 1;
|
||||
m->mh = mpg123_new(NULL, NULL);
|
||||
if (!m->mh) {
|
||||
lwsl_err("%s: unable to make new mp3\n",
|
||||
__func__);
|
||||
goto bail;
|
||||
}
|
||||
mpg123_format_none(m->mh);
|
||||
r = mpg123_format(m->mh, 16000, MPG123_M_MONO,
|
||||
MPG123_ENC_SIGNED_16);
|
||||
if (r) {
|
||||
lwsl_err("%s: mpg123 format failed %d\n",
|
||||
__func__, r);
|
||||
goto bail1;
|
||||
}
|
||||
r = mpg123_open_feed(m->mh);
|
||||
if (r) {
|
||||
lwsl_err("%s: mpg123 open feed failed %d\n",
|
||||
__func__, r);
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bail1:
|
||||
mpg123_delete(m->mh);
|
||||
m->mh = NULL;
|
||||
|
||||
bail:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_avs_metadata_rx(void *userobj, const uint8_t *buf, size_t len, int flags);
|
||||
|
||||
/*
|
||||
* This is called when the mp3 has drained it's input buffer and destroyed
|
||||
* itself.
|
||||
*/
|
||||
|
||||
static int
|
||||
drain_end_cb(void *v)
|
||||
{
|
||||
ss_avs_metadata_t *m = (ss_avs_metadata_t *)v;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
int tot = 0;
|
||||
|
||||
lwsl_err("%s\n", __func__);
|
||||
|
||||
/*
|
||||
* We have drained and destroyed the existing mp3 session. Is there
|
||||
* a new one pending?
|
||||
*/
|
||||
|
||||
m->first_mp3 = 1;
|
||||
m->mp3_state = LAMP3STATE_IDLE;
|
||||
|
||||
if (lws_buflist_total_len(&m->dribble)) {
|
||||
/* we started another one */
|
||||
|
||||
/* resume tx credit top up */
|
||||
lws_sul_schedule(context, 0, &m->sul, use_buffer_250ms, 1);
|
||||
|
||||
if (ss_avs_mp3_open(m))
|
||||
return 1;
|
||||
|
||||
m->mp3_state = LAMP3STATE_SPOOLING;
|
||||
|
||||
/*
|
||||
* Dump what we stashed from draining into the new mp3
|
||||
*/
|
||||
|
||||
while (lws_buflist_total_len(&m->dribble)) {
|
||||
size_t s;
|
||||
uint8_t *u, t;
|
||||
|
||||
s = lws_buflist_next_segment_len(&m->dribble, &u);
|
||||
t = m->stash_eom[m->se_tail];
|
||||
lwsl_notice("%s: preload %d: %d\n", __func__, (int)s, t);
|
||||
|
||||
mpg123_feed(m->mh, u, s);
|
||||
lws_buflist_use_segment(&m->dribble, s);
|
||||
if (m->first_mp3) {
|
||||
play_mp3(m->mh, NULL, NULL);
|
||||
m->first_mp3 = 0;
|
||||
}
|
||||
|
||||
tot += s;
|
||||
|
||||
m->se_tail = (m->se_tail + 1) % sizeof(m->stash_eom);
|
||||
if (t) {
|
||||
lwsl_notice("%s: preloaded EOM\n", __func__);
|
||||
|
||||
/*
|
||||
* We stashed the whole of the message, we need
|
||||
* to also do the EOM processing. We will come
|
||||
* back here if there's another message in the
|
||||
* stash.
|
||||
*/
|
||||
|
||||
m->mp3_state = LAMP3STATE_DRAINING;
|
||||
if (m->mh)
|
||||
play_mp3(NULL, drain_end_cb, m);
|
||||
|
||||
lws_ss_add_peer_tx_credit(m->ss, tot);
|
||||
#if 0
|
||||
/*
|
||||
* Put a hold on bringing in any more data
|
||||
*/
|
||||
lws_sul_schedule(context, 0, &m->sul, NULL,
|
||||
LWS_SET_TIMER_USEC_CANCEL);
|
||||
#endif
|
||||
/* destroy our copy of the handle */
|
||||
m->mh = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lws_ss_add_peer_tx_credit(m->ss, tot);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_avs_metadata_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
ss_avs_metadata_t *m = (ss_avs_metadata_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
int n = 0, hit = 0;
|
||||
|
||||
lwsl_notice("%s: len %d, flags %d (est peer txcr %d)\n", __func__,
|
||||
(int)len, flags, lws_ss_get_est_peer_tx_credit(m->ss));
|
||||
|
||||
// lwsl_hexdump_warn(buf, len);
|
||||
|
||||
if ((flags & LWSSS_FLAG_SOM) && !m->mh && !m->seen) {
|
||||
m->mp3_mime_match = 0;
|
||||
m->seen = 0;
|
||||
m->inside_mp3 = 0;
|
||||
}
|
||||
|
||||
if (!m->inside_mp3) {
|
||||
/*
|
||||
* Identify the part with the mp3 in, if any
|
||||
*/
|
||||
|
||||
while (n < (int)len - 24) {
|
||||
if (!m->seen) {
|
||||
if (buf[n] == mp3_mimetype[m->mp3_mime_match]) {
|
||||
m->mp3_mime_match++;
|
||||
if (m->mp3_mime_match == 24) {
|
||||
m->mp3_mime_match = 0;
|
||||
m->seen = 1;
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
m->mp3_mime_match = 0;
|
||||
} else {
|
||||
if (buf[n] == match2[m->mp3_mime_match]) {
|
||||
m->mp3_mime_match++;
|
||||
if (m->mp3_mime_match == 4) {
|
||||
m->seen = 0;
|
||||
m->mp3_mime_match = 0;
|
||||
hit = 1;
|
||||
n++;
|
||||
buf += n;
|
||||
len -= n;
|
||||
lwsl_notice("identified reply...\n");
|
||||
m->inside_mp3 = 1;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
m->mp3_mime_match = 0;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!hit) {
|
||||
lws_ss_add_peer_tx_credit(m->ss, len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// lwsl_notice("%s: state %d\n", __func__, m->mp3_state);
|
||||
|
||||
switch (m->mp3_state) {
|
||||
case LAMP3STATE_IDLE:
|
||||
|
||||
if (hit) {
|
||||
|
||||
lws_ss_add_peer_tx_credit(m->ss, n);
|
||||
|
||||
if (ss_avs_mp3_open(m))
|
||||
goto bail;
|
||||
|
||||
lws_sul_schedule(context, 0, &m->sul, use_buffer_250ms, 1);
|
||||
m->mp3_state = LAMP3STATE_SPOOLING;
|
||||
break;
|
||||
}
|
||||
|
||||
lws_ss_add_peer_tx_credit(m->ss, len);
|
||||
|
||||
if (!m->inside_mp3)
|
||||
break;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LAMP3STATE_SPOOLING:
|
||||
|
||||
if (m->dribble)
|
||||
goto draining;
|
||||
|
||||
if (len) {
|
||||
/*
|
||||
* We are shoving encoded mp3 into mpg123-allocated heap
|
||||
* buffers... unfortunately mpg123 doesn't seem to
|
||||
* expose where it is in its allocated input so we can
|
||||
* track how much is stashed. Instead while in playback
|
||||
* mode, we assume 64kbps mp3 encoding, ie, 8KB/s, and
|
||||
* run a sul that allows an additional 2KB tx credit
|
||||
* every 250ms, with 4KB initial credit.
|
||||
*/
|
||||
lwsl_notice("%s: SPOOL %d\n", __func__, (int)len);
|
||||
mpg123_feed(m->mh, buf, len);
|
||||
|
||||
if (m->first_mp3) {
|
||||
lws_sul_schedule(context, 0, &m->sul,
|
||||
use_buffer_250ms, 1);
|
||||
// lws_ss_add_peer_tx_credit(m->ss,
|
||||
// len + (MAX_MP3_IN_BUFFERING_BYTES / 2));
|
||||
play_mp3(m->mh, NULL, NULL);
|
||||
} //else
|
||||
// lws_ss_add_peer_tx_credit(m->ss, len);
|
||||
m->first_mp3 = 0;
|
||||
}
|
||||
|
||||
if (flags & LWSSS_FLAG_EOM) {
|
||||
/*
|
||||
* This means one "message" / mime part with mp3 data
|
||||
* has finished coming in. But there may be whole other
|
||||
* parts with other mp3s following, with potentially
|
||||
* different mp3 parameters. So we want to tell this
|
||||
* one to drain and finish and destroy the current mp3
|
||||
* object before we go on.
|
||||
*
|
||||
* But not knowing the length of the current one, there
|
||||
* will already be outstanding tx credit at the server,
|
||||
* so it's going to spam us with the next part before we
|
||||
* have the new mp3 sink for it.
|
||||
*/
|
||||
lwsl_notice("%s: EOM\n", __func__);
|
||||
m->mp3_mime_match = 0;
|
||||
m->seen = 0;
|
||||
m->mp3_state = LAMP3STATE_DRAINING;
|
||||
/* from input POV, we're no longer inside an mp3 */
|
||||
m->inside_mp3 = 0;
|
||||
if (m->mh)
|
||||
play_mp3(NULL, drain_end_cb, m);
|
||||
#if 0
|
||||
/*
|
||||
* Put a hold on bringing in any more data
|
||||
*/
|
||||
lws_sul_schedule(context, 0, &m->sul, NULL,
|
||||
LWS_SET_TIMER_USEC_CANCEL);
|
||||
#endif
|
||||
/* destroy our copy of the handle */
|
||||
m->mh = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case LAMP3STATE_DRAINING:
|
||||
|
||||
draining:
|
||||
if (buf && len && m->inside_mp3) {
|
||||
lwsl_notice("%s: DRAINING: stashing %d: %d %d %d\n",
|
||||
__func__, (int)len, !!(flags & LWSSS_FLAG_EOM),
|
||||
m->se_head, m->se_tail);
|
||||
lwsl_hexdump_notice(buf, len);
|
||||
if (lws_buflist_append_segment(&m->dribble, buf, len) < 0)
|
||||
goto bail;
|
||||
|
||||
m->stash_eom[m->se_head] = !!(flags & LWSSS_FLAG_EOM);
|
||||
m->se_head = (m->se_head + 1) % sizeof(m->stash_eom);
|
||||
lwsl_notice("%s: next head %d\n", __func__, m->se_head);
|
||||
|
||||
lws_ss_add_peer_tx_credit(m->ss, len);
|
||||
}
|
||||
|
||||
if (flags & LWSSS_FLAG_EOM) {
|
||||
if (!len && m->se_head != m->se_tail) {
|
||||
/* 0-len EOM... retrospectively mark last stash */
|
||||
lwsl_notice("%s: retro EOM\n", __func__);
|
||||
m->stash_eom[(m->se_head - 1) % sizeof(m->stash_eom)] = 1;
|
||||
}
|
||||
|
||||
lwsl_notice("%s: Draining EOM\n", __func__);
|
||||
m->inside_mp3 = 0;
|
||||
}
|
||||
/*
|
||||
* Don't provide any additional tx credit... we're just
|
||||
* mopping up the overspill from the previous mp3 credit
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Because this is multipart mime in h2 currently, use a "rideshare" to handle
|
||||
* first the native metadata on this secure stream, then the "rideshare" audio
|
||||
* stream mentioned in the policy.
|
||||
*
|
||||
* Lws takes care of interleaving the multipart mime pieces since the policy
|
||||
* calls for it.
|
||||
*/
|
||||
|
||||
static int
|
||||
ss_avs_metadata_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags)
|
||||
{
|
||||
ss_avs_metadata_t *m = (ss_avs_metadata_t *)userobj;
|
||||
size_t tot;
|
||||
int n;
|
||||
|
||||
// lwsl_notice("%s %d\n", __func__, (int)m->pos);
|
||||
|
||||
if ((long)m->pos < 0) {
|
||||
*len = 0;
|
||||
lwsl_info("%s: skip\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!strcmp(lws_ss_rideshare(m->ss), "avs_audio")) {
|
||||
|
||||
/* audio rideshare part */
|
||||
|
||||
if (!m->pos)
|
||||
*flags |= LWSSS_FLAG_SOM;
|
||||
|
||||
n = spool_capture(buf, *len);
|
||||
if (n > 0)
|
||||
*len = n;
|
||||
else
|
||||
*len = 0;
|
||||
if (!n) {
|
||||
lwsl_info("%s: trying to skip tx\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
m->pos += *len;
|
||||
|
||||
if (n < 0) {
|
||||
*flags |= LWSSS_FLAG_EOM;
|
||||
m->pos = (long)-1l; /* ban subsequent until new stream */
|
||||
}
|
||||
|
||||
lwsl_notice("%s: tx audio %d\n", __func__, (int)*len);
|
||||
|
||||
#if 0
|
||||
{
|
||||
int ff = open("/tmp/z1", O_RDWR | O_CREAT | O_APPEND, 0666);
|
||||
if (ff == -1)
|
||||
lwsl_err("%s: errno %d\n", __func__, errno);
|
||||
write(ff, buf, *len);
|
||||
close(ff);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* metadata part */
|
||||
|
||||
tot = strlen(metadata);
|
||||
|
||||
if (!m->pos)
|
||||
*flags |= LWSSS_FLAG_SOM;
|
||||
|
||||
if (*len > tot - m->pos)
|
||||
*len = tot - m->pos;
|
||||
|
||||
memcpy(buf, metadata + m->pos, *len);
|
||||
|
||||
m->pos += *len;
|
||||
|
||||
if (m->pos == tot) {
|
||||
lwsl_notice("metadata done\n");
|
||||
*flags |= LWSSS_FLAG_EOM;
|
||||
m->pos = 0; /* for next time */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_avs_metadata_state(void *userobj, void *sh,
|
||||
lws_ss_constate_t state, lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
ss_avs_metadata_t *m = (ss_avs_metadata_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
|
||||
lwsl_notice("%s: %p: %s, ord 0x%x\n", __func__, m->ss,
|
||||
lws_ss_state_name(state), (unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
lws_ss_client_connect(m->ss);
|
||||
break;
|
||||
case LWSSSCS_CONNECTING:
|
||||
m->pos = 0;
|
||||
break;
|
||||
case LWSSSCS_CONNECTED:
|
||||
lwsl_info("%s: CONNECTED\n", __func__);
|
||||
lws_ss_request_tx(m->ss);
|
||||
break;
|
||||
case LWSSSCS_DISCONNECTED:
|
||||
lws_sul_schedule(context, 0, &m->sul, NULL,
|
||||
LWS_SET_TIMER_USEC_CANCEL);
|
||||
//if (m->mh) {
|
||||
play_mp3(NULL, NULL, NULL);
|
||||
m->mh = NULL;
|
||||
//}
|
||||
/*
|
||||
* For this stream encapsulating an alexa exchange, dropping
|
||||
* is the end of its life
|
||||
*/
|
||||
return 1;
|
||||
|
||||
case LWSSSCS_DESTROYING:
|
||||
lws_buflist_destroy_all_segments(&m->dribble);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* avs event
|
||||
*/
|
||||
|
||||
static int
|
||||
ss_avs_event_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_avs_event_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags)
|
||||
{
|
||||
return 1; /* don't transmit anything */
|
||||
}
|
||||
|
||||
static int
|
||||
ss_avs_event_state(void *userobj, void *sh,
|
||||
lws_ss_constate_t state, lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
|
||||
(unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
mpg123_init();
|
||||
break;
|
||||
case LWSSSCS_CONNECTING:
|
||||
break;
|
||||
case LWSSSCS_CONNECTED:
|
||||
lwsl_user("Connected to Alexa... speak \"Alexa, ...\"\n");
|
||||
break;
|
||||
case LWSSSCS_DISCONNECTED:
|
||||
lwsl_user("Disconnected from Alexa\n");
|
||||
break;
|
||||
case LWSSSCS_DESTROYING:
|
||||
mpg123_exit();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
avs_query_start(struct lws_context *context)
|
||||
{
|
||||
lws_ss_info_t ssi;
|
||||
|
||||
lwsl_notice("%s:\n", __func__);
|
||||
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.handle_offset = offsetof(ss_avs_metadata_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(ss_avs_metadata_t, opaque_data);
|
||||
ssi.rx = ss_avs_metadata_rx;
|
||||
ssi.tx = ss_avs_metadata_tx;
|
||||
ssi.state = ss_avs_metadata_state;
|
||||
ssi.user_alloc = sizeof(ss_avs_metadata_t);
|
||||
ssi.streamtype = "avs_metadata";
|
||||
|
||||
ssi.manual_initial_tx_credit = 8192;
|
||||
|
||||
if (lws_ss_create(context, 0, &ssi, context, &hss_avs_sync, NULL, NULL)) {
|
||||
lwsl_err("%s: failed to create avs metadata secstream\n",
|
||||
__func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
lwsl_user("%s: created query stream %p\n", __func__, hss_avs_sync);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
avs_example_start(struct lws_context *context)
|
||||
{
|
||||
lws_ss_info_t ssi;
|
||||
|
||||
if (hss_avs_event)
|
||||
return 0;
|
||||
|
||||
lwsl_info("%s: Starting AVS stream\n", __func__);
|
||||
|
||||
/* AVS wants us to establish the long poll event stream first */
|
||||
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.handle_offset = offsetof(ss_avs_event_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(ss_avs_event_t, opaque_data);
|
||||
ssi.rx = ss_avs_event_rx;
|
||||
ssi.tx = ss_avs_event_tx;
|
||||
ssi.state = ss_avs_event_state;
|
||||
ssi.user_alloc = sizeof(ss_avs_event_t);
|
||||
ssi.streamtype = "avs_event";
|
||||
|
||||
if (lws_ss_create(context, 0, &ssi, context, &hss_avs_event, NULL, NULL)) {
|
||||
lwsl_err("%s: failed to create avs event secure stream\n",
|
||||
__func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,469 @@
|
|||
/*
|
||||
* alsa audio handling
|
||||
*
|
||||
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <pv_porcupine.h>
|
||||
|
||||
#include <mpg123.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
extern struct lws_ss_handle *hss_avs_event, *hss_avs_sync;
|
||||
|
||||
int
|
||||
avs_query_start(struct lws_context *context);
|
||||
|
||||
enum {
|
||||
MODE_IDLE,
|
||||
MODE_CAPTURING,
|
||||
MODE_PLAYING
|
||||
};
|
||||
|
||||
struct raw_vhd {
|
||||
int16_t p[8 * 1024]; /* 500ms at 16kHz 16-bit PCM */
|
||||
pv_porcupine_object_t *porc;
|
||||
snd_pcm_t *pcm_capture;
|
||||
snd_pcm_t *pcm_playback;
|
||||
snd_pcm_hw_params_t *params;
|
||||
snd_pcm_uframes_t frames;
|
||||
int16_t *porcbuf;
|
||||
|
||||
mpg123_handle *mh;
|
||||
|
||||
mp3_done_cb done_cb;
|
||||
void *opaque;
|
||||
|
||||
int mode;
|
||||
int rate;
|
||||
|
||||
int porc_spf;
|
||||
int filefd;
|
||||
int rpos;
|
||||
int wpos;
|
||||
int porcpos;
|
||||
int npos;
|
||||
int times;
|
||||
int quietcount;
|
||||
int anycount;
|
||||
|
||||
int wplay;
|
||||
int rplay;
|
||||
|
||||
char last_wake_detect;
|
||||
char destroy_mh_on_drain;
|
||||
};
|
||||
|
||||
static struct raw_vhd *avhd;
|
||||
|
||||
/*
|
||||
* called from alexa.c to grab the next chunk of audio capture buffer
|
||||
* for upload
|
||||
*/
|
||||
|
||||
int
|
||||
spool_capture(uint8_t *buf, size_t len)
|
||||
{
|
||||
int16_t *sam = (int16_t *)buf;
|
||||
size_t s, os;
|
||||
|
||||
if (avhd->mode != MODE_CAPTURING)
|
||||
return -1;
|
||||
|
||||
os = s = len / 2;
|
||||
|
||||
while (s && avhd->wpos != avhd->npos) {
|
||||
*sam++ = avhd->p[avhd->npos];
|
||||
avhd->npos = (avhd->npos + 1) % LWS_ARRAY_SIZE(avhd->p);
|
||||
s--;
|
||||
}
|
||||
|
||||
lwsl_info("Copied %d samples (%d %d)\n", (int)(os - s),
|
||||
avhd->wpos, avhd->npos);
|
||||
|
||||
return (os - s) * 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from alexa.c to control when the mp3 playback should begin and end
|
||||
*/
|
||||
|
||||
int
|
||||
play_mp3(mpg123_handle *mh, mp3_done_cb cb, void *opaque)
|
||||
{
|
||||
if (mh) {
|
||||
avhd->mh = mh;
|
||||
avhd->mode = MODE_PLAYING;
|
||||
snd_pcm_prepare(avhd->pcm_playback);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
avhd->destroy_mh_on_drain = 1;
|
||||
avhd->done_cb = cb;
|
||||
avhd->opaque = opaque;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper used to set alsa hwparams on both capture and playback channels
|
||||
*/
|
||||
|
||||
static int
|
||||
set_hw_params(struct lws_vhost *vh, snd_pcm_t **pcm, int type)
|
||||
{
|
||||
unsigned int rate = pv_sample_rate(); /* it's 16kHz */
|
||||
snd_pcm_hw_params_t *params;
|
||||
lws_sock_file_fd_type u;
|
||||
struct pollfd pfd;
|
||||
struct lws *wsi1;
|
||||
int n;
|
||||
|
||||
n = snd_pcm_open(pcm, "default", type, SND_PCM_NONBLOCK);
|
||||
if (n < 0) {
|
||||
lwsl_err("%s: Can't open default for playback: %s\n",
|
||||
__func__, snd_strerror(n));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (snd_pcm_poll_descriptors(*pcm, &pfd, 1) != 1) {
|
||||
lwsl_err("%s: failed to get playback desc\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
u.filefd = (lws_filefd_type)(long long)pfd.fd;
|
||||
wsi1 = lws_adopt_descriptor_vhost(vh, LWS_ADOPT_RAW_FILE_DESC, u,
|
||||
"lws-audio-test", NULL);
|
||||
if (!wsi1) {
|
||||
lwsl_err("%s: Failed to adopt playback desc\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
if (type == SND_PCM_STREAM_PLAYBACK)
|
||||
lws_rx_flow_control(wsi1, 0); /* no POLLIN */
|
||||
|
||||
snd_pcm_hw_params_malloc(¶ms);
|
||||
snd_pcm_hw_params_any(*pcm, params);
|
||||
|
||||
n = snd_pcm_hw_params_set_access(*pcm, params,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (n < 0)
|
||||
goto bail1;
|
||||
|
||||
n = snd_pcm_hw_params_set_format(*pcm, params, SND_PCM_FORMAT_S16_LE);
|
||||
if (n < 0)
|
||||
goto bail1;
|
||||
|
||||
n = snd_pcm_hw_params_set_channels(*pcm, params, 1);
|
||||
if (n < 0)
|
||||
goto bail1;
|
||||
|
||||
n = snd_pcm_hw_params_set_rate_near(*pcm, params, &rate, 0);
|
||||
if (n < 0)
|
||||
goto bail1;
|
||||
|
||||
lwsl_notice("%s: %s rate %d\n", __func__,
|
||||
type == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture", rate);
|
||||
|
||||
n = snd_pcm_hw_params(*pcm, params);
|
||||
snd_pcm_hw_params_free(params);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
return 0;
|
||||
|
||||
bail1:
|
||||
snd_pcm_hw_params_free(params);
|
||||
bail:
|
||||
lwsl_err("%s: Set hw params failed: %s\n", __func__, snd_strerror(n));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The lws RAW file protocol handler that wraps ALSA.
|
||||
*
|
||||
* The timing is coming from ALSA capture channel... since they are both set to
|
||||
* 16kHz, it's enough just to have the one.
|
||||
*/
|
||||
|
||||
static int
|
||||
callback_audio(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len)
|
||||
{
|
||||
struct raw_vhd *vhd = (struct raw_vhd *)lws_protocol_vh_priv_get(
|
||||
lws_get_vhost(wsi), lws_get_protocol(wsi));
|
||||
uint16_t rands[50];
|
||||
int16_t temp[256];
|
||||
bool det;
|
||||
long avg;
|
||||
int n, s;
|
||||
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_PROTOCOL_INIT:
|
||||
|
||||
if (avhd) /* just on one vhost */
|
||||
return 0;
|
||||
|
||||
avhd = vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
|
||||
lws_get_protocol(wsi), sizeof(struct raw_vhd));
|
||||
|
||||
/*
|
||||
* Set up the wakeword library
|
||||
*/
|
||||
|
||||
n = pv_porcupine_init("porcupine_params.pv", "alexa_linux.ppn",
|
||||
1.0, &vhd->porc);
|
||||
if (n) {
|
||||
lwsl_err("%s: porcupine init fail %d\n", __func__, n);
|
||||
|
||||
return -1;
|
||||
}
|
||||
vhd->porc_spf = pv_porcupine_frame_length();
|
||||
vhd->porcbuf = malloc(vhd->porc_spf * 2);
|
||||
lwsl_info("%s: %s porc frame length is %d samples\n", __func__,
|
||||
lws_get_vhost_name(lws_get_vhost(wsi)),
|
||||
vhd->porc_spf);
|
||||
|
||||
vhd->rate = pv_sample_rate(); /* 16kHz */
|
||||
|
||||
/* set up alsa */
|
||||
|
||||
if (set_hw_params(lws_get_vhost(wsi), &vhd->pcm_playback,
|
||||
SND_PCM_STREAM_PLAYBACK)) {
|
||||
lwsl_err("%s: Can't open default for playback\n",
|
||||
__func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_hw_params(lws_get_vhost(wsi), &vhd->pcm_capture,
|
||||
SND_PCM_STREAM_CAPTURE)) {
|
||||
lwsl_err("%s: Can't open default for capture\n",
|
||||
__func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
snd_config_update_free_global();
|
||||
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_PROTOCOL_DESTROY:
|
||||
lwsl_info("%s: LWS_CALLBACK_PROTOCOL_DESTROY\n", __func__);
|
||||
if (!vhd)
|
||||
break;
|
||||
|
||||
if (vhd->porcbuf) {
|
||||
free(vhd->porcbuf);
|
||||
vhd->porcbuf = NULL;
|
||||
}
|
||||
if (vhd->pcm_playback) {
|
||||
snd_pcm_drop(vhd->pcm_playback);
|
||||
snd_pcm_close(vhd->pcm_playback);
|
||||
vhd->pcm_playback = NULL;
|
||||
}
|
||||
if (vhd->pcm_capture) {
|
||||
snd_pcm_drop(vhd->pcm_capture);
|
||||
snd_pcm_close(vhd->pcm_capture);
|
||||
vhd->pcm_capture = NULL;
|
||||
}
|
||||
if (vhd->porc) {
|
||||
pv_porcupine_delete(vhd->porc);
|
||||
vhd->porc = NULL;
|
||||
}
|
||||
|
||||
/* avoid most of the valgrind mess from alsa */
|
||||
snd_config_update_free_global();
|
||||
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_CLOSE_FILE:
|
||||
lwsl_info("%s: closed\n", __func__);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_RX_FILE:
|
||||
/* we come here about every 250ms */
|
||||
|
||||
/*
|
||||
* Playing back the mp3?
|
||||
*/
|
||||
if (vhd->mode == MODE_PLAYING && vhd->mh) {
|
||||
size_t amt, try;
|
||||
|
||||
do {
|
||||
try = snd_pcm_avail(vhd->pcm_playback);
|
||||
if (try > LWS_ARRAY_SIZE(vhd->p))
|
||||
try = LWS_ARRAY_SIZE(vhd->p);
|
||||
|
||||
n = mpg123_read(vhd->mh, (uint8_t *)vhd->p,
|
||||
try * 2, &amt);
|
||||
lwsl_info("%s: PLAYING: mpg123 read %d, n %d\n",
|
||||
__func__, (int)amt, n);
|
||||
if (n == MPG123_NEW_FORMAT) {
|
||||
snd_pcm_start(vhd->pcm_playback);
|
||||
memset(vhd->p, 0, try);
|
||||
snd_pcm_writei(vhd->pcm_playback,
|
||||
vhd->p, try / 2);
|
||||
snd_pcm_prepare(vhd->pcm_playback);
|
||||
}
|
||||
} while (n == MPG123_NEW_FORMAT);
|
||||
|
||||
if (amt) {
|
||||
n = snd_pcm_writei(vhd->pcm_playback,
|
||||
vhd->p, amt / 2);
|
||||
if (n < 0)
|
||||
lwsl_notice("%s: snd_pcm_writei: %d %s\n",
|
||||
__func__, n, snd_strerror(n));
|
||||
if (n == -EPIPE) {
|
||||
lwsl_err("%s: did EPIPE prep\n", __func__);
|
||||
snd_pcm_prepare(vhd->pcm_playback);
|
||||
}
|
||||
} else
|
||||
if (vhd->destroy_mh_on_drain &&
|
||||
n != MPG123_NEW_FORMAT) {
|
||||
snd_pcm_drain(vhd->pcm_playback);
|
||||
vhd->destroy_mh_on_drain = 0;
|
||||
lwsl_notice("%s: mp3 destroyed\n",
|
||||
__func__);
|
||||
mpg123_close(vhd->mh);
|
||||
mpg123_delete(vhd->mh);
|
||||
vhd->mh = NULL;
|
||||
vhd->mode = MODE_IDLE;
|
||||
|
||||
if (vhd->done_cb)
|
||||
vhd->done_cb(vhd->opaque);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the capture data
|
||||
*/
|
||||
|
||||
n = snd_pcm_readi(vhd->pcm_capture, temp, LWS_ARRAY_SIZE(temp));
|
||||
s = 0;
|
||||
while (s < n) {
|
||||
vhd->p[(vhd->wpos + s) % LWS_ARRAY_SIZE(vhd->p)] = temp[s];
|
||||
s++;
|
||||
}
|
||||
|
||||
if (vhd->mode == MODE_CAPTURING) {
|
||||
|
||||
/*
|
||||
* We are recording an utterance.
|
||||
*
|
||||
* Estimate the sound density in the frame by picking 50
|
||||
* samples at random and averaging the sampled
|
||||
* [abs()^2] / 10000 to create a Figure of Merit.
|
||||
*
|
||||
* Speaking on my laptop gets us 1000 - 5000, silence
|
||||
* is typ under 30. The wakeword tells us there was
|
||||
* speech at the start, end the capture when there's
|
||||
* ~750ms (12000 samples) under 125 FOM.
|
||||
*/
|
||||
|
||||
#define SILENCE_THRESH 125
|
||||
|
||||
avg = 0;
|
||||
lws_get_random(lws_get_context(wsi), rands, sizeof(rands));
|
||||
for (s = 0; s < (int)LWS_ARRAY_SIZE(rands); s++) {
|
||||
long q;
|
||||
|
||||
q = temp[rands[s] % n];
|
||||
|
||||
avg += (q * q);
|
||||
}
|
||||
avg = (avg / (int)LWS_ARRAY_SIZE(rands)) / 10000;
|
||||
|
||||
lwsl_notice("est audio energy: %ld %d\n", avg, vhd->mode);
|
||||
|
||||
/*
|
||||
* Only start looking for "silence" after 1.5s, in case
|
||||
* he does a long pause after the wakeword
|
||||
*/
|
||||
|
||||
if (vhd->anycount < (3 *vhd->rate) / 2 &&
|
||||
avg < SILENCE_THRESH) {
|
||||
vhd->quietcount += n;
|
||||
/* then 500ms of "silence" does it for us */
|
||||
if (vhd->quietcount >= ((vhd->rate * 3) / 4)) {
|
||||
lwsl_warn("%s: ended capture\n", __func__);
|
||||
vhd->mode = MODE_IDLE;
|
||||
vhd->quietcount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we're not "silent", reset the count */
|
||||
if (avg > SILENCE_THRESH * 2)
|
||||
vhd->quietcount = 0;
|
||||
|
||||
/*
|
||||
* Since we are in capturing mode, we have something
|
||||
* new to send now.
|
||||
*
|
||||
* We must send an extra one at the end so we can finish
|
||||
* the tx.
|
||||
*/
|
||||
lws_ss_request_tx(hss_avs_sync);
|
||||
}
|
||||
|
||||
/*
|
||||
* Just waiting for a wakeword
|
||||
*/
|
||||
|
||||
while (vhd->mode == MODE_IDLE) {
|
||||
int m = 0, ppold = vhd->porcpos;
|
||||
|
||||
s = (vhd->wpos - vhd->porcpos) % LWS_ARRAY_SIZE(vhd->p);
|
||||
if (s < vhd->porc_spf)
|
||||
goto eol;
|
||||
|
||||
while (m < vhd->porc_spf) {
|
||||
vhd->porcbuf[m++] = avhd->p[vhd->porcpos];
|
||||
vhd->porcpos = (vhd->porcpos + 1) %
|
||||
LWS_ARRAY_SIZE(vhd->p);
|
||||
}
|
||||
|
||||
if (pv_porcupine_process(vhd->porc, vhd->porcbuf, &det))
|
||||
lwsl_err("%s: porc_process failed\n", __func__);
|
||||
|
||||
if (!det && vhd->last_wake_detect &&
|
||||
vhd->mode == MODE_IDLE) {
|
||||
lwsl_warn("************* Wakeword\n");
|
||||
if (!avs_query_start(lws_get_context(wsi))) {
|
||||
vhd->mode = MODE_CAPTURING;
|
||||
vhd->quietcount = 0;
|
||||
vhd->last_wake_detect = det;
|
||||
vhd->npos = ppold;
|
||||
break;
|
||||
}
|
||||
}
|
||||
vhd->last_wake_detect = det;
|
||||
}
|
||||
|
||||
eol:
|
||||
vhd->wpos = (vhd->wpos + n) % LWS_ARRAY_SIZE(vhd->p);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws_protocols protocol_audio_test =
|
||||
{ "lws-audio-test", callback_audio, 0, 0 };
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* lws-minimal-secure-streams-alexa
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
extern int
|
||||
avs_example_start(struct lws_context *context);
|
||||
|
||||
static int interrupted;
|
||||
static lws_state_notify_link_t nl;
|
||||
|
||||
#if !defined(LWS_SS_USE_SSPC)
|
||||
|
||||
/*
|
||||
* If not using the proxy, we need to bring our own policy
|
||||
*/
|
||||
|
||||
static const char * const default_ss_policy =
|
||||
"{"
|
||||
"\"release\":" "\"01234567\","
|
||||
"\"product\":" "\"myproduct\","
|
||||
"\"schema-version\":" "1,"
|
||||
"\"retry\": [" /* named backoff / retry strategies */
|
||||
"{\"default\": {"
|
||||
"\"backoff\": [" "1000,"
|
||||
"2000,"
|
||||
"3000,"
|
||||
"5000,"
|
||||
"10000"
|
||||
"],"
|
||||
"\"conceal\":" "5,"
|
||||
"\"jitterpc\":" "20,"
|
||||
"\"svalidping\":" "60,"
|
||||
"\"svalidhup\":" "64"
|
||||
"}}"
|
||||
"],"
|
||||
"\"certs\": [" /* named individual certificates in BASE64 DER */
|
||||
"{\"digicert_global_root_g2\": \"" /* api.amazon.com 2038-01 */
|
||||
"MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh"
|
||||
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3"
|
||||
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH"
|
||||
"MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT"
|
||||
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j"
|
||||
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG"
|
||||
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI"
|
||||
"2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx"
|
||||
"1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ"
|
||||
"q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz"
|
||||
"tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ"
|
||||
"vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP"
|
||||
"BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV"
|
||||
"5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY"
|
||||
"1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4"
|
||||
"NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG"
|
||||
"Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91"
|
||||
"8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe"
|
||||
"pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl"
|
||||
"MrY="
|
||||
"\"},"
|
||||
"{\"digicert_global_ca_g2\": \"" /* api.amazon.com 2028-08 */
|
||||
"MIIEizCCA3OgAwIBAgIQDI7gyQ1qiRWIBAYe4kH5rzANBgkqhkiG9w0BAQsFADBh"
|
||||
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3"
|
||||
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH"
|
||||
"MjAeFw0xMzA4MDExMjAwMDBaFw0yODA4MDExMjAwMDBaMEQxCzAJBgNVBAYTAlVT"
|
||||
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxHjAcBgNVBAMTFURpZ2lDZXJ0IEdsb2Jh"
|
||||
"bCBDQSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNIfL7zBYZd"
|
||||
"W9UvhU5L4IatFaxhz1uvPmoKR/uadpFgC4przc/cV35gmAvkVNlW7SHMArZagV+X"
|
||||
"au4CLyMnuG3UsOcGAngLH1ypmTb+u6wbBfpXzYEQQGfWMItYNdSWYb7QjHqXnxr5"
|
||||
"IuYUL6nG6AEfq/gmD6yOTSwyOR2Bm40cZbIc22GoiS9g5+vCShjEbyrpEJIJ7RfR"
|
||||
"ACvmfe8EiRROM6GyD5eHn7OgzS+8LOy4g2gxPR/VSpAQGQuBldYpdlH5NnbQtwl6"
|
||||
"OErXb4y/E3w57bqukPyV93t4CTZedJMeJfD/1K2uaGvG/w/VNfFVbkhJ+Pi474j4"
|
||||
"8V4Rd6rfArMCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0P"
|
||||
"AQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29j"
|
||||
"c3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9jcmw0LmRp"
|
||||
"Z2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwN6A1oDOGMWh0dHA6"
|
||||
"Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwPQYD"
|
||||
"VR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2lj"
|
||||
"ZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFCRuKy3QapJRUSVpAaqaR6aJ50AgMB8GA1Ud"
|
||||
"IwQYMBaAFE4iVCAYlebjbuYP+vq5Eu0GF485MA0GCSqGSIb3DQEBCwUAA4IBAQAL"
|
||||
"OYSR+ZfrqoGvhOlaOJL84mxZvzbIRacxAxHhBsCsMsdaVSnaT0AC9aHesO3ewPj2"
|
||||
"dZ12uYf+QYB6z13jAMZbAuabeGLJ3LhimnftiQjXS8X9Q9ViIyfEBFltcT8jW+rZ"
|
||||
"8uckJ2/0lYDblizkVIvP6hnZf1WZUXoOLRg9eFhSvGNoVwvdRLNXSmDmyHBwW4co"
|
||||
"atc7TlJFGa8kBpJIERqLrqwYElesA8u49L3KJg6nwd3jM+/AVTANlVlOnAM2BvjA"
|
||||
"jxSZnE0qnsHhfTuvcqdFuhOWKU4Z0BqYBvQ3lBetoxi6PrABDJXWKTUgNX31EGDk"
|
||||
"92hiHuwZ4STyhxGs6QiA"
|
||||
"\"},"
|
||||
"{\"amazon_root_ca_1\": \""
|
||||
"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF"
|
||||
"ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6"
|
||||
"b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL"
|
||||
"MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv"
|
||||
"b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj"
|
||||
"ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM"
|
||||
"9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw"
|
||||
"IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6"
|
||||
"VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L"
|
||||
"93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm"
|
||||
"jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC"
|
||||
"AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA"
|
||||
"A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI"
|
||||
"U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs"
|
||||
"N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv"
|
||||
"o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU"
|
||||
"5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy"
|
||||
"rqXRfboQnoZsG4q5WTP468SQvvG5"
|
||||
"\"},"
|
||||
"{\"starfield_services_root_ca\": \""
|
||||
"MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx"
|
||||
"EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT"
|
||||
"HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs"
|
||||
"ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5"
|
||||
"MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD"
|
||||
"VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy"
|
||||
"ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy"
|
||||
"dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI"
|
||||
"hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p"
|
||||
"OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2"
|
||||
"8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K"
|
||||
"Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe"
|
||||
"hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk"
|
||||
"6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw"
|
||||
"DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q"
|
||||
"AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI"
|
||||
"bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB"
|
||||
"ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z"
|
||||
"qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd"
|
||||
"iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn"
|
||||
"0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN"
|
||||
"sSi6"
|
||||
"\"},"
|
||||
"{\"starfield_class_2_ca\": \""
|
||||
"MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl"
|
||||
"MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp"
|
||||
"U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw"
|
||||
"NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE"
|
||||
"ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp"
|
||||
"ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3"
|
||||
"DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf"
|
||||
"8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN"
|
||||
"+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0"
|
||||
"X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa"
|
||||
"K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA"
|
||||
"1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G"
|
||||
"A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR"
|
||||
"zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0"
|
||||
"YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD"
|
||||
"bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w"
|
||||
"DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3"
|
||||
"L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D"
|
||||
"eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl"
|
||||
"xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp"
|
||||
"VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY"
|
||||
"WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q="
|
||||
"\"}"
|
||||
"],"
|
||||
"\"trust_stores\": [" /* named cert chains */
|
||||
"{" /* chain for alexa.na.gateway.devices.a2z.com */
|
||||
"\"name\": \"avs_via_starfield\","
|
||||
"\"stack\": ["
|
||||
"\"starfield_class_2_ca\","
|
||||
"\"starfield_services_root_ca\""
|
||||
"]"
|
||||
"},"
|
||||
"{" /* chain for api.amazon.com */
|
||||
"\"name\": \"api_amazon_com\","
|
||||
"\"stack\": ["
|
||||
"\"digicert_global_ca_g2\","
|
||||
"\"digicert_global_root_g2\""
|
||||
"]"
|
||||
"}"
|
||||
"],"
|
||||
"\"s\": [" /* the supported stream types */
|
||||
"{\"api_amazon_com_auth\": {"
|
||||
"\"endpoint\":" "\"api.amazon.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h1\","
|
||||
"\"http_method\":" "\"POST\","
|
||||
"\"http_url\":" "\"auth/o2/token\","
|
||||
"\"plugins\":" "[],"
|
||||
"\"opportunistic\":" "true,"
|
||||
"\"tls\":" "true,"
|
||||
"\"h2q_oflow_txcr\":" "true,"
|
||||
"\"http_www_form_urlencoded\":" "true,"
|
||||
"\"http_no_content_length\":" "true,"
|
||||
"\"retry\":" "\"default\","
|
||||
"\"tls_trust_store\":" "\"api_amazon_com\""
|
||||
"}},"
|
||||
/*
|
||||
* long poll event listener
|
||||
*/
|
||||
"{\"avs_event\": {"
|
||||
"\"endpoint\":" "\"alexa.na.gateway.devices.a2z.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h2\","
|
||||
"\"http_method\":" "\"GET\","
|
||||
"\"http_url\":" "\"v20160207/directives\","
|
||||
"\"h2q_oflow_txcr\":" "true,"
|
||||
"\"http_auth_header\":" "\"authorization:\","
|
||||
"\"http_auth_preamble\":" "\"Bearer \","
|
||||
"\"nailed_up\":" "true,"
|
||||
"\"long_poll\":" "true,"
|
||||
"\"retry\":" "\"default\","
|
||||
"\"plugins\":" "[],"
|
||||
"\"tls\":" "true,"
|
||||
"\"tls_trust_store\":" "\"avs_via_starfield\""
|
||||
"}},"
|
||||
/*
|
||||
* Utterance metadata and audio send and reply processing.
|
||||
*
|
||||
* "Rideshare" and http_multipart_mime means these both go out
|
||||
* in one multipart http transaction.
|
||||
*/
|
||||
"{\"avs_metadata\": {"
|
||||
"\"endpoint\":" "\"alexa.na.gateway.devices.a2z.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h2\","
|
||||
"\"http_method\":" "\"POST\","
|
||||
"\"http_url\":" "\"v20160207/events\","
|
||||
"\"opportunistic\":" "true,"
|
||||
"\"h2q_oflow_txcr\":" "true,"
|
||||
"\"http_auth_header\":" "\"authorization:\","
|
||||
"\"http_auth_preamble\":" "\"Bearer \","
|
||||
"\"http_multipart_name\":" "\"metadata\","
|
||||
"\"http_mime_content_type\":" "\"application/json; charset=UTF-8\","
|
||||
"\"http_no_content_length\":" "true,"
|
||||
"\"rideshare\":" "\"avs_audio\","
|
||||
"\"retry\":" "\"default\","
|
||||
"\"plugins\":" "[],"
|
||||
"\"tls\":" "true,"
|
||||
"\"tls_trust_store\":" "\"avs_via_starfield\""
|
||||
"}},"
|
||||
"{\"avs_audio\": {"
|
||||
"\"endpoint\":" "\"alexa.na.gateway.devices.a2z.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h2\","
|
||||
"\"http_method\":" "\"POST\","
|
||||
"\"http_url\":" "\"v20160207/events\","
|
||||
"\"plugins\":" "[],"
|
||||
"\"tls\":" "true,"
|
||||
"\"h2q_oflow_txcr\":" "true,"
|
||||
"\"http_auth_header\":" "\"authorization:\","
|
||||
"\"http_auth_preamble\":" "\"Bearer \","
|
||||
"\"http_multipart_name\":" "\"audio\","
|
||||
"\"http_mime_content_type\":" "\"application/octet-stream\","
|
||||
"\"http_no_content_length\":" "true,"
|
||||
"\"retry\":" "\"default\","
|
||||
"\"tls_trust_store\":" "\"avs_via_starfield\""
|
||||
"}}"
|
||||
"]"
|
||||
"}"
|
||||
;
|
||||
|
||||
#endif
|
||||
|
||||
static const char *canned_root_token_payload =
|
||||
"grant_type=refresh_token"
|
||||
"&refresh_token=Atzr|IwEBIJedGXjDqsU_vMxykqOMg"
|
||||
"SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP"
|
||||
"zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y"
|
||||
"0flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW"
|
||||
"k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE"
|
||||
"iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S"
|
||||
"KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc"
|
||||
"AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI"
|
||||
"xL_hDCcTho8opCVX-6QhJHl6SQFlTw13"
|
||||
"&client_id="
|
||||
"amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d";
|
||||
|
||||
/*
|
||||
* Register the root token, and make the sticky AVS connection at the
|
||||
* appropriate times during system startup
|
||||
*/
|
||||
|
||||
static int
|
||||
app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
|
||||
int current, int target)
|
||||
{
|
||||
struct lws_context *context = lws_system_context_from_system_mgr(mgr);
|
||||
lws_system_blob_t *ab = lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */);
|
||||
size_t size;
|
||||
|
||||
/*
|
||||
* For the things we care about, let's notice if we are trying to get
|
||||
* past them when we haven't solved them yet, and make the system
|
||||
* state wait while we trigger the dependent action.
|
||||
*/
|
||||
switch (target) {
|
||||
case LWS_SYSTATE_REGISTERED:
|
||||
size = lws_system_blob_get_size(ab);
|
||||
if (size)
|
||||
break;
|
||||
|
||||
/* let's register our canned root token so auth can use it */
|
||||
lws_system_blob_direct_set(ab,
|
||||
(const uint8_t *)canned_root_token_payload,
|
||||
strlen(canned_root_token_payload));
|
||||
break;
|
||||
case LWS_SYSTATE_OPERATIONAL:
|
||||
if (current == target)
|
||||
avs_example_start(context);
|
||||
break;
|
||||
case LWS_SYSTATE_POLICY_INVALID:
|
||||
/*
|
||||
* This is a NOP since we used direct set... but in a real
|
||||
* system this could easily change to be done on the heap, then
|
||||
* this would be important
|
||||
*/
|
||||
lws_system_blob_destroy(lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH,
|
||||
1 /* AUTH_IDX_ROOT */));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
static lws_state_notify_link_t * const app_notifier_list[] = {
|
||||
&nl, NULL
|
||||
};
|
||||
|
||||
extern struct lws_protocols protocol_audio_test;
|
||||
static const struct lws_protocols *protocols[] = {
|
||||
&protocol_audio_test,
|
||||
#if defined(LWS_SS_USE_SSPC)
|
||||
lws_sspc_protocols,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
int n = 0;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
lws_cmdline_option_handle_builtin(argc, argv, &info);
|
||||
|
||||
lwsl_user("LWS secure streams - Alexa voice test [-d<verb>]\n");
|
||||
|
||||
info.fd_limit_per_thread = 1 + 6 + 1;
|
||||
#if !defined(LWS_SS_USE_SSPC)
|
||||
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
|
||||
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
info.pss_policies_json = default_ss_policy;
|
||||
#else
|
||||
{
|
||||
const char *p;
|
||||
|
||||
/* connect to ssproxy via UDS by default, else via
|
||||
* tcp connection to this port */
|
||||
if ((p = lws_cmdline_option(argc, argv, "-p")))
|
||||
info.ss_proxy_port = atoi(p);
|
||||
|
||||
/* UDS "proxy.ss.lws" in abstract namespace, else this socket
|
||||
* path; when -p given this can specify the network interface
|
||||
* to bind to */
|
||||
if ((p = lws_cmdline_option(argc, argv, "-i")))
|
||||
info.ss_proxy_bind = p;
|
||||
|
||||
/* if -p given, -a specifies the proxy address to connect to */
|
||||
if ((p = lws_cmdline_option(argc, argv, "-a")))
|
||||
info.ss_proxy_address = p;
|
||||
}
|
||||
#endif
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
info.pprotocols = protocols;
|
||||
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
info.detailed_latency_cb = lws_det_lat_plot_cb;
|
||||
info.detailed_latency_filepath = "/tmp/lws-latency-ssproxy";
|
||||
#endif
|
||||
|
||||
/* integrate us with lws system state management when context created */
|
||||
nl.name = "app";
|
||||
nl.notify_cb = app_system_state_nf;
|
||||
info.register_notifier_list = app_notifier_list;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* create an explicit vhost so the sound protocol is initialized */
|
||||
|
||||
info.vhost_name = "asound";
|
||||
if (!lws_create_vhost(context, &info)) {
|
||||
lwsl_err("lws init failed\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* the event loop */
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 0);
|
||||
|
||||
bail:
|
||||
lws_context_destroy(context);
|
||||
lwsl_user("Completed\n");
|
||||
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,8 @@
|
|||
typedef int (*mp3_done_cb)(void *opaque);
|
||||
|
||||
int
|
||||
play_mp3(mpg123_handle *mh, mp3_done_cb cb, void *opaque);
|
||||
|
||||
|
||||
int
|
||||
spool_capture(uint8_t *buf, size_t len);
|
|
@ -0,0 +1,91 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-minimal-secure-streams-avs)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_ROLE_H1 1 requirements)
|
||||
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
|
||||
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} main.c avs.c)
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
add_compile_options(-DLWS_SS_USE_SSPC)
|
||||
|
||||
add_executable(${SAMP}-client main-client.c avs.c)
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP}-client websockets_shared)
|
||||
add_dependencies(${SAMP}-client websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP}-client websockets)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endif()
|
418
minimal-examples/secure-streams/minimal-secure-streams-avs/avs.c
Normal file
418
minimal-examples/secure-streams/minimal-secure-streams-avs/avs.c
Normal file
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
* lws-minimal-secure-streams-avs
|
||||
*
|
||||
* Written in 2019-2020 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* This sends a canned WAV and received (and discards) the mp3 response.
|
||||
* However it rate-limits the response reception to manage a small ringbuffer
|
||||
* using ss / h2 flow control apis, reflecting consumption at 64kbps and only
|
||||
* and 8KB buffer, indtended to model optimizing rx buffering on mp3 playback
|
||||
* on a constrained device.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
extern int interrupted, bad;
|
||||
static struct lws_ss_handle *hss_avs_event, *hss_avs_sync;
|
||||
static uint8_t *wav;
|
||||
static size_t wav_len;
|
||||
|
||||
typedef struct ss_avs_event {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
struct lejp_ctx jctx;
|
||||
} ss_avs_event_t;
|
||||
|
||||
typedef struct ss_avs_metadata {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
struct lejp_ctx jctx;
|
||||
size_t pos;
|
||||
|
||||
/*
|
||||
* We simulate a ringbuffer that is used up by a sul at 64Kbit/sec
|
||||
* rate, and managed at the same rate using tx credit
|
||||
*/
|
||||
|
||||
lws_sorted_usec_list_t sul;
|
||||
uint8_t buf[8192];
|
||||
int head;
|
||||
int tail;
|
||||
|
||||
char filled;
|
||||
|
||||
} ss_avs_metadata_t;
|
||||
|
||||
static const char *metadata = "{"
|
||||
"\"event\": {"
|
||||
"\"header\": {"
|
||||
"\"namespace\": \"SpeechRecognizer\","
|
||||
"\"name\": \"Recognize\","
|
||||
"\"messageId\": \"message-123\","
|
||||
"\"dialogRequestId\": \"dialog-request-321\""
|
||||
"},"
|
||||
"\"payload\": {"
|
||||
"\"profile\":" "\"CLOSE_TALK\","
|
||||
"\"format\":" "\"AUDIO_L16_RATE_16000_CHANNELS_1\""
|
||||
"}"
|
||||
"}"
|
||||
"}";
|
||||
|
||||
/*
|
||||
* avs metadata
|
||||
*/
|
||||
|
||||
static void
|
||||
use_buffer_50ms(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
ss_avs_metadata_t *m = lws_container_of(sul, ss_avs_metadata_t, sul);
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
size_t n;
|
||||
int e;
|
||||
|
||||
/*
|
||||
* Use up 50ms-worth (8KB / 20) == 401 bytes of buffered data
|
||||
*/
|
||||
|
||||
/* remaining data in buffer */
|
||||
n = ((m->head - m->tail) % sizeof(m->buf));
|
||||
lwsl_info("%s: avail %d\n", __func__, (int)n);
|
||||
|
||||
if (n < 401)
|
||||
lwsl_err("%s: underrun\n", __func__);
|
||||
|
||||
m->tail = (m->tail + 401) % sizeof(m->buf);
|
||||
n = ((m->head - m->tail) % sizeof(m->buf));
|
||||
|
||||
e = lws_ss_get_est_peer_tx_credit(m->ss);
|
||||
|
||||
lwsl_info("%s: avail after: %d, curr est %d\n", __func__, (int)n, e);
|
||||
|
||||
if (n < (sizeof(m->buf) * 2) / 3 && e < (int)(sizeof(m->buf) - 1 - n)) {
|
||||
lwsl_info("%s: requesting additional %d\n", __func__,
|
||||
(int)(sizeof(m->buf) - 1 - e - n));
|
||||
lws_ss_add_peer_tx_credit(m->ss, (sizeof(m->buf) - 1 - e - n));
|
||||
}
|
||||
|
||||
lws_sul_schedule(context, 0, &m->sul, use_buffer_50ms,
|
||||
50 * LWS_US_PER_MS);
|
||||
}
|
||||
|
||||
static int
|
||||
ss_avs_metadata_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
ss_avs_metadata_t *m = (ss_avs_metadata_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
size_t n, n1;
|
||||
|
||||
lwsl_notice("%s: rideshare %s, len %d, flags 0x%x\n", __func__,
|
||||
lws_ss_rideshare(m->ss), (int)len, flags);
|
||||
// lwsl_hexdump_warn(buf, len);
|
||||
|
||||
n = sizeof(m->buf) - ((m->head - m->tail) % sizeof(m->buf));
|
||||
lwsl_info("%s: len %d, buf h %d, t %d, space %d\n", __func__,
|
||||
(int)len, (int)m->head, (int)m->tail, (int)n);
|
||||
lws_ss_get_est_peer_tx_credit(m->ss);
|
||||
assert (len <= n);
|
||||
|
||||
if (m->head < m->tail) /* |****h-------t**| */
|
||||
memcpy(&m->buf[m->head], buf, len);
|
||||
else { /* |---t*****h-----| */
|
||||
n1 = sizeof(m->buf) - m->head;
|
||||
if (len < n1)
|
||||
n1 = len;
|
||||
memcpy(&m->buf[m->head], buf, n1);
|
||||
if (n1 != len)
|
||||
memcpy(m->buf, buf, len - n1);
|
||||
}
|
||||
|
||||
m->head = (m->head + len) % sizeof(m->buf);
|
||||
|
||||
lws_sul_schedule(context, 0, &m->sul, use_buffer_50ms,
|
||||
50 * LWS_US_PER_MS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_avs_metadata_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags)
|
||||
{
|
||||
ss_avs_metadata_t *m = (ss_avs_metadata_t *)userobj;
|
||||
//struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
size_t tot;
|
||||
|
||||
if ((long)m->pos < 0) {
|
||||
*len = 0;
|
||||
lwsl_notice("%s: skip tx\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
lwsl_notice("%s: rideshare '%s'\n", __func__, lws_ss_rideshare(m->ss));
|
||||
|
||||
if (!strcmp(lws_ss_rideshare(m->ss), "avs_audio")) {
|
||||
/* audio rideshare */
|
||||
|
||||
if (!m->pos)
|
||||
*flags |= LWSSS_FLAG_SOM;
|
||||
|
||||
if (*len > wav_len - m->pos)
|
||||
*len = wav_len - m->pos;
|
||||
|
||||
memcpy(buf, wav + m->pos, *len);
|
||||
m->pos += *len;
|
||||
|
||||
if (m->pos == wav_len) {
|
||||
*flags |= LWSSS_FLAG_EOM;
|
||||
lwsl_info("%s: tx done\n", __func__);
|
||||
m->pos = (long)-1l; /* ban subsequent until new stream */
|
||||
} else
|
||||
lws_ss_request_tx(m->ss);
|
||||
|
||||
lwsl_hexdump_info(buf, *len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* metadata part */
|
||||
|
||||
tot = strlen(metadata);
|
||||
|
||||
if (!m->pos)
|
||||
*flags |= LWSSS_FLAG_SOM;
|
||||
|
||||
if (*len > tot - m->pos)
|
||||
*len = tot - m->pos;
|
||||
|
||||
memcpy(buf, metadata + m->pos, *len);
|
||||
|
||||
m->pos += *len;
|
||||
|
||||
if (m->pos == tot) {
|
||||
*flags |= LWSSS_FLAG_EOM;
|
||||
m->pos = 0; /* for next time */
|
||||
lws_ss_request_tx(m->ss);
|
||||
}
|
||||
|
||||
lwsl_hexdump_info(buf, *len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_avs_metadata_state(void *userobj, void *sh,
|
||||
lws_ss_constate_t state, lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
|
||||
ss_avs_metadata_t *m = (ss_avs_metadata_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
|
||||
lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
|
||||
(unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
lwsl_user("%s: CREATING\n", __func__);
|
||||
lws_ss_client_connect(m->ss);
|
||||
m->pos = 0;
|
||||
break;
|
||||
case LWSSSCS_CONNECTING:
|
||||
break;
|
||||
case LWSSSCS_CONNECTED:
|
||||
lws_ss_request_tx(m->ss);
|
||||
break;
|
||||
case LWSSSCS_ALL_RETRIES_FAILED:
|
||||
/* for this demo app, we want to exit on fail to connect */
|
||||
case LWSSSCS_DISCONNECTED:
|
||||
/* for this demo app, we want to exit after complete flow */
|
||||
lws_sul_schedule(context, 0, &m->sul, use_buffer_50ms,
|
||||
LWS_SET_TIMER_USEC_CANCEL);
|
||||
interrupted = 1;
|
||||
break;
|
||||
case LWSSSCS_DESTROYING:
|
||||
lws_sul_schedule(context, 0, &m->sul, use_buffer_50ms,
|
||||
LWS_SET_TIMER_USEC_CANCEL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* avs event
|
||||
*/
|
||||
|
||||
static int
|
||||
ss_avs_event_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
ss_avs_event_t *m = (ss_avs_event_t *)userobj;
|
||||
// struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
|
||||
lwsl_notice("%s: rideshare %s, len %d, flags 0x%x\n", __func__,
|
||||
lws_ss_rideshare(m->ss), (int)len, flags);
|
||||
|
||||
//lwsl_hexdump_warn(buf, len);
|
||||
|
||||
bad = 0; /* for this demo, receiving something here == success */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ss_avs_event_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags)
|
||||
{
|
||||
ss_avs_event_t *m = (ss_avs_event_t *)userobj;
|
||||
lwsl_notice("%s: rideshare %s\n", __func__, lws_ss_rideshare(m->ss));
|
||||
|
||||
return 1; /* don't transmit anything */
|
||||
}
|
||||
|
||||
static int
|
||||
ss_avs_event_state(void *userobj, void *sh,
|
||||
lws_ss_constate_t state, lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
ss_avs_event_t *m = (ss_avs_event_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
lws_ss_info_t ssi;
|
||||
|
||||
lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
|
||||
(unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
case LWSSSCS_CONNECTING:
|
||||
break;
|
||||
case LWSSSCS_CONNECTED:
|
||||
if (hss_avs_sync)
|
||||
break;
|
||||
|
||||
lwsl_notice("%s: starting the second avs stream\n", __func__);
|
||||
|
||||
/*
|
||||
* When we have established the event stream, we must POST
|
||||
* on another stream within 10s
|
||||
*/
|
||||
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.handle_offset = offsetof(ss_avs_metadata_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(ss_avs_metadata_t,
|
||||
opaque_data);
|
||||
ssi.rx = ss_avs_metadata_rx;
|
||||
ssi.tx = ss_avs_metadata_tx;
|
||||
ssi.state = ss_avs_metadata_state;
|
||||
ssi.user_alloc = sizeof(ss_avs_metadata_t);
|
||||
ssi.streamtype = "avs_metadata";
|
||||
|
||||
/*
|
||||
* We want to allow the other side to fill our buffer, but no
|
||||
* more. But it's a bit tricky when the payload is inside
|
||||
* framing like multipart MIME and contains other parts
|
||||
*/
|
||||
|
||||
ssi.manual_initial_tx_credit =
|
||||
sizeof(((ss_avs_metadata_t *)0)->buf) / 2;
|
||||
|
||||
if (lws_ss_create(context, 0, &ssi, context, &hss_avs_sync,
|
||||
NULL, NULL)) {
|
||||
lwsl_err("%s: failed to create avs metadata secstream\n",
|
||||
__func__);
|
||||
}
|
||||
break;
|
||||
case LWSSSCS_ALL_RETRIES_FAILED:
|
||||
/* for this demo app, we want to exit on fail to connect */
|
||||
interrupted = 1;
|
||||
break;
|
||||
case LWSSSCS_DISCONNECTED:
|
||||
break;
|
||||
case LWSSSCS_DESTROYING:
|
||||
lwsl_notice("%s: DESTROYING\n", __func__);
|
||||
if (wav) {
|
||||
free(wav);
|
||||
wav = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
avs_example_start(struct lws_context *context)
|
||||
{
|
||||
lws_ss_info_t ssi;
|
||||
struct stat stat;
|
||||
int fd;
|
||||
|
||||
if (hss_avs_event)
|
||||
return 0;
|
||||
|
||||
fd = open("./year.wav", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
lwsl_err("%s: failed to open wav file\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
if (fstat(fd, &stat) < 0) {
|
||||
lwsl_err("%s: failed to stat wav file\n", __func__);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
|
||||
wav_len = stat.st_size;
|
||||
wav = malloc(wav_len);
|
||||
if (!wav) {
|
||||
lwsl_err("%s: failed to alloc wav buffer", __func__);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
if (read(fd, wav, wav_len) != (int)wav_len) {
|
||||
lwsl_err("%s: failed to read wav\n", __func__);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
lwsl_user("%s: Starting AVS stream\n", __func__);
|
||||
|
||||
/* AVS wants us to establish the long poll event stream first */
|
||||
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.handle_offset = offsetof(ss_avs_event_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(ss_avs_event_t, opaque_data);
|
||||
ssi.rx = ss_avs_event_rx;
|
||||
ssi.tx = ss_avs_event_tx;
|
||||
ssi.state = ss_avs_event_state;
|
||||
ssi.user_alloc = sizeof(ss_avs_event_t);
|
||||
ssi.streamtype = "avs_event";
|
||||
|
||||
if (lws_ss_create(context, 0, &ssi, context, &hss_avs_event, NULL, NULL)) {
|
||||
lwsl_err("%s: failed to create avs event secure stream\n",
|
||||
__func__);
|
||||
free(wav);
|
||||
wav = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
close(fd);
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* lws-minimal-secure-streams-avs
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
extern int
|
||||
avs_example_start(struct lws_context *context);
|
||||
|
||||
int interrupted, bad = 1;
|
||||
static lws_state_notify_link_t nl;
|
||||
|
||||
static const char *canned_root_token_payload =
|
||||
"grant_type=refresh_token"
|
||||
"&refresh_token=Atzr|IwEBIJedGXjDqsU_vMxykqOMg"
|
||||
"SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP"
|
||||
"zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y"
|
||||
"0flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW"
|
||||
"k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE"
|
||||
"iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S"
|
||||
"KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc"
|
||||
"AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI"
|
||||
"xL_hDCcTho8opCVX-6QhJHl6SQFlTw13"
|
||||
"&client_id="
|
||||
"amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d";
|
||||
|
||||
static int
|
||||
app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
|
||||
int current, int target)
|
||||
{
|
||||
struct lws_context *context = lws_system_context_from_system_mgr(mgr);
|
||||
lws_system_blob_t *ab = lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */);
|
||||
size_t size;
|
||||
|
||||
/*
|
||||
* For the things we care about, let's notice if we are trying to get
|
||||
* past them when we haven't solved them yet, and make the system
|
||||
* state wait while we trigger the dependent action.
|
||||
*/
|
||||
switch (target) {
|
||||
case LWS_SYSTATE_REGISTERED:
|
||||
size = lws_system_blob_get_size(ab);
|
||||
if (size)
|
||||
break;
|
||||
|
||||
/* let's register our canned root token so auth can use it */
|
||||
lws_system_blob_direct_set(ab,
|
||||
(const uint8_t *)canned_root_token_payload,
|
||||
strlen(canned_root_token_payload));
|
||||
break;
|
||||
case LWS_SYSTATE_OPERATIONAL:
|
||||
if (current == LWS_SYSTATE_OPERATIONAL)
|
||||
avs_example_start(context);
|
||||
break;
|
||||
case LWS_SYSTATE_POLICY_INVALID:
|
||||
/*
|
||||
* This is a NOP since we used direct set... but in a real
|
||||
* system this could easily change to be done on the heap, then
|
||||
* this would be important
|
||||
*/
|
||||
lws_system_blob_destroy(lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH,
|
||||
1 /* AUTH_IDX_ROOT */));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
static lws_state_notify_link_t * const app_notifier_list[] = {
|
||||
&nl, NULL
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
int n = 0;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
lws_cmdline_option_handle_builtin(argc, argv, &info);
|
||||
|
||||
lwsl_user("LWS secure streams - AVS test client [-d<verb>]\n");
|
||||
|
||||
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
info.fd_limit_per_thread = 1 + 6 + 1;
|
||||
info.protocols = lws_sspc_protocols;
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
info.detailed_latency_cb = lws_det_lat_plot_cb;
|
||||
info.detailed_latency_filepath = "/tmp/lws-latency-ssproxy";
|
||||
#endif
|
||||
|
||||
/* integrate us with lws system state management when context created */
|
||||
nl.name = "app";
|
||||
nl.notify_cb = app_system_state_nf;
|
||||
info.register_notifier_list = app_notifier_list;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* the event loop */
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 0);
|
||||
|
||||
lws_context_destroy(context);
|
||||
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
|
||||
|
||||
return bad;
|
||||
}
|
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
* lws-minimal-secure-streams-avs
|
||||
*
|
||||
* Written in 2019-2020 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
extern int
|
||||
avs_example_start(struct lws_context *context);
|
||||
|
||||
int interrupted, bad = 1;
|
||||
static lws_state_notify_link_t nl;
|
||||
static const char * const default_ss_policy =
|
||||
"{"
|
||||
"\"release\":" "\"01234567\","
|
||||
"\"product\":" "\"myproduct\","
|
||||
"\"schema-version\":" "1,"
|
||||
// "\"via-socks5\":" "\"127.0.0.1:1080\","
|
||||
"\"retry\": [" /* named backoff / retry strategies */
|
||||
"{\"default\": {"
|
||||
"\"backoff\": [" "1000,"
|
||||
"2000,"
|
||||
"3000,"
|
||||
"5000,"
|
||||
"10000"
|
||||
"],"
|
||||
"\"conceal\":" "5,"
|
||||
"\"jitterpc\":" "20,"
|
||||
"\"svalidping\":" "60,"
|
||||
"\"svalidhup\":" "64"
|
||||
"}}"
|
||||
"],"
|
||||
"\"certs\": [" /* named individual certificates in BASE64 DER */
|
||||
"{\"digicert_global_root_g2\": \"" /* api.amazon.com 2038-01 */
|
||||
"MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh"
|
||||
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3"
|
||||
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH"
|
||||
"MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT"
|
||||
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j"
|
||||
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG"
|
||||
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI"
|
||||
"2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx"
|
||||
"1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ"
|
||||
"q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz"
|
||||
"tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ"
|
||||
"vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP"
|
||||
"BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV"
|
||||
"5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY"
|
||||
"1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4"
|
||||
"NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG"
|
||||
"Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91"
|
||||
"8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe"
|
||||
"pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl"
|
||||
"MrY="
|
||||
"\"},"
|
||||
"{\"digicert_global_ca_g2\": \"" /* api.amazon.com 2028-08 */
|
||||
"MIIEizCCA3OgAwIBAgIQDI7gyQ1qiRWIBAYe4kH5rzANBgkqhkiG9w0BAQsFADBh"
|
||||
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3"
|
||||
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH"
|
||||
"MjAeFw0xMzA4MDExMjAwMDBaFw0yODA4MDExMjAwMDBaMEQxCzAJBgNVBAYTAlVT"
|
||||
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxHjAcBgNVBAMTFURpZ2lDZXJ0IEdsb2Jh"
|
||||
"bCBDQSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNIfL7zBYZd"
|
||||
"W9UvhU5L4IatFaxhz1uvPmoKR/uadpFgC4przc/cV35gmAvkVNlW7SHMArZagV+X"
|
||||
"au4CLyMnuG3UsOcGAngLH1ypmTb+u6wbBfpXzYEQQGfWMItYNdSWYb7QjHqXnxr5"
|
||||
"IuYUL6nG6AEfq/gmD6yOTSwyOR2Bm40cZbIc22GoiS9g5+vCShjEbyrpEJIJ7RfR"
|
||||
"ACvmfe8EiRROM6GyD5eHn7OgzS+8LOy4g2gxPR/VSpAQGQuBldYpdlH5NnbQtwl6"
|
||||
"OErXb4y/E3w57bqukPyV93t4CTZedJMeJfD/1K2uaGvG/w/VNfFVbkhJ+Pi474j4"
|
||||
"8V4Rd6rfArMCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0P"
|
||||
"AQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29j"
|
||||
"c3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9jcmw0LmRp"
|
||||
"Z2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwN6A1oDOGMWh0dHA6"
|
||||
"Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwPQYD"
|
||||
"VR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2lj"
|
||||
"ZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFCRuKy3QapJRUSVpAaqaR6aJ50AgMB8GA1Ud"
|
||||
"IwQYMBaAFE4iVCAYlebjbuYP+vq5Eu0GF485MA0GCSqGSIb3DQEBCwUAA4IBAQAL"
|
||||
"OYSR+ZfrqoGvhOlaOJL84mxZvzbIRacxAxHhBsCsMsdaVSnaT0AC9aHesO3ewPj2"
|
||||
"dZ12uYf+QYB6z13jAMZbAuabeGLJ3LhimnftiQjXS8X9Q9ViIyfEBFltcT8jW+rZ"
|
||||
"8uckJ2/0lYDblizkVIvP6hnZf1WZUXoOLRg9eFhSvGNoVwvdRLNXSmDmyHBwW4co"
|
||||
"atc7TlJFGa8kBpJIERqLrqwYElesA8u49L3KJg6nwd3jM+/AVTANlVlOnAM2BvjA"
|
||||
"jxSZnE0qnsHhfTuvcqdFuhOWKU4Z0BqYBvQ3lBetoxi6PrABDJXWKTUgNX31EGDk"
|
||||
"92hiHuwZ4STyhxGs6QiA"
|
||||
"\"},"
|
||||
"{\"starfield_services_root_ca\": \""
|
||||
"MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx"
|
||||
"EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT"
|
||||
"HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs"
|
||||
"ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5"
|
||||
"MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD"
|
||||
"VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy"
|
||||
"ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy"
|
||||
"dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI"
|
||||
"hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p"
|
||||
"OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2"
|
||||
"8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K"
|
||||
"Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe"
|
||||
"hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk"
|
||||
"6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw"
|
||||
"DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q"
|
||||
"AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI"
|
||||
"bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB"
|
||||
"ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z"
|
||||
"qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd"
|
||||
"iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn"
|
||||
"0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN"
|
||||
"sSi6"
|
||||
"\"},"
|
||||
"{\"starfield_class_2_ca\": \""
|
||||
"MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl"
|
||||
"MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp"
|
||||
"U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw"
|
||||
"NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE"
|
||||
"ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp"
|
||||
"ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3"
|
||||
"DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf"
|
||||
"8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN"
|
||||
"+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0"
|
||||
"X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa"
|
||||
"K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA"
|
||||
"1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G"
|
||||
"A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR"
|
||||
"zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0"
|
||||
"YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD"
|
||||
"bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w"
|
||||
"DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3"
|
||||
"L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D"
|
||||
"eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl"
|
||||
"xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp"
|
||||
"VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY"
|
||||
"WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q="
|
||||
"\"}"
|
||||
"],"
|
||||
"\"trust_stores\": [" /* named cert chains */
|
||||
"{" /* chain for alexa.na.gateway.devices.a2z.com */
|
||||
"\"name\": \"avs_via_starfield\","
|
||||
"\"stack\": ["
|
||||
"\"starfield_class_2_ca\","
|
||||
"\"starfield_services_root_ca\""
|
||||
"]"
|
||||
"},"
|
||||
"{" /* chain for api.amazon.com */
|
||||
"\"name\": \"api_amazon_com\","
|
||||
"\"stack\": ["
|
||||
"\"digicert_global_ca_g2\","
|
||||
"\"digicert_global_root_g2\""
|
||||
"]"
|
||||
"}"
|
||||
"],"
|
||||
"\"s\": [" /* the supported stream types */
|
||||
"{\"api_amazon_com_auth\": {"
|
||||
"\"endpoint\":" "\"api.amazon.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h1\","
|
||||
"\"http_method\":" "\"POST\","
|
||||
"\"http_url\":" "\"auth/o2/token\","
|
||||
"\"plugins\":" "[],"
|
||||
"\"opportunistic\":" "true,"
|
||||
"\"tls\":" "true,"
|
||||
"\"h2q_oflow_txcr\":" "true,"
|
||||
"\"http_www_form_urlencoded\":" "true,"
|
||||
"\"http_no_content_length\":" "true,"
|
||||
"\"retry\":" "\"default\","
|
||||
"\"tls_trust_store\":" "\"api_amazon_com\""
|
||||
"}},"
|
||||
"{\"avs_event\": {"
|
||||
"\"endpoint\":" "\"alexa.na.gateway.devices.a2z.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h2\","
|
||||
"\"http_method\":" "\"GET\","
|
||||
"\"http_url\":" "\"v20160207/directives\","
|
||||
"\"h2q_oflow_txcr\":" "true,"
|
||||
"\"http_auth_header\":" "\"authorization:\","
|
||||
"\"http_auth_preamble\":" "\"Bearer \","
|
||||
"\"nailed_up\":" "true,"
|
||||
"\"long_poll\":" "true,"
|
||||
"\"retry\":" "\"default\","
|
||||
"\"plugins\":" "[],"
|
||||
"\"tls\":" "true,"
|
||||
"\"tls_trust_store\":" "\"avs_via_starfield\""
|
||||
"}},"
|
||||
"{\"avs_metadata\": {"
|
||||
"\"endpoint\":" "\"alexa.na.gateway.devices.a2z.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h2\","
|
||||
"\"http_method\":" "\"POST\","
|
||||
"\"http_url\":" "\"v20160207/events\","
|
||||
"\"http_no_content_length\":" "true,"
|
||||
"\"h2q_oflow_txcr\":" "true,"
|
||||
"\"http_auth_header\":" "\"authorization:\","
|
||||
"\"http_auth_preamble\":" "\"Bearer \","
|
||||
"\"http_multipart_name\":" "\"metadata\","
|
||||
"\"http_mime_content_type\":" "\"application/json; charset=UTF-8\","
|
||||
"\"rideshare\":" "\"avs_audio\","
|
||||
"\"retry\":" "\"default\","
|
||||
"\"plugins\":" "[],"
|
||||
"\"tls\":" "true,"
|
||||
"\"tls_trust_store\":" "\"avs_via_starfield\""
|
||||
"}},"
|
||||
"{\"avs_audio\": {"
|
||||
"\"endpoint\":" "\"alexa.na.gateway.devices.a2z.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h2\","
|
||||
"\"http_method\":" "\"POST\","
|
||||
"\"http_url\":" "\"v20160207/events\","
|
||||
"\"http_no_content_length\":" "true,"
|
||||
"\"plugins\":" "[],"
|
||||
"\"tls\":" "true,"
|
||||
"\"h2q_oflow_txcr\":" "true,"
|
||||
"\"http_auth_header\":" "\"authorization:\","
|
||||
"\"http_auth_preamble\":" "\"Bearer \","
|
||||
"\"http_multipart_name\":" "\"audio\","
|
||||
"\"http_mime_content_type\":" "\"application/octet-stream\","
|
||||
"\"retry\":" "\"default\","
|
||||
"\"tls_trust_store\":" "\"avs_via_starfield\""
|
||||
"}}"
|
||||
"]"
|
||||
"}"
|
||||
;
|
||||
|
||||
static const char *canned_root_token_payload =
|
||||
"grant_type=refresh_token"
|
||||
"&refresh_token=Atzr|IwEBIJedGXjDqsU_vMxykqOMg"
|
||||
"SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP"
|
||||
"zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y"
|
||||
"0flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW"
|
||||
"k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE"
|
||||
"iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S"
|
||||
"KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc"
|
||||
"AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI"
|
||||
"xL_hDCcTho8opCVX-6QhJHl6SQFlTw13"
|
||||
"&client_id="
|
||||
"amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d";
|
||||
|
||||
static int
|
||||
app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
|
||||
int current, int target)
|
||||
{
|
||||
struct lws_context *context = lws_system_context_from_system_mgr(mgr);
|
||||
lws_system_blob_t *ab = lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */);
|
||||
size_t size;
|
||||
|
||||
/*
|
||||
* For the things we care about, let's notice if we are trying to get
|
||||
* past them when we haven't solved them yet, and make the system
|
||||
* state wait while we trigger the dependent action.
|
||||
*/
|
||||
switch (target) {
|
||||
case LWS_SYSTATE_REGISTERED:
|
||||
size = lws_system_blob_get_size(ab);
|
||||
if (size)
|
||||
break;
|
||||
|
||||
/* let's register our canned root token so auth can use it */
|
||||
lws_system_blob_direct_set(ab,
|
||||
(const uint8_t *)canned_root_token_payload,
|
||||
strlen(canned_root_token_payload));
|
||||
break;
|
||||
case LWS_SYSTATE_OPERATIONAL:
|
||||
if (current == LWS_SYSTATE_OPERATIONAL)
|
||||
avs_example_start(context);
|
||||
break;
|
||||
case LWS_SYSTATE_POLICY_INVALID:
|
||||
/*
|
||||
* This is a NOP since we used direct set... but in a real
|
||||
* system this could easily change to be done on the heap, then
|
||||
* this would be important
|
||||
*/
|
||||
lws_system_blob_destroy(lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH,
|
||||
1 /* AUTH_IDX_ROOT */));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
static lws_state_notify_link_t * const app_notifier_list[] = {
|
||||
&nl, NULL
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
int n = 0;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
lws_cmdline_option_handle_builtin(argc, argv, &info);
|
||||
|
||||
lwsl_user("LWS secure streams - AVS test [-d<verb>]\n");
|
||||
|
||||
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
|
||||
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
info.fd_limit_per_thread = 1 + 6 + 1;
|
||||
info.pss_policies_json = default_ss_policy;
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
|
||||
#if defined(LWS_SS_USE_SSPC)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
/* connect to ssproxy via UDS by default, else via
|
||||
* tcp connection to this port */
|
||||
if ((p = lws_cmdline_option(argc, argv, "-p")))
|
||||
info.ss_proxy_port = atoi(p);
|
||||
|
||||
/* UDS "proxy.ss.lws" in abstract namespace, else this socket
|
||||
* path; when -p given this can specify the network interface
|
||||
* to bind to */
|
||||
if ((p = lws_cmdline_option(argc, argv, "-i")))
|
||||
info.ss_proxy_bind = p;
|
||||
|
||||
/* if -p given, -a specifies the proxy address to connect to */
|
||||
if ((p = lws_cmdline_option(argc, argv, "-a")))
|
||||
info.ss_proxy_address = p;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
info.detailed_latency_cb = lws_det_lat_plot_cb;
|
||||
info.detailed_latency_filepath = "/tmp/lws-latency-ssproxy";
|
||||
#endif
|
||||
|
||||
/* integrate us with lws system state management when context created */
|
||||
nl.name = "app";
|
||||
nl.notify_cb = app_system_state_nf;
|
||||
info.register_notifier_list = app_notifier_list;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* the event loop */
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 0);
|
||||
|
||||
lws_context_destroy(context);
|
||||
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
|
||||
|
||||
return bad;
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,81 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-minimal-secure-streams-client-tx)
|
||||
set(SRCS minimal-secure-streams-client-tx.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_ROLE_H1 1 requirements)
|
||||
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
|
||||
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
|
||||
require_lws_config(LWS_WITH_SECURE_STREAMS_PROXY_API 1 requirements)
|
||||
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets)
|
||||
endif()
|
||||
endif()
|
|
@ -0,0 +1,64 @@
|
|||
# lws minimal secure streams
|
||||
|
||||
The application goes to https://warmcat.com and reads index.html there.
|
||||
|
||||
It does it using Secure Streams... the main code in minimal-secure-streams.c
|
||||
just sets up the context and opens a secure stream of type "mintest".
|
||||
|
||||
The handler for state changes and payloads for "mintest" is in ss-myss.c
|
||||
|
||||
The information about how a "mintest" stream should connect and the
|
||||
protocol it uses is kept separated in policy-database.c
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
Commandline option|Meaning
|
||||
---|---
|
||||
-d <loglevel>|Debug verbosity in decimal, eg, -d15
|
||||
-f| Force connecting to the wrong endpoint to check backoff retry flow
|
||||
-p| Run as proxy server for clients to connect to over unix domain socket
|
||||
|
||||
```
|
||||
[2019/08/12 07:16:11:0045] USR: LWS minimal secure streams [-d<verbosity>] [-f]
|
||||
[2019/08/12 07:16:12:6102] USR: myss_state: LWSSSCS_CREATING, ord 0x0
|
||||
[2019/08/12 07:16:12:6107] USR: myss_state: LWSSSCS_POLL, ord 0x0
|
||||
[2019/08/12 07:16:12:6117] N: lws_ss_client_connect: connecting h1get warmcat.com /
|
||||
[2019/08/12 07:16:12:6118] USR: myss_state: LWSSSCS_CONNECTING, ord 0x0
|
||||
[2019/08/12 07:16:13:4171] USR: myss_state: LWSSSCS_CONNECTED, ord 0x0
|
||||
[2019/08/12 07:16:13:4222] USR: myss_rx: len 1024, flags: 1
|
||||
[2019/08/12 07:16:13:4243] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4245] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4246] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4247] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4252] USR: myss_rx: len 1015, flags: 0
|
||||
[2019/08/12 07:16:13:4264] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4265] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4266] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4267] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4269] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4270] USR: myss_rx: len 1015, flags: 0
|
||||
[2019/08/12 07:16:13:4278] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4279] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4280] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4281] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4282] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4284] USR: myss_rx: len 1015, flags: 0
|
||||
[2019/08/12 07:16:13:4287] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4288] USR: myss_rx: len 947, flags: 0
|
||||
[2019/08/12 07:16:13:4293] USR: myss_rx: len 0, flags: 2
|
||||
[2019/08/12 07:16:13:4399] USR: myss_state: LWSSSCS_DISCONNECTED, ord 0x0
|
||||
[2019/08/12 07:16:13:4761] USR: myss_state: LWSSSCS_DESTROYING, ord 0x0
|
||||
[2019/08/12 07:16:13:4781] USR: Completed: OK
|
||||
```
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* lws-minimal-secure-streams-tx
|
||||
*
|
||||
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
*
|
||||
* This demonstrates tx from secure streams.
|
||||
*
|
||||
* It opens a stream and fires small 80-byte payloads on it at 50Hz (20ms)
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define PKT_SIZE 80
|
||||
#define RATE_US 50000
|
||||
|
||||
static int interrupted, bad = 1;
|
||||
|
||||
typedef struct myss {
|
||||
struct lws_sspc_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
lws_sorted_usec_list_t sul;
|
||||
|
||||
int count;
|
||||
char due;
|
||||
} myss_t;
|
||||
|
||||
/* secure streams payload interface */
|
||||
|
||||
static int
|
||||
myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
// myss_t *m = (myss_t *)userobj;
|
||||
|
||||
//lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags);
|
||||
//lwsl_hexdump_info(buf, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
txcb(struct lws_sorted_usec_list *sul)
|
||||
{
|
||||
myss_t *m = lws_container_of(sul, myss_t, sul);
|
||||
|
||||
if (m->count == 1000) {
|
||||
interrupted = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
m->due = 1;
|
||||
lws_sspc_request_tx(m->ss);
|
||||
|
||||
lws_sul_schedule(lws_sspc_get_context(m->ss), 0, &m->sul, txcb, RATE_US);
|
||||
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,
|
||||
int *flags)
|
||||
{
|
||||
myss_t *m = (myss_t *)userobj;
|
||||
|
||||
if (!m->due)
|
||||
return 0;
|
||||
|
||||
m->due = 0;
|
||||
|
||||
if (lws_get_random(lws_sspc_get_context(m->ss), buf, PKT_SIZE) != PKT_SIZE)
|
||||
return 1;
|
||||
|
||||
*len = PKT_SIZE;
|
||||
*flags = 0;
|
||||
if (!m->count)
|
||||
*flags |= LWSSS_FLAG_SOM;
|
||||
if (m->count == 999) {
|
||||
*flags |= LWSSS_FLAG_EOM;
|
||||
lwsl_user("%s: sent final packet\n", __func__);
|
||||
bad = 0;
|
||||
}
|
||||
|
||||
m->count++;
|
||||
|
||||
lws_sul_schedule(lws_sspc_get_context(m->ss), 0, &m->sul, txcb, RATE_US);
|
||||
|
||||
// lwsl_user("%s: sending pkt %d\n", __func__, m->count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
myss_state(void *userobj, void *sh, lws_ss_constate_t state,
|
||||
lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
myss_t *m = (myss_t *)userobj;
|
||||
struct lws_context *context = lws_sspc_get_context(m->ss);
|
||||
|
||||
lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
|
||||
(unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
break;
|
||||
case LWSSSCS_CONNECTED:
|
||||
lws_sul_schedule(context, 0, &m->sul, txcb, RATE_US);
|
||||
break;
|
||||
case LWSSSCS_DISCONNECTED:
|
||||
lws_sul_schedule(context, 0, &m->sul, txcb,
|
||||
LWS_SET_TIMER_USEC_CANCEL);
|
||||
break;
|
||||
case LWSSSCS_ALL_RETRIES_FAILED:
|
||||
/* if we're out of retries, we want to close the app and FAIL */
|
||||
interrupted = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
lws_ss_info_t ssi;
|
||||
const char *p;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-d")))
|
||||
logs = atoi(p);
|
||||
|
||||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS secure streams client TX [-d<verb>]\n");
|
||||
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
|
||||
info.options = //LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
|
||||
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
info.fd_limit_per_thread = 1 + 6 + 1;
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
info.protocols = lws_sspc_protocols;
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
info.detailed_latency_cb = lws_det_lat_plot_cb;
|
||||
info.detailed_latency_filepath = "/tmp/lws-latency-ssclient";
|
||||
#endif
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We're requesting a secure stream via proxy... where and how this
|
||||
* connects are details managed by the proxy policy
|
||||
*/
|
||||
|
||||
memset(&ssi, 0, sizeof ssi);
|
||||
ssi.handle_offset = offsetof(myss_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(myss_t, opaque_data);
|
||||
ssi.rx = myss_rx;
|
||||
ssi.tx = myss_tx;
|
||||
ssi.state = myss_state;
|
||||
ssi.user_alloc = sizeof(myss_t);
|
||||
ssi.streamtype = "spam";
|
||||
|
||||
if (lws_sspc_create(context, 0, &ssi, NULL, NULL, NULL, NULL)) {
|
||||
lwsl_err("%s: create secure stream failed\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* the event loop */
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 0);
|
||||
|
||||
bail:
|
||||
lws_context_destroy(context);
|
||||
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
|
||||
|
||||
return bad;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-minimal-secure-streams-proxy)
|
||||
set(SRCS main.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_ROLE_H1 1 requirements)
|
||||
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
|
||||
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
|
||||
require_lws_config(LWS_WITH_SECURE_STREAMS_PROXY_API 1 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets)
|
||||
endif()
|
||||
endif()
|
|
@ -0,0 +1,33 @@
|
|||
# lws minimal secure streams proxy
|
||||
|
||||
Operates as a secure streams proxy, by default on a listening unix domain socket
|
||||
"proxy.ss.lws" in the Linux abstract namespace.
|
||||
|
||||
Give -p <port> to have it listen on a specific tcp port instead.
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
Commandline option|Meaning
|
||||
---|---
|
||||
-d <loglevel>|Debug verbosity in decimal, eg, -d15
|
||||
-f| Force connecting to the wrong endpoint to check backoff retry flow
|
||||
-p <port>|If not given, proxy listens on a Unix Domain Socket, if given listen on specified tcp port
|
||||
-i <iface>|Optionally specify the UDS path (no -p) or network interface to bind to (if -p also given)
|
||||
|
||||
```
|
||||
[2020/02/26 15:41:27:5768] U: LWS secure streams Proxy [-d<verb>]
|
||||
[2020/02/26 15:41:27:5770] N: lws_ss_policy_set: 2.064KiB, pad 70%: hardcoded
|
||||
[2020/02/26 15:41:27:5771] N: lws_tls_client_create_vhost_context: using mem client CA cert 1391
|
||||
[2020/02/26 15:41:27:8681] N: lws_ss_policy_set: 4.512KiB, pad 15%: updated
|
||||
[2020/02/26 15:41:27:8682] N: lws_tls_client_create_vhost_context: using mem client CA cert 837
|
||||
[2020/02/26 15:41:27:8683] N: lws_tls_client_create_vhost_context: using mem client CA cert 1043
|
||||
[2020/02/26 15:41:27:8684] N: lws_tls_client_create_vhost_context: using mem client CA cert 1167
|
||||
[2020/02/26 15:41:27:8684] N: lws_tls_client_create_vhost_context: using mem client CA cert 1391
|
||||
[2020/02/26 15:41:28:4226] N: ss_api_amazon_auth_rx: acquired 567-byte api.amazon.com auth token, exp 3600s
|
||||
```
|
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* lws-minimal-secure-streams-proxy
|
||||
*
|
||||
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
*
|
||||
* This is the proxy part for examples built to use it to connect to... it has
|
||||
* the policy and the core SS function, but it doesn't contain any of the user
|
||||
* code "business logic"... that's in the clients.
|
||||
*
|
||||
* The proxy side has the policy and performs the onward connection proxying
|
||||
* fulfilment. The clients state the streamtype name they want and ask for the
|
||||
* client to do the connection part.
|
||||
*
|
||||
* Rideshare information is being parsed out at the proxy side; the SSS RX part
|
||||
* also brings with it rideshare names.
|
||||
*
|
||||
* Metadata is passed back over SSS from the client in the TX messages for the
|
||||
* proxy to use per the policy.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
static int interrupted, bad = 1, port = 0 /* unix domain socket */;
|
||||
static const char *ibind = NULL; /* default to unix domain skt "proxy.ss.lws" */
|
||||
static lws_state_notify_link_t nl;
|
||||
|
||||
/*
|
||||
* We just define enough policy so it can fetch the latest one securely
|
||||
*/
|
||||
|
||||
static const char * const default_ss_policy =
|
||||
"{"
|
||||
"\"release\":" "\"01234567\","
|
||||
"\"product\":" "\"myproduct\","
|
||||
"\"schema-version\":" "1,"
|
||||
"\"retry\": [" /* named backoff / retry strategies */
|
||||
"{\"default\": {"
|
||||
"\"backoff\": [" "1000,"
|
||||
"2000,"
|
||||
"3000,"
|
||||
"5000,"
|
||||
"10000"
|
||||
"],"
|
||||
"\"conceal\":" "5,"
|
||||
"\"jitterpc\":" "20,"
|
||||
"\"svalidping\":" "30,"
|
||||
"\"svalidhup\":" "35"
|
||||
"}}"
|
||||
"],"
|
||||
"\"certs\": [" /* named individual certificates in BASE64 DER */
|
||||
/*
|
||||
* Let's Encrypt certs for warmcat.com / libwebsockets.org
|
||||
*
|
||||
* We fetch the real policy from there using SS and switch to
|
||||
* using that.
|
||||
*/
|
||||
"{\"isrg_root_x1\": \"" /* ISRG ROOT X1 */
|
||||
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw"
|
||||
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh"
|
||||
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4"
|
||||
"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu"
|
||||
"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY"
|
||||
"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc"
|
||||
"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+"
|
||||
"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U"
|
||||
"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW"
|
||||
"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH"
|
||||
"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC"
|
||||
"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv"
|
||||
"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn"
|
||||
"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn"
|
||||
"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw"
|
||||
"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI"
|
||||
"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV"
|
||||
"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq"
|
||||
"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL"
|
||||
"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ"
|
||||
"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK"
|
||||
"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5"
|
||||
"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur"
|
||||
"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC"
|
||||
"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc"
|
||||
"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq"
|
||||
"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA"
|
||||
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d"
|
||||
"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc="
|
||||
"\"},"
|
||||
"{\"LEX3_isrg_root_x1\": \"" /* LE X3 signed by ISRG X1 root */
|
||||
"MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw"
|
||||
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh"
|
||||
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1"
|
||||
"WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg"
|
||||
"RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi"
|
||||
"MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX"
|
||||
"NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf"
|
||||
"89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl"
|
||||
"Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc"
|
||||
"Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz"
|
||||
"uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB"
|
||||
"AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU"
|
||||
"BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB"
|
||||
"FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo"
|
||||
"SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js"
|
||||
"LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF"
|
||||
"BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG"
|
||||
"AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD"
|
||||
"VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB"
|
||||
"ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx"
|
||||
"A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM"
|
||||
"UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2"
|
||||
"DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1"
|
||||
"eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu"
|
||||
"OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw"
|
||||
"p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY"
|
||||
"2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0"
|
||||
"ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR"
|
||||
"PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b"
|
||||
"rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt"
|
||||
"\"}"
|
||||
"],"
|
||||
"\"trust_stores\": [" /* named cert chains */
|
||||
"{"
|
||||
"\"name\": \"le_via_isrg\","
|
||||
"\"stack\": ["
|
||||
"\"isrg_root_x1\","
|
||||
"\"LEX3_isrg_root_x1\""
|
||||
"]"
|
||||
"}"
|
||||
"],"
|
||||
"\"s\": ["
|
||||
"{\"fetch_policy\": {"
|
||||
"\"endpoint\":" "\"warmcat.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h1\","
|
||||
"\"http_method\":" "\"GET\","
|
||||
"\"http_url\":" "\"policy/minimal-proxy.json\","
|
||||
"\"tls\":" "true,"
|
||||
"\"opportunistic\":" "true,"
|
||||
"\"retry\":" "\"default\","
|
||||
"\"tls_trust_store\":" "\"le_via_isrg\""
|
||||
"}}"
|
||||
"}"
|
||||
;
|
||||
|
||||
static const char *canned_root_token_payload =
|
||||
"grant_type=refresh_token"
|
||||
"&refresh_token=Atzr|IwEBIJedGXjDqsU_vMxykqOMg"
|
||||
"SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP"
|
||||
"zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y"
|
||||
"0flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW"
|
||||
"k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE"
|
||||
"iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S"
|
||||
"KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc"
|
||||
"AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI"
|
||||
"xL_hDCcTho8opCVX-6QhJHl6SQFlTw13"
|
||||
"&client_id="
|
||||
"amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d";
|
||||
|
||||
static int
|
||||
app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
|
||||
int current, int target)
|
||||
{
|
||||
struct lws_context *context = lws_system_context_from_system_mgr(mgr);
|
||||
lws_system_blob_t *ab = lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */);
|
||||
size_t size;
|
||||
|
||||
/*
|
||||
* For the things we care about, let's notice if we are trying to get
|
||||
* past them when we haven't solved them yet, and make the system
|
||||
* state wait while we trigger the dependent action.
|
||||
*/
|
||||
switch (target) {
|
||||
case LWS_SYSTATE_REGISTERED:
|
||||
size = lws_system_blob_get_size(ab);
|
||||
if (size)
|
||||
break;
|
||||
|
||||
/* let's register our canned root token so auth can use it */
|
||||
lws_system_blob_direct_set(ab,
|
||||
(const uint8_t *)canned_root_token_payload,
|
||||
strlen(canned_root_token_payload));
|
||||
break;
|
||||
case LWS_SYSTATE_OPERATIONAL:
|
||||
if (current == LWS_SYSTATE_OPERATIONAL)
|
||||
/*
|
||||
* At this point we have DHCP, ntp, system auth token
|
||||
* and we can reasonably create the proxy
|
||||
*/
|
||||
if (lws_ss_proxy_create(context, ibind, port)) {
|
||||
lwsl_err("%s: failed to create ss proxy\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case LWS_SYSTATE_POLICY_INVALID:
|
||||
/*
|
||||
* This is a NOP since we used direct set... but in a real
|
||||
* system this could easily change to be done on the heap, then
|
||||
* this would be important
|
||||
*/
|
||||
lws_system_blob_destroy(lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH,
|
||||
1 /* AUTH_IDX_ROOT */));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static lws_state_notify_link_t * const app_notifier_list[] = {
|
||||
&nl, NULL
|
||||
};
|
||||
|
||||
static void
|
||||
sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
const char *p;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-d")))
|
||||
logs = atoi(p);
|
||||
|
||||
/* connect to ssproxy via UDS by default, else via tcp with this port */
|
||||
if ((p = lws_cmdline_option(argc, argv, "-p")))
|
||||
port = atoi(p);
|
||||
|
||||
/* UDS "proxy.ss.lws" in abstract namespace, else this socket path;
|
||||
* when -p given this can specify the network interface to bind to */
|
||||
if ((p = lws_cmdline_option(argc, argv, "-i")))
|
||||
ibind = p;
|
||||
|
||||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS secure streams Proxy [-d<verb>]\n");
|
||||
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
|
||||
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
|
||||
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
info.fd_limit_per_thread = 1 + 6 + 1;
|
||||
info.pss_policies_json = default_ss_policy;
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
info.detailed_latency_cb = lws_det_lat_plot_cb;
|
||||
info.detailed_latency_filepath = "/tmp/lws-latency-ssproxy";
|
||||
#endif
|
||||
|
||||
/* integrate us with lws system state management when context created */
|
||||
nl.name = "app";
|
||||
nl.notify_cb = app_system_state_nf;
|
||||
info.register_notifier_list = app_notifier_list;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* the event loop */
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 0);
|
||||
|
||||
bad = 0;
|
||||
|
||||
lws_context_destroy(context);
|
||||
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
|
||||
|
||||
return bad;
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-minimal-secure-streams-seq)
|
||||
set(SRCS minimal-secure-streams.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_ROLE_H1 1 requirements)
|
||||
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
|
||||
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets)
|
||||
endif()
|
||||
endif()
|
|
@ -0,0 +1,65 @@
|
|||
# lws minimal sequre streams seq
|
||||
|
||||
The application goes to https://warmcat.com and reads index.html there.
|
||||
|
||||
It does it using Secure Streams... the main code in minimal-secure-streams.c
|
||||
just sets up the context and opens a secure stream of type "mintest".
|
||||
|
||||
The handler for state changes and payloads for "mintest" is in ss-myss.c
|
||||
|
||||
The information about how a "mintest" stream should connect and the
|
||||
protocol it uses is kept separated in policy-database.c
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
Commandline option|Meaning
|
||||
---|---
|
||||
-d <loglevel>|Debug verbosity in decimal, eg, -d15
|
||||
-f| Force connecting to the wrong endpoint to check backoff retry flow
|
||||
|
||||
```
|
||||
$ ./lws-minimal-secure-streams-seq
|
||||
[2018/03/04 14:43:20:8562] USER: LWS minimal http client
|
||||
[2018/03/04 14:43:20:8571] NOTICE: Creating Vhost 'default' port -1, 1 protocols, IPv6 on
|
||||
[2018/03/04 14:43:20:8616] NOTICE: created client ssl context for default
|
||||
[2018/03/04 14:43:20:8617] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com
|
||||
[2018/03/04 14:43:21:1496] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com
|
||||
[2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: incoming content length 26520
|
||||
[2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: client connection up
|
||||
[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1015
|
||||
[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1015
|
||||
[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1015
|
||||
[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1015
|
||||
[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1015
|
||||
[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1015
|
||||
[2018/03/04 14:43:22:3022] USER: RECEIVE_CLIENT_HTTP_READ: read 1024
|
||||
[2018/03/04 14:43:22:3022] USER: RECEIVE_CLIENT_HTTP_READ: read 974
|
||||
[2018/03/04 14:43:22:3022] NOTICE: lws_http_client_read: transaction completed says -1
|
||||
[2018/03/04 14:43:23:3042] USER: Completed
|
||||
```
|
||||
|
||||
|
|
@ -0,0 +1,443 @@
|
|||
/*
|
||||
* lws-minimal-secure-streams-seq
|
||||
*
|
||||
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
*
|
||||
* This demonstrates the a minimal http client using secure streams api.
|
||||
*
|
||||
* It visits https://warmcat.com/ and receives the html page there.
|
||||
*
|
||||
* This is the "secure streams" api equivalent of minimal-http-client...
|
||||
* it shows how to use a sequencer to make it easy to build more complex
|
||||
* schemes on top of this example.
|
||||
*
|
||||
* The layering looks like this
|
||||
*
|
||||
* lifetime
|
||||
*
|
||||
* ------ app ------ process
|
||||
* ---- sequencer ---- process
|
||||
* --- secure stream --- process
|
||||
* ------- wsi ------- connection
|
||||
*
|
||||
* see minimal-secure-streams for a similar example without the sequencer.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
static int interrupted, bad = 1, flag_conn_fail, flag_h1post;
|
||||
static const char * const default_ss_policy =
|
||||
"{"
|
||||
"\"release\":" "\"01234567\","
|
||||
"\"product\":" "\"myproduct\","
|
||||
"\"schema-version\":" "1,"
|
||||
"\"retry\": [" /* named backoff / retry strategies */
|
||||
"{\"default\": {"
|
||||
"\"backoff\": [" "1000,"
|
||||
"2000,"
|
||||
"3000,"
|
||||
"5000,"
|
||||
"10000"
|
||||
"],"
|
||||
"\"conceal\":" "5,"
|
||||
"\"jitterpc\":" "20,"
|
||||
"\"svalidping\":" "300,"
|
||||
"\"svalidhup\":" "310"
|
||||
"}}"
|
||||
"],"
|
||||
"\"certs\": [" /* named individual certificates in BASE64 DER */
|
||||
/*
|
||||
* Need to be in order from root cert... notice sometimes as
|
||||
* with Let's Encrypt there are multiple possible validation
|
||||
* paths, all the pieces for one validation path must be
|
||||
* given, excluding the server cert itself. Let's Encrypt
|
||||
* intermediate is signed by their ISRG Root CA but also is
|
||||
* cross-signed by an IdenTrust intermediate that's widely
|
||||
* deployed in browsers. We use the ISRG path because that
|
||||
* way we can skip the extra IdenTrust root cert.
|
||||
*/
|
||||
"{\"isrg_root_x1\": \"" /* ISRG ROOT X1 */
|
||||
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw"
|
||||
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh"
|
||||
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4"
|
||||
"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu"
|
||||
"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY"
|
||||
"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc"
|
||||
"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+"
|
||||
"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U"
|
||||
"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW"
|
||||
"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH"
|
||||
"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC"
|
||||
"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv"
|
||||
"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn"
|
||||
"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn"
|
||||
"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw"
|
||||
"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI"
|
||||
"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV"
|
||||
"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq"
|
||||
"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL"
|
||||
"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ"
|
||||
"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK"
|
||||
"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5"
|
||||
"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur"
|
||||
"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC"
|
||||
"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc"
|
||||
"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq"
|
||||
"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA"
|
||||
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d"
|
||||
"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc="
|
||||
"\"},"
|
||||
"{\"LEX3_isrg_root_x1\": \"" /* LE X3 signed by ISRG X1 root */
|
||||
"MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw"
|
||||
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh"
|
||||
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1"
|
||||
"WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg"
|
||||
"RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi"
|
||||
"MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX"
|
||||
"NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf"
|
||||
"89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl"
|
||||
"Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc"
|
||||
"Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz"
|
||||
"uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB"
|
||||
"AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU"
|
||||
"BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB"
|
||||
"FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo"
|
||||
"SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js"
|
||||
"LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF"
|
||||
"BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG"
|
||||
"AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD"
|
||||
"VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB"
|
||||
"ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx"
|
||||
"A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM"
|
||||
"UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2"
|
||||
"DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1"
|
||||
"eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu"
|
||||
"OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw"
|
||||
"p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY"
|
||||
"2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0"
|
||||
"ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR"
|
||||
"PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b"
|
||||
"rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt"
|
||||
"\"}"
|
||||
"],"
|
||||
"\"trust_stores\": [" /* named cert chains */
|
||||
"{"
|
||||
"\"name\": \"le_via_isrg\","
|
||||
"\"stack\": ["
|
||||
"\"isrg_root_x1\","
|
||||
"\"LEX3_isrg_root_x1\""
|
||||
"]"
|
||||
"}"
|
||||
"],"
|
||||
"\"s\": [" /* the supported stream types */
|
||||
"{\"mintest\": {"
|
||||
"\"endpoint\":" "\"warmcat.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h1\","
|
||||
"\"http_method\":" "\"GET\","
|
||||
"\"http_url\":" "\"index.html\","
|
||||
"\"plugins\":" "[],"
|
||||
"\"tls\":" "true,"
|
||||
"\"opportunistic\":" "true,"
|
||||
"\"retry\":" "\"default\","
|
||||
"\"tls_trust_store\":" "\"le_via_isrg\""
|
||||
"}},"
|
||||
"{\"mintest-fail\": {"
|
||||
"\"endpoint\":" "\"warmcat.com\","
|
||||
"\"port\":" "22,"
|
||||
"\"protocol\":" "\"h1\","
|
||||
"\"http_method\":" "\"GET\","
|
||||
"\"http_url\":" "\"index.html\","
|
||||
"\"plugins\":" "[],"
|
||||
"\"tls\":" "true,"
|
||||
"\"opportunistic\":" "true,"
|
||||
"\"retry\":" "\"default\","
|
||||
"\"tls_trust_store\":" "\"le_via_isrg\""
|
||||
"}},"
|
||||
"{\"minpost\": {"
|
||||
"\"endpoint\":" "\"warmcat.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h1\","
|
||||
"\"http_method\":" "\"POST\","
|
||||
"\"http_url\":" "\"testserver/formtest\","
|
||||
"\"plugins\":" "[],"
|
||||
"\"tls\":" "true,"
|
||||
"\"opportunistic\":" "true,"
|
||||
"\"retry\":" "\"default\","
|
||||
"\"tls_trust_store\":" "\"le_via_isrg\""
|
||||
"}}"
|
||||
"]"
|
||||
"}"
|
||||
;
|
||||
|
||||
typedef struct myss {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
} myss_t;
|
||||
|
||||
/* secure streams payload interface */
|
||||
|
||||
static int
|
||||
myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
// myss_t *m = (myss_t *)userobj;
|
||||
|
||||
lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags);
|
||||
lwsl_hexdump_info(buf, len);
|
||||
|
||||
/*
|
||||
* If we received the whole message, we let the sequencer know it
|
||||
* was a success
|
||||
*/
|
||||
if (flags & LWSSS_FLAG_EOM) {
|
||||
bad = 0;
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,
|
||||
int *flags)
|
||||
{
|
||||
// myss_t *m = (myss_t *)userobj;
|
||||
|
||||
/* in this example, we don't send any payload */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
myss_state(void *userobj, void *sh, lws_ss_constate_t state,
|
||||
lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
myss_t *m = (myss_t *)userobj;
|
||||
|
||||
lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
|
||||
(unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
lws_ss_request_tx(m->ss);
|
||||
break;
|
||||
case LWSSSCS_ALL_RETRIES_FAILED:
|
||||
/* if we're out of retries, we want to close the app and FAIL */
|
||||
interrupted = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
SEQ_IDLE,
|
||||
SEQ_TRY_CONNECT,
|
||||
SEQ_RECONNECT_WAIT,
|
||||
SEQ_CONNECTED,
|
||||
} myseq_state_t;
|
||||
|
||||
typedef struct myseq {
|
||||
struct lws_ss_handle *ss;
|
||||
|
||||
myseq_state_t state;
|
||||
int http_resp;
|
||||
|
||||
uint16_t try;
|
||||
} myseq_t;
|
||||
|
||||
/*
|
||||
* This defines the sequence of things the test app does.
|
||||
*/
|
||||
|
||||
static lws_seq_cb_return_t
|
||||
min_sec_str_sequencer_cb(struct lws_sequencer *seq, void *user, int event,
|
||||
void *v, void *a)
|
||||
{
|
||||
struct myseq *s = (struct myseq *)user;
|
||||
lws_ss_info_t ssi;
|
||||
|
||||
switch ((int)event) {
|
||||
|
||||
/* these messages are created just by virtue of being a sequencer */
|
||||
|
||||
case LWSSEQ_CREATED: /* our sequencer just got started */
|
||||
s->state = SEQ_IDLE;
|
||||
lwsl_notice("%s: LWSSEQ_CREATED\n", __func__);
|
||||
|
||||
/* We're making an outgoing secure stream ourselves */
|
||||
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.handle_offset = offsetof(myss_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(myss_t, opaque_data);
|
||||
ssi.rx = myss_rx;
|
||||
ssi.tx = myss_tx;
|
||||
ssi.state = myss_state;
|
||||
ssi.user_alloc = sizeof(myss_t);
|
||||
|
||||
/* requested to fail (to check backoff)? */
|
||||
if (flag_conn_fail)
|
||||
ssi.streamtype = "mintest-fail";
|
||||
else
|
||||
/* request to check h1 POST */
|
||||
if (flag_h1post)
|
||||
ssi.streamtype = "minpost";
|
||||
else
|
||||
/* default to h1 GET */
|
||||
ssi.streamtype = "mintest";
|
||||
|
||||
if (lws_ss_create(lws_seq_get_context(seq), 0, &ssi, NULL,
|
||||
&s->ss, seq, NULL)) {
|
||||
lwsl_err("%s: failed to create secure stream\n",
|
||||
__func__);
|
||||
|
||||
return LWSSEQ_RET_DESTROY;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWSSEQ_DESTROYED:
|
||||
lwsl_notice("%s: LWSSEQ_DESTROYED\n", __func__);
|
||||
break;
|
||||
|
||||
case LWSSEQ_TIMED_OUT: /* current step timed out */
|
||||
if (s->state == SEQ_RECONNECT_WAIT)
|
||||
lws_ss_request_tx(s->ss);
|
||||
break;
|
||||
|
||||
/*
|
||||
* These messages are created because we have a secure stream that was
|
||||
* bound to this sequencer at creation time. It copies its state
|
||||
* events to us as its sequencer parent. v is the lws_ss_handle_t *
|
||||
*/
|
||||
|
||||
case LWSSEQ_SS_STATE_BASE + LWSSSCS_CREATING:
|
||||
lwsl_info("%s: seq LWSSSCS_CREATING\n", __func__);
|
||||
lws_ss_request_tx(s->ss);
|
||||
break;
|
||||
case LWSSEQ_SS_STATE_BASE + LWSSSCS_DISCONNECTED:
|
||||
lwsl_info("%s: seq LWSSSCS_DISCONNECTED\n", __func__);
|
||||
break;
|
||||
case LWSSEQ_SS_STATE_BASE + LWSSSCS_UNREACHABLE:
|
||||
lwsl_info("%s: seq LWSSSCS_UNREACHABLE\n", __func__);
|
||||
break;
|
||||
case LWSSEQ_SS_STATE_BASE + LWSSSCS_AUTH_FAILED:
|
||||
lwsl_info("%s: seq LWSSSCS_AUTH_FAILED\n", __func__);
|
||||
break;
|
||||
case LWSSEQ_SS_STATE_BASE + LWSSSCS_CONNECTED:
|
||||
lwsl_info("%s: seq LWSSSCS_CONNECTED\n", __func__);
|
||||
break;
|
||||
case LWSSEQ_SS_STATE_BASE + LWSSSCS_CONNECTING:
|
||||
lwsl_info("%s: seq LWSSSCS_CONNECTING\n", __func__);
|
||||
break;
|
||||
case LWSSEQ_SS_STATE_BASE + LWSSSCS_DESTROYING:
|
||||
lwsl_info("%s: seq LWSSSCS_DESTROYING\n", __func__);
|
||||
break;
|
||||
case LWSSEQ_SS_STATE_BASE + LWSSSCS_POLL:
|
||||
/* somebody called lws_ss_poll() on the stream */
|
||||
lwsl_info("%s: seq LWSSSCS_POLL\n", __func__);
|
||||
break;
|
||||
case LWSSEQ_SS_STATE_BASE + LWSSSCS_ALL_RETRIES_FAILED:
|
||||
lwsl_info("%s: seq LWSSSCS_ALL_RETRIES_FAILED\n", __func__);
|
||||
interrupted = 1;
|
||||
break;
|
||||
case LWSSEQ_SS_STATE_BASE + LWSSSCS_QOS_ACK_REMOTE:
|
||||
lwsl_info("%s: seq LWSSSCS_QOS_ACK_REMOTE\n", __func__);
|
||||
break;
|
||||
case LWSSEQ_SS_STATE_BASE + LWSSSCS_QOS_ACK_LOCAL:
|
||||
lwsl_info("%s: seq LWSSSCS_QOS_ACK_LOCAL\n", __func__);
|
||||
break;
|
||||
|
||||
/*
|
||||
* This is the message we send from the ss handler to inform the
|
||||
* sequencer we had the payload properly
|
||||
*/
|
||||
|
||||
case LWSSEQ_USER_BASE:
|
||||
bad = 0;
|
||||
interrupted = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return LWSSEQ_RET_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
lws_seq_info_t i;
|
||||
const char *p;
|
||||
myseq_t *ms;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-d")))
|
||||
logs = atoi(p);
|
||||
|
||||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS minimal secure streams [-d<verbosity>][-f][--h1post]\n");
|
||||
|
||||
flag_conn_fail = !!lws_cmdline_option(argc, argv, "-f");
|
||||
flag_h1post = !!lws_cmdline_option(argc, argv, "--h1post");
|
||||
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
|
||||
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
|
||||
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
info.fd_limit_per_thread = 1 + 1 + 1;
|
||||
info.pss_policies_json = default_ss_policy;
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the sequencer that performs the steps of the test action
|
||||
* from inside the event loop.
|
||||
*/
|
||||
|
||||
memset(&i, 0, sizeof(i));
|
||||
i.context = context;
|
||||
i.user_size = sizeof(myseq_t);
|
||||
i.puser = (void **)&ms;
|
||||
i.cb = min_sec_str_sequencer_cb;
|
||||
i.name = "min-sec-stream-seq";
|
||||
|
||||
if (!lws_seq_create(&i)) {
|
||||
lwsl_err("%s: failed to create sequencer\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* the event loop */
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 0);
|
||||
|
||||
bail:
|
||||
lws_context_destroy(context);
|
||||
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
|
||||
|
||||
return bad;
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-minimal-secure-streams-sink)
|
||||
set(SRCS main.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_ROLE_H1 1 requirements)
|
||||
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
|
||||
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets)
|
||||
endif()
|
||||
endif()
|
|
@ -0,0 +1,64 @@
|
|||
# lws minimal secure streams
|
||||
|
||||
The application goes to https://warmcat.com and reads index.html there.
|
||||
|
||||
It does it using Secure Streams... the main code in minimal-secure-streams.c
|
||||
just sets up the context and opens a secure stream of type "mintest".
|
||||
|
||||
The handler for state changes and payloads for "mintest" is in ss-myss.c
|
||||
|
||||
The information about how a "mintest" stream should connect and the
|
||||
protocol it uses is kept separated in policy-database.c
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
Commandline option|Meaning
|
||||
---|---
|
||||
-d <loglevel>|Debug verbosity in decimal, eg, -d15
|
||||
-f| Force connecting to the wrong endpoint to check backoff retry flow
|
||||
-p| Run as proxy server for clients to connect to over unix domain socket
|
||||
|
||||
```
|
||||
[2019/08/12 07:16:11:0045] USR: LWS minimal secure streams [-d<verbosity>] [-f]
|
||||
[2019/08/12 07:16:12:6102] USR: myss_state: LWSSSCS_CREATING, ord 0x0
|
||||
[2019/08/12 07:16:12:6107] USR: myss_state: LWSSSCS_POLL, ord 0x0
|
||||
[2019/08/12 07:16:12:6117] N: lws_ss_client_connect: connecting h1get warmcat.com /
|
||||
[2019/08/12 07:16:12:6118] USR: myss_state: LWSSSCS_CONNECTING, ord 0x0
|
||||
[2019/08/12 07:16:13:4171] USR: myss_state: LWSSSCS_CONNECTED, ord 0x0
|
||||
[2019/08/12 07:16:13:4222] USR: myss_rx: len 1024, flags: 1
|
||||
[2019/08/12 07:16:13:4243] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4245] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4246] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4247] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4252] USR: myss_rx: len 1015, flags: 0
|
||||
[2019/08/12 07:16:13:4264] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4265] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4266] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4267] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4269] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4270] USR: myss_rx: len 1015, flags: 0
|
||||
[2019/08/12 07:16:13:4278] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4279] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4280] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4281] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4282] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4284] USR: myss_rx: len 1015, flags: 0
|
||||
[2019/08/12 07:16:13:4287] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4288] USR: myss_rx: len 947, flags: 0
|
||||
[2019/08/12 07:16:13:4293] USR: myss_rx: len 0, flags: 2
|
||||
[2019/08/12 07:16:13:4399] USR: myss_state: LWSSSCS_DISCONNECTED, ord 0x0
|
||||
[2019/08/12 07:16:13:4761] USR: myss_state: LWSSSCS_DESTROYING, ord 0x0
|
||||
[2019/08/12 07:16:13:4781] USR: Completed: OK
|
||||
```
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* lws-minimal-secure-streams-sink
|
||||
*
|
||||
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
static int interrupted, bad = 1;
|
||||
static const char * const default_ss_policy =
|
||||
"{"
|
||||
"\"release\":" "\"01234567\","
|
||||
"\"product\":" "\"myproduct\","
|
||||
"\"schema-version\":" "1,"
|
||||
"\"retry\": [" /* named backoff / retry strategies */
|
||||
"{\"default\": {"
|
||||
"\"backoff\": [" "1000,"
|
||||
"2000,"
|
||||
"3000,"
|
||||
"5000,"
|
||||
"10000"
|
||||
"],"
|
||||
"\"conceal\":" "5,"
|
||||
"\"jitterpc\":" "20,"
|
||||
"\"svalidping\":" "300,"
|
||||
"\"svalidhup\":" "310"
|
||||
"}}"
|
||||
"],"
|
||||
"\"certs\": [" /* named individual certificates in BASE64 DER */
|
||||
/*
|
||||
* Need to be in order from root cert... notice sometimes as
|
||||
* with Let's Encrypt there are multiple possible validation
|
||||
* paths, all the pieces for one validation path must be
|
||||
* given, excluding the server cert itself. Let's Encrypt
|
||||
* intermediate is signed by their ISRG Root CA but also is
|
||||
* cross-signed by an IdenTrust intermediate that's widely
|
||||
* deployed in browsers. We use the ISRG path because that
|
||||
* way we can skip the extra IdenTrust root cert.
|
||||
*/
|
||||
"{\"isrg_root_x1\": \"" /* ISRG ROOT X1 */
|
||||
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw"
|
||||
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh"
|
||||
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4"
|
||||
"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu"
|
||||
"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY"
|
||||
"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc"
|
||||
"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+"
|
||||
"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U"
|
||||
"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW"
|
||||
"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH"
|
||||
"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC"
|
||||
"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv"
|
||||
"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn"
|
||||
"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn"
|
||||
"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw"
|
||||
"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI"
|
||||
"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV"
|
||||
"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq"
|
||||
"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL"
|
||||
"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ"
|
||||
"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK"
|
||||
"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5"
|
||||
"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur"
|
||||
"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC"
|
||||
"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc"
|
||||
"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq"
|
||||
"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA"
|
||||
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d"
|
||||
"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc="
|
||||
"\"},"
|
||||
"{\"LEX3_isrg_root_x1\": \"" /* LE X3 signed by ISRG X1 root */
|
||||
"MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw"
|
||||
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh"
|
||||
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1"
|
||||
"WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg"
|
||||
"RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi"
|
||||
"MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX"
|
||||
"NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf"
|
||||
"89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl"
|
||||
"Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc"
|
||||
"Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz"
|
||||
"uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB"
|
||||
"AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU"
|
||||
"BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB"
|
||||
"FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo"
|
||||
"SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js"
|
||||
"LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF"
|
||||
"BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG"
|
||||
"AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD"
|
||||
"VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB"
|
||||
"ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx"
|
||||
"A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM"
|
||||
"UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2"
|
||||
"DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1"
|
||||
"eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu"
|
||||
"OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw"
|
||||
"p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY"
|
||||
"2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0"
|
||||
"ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR"
|
||||
"PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b"
|
||||
"rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt"
|
||||
"\"}"
|
||||
"],"
|
||||
"\"trust_stores\": [" /* named cert chains */
|
||||
"{"
|
||||
"\"name\": \"le_via_isrg\","
|
||||
"\"stack\": ["
|
||||
"\"isrg_root_x1\","
|
||||
"\"LEX3_isrg_root_x1\""
|
||||
"]"
|
||||
"}"
|
||||
"],"
|
||||
"\"s\": [" /* the supported stream types */
|
||||
"{\"\": {"
|
||||
"\"endpoint\":" "\"warmcat.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h2\","
|
||||
"\"http_method\":" "\"GET\","
|
||||
"\"http_url\":" "\"index.html\","
|
||||
"\"plugins\":" "[],"
|
||||
"\"tls\":" "true,"
|
||||
"\"nailed_up\":" "true,"
|
||||
"\"long_poll\":" "true,"
|
||||
"\"retry\":" "\"default\","
|
||||
"\"tls_trust_store\":" "\"le_via_isrg\""
|
||||
"}}"
|
||||
"]"
|
||||
"}"
|
||||
;
|
||||
|
||||
typedef struct myss {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
lws_sorted_usec_list_t sul;
|
||||
|
||||
int count;
|
||||
} myss_t;
|
||||
|
||||
/* secure streams payload interface */
|
||||
|
||||
static int
|
||||
myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
// myss_t *m = (myss_t *)userobj;
|
||||
|
||||
lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags);
|
||||
lwsl_hexdump_info(buf, len);
|
||||
|
||||
/*
|
||||
* If we received the whole message, for our example it means
|
||||
* we are done.
|
||||
*/
|
||||
if (flags & LWSSS_FLAG_EOM) {
|
||||
bad = 0;
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,
|
||||
int *flags)
|
||||
{
|
||||
//myss_t *m = (myss_t *)userobj;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
myss_state(void *userobj, void *sh, lws_ss_constate_t state,
|
||||
lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
myss_t *m = (myss_t *)userobj;
|
||||
|
||||
lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
|
||||
(unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
lws_ss_request_tx(m->ss);
|
||||
break;
|
||||
case LWSSSCS_ALL_RETRIES_FAILED:
|
||||
/* if we're out of retries, we want to close the app and FAIL */
|
||||
interrupted = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
int n = 0;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
lws_cmdline_option_handle_builtin(argc, argv, &info);
|
||||
lwsl_user("LWS secure streams [-d<verb>] [-f] [-p] [--h1post]\n");
|
||||
|
||||
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
|
||||
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
info.fd_limit_per_thread = 1 + 6 + 1;
|
||||
info.pss_policies_json = default_ss_policy;
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// puts(default_ss_policy);
|
||||
|
||||
if (lws_cmdline_option(argc, argv, "-p")) {
|
||||
|
||||
/* we are being the proxy */
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
if (lws_ss_proxy_create(context, NULL, 0)) {
|
||||
lwsl_err("%s: failed to create ss proxy\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
lwsl_notice("%s: secure streams proxy mode\n", __func__);
|
||||
#else
|
||||
lwsl_err("%s: needs cmake LWS_WITH_SECURE_STREAMS_PROXY_API\n",
|
||||
__func__);
|
||||
#endif
|
||||
} else {
|
||||
lws_ss_info_t ssi;
|
||||
|
||||
/* We're making an outgoing secure stream ourselves */
|
||||
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.handle_offset = offsetof(myss_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(myss_t, opaque_data);
|
||||
ssi.rx = myss_rx;
|
||||
ssi.tx = myss_tx;
|
||||
ssi.state = myss_state;
|
||||
ssi.user_alloc = sizeof(myss_t);
|
||||
|
||||
/* requested to fail (to check backoff)? */
|
||||
if (lws_cmdline_option(argc, argv, "-f"))
|
||||
ssi.streamtype = "mintest-fail";
|
||||
else
|
||||
/* request to check h1 POST */
|
||||
if (lws_cmdline_option(argc, argv, "--h1post"))
|
||||
ssi.streamtype = "minpost";
|
||||
else
|
||||
/* default to h1 GET */
|
||||
ssi.streamtype = "mintest";
|
||||
|
||||
if (lws_ss_create(context, 0, &ssi, NULL, NULL, NULL, NULL)) {
|
||||
lwsl_err("%s: failed to create secure stream\n",
|
||||
__func__);
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
/* the event loop */
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 0);
|
||||
|
||||
bail:
|
||||
|
||||
lws_context_destroy(context);
|
||||
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
|
||||
|
||||
return bad;
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-minimal-secure-streams)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_ROLE_H1 1 requirements)
|
||||
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
|
||||
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} minimal-secure-streams.c)
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_SECURE_STREAMS_PROXY_API)
|
||||
add_compile_options(-DLWS_SS_USE_SSPC)
|
||||
|
||||
add_executable(${SAMP}-client minimal-secure-streams.c)
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP}-client websockets_shared)
|
||||
add_dependencies(${SAMP}-client websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP}-client websockets)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endif()
|
|
@ -0,0 +1,64 @@
|
|||
# lws minimal secure streams
|
||||
|
||||
The application goes to https://warmcat.com and reads index.html there.
|
||||
|
||||
It does it using Secure Streams... the main code in minimal-secure-streams.c
|
||||
just sets up the context and opens a secure stream of type "mintest".
|
||||
|
||||
The handler for state changes and payloads for "mintest" is in ss-myss.c
|
||||
|
||||
The information about how a "mintest" stream should connect and the
|
||||
protocol it uses is kept separated in policy-database.c
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
Commandline option|Meaning
|
||||
---|---
|
||||
-d <loglevel>|Debug verbosity in decimal, eg, -d15
|
||||
-f| Force connecting to the wrong endpoint to check backoff retry flow
|
||||
-p| Run as proxy server for clients to connect to over unix domain socket
|
||||
|
||||
```
|
||||
[2019/08/12 07:16:11:0045] USR: LWS minimal secure streams [-d<verbosity>] [-f]
|
||||
[2019/08/12 07:16:12:6102] USR: myss_state: LWSSSCS_CREATING, ord 0x0
|
||||
[2019/08/12 07:16:12:6107] USR: myss_state: LWSSSCS_POLL, ord 0x0
|
||||
[2019/08/12 07:16:12:6117] N: lws_ss_client_connect: connecting h1get warmcat.com /
|
||||
[2019/08/12 07:16:12:6118] USR: myss_state: LWSSSCS_CONNECTING, ord 0x0
|
||||
[2019/08/12 07:16:13:4171] USR: myss_state: LWSSSCS_CONNECTED, ord 0x0
|
||||
[2019/08/12 07:16:13:4222] USR: myss_rx: len 1024, flags: 1
|
||||
[2019/08/12 07:16:13:4243] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4244] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4245] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4246] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4247] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4252] USR: myss_rx: len 1015, flags: 0
|
||||
[2019/08/12 07:16:13:4264] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4265] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4266] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4267] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4268] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4269] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4270] USR: myss_rx: len 1015, flags: 0
|
||||
[2019/08/12 07:16:13:4278] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4279] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4280] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4281] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4282] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4283] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4284] USR: myss_rx: len 1015, flags: 0
|
||||
[2019/08/12 07:16:13:4287] USR: myss_rx: len 1024, flags: 0
|
||||
[2019/08/12 07:16:13:4288] USR: myss_rx: len 947, flags: 0
|
||||
[2019/08/12 07:16:13:4293] USR: myss_rx: len 0, flags: 2
|
||||
[2019/08/12 07:16:13:4399] USR: myss_state: LWSSSCS_DISCONNECTED, ord 0x0
|
||||
[2019/08/12 07:16:13:4761] USR: myss_state: LWSSSCS_DESTROYING, ord 0x0
|
||||
[2019/08/12 07:16:13:4781] USR: Completed: OK
|
||||
```
|
|
@ -0,0 +1,421 @@
|
|||
/*
|
||||
* lws-minimal-secure-streams
|
||||
*
|
||||
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
*
|
||||
* This demonstrates a minimal http client using secure streams api.
|
||||
*
|
||||
* It visits https://warmcat.com/ and receives the html page there.
|
||||
*
|
||||
* This example is built two different ways from the same source... one includes
|
||||
* the policy everything needed to fulfil the stream directly. The other -client
|
||||
* variant has no policy itself and some other minor init changes, and connects
|
||||
* to the -proxy example to actually get the connection done.
|
||||
*
|
||||
* In the -client build case, the example does not even init the tls libraries
|
||||
* since the proxy part will take care of all that.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
/*
|
||||
* uncomment to force network traffic through 127.0.0.1:1080
|
||||
*
|
||||
* On your local machine, you can run a SOCKS5 proxy like this
|
||||
*
|
||||
* $ ssh -N -D 0.0.0.0:1080 localhost -v
|
||||
*
|
||||
* If enabled, this also fetches a remote policy that also
|
||||
* specifies that all traffic should go through the remote
|
||||
* proxy.
|
||||
*/
|
||||
// #define VIA_LOCALHOST_SOCKS
|
||||
|
||||
static int interrupted, bad = 1;
|
||||
static lws_state_notify_link_t nl;
|
||||
|
||||
/*
|
||||
* If the -proxy app is fulfilling our connection, then we don't need to have
|
||||
* the policy in the client.
|
||||
*
|
||||
* When we build with LWS_SS_USE_SSPC, the apis hook up to a proxy process over
|
||||
* a Unix Domain Socket. To test that, you need to separately run the
|
||||
* ./lws-minimal-secure-streams-proxy test app on the same machine.
|
||||
*/
|
||||
|
||||
#if !defined(LWS_SS_USE_SSPC)
|
||||
static const char * const default_ss_policy =
|
||||
"{"
|
||||
"\"release\":" "\"01234567\","
|
||||
"\"product\":" "\"myproduct\","
|
||||
"\"schema-version\":" "1,"
|
||||
#if defined(VIA_LOCALHOST_SOCKS)
|
||||
"\"via-socks5\":" "\"127.0.0.1:1080\","
|
||||
#endif
|
||||
|
||||
"\"retry\": [" /* named backoff / retry strategies */
|
||||
"{\"default\": {"
|
||||
"\"backoff\": [" "1000,"
|
||||
"2000,"
|
||||
"3000,"
|
||||
"5000,"
|
||||
"10000"
|
||||
"],"
|
||||
"\"conceal\":" "5,"
|
||||
"\"jitterpc\":" "20,"
|
||||
"\"svalidping\":" "30,"
|
||||
"\"svalidhup\":" "35"
|
||||
"}}"
|
||||
"],"
|
||||
"\"certs\": [" /* named individual certificates in BASE64 DER */
|
||||
/*
|
||||
* Let's Encrypt certs for warmcat.com / libwebsockets.org
|
||||
*
|
||||
* We fetch the real policy from there using SS and switch to
|
||||
* using that.
|
||||
*/
|
||||
"{\"isrg_root_x1\": \"" /* ISRG ROOT X1 */
|
||||
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw"
|
||||
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh"
|
||||
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4"
|
||||
"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu"
|
||||
"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY"
|
||||
"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc"
|
||||
"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+"
|
||||
"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U"
|
||||
"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW"
|
||||
"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH"
|
||||
"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC"
|
||||
"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv"
|
||||
"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn"
|
||||
"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn"
|
||||
"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw"
|
||||
"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI"
|
||||
"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV"
|
||||
"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq"
|
||||
"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL"
|
||||
"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ"
|
||||
"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK"
|
||||
"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5"
|
||||
"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur"
|
||||
"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC"
|
||||
"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc"
|
||||
"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq"
|
||||
"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA"
|
||||
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d"
|
||||
"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc="
|
||||
"\"},"
|
||||
"{\"LEX3_isrg_root_x1\": \"" /* LE X3 signed by ISRG X1 root */
|
||||
"MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw"
|
||||
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh"
|
||||
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1"
|
||||
"WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg"
|
||||
"RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi"
|
||||
"MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX"
|
||||
"NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf"
|
||||
"89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl"
|
||||
"Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc"
|
||||
"Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz"
|
||||
"uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB"
|
||||
"AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU"
|
||||
"BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB"
|
||||
"FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo"
|
||||
"SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js"
|
||||
"LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF"
|
||||
"BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG"
|
||||
"AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD"
|
||||
"VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB"
|
||||
"ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx"
|
||||
"A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM"
|
||||
"UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2"
|
||||
"DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1"
|
||||
"eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu"
|
||||
"OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw"
|
||||
"p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY"
|
||||
"2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0"
|
||||
"ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR"
|
||||
"PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b"
|
||||
"rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt"
|
||||
"\"}"
|
||||
"],"
|
||||
"\"trust_stores\": [" /* named cert chains */
|
||||
"{"
|
||||
"\"name\": \"le_via_isrg\","
|
||||
"\"stack\": ["
|
||||
"\"isrg_root_x1\","
|
||||
"\"LEX3_isrg_root_x1\""
|
||||
"]"
|
||||
"}"
|
||||
"],"
|
||||
"\"s\": ["
|
||||
"{\"fetch_policy\": {"
|
||||
"\"endpoint\":" "\"warmcat.com\","
|
||||
"\"port\":" "443,"
|
||||
"\"protocol\":" "\"h1\","
|
||||
"\"http_method\":" "\"GET\","
|
||||
#if defined(VIA_LOCALHOST_SOCKS)
|
||||
"\"http_url\":" "\"policy/minimal-proxy-socks.json\","
|
||||
#else
|
||||
"\"http_url\":" "\"policy/minimal-proxy.json\","
|
||||
#endif
|
||||
"\"tls\":" "true,"
|
||||
"\"opportunistic\":" "true,"
|
||||
"\"retry\":" "\"default\","
|
||||
"\"tls_trust_store\":" "\"le_via_isrg\""
|
||||
"}}"
|
||||
"}"
|
||||
;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct myss {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
lws_sorted_usec_list_t sul;
|
||||
} myss_t;
|
||||
|
||||
static const char *canned_root_token_payload =
|
||||
"grant_type=refresh_token"
|
||||
"&refresh_token=Atzr|IwEBIJedGXjDqsU_vMxykqOMg"
|
||||
"SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP"
|
||||
"zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y"
|
||||
"0flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW"
|
||||
"k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE"
|
||||
"iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S"
|
||||
"KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc"
|
||||
"AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI"
|
||||
"xL_hDCcTho8opCVX-6QhJHl6SQFlTw13"
|
||||
"&client_id="
|
||||
"amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d";
|
||||
|
||||
/* secure streams payload interface */
|
||||
|
||||
static int
|
||||
myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
// myss_t *m = (myss_t *)userobj;
|
||||
|
||||
lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags);
|
||||
lwsl_hexdump_info(buf, len);
|
||||
|
||||
/*
|
||||
* If we received the whole message, for our example it means
|
||||
* we are done.
|
||||
*/
|
||||
if (flags & LWSSS_FLAG_EOM) {
|
||||
bad = 0;
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,
|
||||
int *flags)
|
||||
{
|
||||
//myss_t *m = (myss_t *)userobj;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
myss_state(void *userobj, void *sh, lws_ss_constate_t state,
|
||||
lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
myss_t *m = (myss_t *)userobj;
|
||||
|
||||
lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
|
||||
(unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
lws_ss_set_metadata(m->ss, "uptag", "myuptag123", 10);
|
||||
lws_ss_set_metadata(m->ss, "ctype", "myctype", 7);
|
||||
lws_ss_client_connect(m->ss);
|
||||
break;
|
||||
case LWSSSCS_ALL_RETRIES_FAILED:
|
||||
/* if we're out of retries, we want to close the app and FAIL */
|
||||
interrupted = 1;
|
||||
break;
|
||||
case LWSSSCS_QOS_ACK_REMOTE:
|
||||
lwsl_notice("%s: LWSSSCS_QOS_ACK_REMOTE\n", __func__);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
|
||||
int current, int target)
|
||||
{
|
||||
struct lws_context *context = lws_system_context_from_system_mgr(mgr);
|
||||
lws_system_blob_t *ab = lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */);
|
||||
size_t size;
|
||||
|
||||
/*
|
||||
* For the things we care about, let's notice if we are trying to get
|
||||
* past them when we haven't solved them yet, and make the system
|
||||
* state wait while we trigger the dependent action.
|
||||
*/
|
||||
switch (target) {
|
||||
case LWS_SYSTATE_REGISTERED:
|
||||
size = lws_system_blob_get_size(ab);
|
||||
if (size)
|
||||
break;
|
||||
|
||||
/* let's register our canned root token so auth can use it */
|
||||
lws_system_blob_direct_set(ab,
|
||||
(const uint8_t *)canned_root_token_payload,
|
||||
strlen(canned_root_token_payload));
|
||||
break;
|
||||
|
||||
case LWS_SYSTATE_OPERATIONAL:
|
||||
if (current == LWS_SYSTATE_OPERATIONAL) {
|
||||
lws_ss_info_t ssi;
|
||||
|
||||
/* We're making an outgoing secure stream ourselves */
|
||||
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.handle_offset = offsetof(myss_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(myss_t,
|
||||
opaque_data);
|
||||
ssi.rx = myss_rx;
|
||||
ssi.tx = myss_tx;
|
||||
ssi.state = myss_state;
|
||||
ssi.user_alloc = sizeof(myss_t);
|
||||
ssi.streamtype = "mintest";
|
||||
|
||||
if (lws_ss_create(context, 0, &ssi, NULL, NULL,
|
||||
NULL, NULL)) {
|
||||
lwsl_err("%s: failed to create secure stream\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static lws_state_notify_link_t * const app_notifier_list[] = {
|
||||
&nl, NULL
|
||||
};
|
||||
|
||||
static void
|
||||
sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
int n = 0;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
memset(&info, 0, sizeof info);
|
||||
lws_cmdline_option_handle_builtin(argc, argv, &info);
|
||||
|
||||
lwsl_user("LWS secure streams test client [-d<verb>]\n");
|
||||
|
||||
info.fd_limit_per_thread = 1 + 6 + 1;
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
#if defined(LWS_SS_USE_SSPC)
|
||||
info.protocols = lws_sspc_protocols;
|
||||
{
|
||||
const char *p;
|
||||
|
||||
/* connect to ssproxy via UDS by default, else via
|
||||
* tcp connection to this port */
|
||||
if ((p = lws_cmdline_option(argc, argv, "-p")))
|
||||
info.ss_proxy_port = atoi(p);
|
||||
|
||||
/* UDS "proxy.ss.lws" in abstract namespace, else this socket
|
||||
* path; when -p given this can specify the network interface
|
||||
* to bind to */
|
||||
if ((p = lws_cmdline_option(argc, argv, "-i")))
|
||||
info.ss_proxy_bind = p;
|
||||
|
||||
/* if -p given, -a specifies the proxy address to connect to */
|
||||
if ((p = lws_cmdline_option(argc, argv, "-a")))
|
||||
info.ss_proxy_address = p;
|
||||
}
|
||||
#else
|
||||
info.pss_policies_json = default_ss_policy;
|
||||
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
|
||||
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
#endif
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
info.detailed_latency_cb = lws_det_lat_plot_cb;
|
||||
info.detailed_latency_filepath = "/tmp/lws-latency-ssproxy";
|
||||
#endif
|
||||
|
||||
/* integrate us with lws system state management when context created */
|
||||
|
||||
nl.name = "app";
|
||||
nl.notify_cb = app_system_state_nf;
|
||||
info.register_notifier_list = app_notifier_list;
|
||||
|
||||
/* create the context */
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the related lws_system blobs
|
||||
*
|
||||
* ...direct_set() sets a pointer, so the thing pointed to has to have
|
||||
* a suitable lifetime, eg, something that already exists on the heap or
|
||||
* a const string in .rodata like this
|
||||
*/
|
||||
|
||||
lws_system_blob_direct_set(lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_DEVICE_SERIAL, 0),
|
||||
(const uint8_t *)"SN12345678", 10);
|
||||
lws_system_blob_direct_set(lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION, 0),
|
||||
(const uint8_t *)"v0.01", 5);
|
||||
|
||||
/*
|
||||
* ..._heap_append() appends to a buflist kind of arrangement on heap,
|
||||
* just one block is fine, otherwise it will concatenate the fragments
|
||||
* in the order they were appended (and take care of freeing them at
|
||||
* context destroy time). ..._heap_empty() is also available to remove
|
||||
* everything that was already allocated.
|
||||
*
|
||||
* Here we use _heap_append() just so it's tested as well as direct set.
|
||||
*/
|
||||
|
||||
lws_system_blob_heap_append(lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_DEVICE_TYPE, 0),
|
||||
(const uint8_t *)"spacerocket", 11);
|
||||
|
||||
/* the event loop */
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 0);
|
||||
|
||||
lws_context_destroy(context);
|
||||
|
||||
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
|
||||
|
||||
return bad;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ "$COVERITY_SCAN_BRANCH" != 1 -a "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
if [ "$LWS_METHOD" != "mbedtls" ] ; then
|
||||
if [ "$LWS_METHOD" != "mbedtls" -a "$LWS_METHOD" != "ss+mbedtls" ] ; then
|
||||
mkdir build && cd build &&
|
||||
cmake -DOPENSSL_ROOT_DIR="/usr/local/opt/openssl" $CMAKE_ARGS .. &&
|
||||
cmake --build .
|
||||
|
@ -34,7 +34,7 @@ else
|
|||
cmake --build . &&
|
||||
../scripts/h2load-smp.sh
|
||||
else
|
||||
if [ "$LWS_METHOD" = "mbedtls" ] ; then
|
||||
if [ "$LWS_METHOD" = "mbedtls" -o "$LWS_METHOD" = "ss+mbedtls" ] ; then
|
||||
cmake $CMAKE_ARGS .. &&
|
||||
cmake --build . &&
|
||||
sudo make install &&
|
||||
|
|
|
@ -21,7 +21,7 @@ then
|
|||
sudo update-ca-certificates
|
||||
fi
|
||||
|
||||
if [ "$LWS_METHOD" == "mbedtls" ];
|
||||
if [ "$LWS_METHOD" == "mbedtls" -o "$LWS_METHOD" == "ss+mbedtls" ];
|
||||
then
|
||||
sudo apt-get install -y -qq realpath libjemalloc1 libev4 libuv-dev valgrind
|
||||
wget https://libwebsockets.org/openssl-1.1.0-trusty.tar.bz2 -O/tmp/openssl.tar.bz2
|
||||
|
|
Loading…
Add table
Reference in a new issue