From 84f8bdc3d844d5f7143812074d7a98bebe13588a Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 16 Mar 2020 16:56:11 +0000 Subject: [PATCH] ss: support metadata string expansion in endpoint string This lets you set metadata symbols exposed by the streamtype policy into the endpoint address. No ABI change --- lib/secure-streams/README.md | 9 +- lib/secure-streams/secure-streams.c | 18 +- .../CMakeLists.txt | 92 +++++ .../minimal-secure-streams-metadata/README.md | 66 ++++ .../minimal-secure-streams.c | 370 ++++++++++++++++++ 5 files changed, 552 insertions(+), 3 deletions(-) create mode 100644 minimal-examples/secure-streams/minimal-secure-streams-metadata/CMakeLists.txt create mode 100644 minimal-examples/secure-streams/minimal-secure-streams-metadata/README.md create mode 100644 minimal-examples/secure-streams/minimal-secure-streams-metadata/minimal-secure-streams.c diff --git a/lib/secure-streams/README.md b/lib/secure-streams/README.md index 8f815e054..389812a25 100644 --- a/lib/secure-streams/README.md +++ b/lib/secure-streams/README.md @@ -119,7 +119,14 @@ These are an array of policies for the supported stream type names. ### `endpoint` -The DNS address the secure stream should connect to +The DNS address the secure stream should connect to. + +This may contain string symbols which will be replaced with the +corresponding streamtype metadata value at runtime. Eg, if the +streamtype lists a metadata name "region", it's then possible to +define the endpoint as, eg, `${region}.mysite.com`, and before +attempting the connection setting the stream's metadata item +"region" to the desired value, eg, "uk". ### `port` diff --git a/lib/secure-streams/secure-streams.c b/lib/secure-streams/secure-streams.c index 652f6c9a7..8de02b917 100644 --- a/lib/secure-streams/secure-streams.c +++ b/lib/secure-streams/secure-streams.c @@ -187,8 +187,10 @@ lws_ss_client_connect(lws_ss_handle_t *h) { struct lws_client_connect_info i; const struct ss_pcols *ssp; + size_t used_in, used_out; union lws_ss_contemp ct; - char path[128]; + char path[128], ep[96]; + lws_strexp_t exp; if (!h->policy) { lwsl_err("%s: ss with no policy\n", __func__); @@ -226,7 +228,19 @@ lws_ss_client_connect(lws_ss_handle_t *h) } } - i.address = h->policy->endpoint; + /* expand metadata ${symbols} that may be inside the endpoint string */ + + lws_strexp_init(&exp, (void *)h, lws_ss_exp_cb_metadata, ep, sizeof(ep)); + + if (lws_strexp_expand(&exp, h->policy->endpoint, + strlen(h->policy->endpoint), + &used_in, &used_out) != LSTRX_DONE) { + lwsl_err("%s: address strexp failed\n", __func__); + + return -1; + } + + i.address = ep; i.port = h->policy->port; i.host = i.address; i.origin = i.address; diff --git a/minimal-examples/secure-streams/minimal-secure-streams-metadata/CMakeLists.txt b/minimal-examples/secure-streams/minimal-secure-streams-metadata/CMakeLists.txt new file mode 100644 index 000000000..2b1b4b1c7 --- /dev/null +++ b/minimal-examples/secure-streams/minimal-secure-streams-metadata/CMakeLists.txt @@ -0,0 +1,92 @@ +project(minimal-secure-streams-metadata) +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-minimal-secure-streams-metadata) + +# 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 \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() diff --git a/minimal-examples/secure-streams/minimal-secure-streams-metadata/README.md b/minimal-examples/secure-streams/minimal-secure-streams-metadata/README.md new file mode 100644 index 000000000..cb9b3b360 --- /dev/null +++ b/minimal-examples/secure-streams/minimal-secure-streams-metadata/README.md @@ -0,0 +1,66 @@ +# 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 |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 +--force-portal|Force the SS Captive Portal Detection to feel it's behind a portal +--force-no-internet|Force the SS Captive Portal Detection to feel it can't reach the internet + +``` +[2019/08/12 07:16:11:0045] USR: LWS minimal secure streams [-d] [-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 +``` diff --git a/minimal-examples/secure-streams/minimal-secure-streams-metadata/minimal-secure-streams.c b/minimal-examples/secure-streams/minimal-secure-streams-metadata/minimal-secure-streams.c new file mode 100644 index 000000000..1481ec14d --- /dev/null +++ b/minimal-examples/secure-streams/minimal-secure-streams-metadata/minimal-secure-streams.c @@ -0,0 +1,370 @@ +/* + * lws-minimal-secure-streams-metadata + * + * Written in 2010-2020 by Andy Green + * + * 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 +#include +#include + +/* + * 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, force_cpd_fail_portal, + force_cpd_fail_no_internet; +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\": [" + "{\"mintest\": {" + "\"endpoint\":" "\"${servername}\"," + "\"port\":" "443," + "\"protocol\":" "\"h1\"," + "\"http_method\":" "\"GET\"," + "\"http_url\":" "\"\"," + "\"tls\":" "true," + "\"opportunistic\":" "true," + "\"retry\":" "\"default\"," + "\"tls_trust_store\":" "\"le_via_isrg\"," + "\"metadata\": [" + "{\"servername\": \"\"}" + "]" + "}}" + "]}" +; + +#endif + +typedef struct myss { + struct lws_ss_handle *ss; + void *opaque_data; + /* ... application specific state ... */ + lws_sorted_usec_list_t sul; +} 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_set_metadata(m->ss, "servername", "warmcat.com", 11); + 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); + + /* + * 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_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]\n"); + + /* these options are mutually exclusive if given */ + + if (lws_cmdline_option(argc, argv, "--force-portal")) + force_cpd_fail_portal = 1; + + if (lws_cmdline_option(argc, argv, "--force-no-internet")) + force_cpd_fail_no_internet = 1; + + 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 + + /* 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; + } + + + /* 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; +}