diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index aa6fa72f40..f1b9ee82d0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,17 +27,17 @@ jobs: uses: actions/checkout@v3 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} config-file: ./.github/codeql.yml queries: +security-and-quality - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 if: ${{ matrix.language == 'javascript' || matrix.language == 'cpp' }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{ matrix.language }}" diff --git a/.sai.json b/.sai.json index 38420d5d91..db4747393e 100644 --- a/.sai.json +++ b/.sai.json @@ -1,40 +1,75 @@ { "schema": "sai-1", - # We're doing separate install into destdir so that the test server - # has somewhere to go to find its /usr/share content like certs - "platforms": { "rocky9/aarch64-a72a55-rk3588/gcc": { - "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G RPM\";cmake .. ${cmake} && make -j6 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j4 --output-on-failure ${cpack}" + "build": [ + "mkdir -p build destdir; cd build; CCACHE_DISABLE=1 LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib cmake .. ${cmake}", + "cd build && make -j$SAI_PARALLEL && rm -rf ../destdir && make -j$SAI_PARALLEL DESTDIR=../destdir install", + "cd build && ctest -j$SAI_PARALLEL --output-on-failure --repeat until-pass:3", + "cd build && SAI_CPACK=\"-G RPM\" ${cpack}" + ] }, "netbsd-OSX-bigsur/x86_64-intel-i3/llvm": { - "build": "mkdir build destdir; cd build; export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G ZIP\";export MACOSX_DEPLOYMENT_TARGET=12.5 ; cmake .. -DCMAKE_MAKE_PROGRAM=/usr/bin/make ${cmake} && make -j4 && make -j DESTDIR=../destdir install && ctest -j4 --output-on-failure ${cpack}" + "build": [ + "mkdir -p build destdir; cd build; CCACHE_DISABLE=1 LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib MACOSX_DEPLOYMENT_TARGET=12.5 cmake .. -DCMAKE_MAKE_PROGRAM=/usr/bin/make ${cmake}", + "cd build && make -j$SAI_PARALLEL && rm -rf ../destdir && make -j$SAI_PARALLEL DESTDIR=../destdir install", + "cd build && ctest -j$SAI_PARALLEL --output-on-failure --repeat until-pass:3", + "cd build && SAI_CPACK=\"-G ZIP\" ${cpack}" + ] }, "ubuntu-noble/riscv64/gcc": { - "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G DEB\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j4 --output-on-failure ${cpack}" + "build": [ + "mkdir -p build destdir; cd build; CCACHE_DISABLE=1 LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib cmake .. ${cmake}", + "cd build && make -j$SAI_PARALLEL && rm -rf ../destdir && make -j$SAI_PARALLEL DESTDIR=../destdir install", + "cd build && ctest -j$SAI_PARALLEL --output-on-failure --repeat until-pass:3", + "cd build && SAI_CPACK=\"-G DEB\" ${cpack}" + ] }, "rocky9/x86_64-amd/gcc": { - "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G RPM\";cmake .. ${cmake} && make -j6 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j4 --output-on-failure ${cpack}" + "build": [ + "mkdir -p build destdir; cd build; CCACHE_DISABLE=1 LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib cmake .. ${cmake}", + "cd build && make -j$SAI_PARALLEL && rm -rf ../destdir && make -j$SAI_PARALLEL DESTDIR=../destdir install", + "cd build && ctest -j$SAI_PARALLEL --output-on-failure --repeat until-pass:3", + "cd build && SAI_CPACK=\"-G RPM\" ${cpack}" + ] }, "linux-ubuntu-2404/aarch64-a72-bcm2711-rpi4/gcc": { - "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G DEB\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j4 --output-on-failure ${cpack}" + "build": [ + "mkdir -p build destdir; cd build; CCACHE_DISABLE=1 LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib cmake .. ${cmake}", + "cd build && make -j$SAI_PARALLEL && rm -rf ../destdir && make -j$SAI_PARALLEL DESTDIR=../destdir install", + "cd build && ctest -j$SAI_PARALLEL --output-on-failure --repeat until-pass:3", + "cd build && SAI_CPACK=\"-G DEB\" ${cpack}" + ] }, "netbsd/aarch64BE-bcm2837-a53/gcc": { - "build": "mkdir build destdir;cd build;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export CCACHE_DISABLE=1;cmake .. ${cmake};make -j6 && rm -rf ../destdir && make -j6 DESTDIR=../destdir install && /usr/pkg/bin/ctest -j4 --output-on-failure", - "default": false + "default": false, + "build": [ + "mkdir -p build destdir; cd build; CCACHE_DISABLE=1 LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib cmake .. ${cmake}", + "cd build && make -j$SAI_PARALLEL && rm -rf ../destdir && make -j$SAI_PARALLEL DESTDIR=../destdir install", + "cd build && /usr/pkg/bin/ctest -j$SAI_PARALLEL --output-on-failure --repeat until-pass:3" + ] }, "w11/x86_64-amd/msvc": { - "build": "mkdir build && cd build && set SAI_CPACK=\"-G ZIP\" && cmake .. -DOPENSSL_ROOT_DIR=\"C:\\Users\\andy\\vcpkg\\packages\\openssl_x64-windows\" -DLWS_EXT_PTHREAD_INCLUDE_DIR=\"C:\\Program Files (x86)\\pthreads\\include\" -DLWS_EXT_PTHREAD_LIBRARIES=\"C:\\Program Files (x86)\\pthreads\\lib\\x64\\libpthreadGC2.a\" ${cmake} && cmake --build . --config DEBUG && set CTEST_OUTPUT_ON_FAILURE=1 && ctest . -C DEBUG -j1 --output-on-failure", - "default": false + "default": false, + "build": [ + "mkdir -p build; cd build; CCACHE_DISABLE=1 LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib cmake .. -DOPENSSL_ROOT_DIR=\"C:\\Users\\andy\\vcpkg\\packages\\openssl_x64-windows\" -DLWS_EXT_PTHREAD_INCLUDE_DIR=\"C:\\Program Files (x86)\\pthreads\\include\" -DLWS_EXT_PTHREAD_LIBRARIES=\"C:\\Program Files (x86)\\pthreads\\lib\\x64\\libpthreadGC2.a\" ${cmake}", + "cd build && cmake --build . --config DEBUG", + "cd build && /usr/pkg/bin/ctest -j$SAI_PARALLEL --output-on-failure --repeat until-pass:3", + "cd build && SAI_CPACK=\"-G ZIP\" ${cpack}" + ] }, "freertos-espidf/xl6-esp32/gcc": { - # official way to get sdkconfig.h is idf.py menuconfig, but - # no obvious way to do that in CI - "build": "rm -rf ebuild ; mkdir ebuild; cd ebuild; cp -rp ../minimal-examples/embedded/esp32/${cpack} . ; cd ${cpack} ; . /opt/esp/esp-idf/export.sh ; rm -f libwebsockets ; ln -sf ../.. libwebsockets ; idf.py set-target esp32 && cp libwebsockets/minimal-examples/embedded/esp32/${cpack}/sdkconfig . && cp sdkconfig.h build && idf.py ${cmake} build size size-components size-files && cd build && /usr/local/bin/sai-device ${cpack} ESPPORT=0 ctest --output-on-failure", - "default": false + "default": false, + "build": [ + "rm -rf ebuild ; mkdir ebuild; cd ebuild;cp -rp ../minimal-examples/embedded/esp32/${cpack} . ; cd ${cpack} ; rm -flibwebsockets ; ln -sf ../.. libwebsockets", + "cp libwebsockets/minimal-examples/embedded/esp32/${cpack}/sdkconfig . && cp sdkconfig.h build", + ". /opt/esp/esp-idf/export.sh ; idf.py set-target esp32 && idf.py ${cmake} build size size-components size-files", + "cd build && /usr/local/bin/sai-device ${cpack} ESPPORT=0 ctest --output-on-failure" + ] } }, + "configurations": { "default": { "cmake": "", diff --git a/READMEs/README.ctest.md b/READMEs/README.ctest.md index 9fdb1ee7b6..2b905a07c8 100644 --- a/READMEs/README.ctest.md +++ b/READMEs/README.ctest.md @@ -309,14 +309,14 @@ For tests with local buddies using tcp sockets inside the same VM or systemd- nspawn networking context, you cannot just use a well-known port like 7681. ctest itself is usually executed concurrently, and Sai is typically building -multiple different instances concurrently as well (typically 3), so it may be +multiple different instances concurrently as well (perhaps dozens), so it may be running different ctests inside the same VM simultaneously. Different tests can have their own convention for port ranges, to solve the problem about Sai running different tests concurrently inside one ctest. For the case there are multiple ctests running, we can use the env var -`$ENV{SAI_INSTANCE_IDX}`, which is an ordinal like 0 or 1, to further ensure +`$ENV{SAI_INSTANCE_IDX}`, which is an ordinal like 0 or 1 or 33, to further ensure that port selections won't conflict. If not using Sai, you can just set this in the evironment yourself to reflect your build instance index. @@ -326,20 +326,14 @@ in the evironment yourself to reflect your build instance index. # machine context in parallel so they can tread on each other otherwise # set(PORT_HCM_SRV "7670") - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "0") - set(PORT_HCM_SRV 7671) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "1") - set(PORT_HCM_SRV 7672) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "2") - set(PORT_HCM_SRV 7673) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "3") - set(PORT_HCM_SRV 7674) + if ("$ENV{SAI_INSTANCE_IDX}") + math(EXPR PORT_HCM_SRV "7671 + $ENV{SAI_INSTANCE_IDX}") endif() ``` +The default value is for the case you are running the test manually, and not +under Sai. + This is complicated enough that the best approach is copy an existing simple case like the CMakeLists.txt for minimal-http-client and change the names and ports to be unique. diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index 93810c05d2..bf64c7ab05 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -249,6 +249,16 @@ * Diffie-Hellman (DH) key exchange ciphers * (e.g. TLS_DHE_RSA_WITH_AES_256_GCM_SHA384). It's not recommended. */ +#define LWS_SERVER_OPTION_MBEDTLS_VERIFY_CLIENT_CERT_POST_HANDSHAKE ((1ll << 41) | \ + (1ll << 12)) + /**< (VH) An option to be used with mbedtls only, forces server to load + * and store the client cert (without CA dependent check) + * to be able to verify it later (after the handshake); + * provides LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT. + * Note: LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT and + * LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED are ignored if + * LWS_SERVER_OPTION_MBEDTLS_VERIFY_CLIENT_CERT_POST_HANDSHAKE is set */ + /****** add new things just above ---^ ******/ diff --git a/include/libwebsockets/lws-misc.h b/include/libwebsockets/lws-misc.h index 1118165bd8..fec9a41ad5 100644 --- a/include/libwebsockets/lws-misc.h +++ b/include/libwebsockets/lws-misc.h @@ -136,7 +136,7 @@ lws_buflist_linear_use(struct lws_buflist **head, uint8_t *buf, size_t len); * lws_buflist_fragment_use(): copy and consume <= 1 frag from buflist head * * \param head: list head - * \param buf: buffer to copy linearly into + * \param buf: NULL, buffer to copy linearly into * \param len: length of buffer available * \param frag_first: pointer to char written on exit to if this is start of frag * \param frag_fin: pointer to char written on exit to if this is end of frag @@ -148,6 +148,10 @@ lws_buflist_linear_use(struct lws_buflist **head, uint8_t *buf, size_t len); * Since it was consumed, calling again will resume copying out and consuming * from as far as it got the first time. * + * It's legal for buf to be NULL and / or len = 0. In this case nothing is + * "used" and the effect is to set `frag_first` according to if we are at the + * start of the fragment and 0 is returned. + * * Returns the number of bytes written into \p buf. */ LWS_VISIBLE LWS_EXTERN int @@ -178,6 +182,22 @@ lws_buflist_destroy_all_segments(struct lws_buflist **head); LWS_VISIBLE LWS_EXTERN void lws_buflist_describe(struct lws_buflist **head, void *id, const char *reason); +/** + * lws_buflist_get_frag_start_or_NULL(): get pointer to start of fragment + * + * \param head: list head + * + * This gets you a pointer to the start of the fragment payload, no matter + * how much of it you may have 'used' already. This is useful for schemes + * where you prepend something to the payload and need to reference it no + * matter how much of it you have consumed or the fragmentation details. + * + * If the buflist is empty, it will return NULL. + */ +LWS_VISIBLE LWS_EXTERN void * +lws_buflist_get_frag_start_or_NULL(struct lws_buflist **head); + + /* * Optional helpers for closely-managed stream flow control. These are useful @@ -1016,13 +1036,20 @@ lws_assert_fourcc(uint32_t fourcc, uint32_t expected); * which represents a count of us as a human-readable time like " 14.350min", * or " 1.500d". * - * You can produce your own schema. + * You can produce your own schema tables. + * + * lws_humanize_pad() is the same but pads the lhs so that it + * always produces the same length. */ LWS_VISIBLE LWS_EXTERN int lws_humanize(char *buf, size_t len, uint64_t value, const lws_humanize_unit_t *schema); +LWS_VISIBLE LWS_EXTERN int +lws_humanize_pad(char *p, size_t len, uint64_t v, + const lws_humanize_unit_t *schema); + LWS_VISIBLE LWS_EXTERN void lws_ser_wu16be(uint8_t *b, uint16_t u); diff --git a/include/libwebsockets/lws-ws-state.h b/include/libwebsockets/lws-ws-state.h index b1cc54633f..e5b7cc9474 100644 --- a/include/libwebsockets/lws-ws-state.h +++ b/include/libwebsockets/lws-ws-state.h @@ -38,7 +38,7 @@ LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT lws_send_pipe_choked(struct lws *wsi); /** - * lws_is_final_fragment() - tests if last part of ws message + * lws_is_final_fragment() - tests if received fragment is last part of ws message * * \param wsi: lws connection */ @@ -46,13 +46,22 @@ LWS_VISIBLE LWS_EXTERN int lws_is_final_fragment(struct lws *wsi); /** - * lws_is_first_fragment() - tests if first part of ws message + * lws_is_first_fragment() - tests if received fragment is first part of ws message * * \param wsi: lws connection */ LWS_VISIBLE LWS_EXTERN int lws_is_first_fragment(struct lws *wsi); +/** + * lws_ws_sending_multifragment() - return 1 if we are in the middle of sending a multi-fragment message + * + * \param wsi: lws connection + */ + +LWS_VISIBLE LWS_EXTERN int +lws_ws_sending_multifragment(struct lws *wsi); + /** * lws_get_reserved_bits() - access reserved bits of ws frame * \param wsi: lws connection diff --git a/lib/core-net/client/connect3.c b/lib/core-net/client/connect3.c index 3f80f4be5c..59fb52b17e 100644 --- a/lib/core-net/client/connect3.c +++ b/lib/core-net/client/connect3.c @@ -189,7 +189,7 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads, const char *cce = "Unable to connect", *iface, *local_port; const struct sockaddr *psa = NULL; uint16_t port = wsi->conn_port; - char dcce[48], t16[16]; + char dcce[128], t16[16]; lws_dns_sort_t *curr; ssize_t plen = 0; lws_dll2_t *d; diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index 9351aa8e5c..7948380239 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -946,6 +946,8 @@ struct lws_spawn_piped { #if defined(WIN32) HANDLE child_pid; lws_sorted_usec_list_t sul_poll; + FILETIME ft_create; + FILETIME ft_exit; #else pid_t child_pid; diff --git a/lib/core/buflist.c b/lib/core/buflist.c index 511330ca34..24570dff09 100644 --- a/lib/core/buflist.c +++ b/lib/core/buflist.c @@ -239,6 +239,9 @@ lws_buflist_fragment_use(struct lws_buflist **head, uint8_t *buf, if (frag_fin) *frag_fin = (*head)->pos + s == (*head)->len; + if (!buf || !len) + return 0; + memcpy(buf, ((uint8_t *)((*head) + 1)) + LWS_PRE + (*head)->pos, s); len -= s; buf += s; @@ -276,6 +279,17 @@ lws_buflist_describe(struct lws_buflist **head, void *id, const char *reason) } #endif +LWS_VISIBLE LWS_EXTERN void * +lws_buflist_get_frag_start_or_NULL(struct lws_buflist **head) +{ + struct lws_buflist *b = (*head); + + if (!b) + return NULL; /* there is no segment to work on */ + + return ((uint8_t *)b) + sizeof(*b) + LWS_PRE; +} + lws_stateful_ret_t lws_flow_feed(lws_flow_t *flow) { diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c index 7ae41f7584..c57c9bd6e5 100644 --- a/lib/core/libwebsockets.c +++ b/lib/core/libwebsockets.c @@ -1762,23 +1762,37 @@ int lws_humanize(char *p, size_t len, uint64_t v, const lws_humanize_unit_t *schema) { char *obuf = p, *end = p + len; + const lws_humanize_unit_t *s = NULL; do { if (v >= schema->factor || schema->factor == 1) { + if (schema[1].name) + s = &schema[1]; + if (schema->factor == 1) { p += decim(p, v, 4, 0); p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), - "%s", schema->name); + "%s", schema->name); return lws_ptr_diff(p, obuf); } p += decim(p, v / schema->factor, 4, 0); - *p++ = '.'; - p += decim(p, (v % schema->factor) / - (schema->factor / 1000), 3, 1); - + if (s) { + uint64_t iif = schema->factor / s->factor; + + if (s->factor * 1000 == schema->factor || + s->factor * 1024 == schema->factor) { /* decimal */ + *p++ = '.'; + p += decim(p, (v % schema->factor) / + (schema->factor / 1000), 3, 1); + } else { /* imperial fraction, eg, h:m */ + *p++ = ':'; + p += decim(p, (v % schema->factor) / s->factor, + iif >= 100 ? 3 : (iif >= 10 ? 2 : 1), 1); + } + } p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), - "%s", schema->name); + "%s", schema->name); return lws_ptr_diff(p, obuf); } schema++; @@ -1790,6 +1804,31 @@ lws_humanize(char *p, size_t len, uint64_t v, const lws_humanize_unit_t *schema) return 0; } +int +lws_humanize_pad(char *p, size_t len, uint64_t v, const lws_humanize_unit_t *schema) +{ + size_t m, w = 0, n = (size_t)lws_humanize(p, len, v, schema); + const lws_humanize_unit_t *s = schema; + int t; + + while (s->name) { + if (strlen(s->name) > w) + w = strlen(s->name); + s++; + } + + m = (3 + 1 + 3 + w) - (size_t)n; + + for (t = (int)n - 1; t >= 0; t--) + p[(size_t)t + m] = p[t]; + p[m + n] = '\0'; + + for (t = 0; t < (int)m; t++) + p[t] = ' '; + + return (int)(n + m); +} + /* * -1 = fail * 0 = continue diff --git a/lib/misc/lws-struct-sqlite.c b/lib/misc/lws-struct-sqlite.c index f64192fb8c..e71403874e 100644 --- a/lib/misc/lws-struct-sqlite.c +++ b/lib/misc/lws-struct-sqlite.c @@ -662,7 +662,7 @@ lws_struct_sq3_create_table(sqlite3 *pdb, const lws_struct_map_t *schema) p += lws_snprintf(p, (unsigned int)lws_ptr_diff(end, p), ");"); if (sqlite3_exec(pdb, s, NULL, NULL, NULL) != SQLITE_OK) { - lwsl_err("%s: %s: fail\n", __func__, sqlite3_errmsg(pdb)); + lwsl_err("%s: %s: fail: %s\n", __func__, sqlite3_errmsg(pdb), s); return -1; } diff --git a/lib/plat/windows/private-lib-plat-windows.h b/lib/plat/windows/private-lib-plat-windows.h index ef9a8d1b89..bb8778af99 100644 --- a/lib/plat/windows/private-lib-plat-windows.h +++ b/lib/plat/windows/private-lib-plat-windows.h @@ -70,6 +70,12 @@ #if defined(LWS_WITH_TLS) #include +#if defined(LWS_WITH_BORINGSSL) + /* Undefine wincrypt.h symbols that conflict with BoringSSL */ + #undef X509_NAME + #undef X509_EXTENSIONS + #undef PKCS7_SIGNER_INFO +#endif #endif #if defined(LWS_HAVE_PTHREAD_H) diff --git a/lib/plat/windows/windows-sockets.c b/lib/plat/windows/windows-sockets.c index c3cd5414b1..326ab9c29f 100644 --- a/lib/plat/windows/windows-sockets.c +++ b/lib/plat/windows/windows-sockets.c @@ -162,7 +162,7 @@ lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags) * https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options * https://docs.microsoft.com/en-us/previous-versions/windows/desktop/qos/differentiated-services */ - lwsl_warn("%s: priority and ip sockets options not implemented on windows platform\n", __func__); + lwsl_info("%s: priority and ip sockets options not implemented on windows platform\n", __func__); /* @@ -194,7 +194,7 @@ lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags) #endif ret = 1; } else - lwsl_notice("%s: set use exclusive addresses\n", __func__); + lwsl_info("%s: set use exclusive addresses\n", __func__); } diff --git a/lib/plat/windows/windows-spawn.c b/lib/plat/windows/windows-spawn.c index b6df4266f5..b2b38b9980 100644 --- a/lib/plat/windows/windows-spawn.c +++ b/lib/plat/windows/windows-spawn.c @@ -27,6 +27,7 @@ #include #include #include +#include void lws_spawn_timeout(struct lws_sorted_usec_list *sul) @@ -148,12 +149,12 @@ lws_spawn_piped_destroy(struct lws_spawn_piped **_lsp) int lws_spawn_reap(struct lws_spawn_piped *lsp) { - + lws_spawn_resource_us_t res = { }; void *opaque = lsp->info.opaque; lsp_cb_t cb = lsp->info.reap_cb; struct _lws_siginfo_t lsi; PROCESS_MEMORY_COUNTERS pmc; - IO_COUNTERS ic; + // IO_COUNTERS ic; ULARGE_INTEGER uli; FILETIME ftk, ftu; DWORD ex; @@ -233,6 +234,11 @@ lws_spawn_reap(struct lws_spawn_piped *lsp) lwsl_notice("%s: process exit 0x%x\n", __func__, lsi.retcode); lsp->child_pid = NULL; + if (lsp->info.res) + res = *lsp->info.res; + else + res = lsp->res; + /* destroy the lsp itself first (it's freed and plsp set NULL */ if (lsp->info.plsp) @@ -241,7 +247,7 @@ lws_spawn_reap(struct lws_spawn_piped *lsp) /* then do the parent callback informing it's destroyed */ if (cb) - cb(opaque, lsp->info.res ? lsp->info.res : &lsp->res, &lsi, 0); + cb(opaque, &res, &lsi, 0); lwsl_notice("%s: completed reap\n", __func__); @@ -624,3 +630,9 @@ lws_spawn_get_fd_stdxxx(struct lws_spawn_piped *lsp, int std_idx) return (int)(intptr_t)lsp->pipe_fds[std_idx][!!(std_idx == 0)]; } + +int +lws_spawn_prepare_self_cgroup(const char *user, const char *group) +{ + return 0; +} diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c index af7fd27de2..80bc6d4461 100644 --- a/lib/roles/ws/ops-ws.c +++ b/lib/roles/ws/ops-ws.c @@ -915,6 +915,11 @@ lws_server_init_wsi_for_ws(struct lws *wsi) } +int +lws_ws_sending_multifragment(struct lws *wsi) +{ + return wsi->ws->last_valid && !wsi->ws->last_fin; +} int lws_is_final_fragment(struct lws *wsi) @@ -1830,12 +1835,37 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len, switch ((*wp) & 0xf) { case LWS_WRITE_TEXT: n = LWSWSOPC_TEXT_FRAME; + if (wsi->ws->last_valid && !wsi->ws->last_fin) { + lwsl_wsi_err(wsi, "Sending TEXT after previous frame that lacked FIN"); + assert(0); + } + wsi->ws->last_valid = 1; + wsi->ws->last_opcode = (uint8_t)n; + wsi->ws->last_fin = !((*wp) & LWS_WRITE_NO_FIN); break; case LWS_WRITE_BINARY: n = LWSWSOPC_BINARY_FRAME; + if (wsi->ws->last_valid && !wsi->ws->last_fin) { + lwsl_wsi_err(wsi, "Sending BINARY after previous frame that lacked FIN"); + assert(0); + } + wsi->ws->last_valid = 1; + wsi->ws->last_opcode = (uint8_t)n; + wsi->ws->last_fin = !((*wp) & LWS_WRITE_NO_FIN); break; case LWS_WRITE_CONTINUATION: n = LWSWSOPC_CONTINUATION; + if (wsi->ws->last_valid && wsi->ws->last_fin) { + lwsl_wsi_err(wsi, "Sending CONTINUATION after previous frame that had FIN"); + assert(0); + } + if (!wsi->ws->last_valid) { + lwsl_wsi_err(wsi, "Sending CONTINUATION as first frame"); + assert(0); + } + wsi->ws->last_valid = 1; + wsi->ws->last_opcode = (uint8_t)n; + wsi->ws->last_fin = !((*wp) & LWS_WRITE_NO_FIN); break; case LWS_WRITE_CLOSE: diff --git a/lib/roles/ws/private-lib-roles-ws.h b/lib/roles/ws/private-lib-roles-ws.h index 9bb8c840c0..573d1bb04e 100644 --- a/lib/roles/ws/private-lib-roles-ws.h +++ b/lib/roles/ws/private-lib-roles-ws.h @@ -137,6 +137,10 @@ struct _lws_websocket_related { uint32_t rx_ubuf_head; uint32_t rx_ubuf_alloc; + uint8_t last_valid; + uint8_t last_opcode; /* for outgoing state validation */ + uint8_t last_fin; /* for outgoing state validation */ + uint8_t pong_payload_len; uint8_t mask_idx; uint8_t opcode; diff --git a/lib/secure-streams/protocols/ss-ws.c b/lib/secure-streams/protocols/ss-ws.c index f275f6964e..b47591f8ef 100644 --- a/lib/secure-streams/protocols/ss-ws.c +++ b/lib/secure-streams/protocols/ss-ws.c @@ -113,6 +113,9 @@ secstream_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user, case LWS_CALLBACK_ESTABLISHED: case LWS_CALLBACK_CLIENT_ESTABLISHED: + if (!h) { + return LWSSSSRET_DISCONNECT_ME; + } h->retry = 0; h->seqstate = SSSEQ_CONNECTED; lws_sul_cancel(&h->sul); diff --git a/lib/secure-streams/secure-streams.c b/lib/secure-streams/secure-streams.c index d973bf84fc..7df00e83e0 100644 --- a/lib/secure-streams/secure-streams.c +++ b/lib/secure-streams/secure-streams.c @@ -94,6 +94,7 @@ const uint32_t ss_state_txn_validity[] = { (1 << LWSSSCS_DESTROYING), [LWSSSCS_DISCONNECTED] = (1 << LWSSSCS_CONNECTING) | + (1 << LWSSSCS_CONNECTED) | (1 << LWSSSCS_TIMEOUT) | (1 << LWSSSCS_POLL) | (1 << LWSSSCS_DESTROYING), @@ -101,6 +102,7 @@ const uint32_t ss_state_txn_validity[] = { [LWSSSCS_UNREACHABLE] = (1 << LWSSSCS_ALL_RETRIES_FAILED) | (1 << LWSSSCS_TIMEOUT) | (1 << LWSSSCS_POLL) | + (1 << LWSSSCS_UNREACHABLE) | (1 << LWSSSCS_CONNECTING) | /* win conn failure > retry > succ */ (1 << LWSSSCS_CONNECTED) | diff --git a/lib/tls/mbedtls/mbedtls-server.c b/lib/tls/mbedtls/mbedtls-server.c index 0af459ca1a..c86f2cb7db 100644 --- a/lib/tls/mbedtls/mbedtls-server.c +++ b/lib/tls/mbedtls/mbedtls-server.c @@ -31,6 +31,15 @@ lws_tls_server_client_cert_verify_config(struct lws_vhost *vh) { int verify_options = SSL_VERIFY_PEER; + if (lws_check_opt(vh->options, + LWS_SERVER_OPTION_MBEDTLS_VERIFY_CLIENT_CERT_POST_HANDSHAKE)) { + verify_options |= SSL_VERIFY_POST_HANDSHAKE; + SSL_CTX_set_verify(vh->tls.ssl_ctx, verify_options, NULL); + lwsl_notice("%s: vh %s can verify client cert post-handshake\n", + __func__, vh->name); + return 0; + } + /* as a server, are we requiring clients to identify themselves? */ if (!lws_check_opt(vh->options, LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) { diff --git a/lib/tls/mbedtls/wrapper/include/internal/ssl_code.h b/lib/tls/mbedtls/wrapper/include/internal/ssl_code.h index 80fdbb20f3..ade547cbed 100644 --- a/lib/tls/mbedtls/wrapper/include/internal/ssl_code.h +++ b/lib/tls/mbedtls/wrapper/include/internal/ssl_code.h @@ -31,6 +31,7 @@ # define SSL_VERIFY_PEER 0x01 # define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02 # define SSL_VERIFY_CLIENT_ONCE 0x04 +# define SSL_VERIFY_POST_HANDSHAKE 0x08 /* * The following 3 states are kept in ssl->rlayer.rstate when reads fail, you diff --git a/lib/tls/mbedtls/wrapper/platform/ssl_pm.c b/lib/tls/mbedtls/wrapper/platform/ssl_pm.c index ef5eab5e0d..789ec95a45 100755 --- a/lib/tls/mbedtls/wrapper/platform/ssl_pm.c +++ b/lib/tls/mbedtls/wrapper/platform/ssl_pm.c @@ -257,7 +257,9 @@ static int ssl_pm_reload_crt(SSL *ssl) struct pkey_pm *pkey_pm = (struct pkey_pm *)ssl->cert->pkey->pkey_pm; struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm; - if ((ssl->verify_mode & SSL_VERIFY_PEER) > 0) + if ((ssl->verify_mode & SSL_VERIFY_POST_HANDSHAKE) > 0) + mode = MBEDTLS_SSL_VERIFY_OPTIONAL; + else if ((ssl->verify_mode & SSL_VERIFY_PEER) > 0) mode = MBEDTLS_SSL_VERIFY_REQUIRED; else if ((ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) > 0) mode = MBEDTLS_SSL_VERIFY_OPTIONAL; @@ -1059,14 +1061,16 @@ void SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx) #if defined(LWS_HAVE_mbedtls_ssl_set_hs_authmode) - if ((ctx->verify_mode & SSL_VERIFY_PEER) > 0) + if ((ctx->verify_mode & SSL_VERIFY_POST_HANDSHAKE) > 0) + mode = MBEDTLS_SSL_VERIFY_OPTIONAL; + else if ((ctx->verify_mode & SSL_VERIFY_PEER) > 0) mode = MBEDTLS_SSL_VERIFY_REQUIRED; else if ((ctx->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) > 0) mode = MBEDTLS_SSL_VERIFY_REQUIRED; else if (ctx->verify_mode == SSL_VERIFY_CLIENT_ONCE) mode = MBEDTLS_SSL_VERIFY_UNSET; else - mode = MBEDTLS_SSL_VERIFY_NONE; + mode = MBEDTLS_SSL_VERIFY_NONE; #endif /* apply new ctx cert to ssl */ diff --git a/minimal-examples-lowlevel/api-tests/api-test-lws_spawn/main.c b/minimal-examples-lowlevel/api-tests/api-test-lws_spawn/main.c index 3c5f0e27c6..0aa3f3fe35 100644 --- a/minimal-examples-lowlevel/api-tests/api-test-lws_spawn/main.c +++ b/minimal-examples-lowlevel/api-tests/api-test-lws_spawn/main.c @@ -166,8 +166,13 @@ reap_cb(void *opaque, const lws_spawn_resource_us_t *res, siginfo_t *si, test_phase_t last_phase = ts->phase; if (si) { +#if defined(WIN32) + lwsl_user("%s: Reap callback for phase %d, exit code %d\n", + __func__, (int)last_phase, (int)si->retcode); +#else lwsl_user("%s: Reap callback for phase %d, exit code %d\n", __func__, (int)last_phase, si->si_status); +#endif lwsl_notice(" CPU us: user %llu, sys %llu\n", (unsigned long long)res->us_cpu_user, (unsigned long long)res->us_cpu_sys); @@ -179,10 +184,18 @@ reap_cb(void *opaque, const lws_spawn_resource_us_t *res, siginfo_t *si, lwsl_err("%s: Spawned process was killed by timeout\n", __func__); ts->result = 1; - } else if (si->si_status != 0) { - lwsl_err("%s: Spawned process failed with exit code %d\n", + } else { +#if defined(WIN32) + if (si->retcode != 0) { + lwsl_err("%s: Spawned process failed with exit code %d\n", + __func__, (int)si->retcode); +#else + if (si->si_status != 0) { + lwsl_err("%s: Spawned process failed with exit code %d\n", __func__, si->si_status); - ts->result = 1; +#endif + ts->result = 1; + } } if (res->us_cpu_user == 0 && res->us_cpu_sys == 0) { diff --git a/minimal-examples-lowlevel/http-client/minimal-http-client-multi/CMakeLists.txt b/minimal-examples-lowlevel/http-client/minimal-http-client-multi/CMakeLists.txt index 871e1263be..8c51837c21 100644 --- a/minimal-examples-lowlevel/http-client/minimal-http-client-multi/CMakeLists.txt +++ b/minimal-examples-lowlevel/http-client/minimal-http-client-multi/CMakeLists.txt @@ -33,19 +33,9 @@ if (requirements) # machine context in parallel so they can tread on each other otherwise # set(PORT_HCM_SRV "7670") - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "0") - set(PORT_HCM_SRV 7671) + if ("$ENV{SAI_INSTANCE_IDX}") + math(EXPR PORT_HCM_SRV "7671 + $ENV{SAI_INSTANCE_IDX}") endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "1") - set(PORT_HCM_SRV 7672) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "2") - set(PORT_HCM_SRV 7673) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "3") - set(PORT_HCM_SRV 7674) - endif() - # hack if (NOT WIN32 AND LWS_WITH_SERVER) diff --git a/minimal-examples-lowlevel/http-client/minimal-http-client-post/CMakeLists.txt b/minimal-examples-lowlevel/http-client/minimal-http-client-post/CMakeLists.txt index 1efd356413..9d73823b35 100644 --- a/minimal-examples-lowlevel/http-client/minimal-http-client-post/CMakeLists.txt +++ b/minimal-examples-lowlevel/http-client/minimal-http-client-post/CMakeLists.txt @@ -29,19 +29,10 @@ if (requirements) # machine context in parallel so they can tread on each other otherwise # set(PORT_HCP_SRV "7640") - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "0") - set(PORT_HCP_SRV 7641) + if ("$ENV{SAI_INSTANCE_IDX}") + math(EXPR PORT_HCP_SRV "7671 + $ENV{SAI_INSTANCE_IDX}") endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "1") - set(PORT_HCP_SRV 7642) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "2") - set(PORT_HCP_SRV 7643) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "3") - set(PORT_HCP_SRV 7644) - endif() - + # hack if (NOT WIN32 AND LWS_WITH_SERVER) diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt b/minimal-examples-lowlevel/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt index 02b16f8f53..5f5110d020 100644 --- a/minimal-examples-lowlevel/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt @@ -111,17 +111,8 @@ if (requirements) # set(PORT_HSEF_SRV "961") - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "0") - set(PORT_HSEF_SRV 962) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "1") - set(PORT_HSEF_SRV 963) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "2") - set(PORT_HSEF_SRV 964) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "3") - set(PORT_HSEF_SRV 965) + if ("$ENV{SAI_INSTANCE_IDX}") + math(EXPR PORT_HSEF_SRV "962 + $ENV{SAI_INSTANCE_IDX}") endif() if (websockets_shared) diff --git a/minimal-examples-lowlevel/ws-client/minimal-ws-client-spam/CMakeLists.txt b/minimal-examples-lowlevel/ws-client/minimal-ws-client-spam/CMakeLists.txt index c39fcb322d..14132a2728 100644 --- a/minimal-examples-lowlevel/ws-client/minimal-ws-client-spam/CMakeLists.txt +++ b/minimal-examples-lowlevel/ws-client/minimal-ws-client-spam/CMakeLists.txt @@ -27,19 +27,10 @@ if (requirements) # instantiate the server per sai builder instance, they are running in the same # machine context in parallel so they can tread on each other otherwise # - set(PORT_WCS_SRV "7620") - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "0") - set(PORT_WCS_SRV 7621) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "1") - set(PORT_WCS_SRV 7622) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "2") - set(PORT_WCS_SRV 7623) - endif() - if ("$ENV{SAI_INSTANCE_IDX}" STREQUAL "3") - set(PORT_WCS_SRV 7624) - endif() + set(PORT_WCS_SRV "7620") + if ("$ENV{SAI_INSTANCE_IDX}") + math(EXPR PORT_WCS_SRV "7621 + $ENV{SAI_INSTANCE_IDX}") + endif() # hack if (WIN32) diff --git a/plugins/deaddrop/protocol_lws_deaddrop.c b/plugins/deaddrop/protocol_lws_deaddrop.c index 2a2cce4866..57c8e43c79 100644 --- a/plugins/deaddrop/protocol_lws_deaddrop.c +++ b/plugins/deaddrop/protocol_lws_deaddrop.c @@ -371,20 +371,13 @@ file_upload_cb(void *data, const char *name, const char *filename, static int format_result(struct pss_deaddrop *pss) { - unsigned char *p, *start, *end; - - p = (unsigned char *)pss->result + LWS_PRE; - start = p; - end = p + sizeof(pss->result) - LWS_PRE - 1; - - p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), - "" - "" - ""); - p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), ""); - - return (int)lws_ptr_diff(p, start); + /* + * We don't want to send any entity body back for the upload + * POST. The success / failure is indicated by the + * HTTP response code. The javascript on the client side that + * did the post is not expecting to navigate to a new page. + */ + return 0; } @@ -463,7 +456,7 @@ handler_server_protocol_destroy(struct vhd_deaddrop *vhd) #endif } -static void +static int handler_server_http(struct vhd_deaddrop *vhd, struct pss_deaddrop *pss, struct lws *wsi) { @@ -484,12 +477,14 @@ handler_server_http(struct vhd_deaddrop *vhd, struct pss_deaddrop *pss, meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len); if (meth != LWSHUMETH_POST || !uri_ptr) - return; + return 1; if (!strstr(uri_ptr, "/upload/")) - return; + return 1; pss->vhd = vhd; pss->wsi = wsi; + + return 0; } static int @@ -566,8 +561,8 @@ handler_server_http_writeable(struct vhd_deaddrop *vhd, /* first send the headers ... */ n = lws_write(wsi, start, lws_ptr_diff_size_t(p, start), - LWS_WRITE_HTTP_HEADERS | - LWS_WRITE_H2_STREAM_END); + LWS_WRITE_HTTP_HEADERS );//| + //LWS_WRITE_H2_STREAM_END); if (n < 0) return 1; @@ -863,7 +858,8 @@ callback_deaddrop(struct lws *wsi, enum lws_callback_reasons reason, break; case LWS_CALLBACK_HTTP: - handler_server_http(vhd, pss, wsi); + if (!handler_server_http(vhd, pss, wsi)) + return 0; break; case LWS_CALLBACK_PROTOCOL_DESTROY: @@ -889,7 +885,8 @@ callback_deaddrop(struct lws *wsi, enum lws_callback_reasons reason, break; case LWS_CALLBACK_SERVER_WRITEABLE: - return handler_server_ws_writeable(vhd, pss, wsi); + handler_server_ws_writeable(vhd, pss, wsi); + return 0; /* POST-related */ @@ -898,12 +895,12 @@ callback_deaddrop(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_HTTP_BODY_COMPLETION: handler_server_http_body_completion(pss, wsi); - break; + return 0; case LWS_CALLBACK_HTTP_WRITEABLE: switch (handler_server_http_writeable(vhd, pss, wsi)) { case 0: - break; + return 0; case 1: goto bail; case 2: