diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index 1f111900e..1b510b371 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -907,6 +907,15 @@ rops_adoption_bind_h1(struct lws *wsi, int type, const char *vh_prot_name) return 1; } +#if defined(LWS_WITH_SERVER) && defined(LWS_WITH_SECURE_STREAMS) + if (wsi->a.vhost->ss_handle && + wsi->a.vhost->ss_handle->policy->protocol == LWSSSP_RAW) { + lws_role_transition(wsi, LWSIFR_SERVER, (type & LWS_ADOPT_ALLOW_SSL) ? + LRS_SSL_INIT : LRS_ESTABLISHED, &role_ops_raw_skt); + return 1; + } +#endif + /* If Non-TLS and HTTP2 prior knowledge is enabled, skip to clear text HTTP2 */ #if defined(LWS_WITH_HTTP2) diff --git a/lib/secure-streams/protocols/ss-raw.c b/lib/secure-streams/protocols/ss-raw.c index 604883e63..d6b96dff1 100644 --- a/lib/secure-streams/protocols/ss-raw.c +++ b/lib/secure-streams/protocols/ss-raw.c @@ -58,6 +58,9 @@ secstream_raw(struct lws *wsi, enum lws_callback_reasons reason, void *user, h->policy ? h->policy->streamtype : "no policy"); h->wsi = NULL; if (h->policy && !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) && +#if defined(LWS_WITH_SERVER) + !(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not server */ +#endif !h->txn_ok && !wsi->a.context->being_destroyed) if (lws_ss_backoff(h)) /* has been destroyed */ @@ -77,6 +80,10 @@ secstream_raw(struct lws *wsi, enum lws_callback_reasons reason, void *user, lws_validity_confirmed(wsi); break; + case LWS_CALLBACK_RAW_ADOPT: + lwsl_info("%s: RAW_ADOPT\n", __func__); + break; + /* chunks of chunked content, with header removed */ case LWS_CALLBACK_RAW_RX: if (!h || !h->info.rx) @@ -131,7 +138,7 @@ secstream_raw(struct lws *wsi, enum lws_callback_reasons reason, void *user, break; } - return lws_callback_http_dummy(wsi, reason, user, in, len); + return 0; } static int diff --git a/minimal-examples/secure-streams/minimal-secure-streams-server-raw/CMakeLists.txt b/minimal-examples/secure-streams/minimal-secure-streams-server-raw/CMakeLists.txt new file mode 100644 index 000000000..c6debce03 --- /dev/null +++ b/minimal-examples/secure-streams/minimal-secure-streams-server-raw/CMakeLists.txt @@ -0,0 +1,26 @@ +project(lws-minimal-secure-streams-server-raw C) +cmake_minimum_required(VERSION 2.8) +find_package(libwebsockets CONFIG REQUIRED) +list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR}) +include(CheckCSourceCompiles) +include(LwsCheckRequirements) + +set(SAMP lws-minimal-secure-streams-server-raw) +set(SRCS main.c ss-server.c) + +set(requirements 1) +require_lws_config(LWS_ROLE_H1 1 requirements) +require_lws_config(LWS_WITH_SERVER 1 requirements) +require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) +require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) + endif() +endif() diff --git a/minimal-examples/secure-streams/minimal-secure-streams-server-raw/README.md b/minimal-examples/secure-streams/minimal-secure-streams-server-raw/README.md new file mode 100644 index 000000000..9580c4ee1 --- /dev/null +++ b/minimal-examples/secure-streams/minimal-secure-streams-server-raw/README.md @@ -0,0 +1,54 @@ +# lws minimal secure streams server raw + +The application sets up a raw tcp server on localhost:7681 + +It does it using Secure Streams... information about how the server should +operate is held in JSON policy in main.c + +Connecting to the server using `echo "hello" | nc --no-shutdown 127.0.0.1 7681` +will send "hello" which is hexdumped to console by the rx function, then +will receive an incrementing message at 100ms intervals. + +Note there are two incomaptible versions of netcat around, this is from Fedora's +nmap-ncat, the --no-shutdown is needed to stop it hanging up itself after it +has sent its stdin. + +## build + +``` + $ cmake . && make +``` + +## usage + +Commandline option|Meaning +---|--- +-d |Debug verbosity in decimal, eg, -d15 + +``` +[2020/07/28 10:25:54:6747] U: LWS Secure Streams Server Raw +[2020/07/28 10:25:54:7194] N: LWS: 4.0.99-v4.0.0-247-g58be599aa, loglevel 1031 +[2020/07/28 10:25:54:7198] N: NET CLI SRV H1 H2 WS MQTT SS-JSON-POL SSPROX ASYNC_DNS IPv6-absent +[2020/07/28 10:25:54:9376] N: lws_adopt_descriptor_vhost2: wsi 0x5317d30, vhost system ss_handle (nil) +[2020/07/28 10:25:54:9442] N: lws_adopt_descriptor_vhost2: wsi 0x53182c0, vhost system ss_handle (nil) +[2020/07/28 10:25:54:9920] N: smd_cb: creating server stream +[2020/07/28 10:25:54:9963] N: lws_ss_create: created server myrawserver +[2020/07/28 10:26:00:1065] N: secstream_raw: RAW_ADOPT +[2020/07/28 10:26:00:1068] N: lws_adopt_descriptor_vhost2: wsi 0x531a6b0, vhost myrawserver ss_handle 0x5319ac0 +[2020/07/28 10:26:00:1088] U: myss_raw_state: 0x531aad0 LWSSSCS_CREATING, ord 0x0 +[2020/07/28 10:26:00:1094] U: myss_raw_state: 0x531aad0 LWSSSCS_CONNECTING, ord 0x0 +[2020/07/28 10:26:00:1096] U: myss_raw_state: 0x531aad0 LWSSSCS_CONNECTED, ord 0x0 +[2020/07/28 10:26:00:1172] U: myss_raw_rx: len 6, flags: 0 +[2020/07/28 10:26:02:8516] U: myss_raw_state: 0x531aad0 LWSSSCS_DISCONNECTED, ord 0x0 +[2020/07/28 10:26:02:8545] U: myss_raw_state: 0x531aad0 LWSSSCS_DESTROYING, ord 0x0 +^C[2020/07/28 10:26:04:9608] U: myss_raw_state: 0x5319ac0 LWSSSCS_DESTROYING, ord 0x0 +[2020/07/28 10:26:04:9723] U: Completed: OK +``` + +``` +$ echo "hello" | nc --no-shutdown 127.0.0.1 7681 +hello from raw 0 +hello from raw 1 +hello from raw 2 +... +``` diff --git a/minimal-examples/secure-streams/minimal-secure-streams-server-raw/main.c b/minimal-examples/secure-streams/minimal-secure-streams-server-raw/main.c new file mode 100644 index 000000000..95c49f487 --- /dev/null +++ b/minimal-examples/secure-streams/minimal-secure-streams-server-raw/main.c @@ -0,0 +1,104 @@ +/* + * lws-minimal-secure-streams-server + * + * Written in 2010-2020 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + */ + +#include +#include +#include + +extern const lws_ss_info_t ssi_client, ssi_server; + +static struct lws_context *context; +int interrupted, bad = 1; +static const char * const default_ss_policy = + "{" + "\"release\":" "\"01234567\"," + "\"product\":" "\"myproduct\"," + "\"schema-version\":" "1," + "\"s\": [" + + /* + * This streamtype represents a raw server listening on :7681, + * without tls + */ + + "{\"myrawserver\": {" + /* if given, "endpoint" is network if to bind to */ + "\"server\":" "true," + "\"port\":" "7681," + "\"protocol\":" "\"raw\"" + "}}" + + "]" + "}" +; + +static int +smd_cb(void *opaque, lws_smd_class_t c, lws_usec_t ts, void *buf, size_t len) +{ + if ((c & LWSSMDCL_SYSTEM_STATE) && + !lws_json_simple_strcmp(buf, len, "\"state\":", "OPERATIONAL")) { + + /* create the secure streams */ + + lwsl_notice("%s: creating server stream\n", __func__); + + if (lws_ss_create(context, 0, &ssi_server, NULL, NULL, + NULL, NULL)) { + lwsl_err("%s: failed to create secure stream\n", + __func__); + return -1; + } + } + + return 0; +} + +static void +sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + 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 Server Raw\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; + info.early_smd_cb = smd_cb; + info.early_smd_class_filter = LWSSMDCL_SYSTEM_STATE; + + 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; +} diff --git a/minimal-examples/secure-streams/minimal-secure-streams-server-raw/ss-server.c b/minimal-examples/secure-streams/minimal-secure-streams-server-raw/ss-server.c new file mode 100644 index 000000000..63925c951 --- /dev/null +++ b/minimal-examples/secure-streams/minimal-secure-streams-server-raw/ss-server.c @@ -0,0 +1,111 @@ +/* + * lws-minimal-secure-streams-server + * + * Written in 2010-2020 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + */ + +#include +#include + +extern int interrupted, bad; + +typedef struct myss { + struct lws_ss_handle *ss; + void *opaque_data; + /* ... application specific state ... */ + + lws_sorted_usec_list_t sul; + int count; + char upgraded; + +} myss_srv_t; + +/* + * This is the Secure Streams Server RX and TX + */ + +static int +myss_raw_rx(void *userobj, const uint8_t *buf, size_t len, int flags) +{ +// myss_srv_t *m = (myss_srv_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; +} + +/* this is the callback that mediates sending the incrementing number */ + +static void +spam_sul_cb(struct lws_sorted_usec_list *sul) +{ + myss_srv_t *m = lws_container_of(sul, myss_srv_t, sul); + + lws_ss_request_tx(m->ss); + + lws_sul_schedule(lws_ss_get_context(m->ss), 0, &m->sul, spam_sul_cb, + 100 * LWS_US_PER_MS); +} + +static int +myss_raw_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, + int *flags) +{ + myss_srv_t *m = (myss_srv_t *)userobj; + + *flags = LWSSS_FLAG_SOM | LWSSS_FLAG_EOM; + + *len = lws_snprintf((char *)buf, *len, "hello from raw %d\n", m->count++); + + lws_sul_schedule(lws_ss_get_context(m->ss), 0, &m->sul, spam_sul_cb, + 100 * LWS_US_PER_MS); + + return 0; +} + +static int +myss_raw_state(void *userobj, void *sh, lws_ss_constate_t state, + lws_ss_tx_ordinal_t ack) +{ + myss_srv_t *m = (myss_srv_t *)userobj; + + lwsl_user("%s: %p %s, ord 0x%x\n", __func__, m->ss, + lws_ss_state_name(state), (unsigned int)ack); + + switch (state) { + case LWSSSCS_DISCONNECTED: + lws_sul_cancel(&m->sul); + break; + case LWSSSCS_CONNECTED: + lws_ss_request_tx(m->ss); + break; + + default: + break; + } + + return 0; +} + +const lws_ss_info_t ssi_server = { + .handle_offset = offsetof(myss_srv_t, ss), + .opaque_user_data_offset = offsetof(myss_srv_t, opaque_data), + .streamtype = "myrawserver", + .rx = myss_raw_rx, + .tx = myss_raw_tx, + .state = myss_raw_state, + .user_alloc = sizeof(myss_srv_t), +};