From 84a57540ab809ee3c0647133dcad5f36cffa88c4 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sun, 13 Jan 2019 06:58:21 +0800 Subject: [PATCH] LWS_WITH_NETWORK: cmake option for no network code --- .travis.yml | 1 + CMakeLists.txt | 221 +- README.md | 6 + cmake/lws_config.h.in | 1 + lib/{core => core-net}/adopt.c | 0 lib/core-net/client.c | 85 + lib/core-net/close.c | 518 ++++ lib/{core => core-net}/connect.c | 0 lib/{core => core-net}/dummy-callback.c | 0 lib/core-net/network.c | 457 ++++ lib/{core => core-net}/output.c | 0 lib/{core => core-net}/pollfd.c | 0 lib/core-net/private.h | 1048 ++++++++ lib/core-net/server.c | 299 +++ lib/{core => core-net}/service.c | 4 + lib/core-net/stats.c | 263 ++ lib/core-net/vhost.c | 1241 +++++++++ lib/core-net/wsi-timeout.c | 251 ++ lib/core-net/wsi.c | 883 +++++++ lib/core/context.c | 1257 +-------- lib/core/libwebsockets.c | 3229 +---------------------- lib/core/logs.c | 273 ++ lib/core/private.h | 1028 +------- lib/core/vfs.c | 134 + lib/plat/optee/lws-plat-optee.c | 245 +- lib/plat/optee/network.c | 227 ++ lib/plat/unix/unix-init.c | 7 +- lib/plat/unix/unix-service.c | 2 + lib/plat/windows/windows-service.c | 2 + lib/roles/http/client/client.c | 15 - lib/tls/mbedtls/ssl.c | 18 +- lib/tls/mbedtls/tls.c | 40 + lib/tls/mbedtls/x509.c | 2 + lib/tls/openssl/ssl.c | 97 - lib/tls/openssl/tls.c | 126 + lib/tls/openssl/x509.c | 20 +- lib/tls/private-network.h | 189 ++ lib/tls/private.h | 177 +- lib/tls/tls-network.c | 255 ++ lib/tls/tls.c | 231 +- 40 files changed, 6701 insertions(+), 6151 deletions(-) rename lib/{core => core-net}/adopt.c (100%) create mode 100644 lib/core-net/client.c create mode 100644 lib/core-net/close.c rename lib/{core => core-net}/connect.c (100%) rename lib/{core => core-net}/dummy-callback.c (100%) create mode 100644 lib/core-net/network.c rename lib/{core => core-net}/output.c (100%) rename lib/{core => core-net}/pollfd.c (100%) create mode 100644 lib/core-net/private.h create mode 100644 lib/core-net/server.c rename lib/{core => core-net}/service.c (99%) create mode 100644 lib/core-net/stats.c create mode 100644 lib/core-net/vhost.c create mode 100644 lib/core-net/wsi-timeout.c create mode 100644 lib/core-net/wsi.c create mode 100644 lib/core/logs.c create mode 100644 lib/core/vfs.c create mode 100644 lib/plat/optee/network.c create mode 100644 lib/tls/mbedtls/tls.c create mode 100644 lib/tls/openssl/tls.c create mode 100644 lib/tls/private-network.h create mode 100644 lib/tls/tls-network.c diff --git a/.travis.yml b/.travis.yml index fb94419c60..f1523cde41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ env: - 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" + - LWS_METHOD=nonetwork CMAKE_ARGS="-DLWS_WITH_NETWORK=0" - LWS_METHOD=libev CMAKE_ARGS="-DLWS_WITH_LIBEV=ON" - LWS_METHOD=noipv6 CMAKE_ARGS="-DLWS_IPV6=OFF" - LWS_METHOD=nossl CMAKE_ARGS="-DLWS_WITH_SSL=OFF" diff --git a/CMakeLists.txt b/CMakeLists.txt index 4309ca83fe..ca52fec12d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ option(LWS_FOR_GITOHASHI "Enable features recommended for use with gitohashi" OF # # Major individual features # +option(LWS_WITH_NETWORK "Compile with network-related code" ON) option(LWS_ROLE_H1 "Compile with support for http/1 (needed for ws)" ON) option(LWS_ROLE_WS "Compile with support for websockets" ON) option(LWS_ROLE_DBUS "Compile with support for DBUS" OFF) @@ -159,6 +160,28 @@ if(LWS_WITH_DISTRO_RECOMMENDED) set(LWS_WITH_JOSE 1) endif() +if (NOT LWS_WITH_NETWORK) + set(LWS_ROLE_H1 0) + set(LWS_ROLE_WS 0) + set(LWS_ROLE_RAW 0) + set(LWS_WITHOUT_EXTENSIONS 1) + set(LWS_WITHOUT_SERVER 1) + set(LWS_WITHOUT_CLIENT 1) + set(LWS_WITH_HTTP2 0) + set(LWS_WITH_SOCKS5 0) + set(LWS_UNIX_SOCK 0) + set(LWS_WITH_HTTP_PROXY 0) + set(LWS_WITH_PLUGINS 0) + set(LWS_WITH_LWSWS 0) + set(LWS_WITH_CGI 0) + set(LWS_ROLE_RAW_PROXY 0) + set(LWS_WITH_PEER_LIMITS 0) + set(LWS_WITH_GENERIC_SESSIONS 0) + set(LWS_WITH_HTTP_STREAM_COMPRESSION 0) + set(LWS_WITH_HTTP_BROTLI 0) + set(LWS_WITH_POLL 0) +endif() + # do you care about this? Then send me a patch where it disables it on travis # but allows it on APPLE if (APPLE) @@ -414,10 +437,6 @@ if (LWS_WITH_CYASSL) set(LWS_WOLFSSL_INCLUDE_DIRS ${LWS_CYASSL_INCLUDE_DIRS} CACHE PATH "Path to wolfSSL/CyaSSL header files" FORCE) endif() -if (LWS_WITHOUT_CLIENT AND LWS_WITHOUT_SERVER) - message(FATAL_ERROR "Makes no sense to compile with neither client nor server.") -endif() - if (NOT (LWS_WITH_STATIC OR LWS_WITH_SHARED)) message(FATAL_ERROR "Makes no sense to compile with neither static nor shared libraries.") endif() @@ -863,15 +882,35 @@ set(HDR_PUBLIC set(SOURCES lib/core/alloc.c lib/core/context.c - lib/core/dummy-callback.c lib/core/libwebsockets.c - lib/core/output.c - lib/core/pollfd.c - lib/core/service.c + lib/core/logs.c lib/misc/base64-decode.c + lib/core/vfs.c lib/misc/lws-ring.c - lib/core/adopt.c - lib/roles/pipe/ops-pipe.c) +) + +if (LWS_WITH_NETWORK) + list(APPEND SOURCES + lib/core-net/dummy-callback.c + lib/core-net/output.c + lib/core-net/close.c + lib/core-net/network.c + lib/core-net/vhost.c + lib/core-net/pollfd.c + lib/core-net/service.c + lib/core-net/stats.c + lib/core-net/wsi.c + lib/core-net/wsi-timeout.c + lib/core-net/adopt.c + lib/roles/pipe/ops-pipe.c + ) + + if (LWS_WITH_STATS) + list(APPEND SOURCES + lib/core-net/stats.c + ) + endif() +endif() if (LWS_WITH_THREADPOOL AND UNIX AND LWS_HAVE_PTHREAD_H) list(APPEND SOURCES lib/misc/threadpool/threadpool.c) @@ -962,94 +1001,113 @@ endif() if (NOT LWS_WITHOUT_CLIENT) list(APPEND SOURCES - lib/core/connect.c + lib/core-net/connect.c + lib/core-net/client.c lib/roles/http/client/client.c lib/roles/http/client/client-handshake.c) endif() if (NOT LWS_WITHOUT_SERVER) list(APPEND SOURCES + lib/core-net/server.c lib/roles/listen/ops-listen.c) endif() if (LWS_WITH_MBEDTLS) set(LWS_WITH_SSL ON) - list(APPEND HDR_PRIVATE - lib/tls/mbedtls/wrapper/include/internal/ssl3.h - lib/tls/mbedtls/wrapper/include/internal/ssl_cert.h - lib/tls/mbedtls/wrapper/include/internal/ssl_code.h - lib/tls/mbedtls/wrapper/include/internal/ssl_dbg.h - lib/tls/mbedtls/wrapper/include/internal/ssl_lib.h - lib/tls/mbedtls/wrapper/include/internal/ssl_methods.h - lib/tls/mbedtls/wrapper/include/internal/ssl_pkey.h - lib/tls/mbedtls/wrapper/include/internal/ssl_stack.h - lib/tls/mbedtls/wrapper/include/internal/ssl_types.h - lib/tls/mbedtls/wrapper/include/internal/ssl_x509.h - lib/tls/mbedtls/wrapper/include/internal/tls1.h - lib/tls/mbedtls/wrapper/include/internal/x509_vfy.h) - - list(APPEND HDR_PRIVATE - lib/tls/mbedtls/wrapper/include/openssl/ssl.h) - - list(APPEND HDR_PRIVATE - lib/tls/mbedtls/wrapper/include/platform/ssl_pm.h - lib/tls/mbedtls/wrapper/include/platform/ssl_port.h) - include_directories(lib/tls/mbedtls/wrapper/include) include_directories(lib/tls/mbedtls/wrapper/include/platform) include_directories(lib/tls/mbedtls/wrapper/include/internal) include_directories(lib/tls/mbedtls/wrapper/include/openssl) - - list(APPEND SOURCES - lib/tls/mbedtls/wrapper/library/ssl_cert.c - lib/tls/mbedtls/wrapper/library/ssl_lib.c - lib/tls/mbedtls/wrapper/library/ssl_methods.c - lib/tls/mbedtls/wrapper/library/ssl_pkey.c - lib/tls/mbedtls/wrapper/library/ssl_stack.c - lib/tls/mbedtls/wrapper/library/ssl_x509.c) - - list(APPEND SOURCES - lib/tls/mbedtls/wrapper/platform/ssl_pm.c - lib/tls/mbedtls/wrapper/platform/ssl_port.c) + + if (LWS_WITH_NETWORK) + list(APPEND HDR_PRIVATE + lib/tls/mbedtls/wrapper/include/internal/ssl3.h + lib/tls/mbedtls/wrapper/include/internal/ssl_cert.h + lib/tls/mbedtls/wrapper/include/internal/ssl_code.h + lib/tls/mbedtls/wrapper/include/internal/ssl_dbg.h + lib/tls/mbedtls/wrapper/include/internal/ssl_lib.h + lib/tls/mbedtls/wrapper/include/internal/ssl_methods.h + lib/tls/mbedtls/wrapper/include/internal/ssl_pkey.h + lib/tls/mbedtls/wrapper/include/internal/ssl_stack.h + lib/tls/mbedtls/wrapper/include/internal/ssl_types.h + lib/tls/mbedtls/wrapper/include/internal/ssl_x509.h + lib/tls/mbedtls/wrapper/include/internal/tls1.h + lib/tls/mbedtls/wrapper/include/internal/x509_vfy.h) + + list(APPEND HDR_PRIVATE + lib/tls/mbedtls/wrapper/include/openssl/ssl.h) + + list(APPEND HDR_PRIVATE + lib/tls/mbedtls/wrapper/include/platform/ssl_pm.h + lib/tls/mbedtls/wrapper/include/platform/ssl_port.h) + + list(APPEND SOURCES + lib/tls/mbedtls/wrapper/library/ssl_cert.c + lib/tls/mbedtls/wrapper/library/ssl_lib.c + lib/tls/mbedtls/wrapper/library/ssl_methods.c + lib/tls/mbedtls/wrapper/library/ssl_pkey.c + lib/tls/mbedtls/wrapper/library/ssl_stack.c + lib/tls/mbedtls/wrapper/library/ssl_x509.c) + + list(APPEND SOURCES + lib/tls/mbedtls/wrapper/platform/ssl_pm.c + lib/tls/mbedtls/wrapper/platform/ssl_port.c) + endif() endif() if (LWS_WITH_SSL) list(APPEND SOURCES lib/tls/tls.c ) + if (LWS_WITH_NETWORK) + list(APPEND SOURCES + lib/tls/tls-network.c + ) + endif() - if (LWS_WITH_MBEDTLS) + if (LWS_WITH_MBEDTLS) + list(APPEND SOURCES + lib/tls/mbedtls/tls.c + lib/tls/mbedtls/x509.c + ) + if (LWS_WITH_NETWORK) list(APPEND SOURCES lib/tls/mbedtls/ssl.c - lib/tls/mbedtls/x509.c ) - if (LWS_WITH_GENCRYPTO) - list(APPEND SOURCES - lib/tls/mbedtls/lws-genhash.c - lib/tls/mbedtls/lws-genrsa.c - lib/tls/mbedtls/lws-genaes.c - lib/tls/lws-genec-common.c - lib/tls/mbedtls/lws-genec.c - lib/tls/mbedtls/lws-gencrypto.c - ) - endif() - else() + endif() + if (LWS_WITH_GENCRYPTO) + list(APPEND SOURCES + lib/tls/mbedtls/lws-genhash.c + lib/tls/mbedtls/lws-genrsa.c + lib/tls/mbedtls/lws-genaes.c + lib/tls/lws-genec-common.c + lib/tls/mbedtls/lws-genec.c + lib/tls/mbedtls/lws-gencrypto.c + ) + endif() + else() + list(APPEND SOURCES + lib/tls/openssl/tls.c + lib/tls/openssl/x509.c + ) + if (LWS_WITH_NETWORK) list(APPEND SOURCES lib/tls/openssl/ssl.c - lib/tls/openssl/x509.c ) - if (LWS_WITH_GENCRYPTO) - list(APPEND SOURCES - lib/tls/openssl/lws-genhash.c - lib/tls/openssl/lws-genrsa.c - lib/tls/openssl/lws-genaes.c - lib/tls/lws-genec-common.c - lib/tls/openssl/lws-genec.c - lib/tls/openssl/lws-gencrypto.c - ) - endif() endif() + if (LWS_WITH_GENCRYPTO) + list(APPEND SOURCES + lib/tls/openssl/lws-genhash.c + lib/tls/openssl/lws-genrsa.c + lib/tls/openssl/lws-genaes.c + lib/tls/lws-genec-common.c + lib/tls/openssl/lws-genec.c + lib/tls/openssl/lws-gencrypto.c + ) + endif() + endif() if (NOT LWS_WITHOUT_SERVER) list(APPEND SOURCES @@ -1105,6 +1163,11 @@ else() list(APPEND SOURCES lib/plat/optee/lws-plat-optee.c ) + if (LWS_WITH_NETWORK) + list(APPEND SOURCES + lib/plat/optee/network.c + ) + endif() else() if (LWS_WITH_ESP32) list(APPEND SOURCES @@ -1120,14 +1183,18 @@ else() else() list(APPEND SOURCES lib/plat/unix/unix-caps.c - lib/plat/unix/unix-fds.c lib/plat/unix/unix-file.c lib/plat/unix/unix-misc.c lib/plat/unix/unix-init.c - lib/plat/unix/unix-pipe.c - lib/plat/unix/unix-service.c - lib/plat/unix/unix-sockets.c ) + if (LWS_WITH_NETWORK) + list(APPEND SOURCES + lib/plat/unix/unix-pipe.c + lib/plat/unix/unix-service.c + lib/plat/unix/unix-sockets.c + lib/plat/unix/unix-fds.c + ) + endif() if (LWS_WITH_PLUGINS AND LWS_WITH_LIBUV) list(APPEND SOURCES lib/plat/unix/unix-plugins.c) @@ -1155,22 +1222,22 @@ if (LWS_WITH_HTTP_PROXY) lib/roles/http/server/rewrite.c) endif() -if (LWS_WITH_POLL) +if (LWS_WITH_POLL AND LWS_WITH_NETWORK) list(APPEND SOURCES lib/event-libs/poll/poll.c) endif() -if (LWS_WITH_LIBUV) +if (LWS_WITH_LIBUV AND LWS_WITH_NETWORK) list(APPEND SOURCES lib/event-libs/libuv/libuv.c) endif() -if (LWS_WITH_LIBEVENT) +if (LWS_WITH_LIBEVENT AND LWS_WITH_NETWORK) list(APPEND SOURCES lib/event-libs/libevent/libevent.c) endif() -if (LWS_WITH_LIBEV) +if (LWS_WITH_LIBEV AND LWS_WITH_NETWORK) list(APPEND SOURCES lib/event-libs/libev/libev.c) endif() @@ -1179,7 +1246,7 @@ if (LWS_WITH_LEJP) list(APPEND SOURCES lib/misc/lejp.c) endif() -if (LWS_WITH_LEJP_CONF) +if (LWS_WITH_LEJP_CONF AND LWS_WITH_NETWORK) list(APPEND SOURCES "lib/roles/http/server/lejp-conf.c" ) diff --git a/README.md b/README.md index acefc2ab07..0678dea7d1 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,12 @@ News ## New features on master + - `LWS_WITH_NETWORK` cmake option (default on) allows one-step removal of vhost, + wsi, roles, event loop and all network-related code from the build. This + enables use-cases where you actually need unrelated features like JOSE or FTS + compactly. lws_context still exists and if tls is enabled, the tls-related code + is still built so the crypto is available, just nothing related to network. + - New Crypto-agile APIs + JOSE / JWS / JWE / JWK support... apis work exactly the same with OpenSSL or mbedTLS tls library backends, and allow key cycling and crypto algorithm changes while allowing for grace periods diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index 87112a931e..d54cbf6627 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -95,6 +95,7 @@ #cmakedefine LWS_WITH_LIBUV #cmakedefine LWS_WITH_LWSAC #cmakedefine LWS_WITH_MBEDTLS +#cmakedefine LWS_WITH_NETWORK #cmakedefine LWS_WITH_NO_LOGS #cmakedefine LWS_WITHOUT_CLIENT #cmakedefine LWS_WITHOUT_EXTENSIONS diff --git a/lib/core/adopt.c b/lib/core-net/adopt.c similarity index 100% rename from lib/core/adopt.c rename to lib/core-net/adopt.c diff --git a/lib/core-net/client.c b/lib/core-net/client.c new file mode 100644 index 0000000000..73ad436ac6 --- /dev/null +++ b/lib/core-net/client.c @@ -0,0 +1,85 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + + +LWS_VISIBLE int +lws_set_proxy(struct lws_vhost *vhost, const char *proxy) +{ + char *p; + char authstring[96]; + + if (!proxy) + return -1; + + /* we have to deal with a possible redundant leading http:// */ + if (!strncmp(proxy, "http://", 7)) + proxy += 7; + + p = strrchr(proxy, '@'); + if (p) { /* auth is around */ + + if ((unsigned int)(p - proxy) > sizeof(authstring) - 1) + goto auth_too_long; + + lws_strncpy(authstring, proxy, p - proxy + 1); + // null termination not needed on input + if (lws_b64_encode_string(authstring, lws_ptr_diff(p, proxy), + vhost->proxy_basic_auth_token, + sizeof vhost->proxy_basic_auth_token) < 0) + goto auth_too_long; + + lwsl_info(" Proxy auth in use\n"); + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + proxy = p + 1; +#endif + } else + vhost->proxy_basic_auth_token[0] = '\0'; + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + lws_strncpy(vhost->http.http_proxy_address, proxy, + sizeof(vhost->http.http_proxy_address)); + + p = strchr(vhost->http.http_proxy_address, ':'); + if (!p && !vhost->http.http_proxy_port) { + lwsl_err("http_proxy needs to be ads:port\n"); + + return -1; + } else { + if (p) { + *p = '\0'; + vhost->http.http_proxy_port = atoi(p + 1); + } + } + + lwsl_info(" Proxy %s:%u\n", vhost->http.http_proxy_address, + vhost->http.http_proxy_port); +#endif + return 0; + +auth_too_long: + lwsl_err("proxy auth too long\n"); + + return -1; +} + diff --git a/lib/core-net/close.c b/lib/core-net/close.c new file mode 100644 index 0000000000..584e574e0c --- /dev/null +++ b/lib/core-net/close.c @@ -0,0 +1,518 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + + +void +__lws_free_wsi(struct lws *wsi) +{ + if (!wsi) + return; + + /* + * Protocol user data may be allocated either internally by lws + * or by specified the user. We should only free what we allocated. + */ + if (wsi->protocol && wsi->protocol->per_session_data_size && + wsi->user_space && !wsi->user_space_externally_allocated) + lws_free(wsi->user_space); + + lws_buflist_destroy_all_segments(&wsi->buflist); + lws_buflist_destroy_all_segments(&wsi->buflist_out); + lws_free_set_NULL(wsi->udp); + + if (wsi->vhost && wsi->vhost->lserv_wsi == wsi) + wsi->vhost->lserv_wsi = NULL; +#if !defined(LWS_NO_CLIENT) + lws_dll_lws_remove(&wsi->dll_active_client_conns); +#endif + wsi->context->count_wsi_allocated--; + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + __lws_header_table_detach(wsi, 0); +#endif + __lws_same_vh_protocol_remove(wsi); +#if !defined(LWS_NO_CLIENT) + lws_client_stash_destroy(wsi); + lws_free_set_NULL(wsi->client_hostname_copy); +#endif + + if (wsi->role_ops->destroy_role) + wsi->role_ops->destroy_role(wsi); + +#if defined(LWS_WITH_PEER_LIMITS) + lws_peer_track_wsi_close(wsi->context, wsi->peer); + wsi->peer = NULL; +#endif + + /* since we will destroy the wsi, make absolutely sure now */ + +#if defined(LWS_WITH_OPENSSL) + __lws_ssl_remove_wsi_from_buffered_list(wsi); +#endif + __lws_remove_from_timeout_list(wsi); + + if (wsi->context->event_loop_ops->destroy_wsi) + wsi->context->event_loop_ops->destroy_wsi(wsi); + + lws_vhost_unbind_wsi(wsi); + + lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi, + wsi->context->count_wsi_allocated); + + lws_free(wsi); +} + + +void +lws_remove_child_from_any_parent(struct lws *wsi) +{ + struct lws **pwsi; + int seen = 0; + + if (!wsi->parent) + return; + + /* detach ourselves from parent's child list */ + pwsi = &wsi->parent->child_list; + while (*pwsi) { + if (*pwsi == wsi) { + lwsl_info("%s: detach %p from parent %p\n", __func__, + wsi, wsi->parent); + + if (wsi->parent->protocol) + wsi->parent->protocol->callback(wsi, + LWS_CALLBACK_CHILD_CLOSING, + wsi->parent->user_space, wsi, 0); + + *pwsi = wsi->sibling_list; + seen = 1; + break; + } + pwsi = &(*pwsi)->sibling_list; + } + if (!seen) + lwsl_err("%s: failed to detach from parent\n", __func__); + + wsi->parent = NULL; +} + + +void +__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; + struct lws_context *context; + int n; + + lwsl_info("%s: %p: caller: %s\n", __func__, wsi, caller); + + if (!wsi) + return; + + lws_access_log(wsi); + + context = wsi->context; + pt = &context->pt[(int)wsi->tsi]; + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_CLOSE, 1); + +#if !defined(LWS_NO_CLIENT) + + lws_free_set_NULL(wsi->client_hostname_copy); + /* we are no longer an active client connection that can piggyback */ + lws_dll_lws_remove(&wsi->dll_active_client_conns); + + /* + * if we have wsi in our transaction queue, if we are closing we + * must go through and close all those first + */ + if (wsi->vhost) { + if ((int)reason != -1) + lws_vhost_lock(wsi->vhost); + lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, + wsi->dll_client_transaction_queue_head.next) { + struct lws *w = lws_container_of(d, struct lws, + dll_client_transaction_queue); + + __lws_close_free_wsi(w, -1, "trans q leader closing"); + } lws_end_foreach_dll_safe(d, d1); + + /* + * !!! If we are closing, but we have pending pipelined + * transaction results we already sent headers for, that's going + * to destroy sync for HTTP/1 and leave H2 stream with no live + * swsi. + * + * However this is normal if we are being closed because the + * transaction queue leader is closing. + */ + lws_dll_lws_remove(&wsi->dll_client_transaction_queue); + if ((int)reason !=-1) + lws_vhost_unlock(wsi->vhost); + } +#endif + + /* if we have children, close them first */ + if (wsi->child_list) { + wsi2 = wsi->child_list; + while (wsi2) { + wsi1 = wsi2->sibling_list; + wsi2->parent = NULL; + /* stop it doing shutdown processing */ + wsi2->socket_is_permanently_unusable = 1; + __lws_close_free_wsi(wsi2, reason, + "general child recurse"); + wsi2 = wsi1; + } + wsi->child_list = NULL; + } + + if (wsi->role_ops == &role_ops_raw_file) { + lws_remove_child_from_any_parent(wsi); + __remove_wsi_socket_from_fds(wsi); + wsi->protocol->callback(wsi, wsi->role_ops->close_cb[0], + wsi->user_space, NULL, 0); + goto async_close; + } + + wsi->wsistate_pre_close = wsi->wsistate; + +#ifdef LWS_WITH_CGI + if (wsi->role_ops == &role_ops_cgi) { + /* we are not a network connection, but a handler for CGI io */ + if (wsi->parent && wsi->parent->http.cgi) { + + if (wsi->cgi_channel == LWS_STDOUT) + lws_cgi_remove_and_kill(wsi->parent); + + /* end the binding between us and master */ + wsi->parent->http.cgi->stdwsi[(int)wsi->cgi_channel] = + NULL; + } + wsi->socket_is_permanently_unusable = 1; + + goto just_kill_connection; + } + + if (wsi->http.cgi) + lws_cgi_remove_and_kill(wsi); +#endif + +#if !defined(LWS_NO_CLIENT) + lws_client_stash_destroy(wsi); +#endif + + if (wsi->role_ops == &role_ops_raw_skt) { + wsi->socket_is_permanently_unusable = 1; + goto just_kill_connection; + } +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + if (lwsi_role_http(wsi) && lwsi_role_server(wsi) && + wsi->http.fop_fd != NULL) + lws_vfs_file_close(&wsi->http.fop_fd); +#endif + + if (lwsi_state(wsi) == LRS_DEAD_SOCKET) + return; + + if (wsi->socket_is_permanently_unusable || + reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY || + lwsi_state(wsi) == LRS_SHUTDOWN) + goto just_kill_connection; + + switch (lwsi_state_PRE_CLOSE(wsi)) { + case LRS_DEAD_SOCKET: + return; + + /* we tried the polite way... */ + case LRS_WAITING_TO_SEND_CLOSE: + case LRS_AWAITING_CLOSE_ACK: + case LRS_RETURNED_CLOSE: + goto just_kill_connection; + + case LRS_FLUSHING_BEFORE_CLOSE: + if (lws_has_buffered_out(wsi) +#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) + || wsi->http.comp_ctx.buflist_comp || + wsi->http.comp_ctx.may_have_more +#endif + ) { + lws_callback_on_writable(wsi); + return; + } + lwsl_info("%p: end LRS_FLUSHING_BEFORE_CLOSE\n", wsi); + goto just_kill_connection; + default: + if (lws_has_buffered_out(wsi) +#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) + || wsi->http.comp_ctx.buflist_comp || + wsi->http.comp_ctx.may_have_more +#endif + ) { + lwsl_info("%p: LRS_FLUSHING_BEFORE_CLOSE\n", wsi); + lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE); + __lws_set_timeout(wsi, + PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5); + return; + } + break; + } + + if (lwsi_state(wsi) == LRS_WAITING_CONNECT || + lwsi_state(wsi) == LRS_H1C_ISSUE_HANDSHAKE) + goto just_kill_connection; + + if (!wsi->told_user_closed && wsi->user_space && wsi->protocol && + wsi->protocol_bind_balance) { + wsi->protocol->callback(wsi, + wsi->role_ops->protocol_unbind_cb[ + !!lwsi_role_server(wsi)], + wsi->user_space, (void *)__func__, 0); + wsi->protocol_bind_balance = 0; + } + + /* + * signal we are closing, lws_write will + * add any necessary version-specific stuff. If the write fails, + * no worries we are closing anyway. If we didn't initiate this + * close, then our state has been changed to + * LRS_RETURNED_CLOSE and we will skip this. + * + * Likewise if it's a second call to close this connection after we + * sent the close indication to the peer already, we are in state + * LRS_AWAITING_CLOSE_ACK and will skip doing this a second time. + */ + + if (wsi->role_ops->close_via_role_protocol && + wsi->role_ops->close_via_role_protocol(wsi, reason)) + return; + +just_kill_connection: + + if (wsi->role_ops->close_kill_connection) + wsi->role_ops->close_kill_connection(wsi, reason); + + lws_remove_child_from_any_parent(wsi); + n = 0; + + if (!wsi->told_user_closed && wsi->user_space && + wsi->protocol_bind_balance) { + lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi, + wsi->protocol->name); + wsi->protocol->callback(wsi, + wsi->role_ops->protocol_unbind_cb[ + !!lwsi_role_server(wsi)], + wsi->user_space, (void *)__func__, 0); + wsi->protocol_bind_balance = 0; + } + + if ((lwsi_state(wsi) == LRS_WAITING_SERVER_REPLY || + lwsi_state(wsi) == LRS_WAITING_CONNECT) && !wsi->already_did_cce) + wsi->protocol->callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, NULL, 0); + + /* + * Testing with ab shows that we have to stage the socket close when + * the system is under stress... shutdown any further TX, change the + * state to one that won't emit anything more, and wait with a timeout + * for the POLLIN to show a zero-size rx before coming back and doing + * the actual close. + */ + if (wsi->role_ops != &role_ops_raw_skt && !lwsi_role_client(wsi) && + lwsi_state(wsi) != LRS_SHUTDOWN && + lwsi_state(wsi) != LRS_UNCONNECTED && + reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY && + !wsi->socket_is_permanently_unusable) { + +#if defined(LWS_WITH_TLS) + if (lws_is_ssl(wsi) && wsi->tls.ssl) { + n = 0; + switch (__lws_tls_shutdown(wsi)) { + case LWS_SSL_CAPABLE_DONE: + case LWS_SSL_CAPABLE_ERROR: + case LWS_SSL_CAPABLE_MORE_SERVICE_READ: + case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE: + case LWS_SSL_CAPABLE_MORE_SERVICE: + break; + } + } else +#endif + { + lwsl_info("%s: shutdown conn: %p (sk %d, state 0x%x)\n", + __func__, wsi, (int)(long)wsi->desc.sockfd, + lwsi_state(wsi)); + if (!wsi->socket_is_permanently_unusable && + lws_socket_is_valid(wsi->desc.sockfd)) { + wsi->socket_is_permanently_unusable = 1; + n = shutdown(wsi->desc.sockfd, SHUT_WR); + } + } + if (n) + lwsl_debug("closing: shutdown (state 0x%x) ret %d\n", + lwsi_state(wsi), LWS_ERRNO); + + /* + * This causes problems on WINCE / ESP32 with disconnection + * when the events are half closing connection + */ +#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP32) + /* libuv: no event available to guarantee completion */ + if (!wsi->socket_is_permanently_unusable && + lws_socket_is_valid(wsi->desc.sockfd) && + lwsi_state(wsi) != LRS_SHUTDOWN && + context->event_loop_ops->periodic_events_available) { + __lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN); + lwsi_set_state(wsi, LRS_SHUTDOWN); + __lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH, + context->timeout_secs); + + return; + } +#endif + } + + lwsl_debug("%s: real just_kill_connection: %p (sockfd %d)\n", __func__, + wsi, wsi->desc.sockfd); + +#ifdef LWS_WITH_HUBBUB + if (wsi->http.rw) { + lws_rewrite_destroy(wsi->http.rw); + wsi->http.rw = NULL; + } +#endif + + if (wsi->http.pending_return_headers) + lws_free_set_NULL(wsi->http.pending_return_headers); + + /* + * we won't be servicing or receiving anything further from this guy + * delete socket from the internal poll list if still present + */ + __lws_ssl_remove_wsi_from_buffered_list(wsi); + __lws_remove_from_timeout_list(wsi); + lws_dll_lws_remove(&wsi->dll_hrtimer); + + /* don't repeat event loop stuff */ + if (wsi->told_event_loop_closed) + return; + + /* checking return redundant since we anyway close */ + if (wsi->desc.sockfd != LWS_SOCK_INVALID) + __remove_wsi_socket_from_fds(wsi); + else + __lws_same_vh_protocol_remove(wsi); + + lwsi_set_state(wsi, LRS_DEAD_SOCKET); + lws_buflist_destroy_all_segments(&wsi->buflist); + lws_dll_lws_remove(&wsi->dll_buflist); + + if (wsi->role_ops->close_role) + wsi->role_ops->close_role(pt, wsi); + + /* tell the user it's all over for this guy */ + + if ((lwsi_state_est_PRE_CLOSE(wsi) || + lwsi_state_PRE_CLOSE(wsi) == LRS_WAITING_SERVER_REPLY) && + !wsi->told_user_closed && + wsi->role_ops->close_cb[lwsi_role_server(wsi)]) { + const struct lws_protocols *pro = wsi->protocol; + + if (!wsi->protocol) + pro = &wsi->vhost->protocols[0]; + + if (!wsi->upgraded_to_http2 || !lwsi_role_client(wsi)) + /* + * The network wsi for a client h2 connection shouldn't + * call back for its role: the child stream connections + * own the role. Otherwise h2 will call back closed + * one too many times as the children do it and then + * the closing network stream. + */ + pro->callback(wsi, + wsi->role_ops->close_cb[lwsi_role_server(wsi)], + wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + +async_close: + wsi->socket_is_permanently_unusable = 1; + + if (wsi->context->event_loop_ops->wsi_logical_close) + if (wsi->context->event_loop_ops->wsi_logical_close(wsi)) + return; + + __lws_close_free_wsi_final(wsi); +} + +void +__lws_close_free_wsi_final(struct lws *wsi) +{ + int n; + + if (!wsi->shadow && + lws_socket_is_valid(wsi->desc.sockfd) && !lws_ssl_close(wsi)) { + lwsl_debug("%s: wsi %p: fd %d\n", __func__, wsi, wsi->desc.sockfd); + n = compatible_close(wsi->desc.sockfd); + if (n) + lwsl_debug("closing: close ret %d\n", LWS_ERRNO); + + wsi->desc.sockfd = LWS_SOCK_INVALID; + } + + /* outermost destroy notification for wsi (user_space still intact) */ + if (wsi->vhost) + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, + wsi->user_space, NULL, 0); + +#ifdef LWS_WITH_CGI + if (wsi->http.cgi) { + + for (n = 0; n < 3; n++) { + if (wsi->http.cgi->pipe_fds[n][!!(n == 0)] == 0) + lwsl_err("ZERO FD IN CGI CLOSE"); + + if (wsi->http.cgi->pipe_fds[n][!!(n == 0)] >= 0) + close(wsi->http.cgi->pipe_fds[n][!!(n == 0)]); + } + + lws_free(wsi->http.cgi); + } +#endif + + __lws_free_wsi(wsi); +} + + +void +lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *caller) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + lws_pt_lock(pt, __func__); + __lws_close_free_wsi(wsi, reason, caller); + lws_pt_unlock(pt); +} + + diff --git a/lib/core/connect.c b/lib/core-net/connect.c similarity index 100% rename from lib/core/connect.c rename to lib/core-net/connect.c diff --git a/lib/core/dummy-callback.c b/lib/core-net/dummy-callback.c similarity index 100% rename from lib/core/dummy-callback.c rename to lib/core-net/dummy-callback.c diff --git a/lib/core-net/network.c b/lib/core-net/network.c new file mode 100644 index 0000000000..c4b3404df0 --- /dev/null +++ b/lib/core-net/network.c @@ -0,0 +1,457 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + +#if !defined(LWS_WITH_ESP32) && !defined(LWS_PLAT_OPTEE) +LWS_VISIBLE int +interface_to_sa(struct lws_vhost *vh, const char *ifname, + struct sockaddr_in *addr, size_t addrlen) +{ + int ipv6 = 0; +#ifdef LWS_WITH_IPV6 + ipv6 = LWS_IPV6_ENABLED(vh); +#endif + (void)vh; + + return lws_interface_to_sa(ipv6, ifname, addr, addrlen); +} +#endif + +#ifndef LWS_PLAT_OPTEE +static int +lws_get_addresses(struct lws_vhost *vh, void *ads, char *name, + int name_len, char *rip, int rip_len) +{ + struct addrinfo ai, *res; + struct sockaddr_in addr4; + + rip[0] = '\0'; + name[0] = '\0'; + addr4.sin_family = AF_UNSPEC; + +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(vh)) { + if (!lws_plat_inet_ntop(AF_INET6, + &((struct sockaddr_in6 *)ads)->sin6_addr, + rip, rip_len)) { + lwsl_err("inet_ntop: %s", strerror(LWS_ERRNO)); + return -1; + } + + // Strip off the IPv4 to IPv6 header if one exists + if (strncmp(rip, "::ffff:", 7) == 0) + memmove(rip, rip + 7, strlen(rip) - 6); + + getnameinfo((struct sockaddr *)ads, sizeof(struct sockaddr_in6), + name, name_len, NULL, 0, 0); + + return 0; + } else +#endif + { + struct addrinfo *result; + + memset(&ai, 0, sizeof ai); + ai.ai_family = PF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; +#if !defined(LWS_WITH_ESP32) + if (getnameinfo((struct sockaddr *)ads, + sizeof(struct sockaddr_in), + name, name_len, NULL, 0, 0)) + return -1; +#endif + + if (getaddrinfo(name, NULL, &ai, &result)) + return -1; + + res = result; + while (addr4.sin_family == AF_UNSPEC && res) { + switch (res->ai_family) { + case AF_INET: + addr4.sin_addr = + ((struct sockaddr_in *)res->ai_addr)->sin_addr; + addr4.sin_family = AF_INET; + break; + } + + res = res->ai_next; + } + freeaddrinfo(result); + } + + if (addr4.sin_family == AF_UNSPEC) + return -1; + + if (lws_plat_inet_ntop(AF_INET, &addr4.sin_addr, rip, rip_len) == NULL) + return -1; + + return 0; +} + + +LWS_VISIBLE const char * +lws_get_peer_simple(struct lws *wsi, char *name, int namelen) +{ + socklen_t len, olen; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin4; + int af = AF_INET; + void *p, *q; + + wsi = lws_get_network_wsi(wsi); + +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + len = sizeof(sin6); + p = &sin6; + af = AF_INET6; + q = &sin6.sin6_addr; + } else +#endif + { + len = sizeof(sin4); + p = &sin4; + q = &sin4.sin_addr; + } + + olen = len; + if (getpeername(wsi->desc.sockfd, p, &len) < 0 || len > olen) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + return NULL; + } + + return lws_plat_inet_ntop(af, q, name, namelen); +} +#endif + +LWS_VISIBLE void +lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name, + int name_len, char *rip, int rip_len) +{ +#ifndef LWS_PLAT_OPTEE + socklen_t len; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin4; + struct lws_context *context = wsi->context; + int ret = -1; + void *p; + + rip[0] = '\0'; + name[0] = '\0'; + + lws_latency_pre(context, wsi); + +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + len = sizeof(sin6); + p = &sin6; + } else +#endif + { + len = sizeof(sin4); + p = &sin4; + } + + if (getpeername(fd, p, &len) < 0) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + goto bail; + } + + ret = lws_get_addresses(wsi->vhost, p, name, name_len, rip, rip_len); + +bail: + lws_latency(context, wsi, "lws_get_peer_addresses", ret, 1); +#endif + (void)wsi; + (void)fd; + (void)name; + (void)name_len; + (void)rip; + (void)rip_len; +} + + + +/* note: this returns a random port, or one of these <= 0 return codes: + * + * LWS_ITOSA_USABLE: the interface is usable, returned if so and sockfd invalid + * LWS_ITOSA_NOT_EXIST: the requested iface does not even exist + * LWS_ITOSA_NOT_USABLE: the requested iface exists but is not usable (eg, no IP) + * LWS_ITOSA_BUSY: the port at the requested iface + port is already in use + */ + +LWS_EXTERN int +lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, + const char *iface) +{ +#ifdef LWS_WITH_UNIX_SOCK + struct sockaddr_un serv_unix; +#endif +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 serv_addr6; +#endif + struct sockaddr_in serv_addr4; +#ifndef LWS_PLAT_OPTEE + socklen_t len = sizeof(struct sockaddr_storage); +#endif + int n; +#if !defined(LWS_WITH_ESP32) && !defined(LWS_PLAT_OPTEE) + int m; +#endif + struct sockaddr_storage sin; + struct sockaddr *v; + + memset(&sin, 0, sizeof(sin)); + +#if defined(LWS_WITH_UNIX_SOCK) + if (LWS_UNIX_SOCK_ENABLED(vhost)) { + v = (struct sockaddr *)&serv_unix; + n = sizeof(struct sockaddr_un); + bzero((char *) &serv_unix, sizeof(serv_unix)); + serv_unix.sun_family = AF_UNIX; + if (!iface) + return LWS_ITOSA_NOT_EXIST; + if (sizeof(serv_unix.sun_path) <= strlen(iface)) { + lwsl_err("\"%s\" too long for UNIX domain socket\n", + iface); + return LWS_ITOSA_NOT_EXIST; + } + strcpy(serv_unix.sun_path, iface); + if (serv_unix.sun_path[0] == '@') + serv_unix.sun_path[0] = '\0'; + else + unlink(serv_unix.sun_path); + + } else +#endif +#if defined(LWS_WITH_IPV6) && !defined(LWS_WITH_ESP32) + if (LWS_IPV6_ENABLED(vhost)) { + v = (struct sockaddr *)&serv_addr6; + n = sizeof(struct sockaddr_in6); + bzero((char *) &serv_addr6, sizeof(serv_addr6)); + if (iface) { + m = interface_to_sa(vhost, iface, + (struct sockaddr_in *)v, n); + if (m == LWS_ITOSA_NOT_USABLE) { + lwsl_info("%s: netif %s: Not usable\n", + __func__, iface); + return m; + } + if (m == LWS_ITOSA_NOT_EXIST) { + lwsl_info("%s: netif %s: Does not exist\n", + __func__, iface); + return m; + } + serv_addr6.sin6_scope_id = lws_get_addr_scope(iface); + } + + serv_addr6.sin6_family = AF_INET6; + serv_addr6.sin6_port = htons(port); + } else +#endif + { + v = (struct sockaddr *)&serv_addr4; + n = sizeof(serv_addr4); + bzero((char *) &serv_addr4, sizeof(serv_addr4)); + serv_addr4.sin_addr.s_addr = INADDR_ANY; + serv_addr4.sin_family = AF_INET; + +#if !defined(LWS_WITH_ESP32) && !defined(LWS_PLAT_OPTEE) + if (iface) { + m = interface_to_sa(vhost, iface, + (struct sockaddr_in *)v, n); + if (m == LWS_ITOSA_NOT_USABLE) { + lwsl_info("%s: netif %s: Not usable\n", + __func__, iface); + return m; + } + if (m == LWS_ITOSA_NOT_EXIST) { + lwsl_info("%s: netif %s: Does not exist\n", + __func__, iface); + return m; + } + } +#endif + serv_addr4.sin_port = htons(port); + } /* ipv4 */ + + /* just checking for the interface extant */ + if (sockfd == LWS_SOCK_INVALID) + return LWS_ITOSA_USABLE; + + n = bind(sockfd, v, n); +#ifdef LWS_WITH_UNIX_SOCK + if (n < 0 && LWS_UNIX_SOCK_ENABLED(vhost)) { + lwsl_err("ERROR on binding fd %d to \"%s\" (%d %d)\n", + sockfd, iface, n, LWS_ERRNO); + return LWS_ITOSA_NOT_EXIST; + } else +#endif + if (n < 0) { + lwsl_err("ERROR on binding fd %d to port %d (%d %d)\n", + sockfd, port, n, LWS_ERRNO); + + /* if something already listening, tell caller to fail permanently */ + + if (LWS_ERRNO == LWS_EADDRINUSE) + return LWS_ITOSA_BUSY; + + /* otherwise ask caller to retry later */ + + return LWS_ITOSA_NOT_EXIST; + } + +#if defined(LWS_WITH_UNIX_SOCK) + if (LWS_UNIX_SOCK_ENABLED(vhost) && vhost->context->uid) + if (chown(serv_unix.sun_path, vhost->context->uid, + vhost->context->gid)) + lwsl_notice("%s: chown for unix skt %s failed\n", + __func__, serv_unix.sun_path); +#endif + +#ifndef LWS_PLAT_OPTEE + if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) + lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO)); + else +#endif +#if defined(LWS_WITH_IPV6) + port = (sin.ss_family == AF_INET6) ? + ntohs(((struct sockaddr_in6 *) &sin)->sin6_port) : + ntohs(((struct sockaddr_in *) &sin)->sin_port); +#else + { + struct sockaddr_in sain; + memcpy(&sain, &sin, sizeof(sain)); + port = ntohs(sain.sin_port); + } +#endif + + return port; +} + +#if defined(LWS_WITH_IPV6) +LWS_EXTERN unsigned long +lws_get_addr_scope(const char *ipaddr) +{ + unsigned long scope = 0; + +#ifndef WIN32 + struct ifaddrs *addrs, *addr; + char ip[NI_MAXHOST]; + unsigned int i; + + getifaddrs(&addrs); + for (addr = addrs; addr; addr = addr->ifa_next) { + if (!addr->ifa_addr || + addr->ifa_addr->sa_family != AF_INET6) + continue; + + getnameinfo(addr->ifa_addr, + sizeof(struct sockaddr_in6), + ip, sizeof(ip), + NULL, 0, NI_NUMERICHOST); + + i = 0; + while (ip[i]) + if (ip[i++] == '%') { + ip[i - 1] = '\0'; + break; + } + + if (!strcmp(ip, ipaddr)) { + scope = if_nametoindex(addr->ifa_name); + break; + } + } + freeifaddrs(addrs); +#else + PIP_ADAPTER_ADDRESSES adapter, addrs = NULL; + PIP_ADAPTER_UNICAST_ADDRESS addr; + ULONG size = 0; + DWORD ret; + struct sockaddr_in6 *sockaddr; + char ip[NI_MAXHOST]; + unsigned int i; + int found = 0; + + for (i = 0; i < 5; i++) + { + ret = GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, + NULL, addrs, &size); + if ((ret == NO_ERROR) || (ret == ERROR_NO_DATA)) { + break; + } else if (ret == ERROR_BUFFER_OVERFLOW) + { + if (addrs) + free(addrs); + addrs = (IP_ADAPTER_ADDRESSES *)malloc(size); + } else + { + if (addrs) + { + free(addrs); + addrs = NULL; + } + lwsl_err("Failed to get IPv6 address table (%d)", ret); + break; + } + } + + if ((ret == NO_ERROR) && (addrs)) { + adapter = addrs; + while (adapter && !found) { + addr = adapter->FirstUnicastAddress; + while (addr && !found) { + if (addr->Address.lpSockaddr->sa_family == + AF_INET6) { + sockaddr = (struct sockaddr_in6 *) + (addr->Address.lpSockaddr); + + lws_plat_inet_ntop(sockaddr->sin6_family, + &sockaddr->sin6_addr, + ip, sizeof(ip)); + + if (!strcmp(ip, ipaddr)) { + scope = sockaddr->sin6_scope_id; + found = 1; + break; + } + } + addr = addr->Next; + } + adapter = adapter->Next; + } + } + if (addrs) + free(addrs); +#endif + + return scope; +} +#endif + + + diff --git a/lib/core/output.c b/lib/core-net/output.c similarity index 100% rename from lib/core/output.c rename to lib/core-net/output.c diff --git a/lib/core/pollfd.c b/lib/core-net/pollfd.c similarity index 100% rename from lib/core/pollfd.c rename to lib/core-net/pollfd.c diff --git a/lib/core-net/private.h b/lib/core-net/private.h new file mode 100644 index 0000000000..3688e0697c --- /dev/null +++ b/lib/core-net/private.h @@ -0,0 +1,1048 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#if !defined(__LWS_CORE_NET_PRIVATE_H__) +#define __LWS_CORE_NET_PRIVATE_H__ + +#include "roles/private.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * All lws_tls...() functions must return this type, converting the + * native backend result and doing the extra work to determine which one + * as needed. + * + * Native TLS backend return codes are NOT ALLOWED outside the backend. + * + * Non-SSL mode also uses these types. + */ +enum lws_ssl_capable_status { + LWS_SSL_CAPABLE_ERROR = -1, /* it failed */ + LWS_SSL_CAPABLE_DONE = 0, /* it succeeded */ + LWS_SSL_CAPABLE_MORE_SERVICE_READ = -2, /* retry WANT_READ */ + LWS_SSL_CAPABLE_MORE_SERVICE_WRITE = -3, /* retry WANT_WRITE */ + LWS_SSL_CAPABLE_MORE_SERVICE = -4, /* general retry */ +}; + + +/* + * + * ------ roles ------ + * + */ + +/* null-terminated array of pointers to roles lws built with */ +extern const struct lws_role_ops *available_roles[]; + +#define LWS_FOR_EVERY_AVAILABLE_ROLE_START(xx) { \ + const struct lws_role_ops **ppxx = available_roles; \ + while (*ppxx) { \ + const struct lws_role_ops *xx = *ppxx++; + +#define LWS_FOR_EVERY_AVAILABLE_ROLE_END }} + +/* + * + * ------ event_loop ops ------ + * + */ + +/* enums of socks version */ +enum socks_version { + SOCKS_VERSION_4 = 4, + SOCKS_VERSION_5 = 5 +}; + +/* enums of subnegotiation version */ +enum socks_subnegotiation_version { + SOCKS_SUBNEGOTIATION_VERSION_1 = 1, +}; + +/* enums of socks commands */ +enum socks_command { + SOCKS_COMMAND_CONNECT = 1, + SOCKS_COMMAND_BIND = 2, + SOCKS_COMMAND_UDP_ASSOCIATE = 3 +}; + +/* enums of socks address type */ +enum socks_atyp { + SOCKS_ATYP_IPV4 = 1, + SOCKS_ATYP_DOMAINNAME = 3, + SOCKS_ATYP_IPV6 = 4 +}; + +/* enums of socks authentication methods */ +enum socks_auth_method { + SOCKS_AUTH_NO_AUTH = 0, + SOCKS_AUTH_GSSAPI = 1, + SOCKS_AUTH_USERNAME_PASSWORD = 2 +}; + +/* enums of subnegotiation status */ +enum socks_subnegotiation_status { + SOCKS_SUBNEGOTIATION_STATUS_SUCCESS = 0, +}; + +/* enums of socks request reply */ +enum socks_request_reply { + SOCKS_REQUEST_REPLY_SUCCESS = 0, + SOCKS_REQUEST_REPLY_FAILURE_GENERAL = 1, + SOCKS_REQUEST_REPLY_CONNECTION_NOT_ALLOWED = 2, + SOCKS_REQUEST_REPLY_NETWORK_UNREACHABLE = 3, + SOCKS_REQUEST_REPLY_HOST_UNREACHABLE = 4, + SOCKS_REQUEST_REPLY_CONNECTION_REFUSED = 5, + SOCKS_REQUEST_REPLY_TTL_EXPIRED = 6, + SOCKS_REQUEST_REPLY_COMMAND_NOT_SUPPORTED = 7, + SOCKS_REQUEST_REPLY_ATYP_NOT_SUPPORTED = 8 +}; + +/* enums used to generate socks messages */ +enum socks_msg_type { + /* greeting */ + SOCKS_MSG_GREETING, + /* credential, user name and password */ + SOCKS_MSG_USERNAME_PASSWORD, + /* connect command */ + SOCKS_MSG_CONNECT +}; + +enum { + LWS_RXFLOW_ALLOW = (1 << 0), + LWS_RXFLOW_PENDING_CHANGE = (1 << 1), +}; + +enum lws_parser_return { + LPR_OK = 0, + LPR_FAIL = -1, + LPR_DO_FALLBACK = 2, + LPR_FORBIDDEN = -2 +}; + +typedef union { +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 sa6; +#endif + struct sockaddr_in sa4; +} sockaddr46; + + +#if defined(LWS_WITH_PEER_LIMITS) +struct lws_peer { + struct lws_peer *next; + struct lws_peer *peer_wait_list; + + time_t time_created; + time_t time_closed_all; + + uint8_t addr[32]; + uint32_t hash; + uint32_t count_wsi; + uint32_t total_wsi; + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + struct lws_peer_role_http http; +#endif + + uint8_t af; +}; +#endif + +enum { + LWS_EV_READ = (1 << 0), + LWS_EV_WRITE = (1 << 1), + LWS_EV_START = (1 << 2), + LWS_EV_STOP = (1 << 3), + + LWS_EV_PREPARE_DELETION = (1u << 31), +}; + +#ifdef LWS_WITH_IPV6 +#define LWS_IPV6_ENABLED(vh) \ + (!lws_check_opt(vh->context->options, LWS_SERVER_OPTION_DISABLE_IPV6) && \ + !lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_IPV6)) +#else +#define LWS_IPV6_ENABLED(context) (0) +#endif + +#ifdef LWS_WITH_UNIX_SOCK +#define LWS_UNIX_SOCK_ENABLED(vhost) \ + (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK) +#else +#define LWS_UNIX_SOCK_ENABLED(vhost) (0) +#endif + +enum uri_path_states { + URIPS_IDLE, + URIPS_SEEN_SLASH, + URIPS_SEEN_SLASH_DOT, + URIPS_SEEN_SLASH_DOT_DOT, +}; + +enum uri_esc_states { + URIES_IDLE, + URIES_SEEN_PERCENT, + URIES_SEEN_PERCENT_H1, +}; + + +#ifndef LWS_NO_CLIENT +struct client_info_stash { + char *address; + char *path; + char *host; + char *origin; + char *protocol; + char *method; + char *iface; + char *alpn; + void *opaque_user_data; /* not allocated or freed by lws */ +}; +#endif + +#define lws_wsi_is_udp(___wsi) (!!___wsi->udp) + +#define LWS_H2_FRAME_HEADER_LENGTH 9 + +struct lws_timed_vh_protocol { + struct lws_timed_vh_protocol *next; + const struct lws_protocols *protocol; + struct lws_vhost *vhost; /* only used for pending processing */ + time_t time; + int reason; + int tsi_req; +}; + +/* + * so we can have n connections being serviced simultaneously, + * these things need to be isolated per-thread. + */ + +struct lws_context_per_thread { +#if LWS_MAX_SMP > 1 + pthread_mutex_t lock_stats; + struct lws_mutex_refcount mr; + pthread_t self; +#endif + + struct lws_context *context; + + /* + * usable by anything in the service code, but only if the scope + * does not last longer than the service action (since next service + * of any socket can likewise use it and overwrite) + */ + unsigned char *serv_buf; + + struct lws_dll_lws dll_head_timeout; + struct lws_dll_lws dll_head_hrtimer; + struct lws_dll_lws dll_head_buflist; /* guys with pending rxflow */ + +#if defined(LWS_WITH_TLS) + struct lws_pt_tls tls; +#endif + + struct lws_pollfd *fds; + volatile struct lws_foreign_thread_pollfd * volatile foreign_pfd_list; +#ifdef _WIN32 + WSAEVENT events; +#endif + lws_sockfd_type dummy_pipe_fds[2]; + struct lws *pipe_wsi; + + /* --- role based members --- */ + +#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) + struct lws_pt_role_ws ws; +#endif +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + struct lws_pt_role_http http; +#endif +#if defined(LWS_ROLE_DBUS) + struct lws_pt_role_dbus dbus; +#endif + + /* --- event library based members --- */ + +#if defined(LWS_WITH_LIBEV) + struct lws_pt_eventlibs_libev ev; +#endif +#if defined(LWS_WITH_LIBUV) + struct lws_pt_eventlibs_libuv uv; +#endif +#if defined(LWS_WITH_LIBEVENT) + struct lws_pt_eventlibs_libevent event; +#endif + +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || \ + defined(LWS_WITH_LIBEVENT) + struct lws_signal_watcher w_sigint; +#endif + + /* --- */ + + unsigned long count_conns; + unsigned int fds_count; + + /* + * set to the Thread ID that's doing the service loop just before entry + * to poll indicates service thread likely idling in poll() + * volatile because other threads may check it as part of processing + * for pollfd event change. + */ + volatile int service_tid; + int service_tid_detected; + + volatile unsigned char inside_poll; + volatile unsigned char foreign_spinlock; + + unsigned char tid; + + unsigned char inside_service:1; + unsigned char event_loop_foreign:1; + unsigned char event_loop_destroy_processing_done:1; +}; + +struct lws_conn_stats { + unsigned long long rx, tx; + unsigned long h1_conn, h1_trans, h2_trans, ws_upg, h2_alpn, h2_subs, + h2_upg, rejected; +}; + +/* + * virtual host -related context information + * vhostwide SSL context + * vhostwide proxy + * + * hierarchy: + * + * context -> vhost -> wsi + * + * incoming connection non-SSL vhost binding: + * + * listen socket -> wsi -> select vhost after first headers + * + * incoming connection SSL vhost binding: + * + * SSL SNI -> wsi -> bind after SSL negotiation + */ + + +struct lws_vhost { +#if !defined(LWS_WITHOUT_CLIENT) + char proxy_basic_auth_token[128]; +#endif +#if LWS_MAX_SMP > 1 + pthread_mutex_t lock; + char close_flow_vs_tsi[LWS_MAX_SMP]; +#endif + +#if defined(LWS_ROLE_H2) + struct lws_vhost_role_h2 h2; +#endif +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + struct lws_vhost_role_http http; +#endif +#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) + struct lws_vhost_role_ws ws; +#endif + +#if defined(LWS_WITH_SOCKS5) + char socks_proxy_address[128]; + char socks_user[96]; + char socks_password[96]; +#endif +#if defined(LWS_WITH_LIBEV) + struct lws_io_watcher w_accept; +#endif + struct lws_conn_stats conn_stats; + struct lws_context *context; + struct lws_vhost *vhost_next; + + struct lws *lserv_wsi; + const char *name; + const char *iface; + const char *listen_accept_role; + const char *listen_accept_protocol; + + void (*finalize)(struct lws_vhost *vh, void *arg); + void *finalize_arg; + +#if !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32) + int bind_iface; +#endif + const struct lws_protocols *protocols; + void **protocol_vh_privs; + const struct lws_protocol_vhost_options *pvo; + const struct lws_protocol_vhost_options *headers; + struct lws_dll_lws *same_vh_protocol_heads; + struct lws_vhost *no_listener_vhost_list; +#if !defined(LWS_NO_CLIENT) + struct lws_dll_lws dll_active_client_conns; +#endif + +#if defined(LWS_WITH_TLS) + struct lws_vhost_tls tls; +#endif + + struct lws_timed_vh_protocol *timed_vh_protocol_list; + void *user; + + int listen_port; + +#if defined(LWS_WITH_SOCKS5) + unsigned int socks_proxy_port; +#endif + unsigned int options; + int count_protocols; + int ka_time; + int ka_probes; + int ka_interval; + int keepalive_timeout; + int timeout_secs_ah_idle; + + int count_bound_wsi; + +#ifdef LWS_WITH_ACCESS_LOG + int log_fd; +#endif + + unsigned int created_vhost_protocols:1; + unsigned int being_destroyed:1; + + unsigned char default_protocol_index; + unsigned char raw_protocol_index; +}; + +void +__lws_vhost_destroy2(struct lws_vhost *vh); + +struct lws { + /* structs */ + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + struct _lws_http_mode_related http; +#endif +#if defined(LWS_ROLE_H2) + struct _lws_h2_related h2; +#endif +#if defined(LWS_ROLE_WS) + struct _lws_websocket_related *ws; /* allocated if we upgrade to ws */ +#endif +#if defined(LWS_ROLE_DBUS) + struct _lws_dbus_mode_related dbus; +#endif + + const struct lws_role_ops *role_ops; + lws_wsi_state_t wsistate; + lws_wsi_state_t wsistate_pre_close; + + /* lifetime members */ + +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || \ + defined(LWS_WITH_LIBEVENT) + struct lws_io_watcher w_read; +#endif +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBEVENT) + struct lws_io_watcher w_write; +#endif + + /* pointers */ + + struct lws_context *context; + struct lws_vhost *vhost; + struct lws *parent; /* points to parent, if any */ + struct lws *child_list; /* points to first child */ + struct lws *sibling_list; /* subsequent children at same level */ + + const struct lws_protocols *protocol; + struct lws_dll_lws same_vh_protocol; + + struct lws_dll_lws dll_timeout; + struct lws_dll_lws dll_hrtimer; + struct lws_dll_lws dll_buflist; /* guys with pending rxflow */ + +#if defined(LWS_WITH_THREADPOOL) + struct lws_threadpool_task *tp_task; +#endif + +#if defined(LWS_WITH_PEER_LIMITS) + struct lws_peer *peer; +#endif + + struct lws_udp *udp; +#ifndef LWS_NO_CLIENT + struct client_info_stash *stash; + char *client_hostname_copy; + struct lws_dll_lws dll_active_client_conns; + struct lws_dll_lws dll_client_transaction_queue_head; + struct lws_dll_lws dll_client_transaction_queue; +#endif + void *user_space; + void *opaque_parent_data; + void *opaque_user_data; + + struct lws_buflist *buflist; /* input-side buflist */ + struct lws_buflist *buflist_out; /* output-side buflist */ + +#if defined(LWS_WITH_TLS) + struct lws_lws_tls tls; +#endif + + lws_sock_file_fd_type desc; /* .filefd / .sockfd */ +#if defined(LWS_WITH_STATS) + uint64_t active_writable_req_us; +#if defined(LWS_WITH_TLS) + uint64_t accept_start_us; +#endif +#endif + + lws_usec_t pending_timer; /* hrtimer fires */ + time_t pending_timeout_set; /* second-resolution timeout start */ + +#ifdef LWS_LATENCY + unsigned long action_start; + unsigned long latency_start; +#endif + + /* ints */ +#define LWS_NO_FDS_POS (-1) + int position_in_fds_table; + +#ifndef LWS_NO_CLIENT + int chunk_remaining; +#endif + unsigned int cache_secs; + + unsigned int hdr_parsing_completed:1; + unsigned int http2_substream:1; + unsigned int upgraded_to_http2:1; + unsigned int h2_stream_carries_ws:1; + unsigned int seen_nonpseudoheader:1; + unsigned int listener:1; + unsigned int user_space_externally_allocated:1; + unsigned int socket_is_permanently_unusable:1; + unsigned int rxflow_change_to:2; + unsigned int conn_stat_done:1; + unsigned int cache_reuse:1; + unsigned int cache_revalidate:1; + unsigned int cache_intermediaries:1; + unsigned int favoured_pollin:1; + unsigned int sending_chunked:1; + unsigned int interpreting:1; + unsigned int already_did_cce:1; + unsigned int told_user_closed:1; + unsigned int told_event_loop_closed:1; + unsigned int waiting_to_send_close_frame:1; + unsigned int close_needs_ack:1; + unsigned int ipv6:1; + unsigned int parent_pending_cb_on_writable:1; + unsigned int cgi_stdout_zero_length:1; + unsigned int seen_zero_length_recv:1; + unsigned int rxflow_will_be_applied:1; + unsigned int event_pipe:1; + unsigned int handling_404:1; + unsigned int protocol_bind_balance:1; + unsigned int unix_skt:1; + unsigned int close_when_buffered_out_drained:1; + + unsigned int could_have_pending:1; /* detect back-to-back writes */ + unsigned int outer_will_close:1; + unsigned int shadow:1; /* we do not control fd lifecycle at all */ + +#ifdef LWS_WITH_ACCESS_LOG + unsigned int access_log_pending:1; +#endif +#ifndef LWS_NO_CLIENT + unsigned int do_ws:1; /* whether we are doing http or ws flow */ + unsigned int chunked:1; /* if the clientside connection is chunked */ + unsigned int client_rx_avail:1; + unsigned int client_http_body_pending:1; + unsigned int transaction_from_pipeline_queue:1; + unsigned int keepalive_active:1; + unsigned int keepalive_rejected:1; + unsigned int client_pipeline:1; + unsigned int client_h2_alpn:1; + unsigned int client_h2_substream:1; +#endif + +#ifdef _WIN32 + unsigned int sock_send_blocking:1; +#endif + +#ifndef LWS_NO_CLIENT + unsigned short c_port; +#endif + unsigned short pending_timeout_limit; + + /* chars */ + + char lws_rx_parse_state; /* enum lws_rx_parse_state */ + char rx_frame_type; /* enum lws_write_protocol */ + char pending_timeout; /* enum pending_timeout */ + char tsi; /* thread service index we belong to */ + char protocol_interpret_idx; + char redirects; + uint8_t rxflow_bitmap; +#ifdef LWS_WITH_CGI + char cgi_channel; /* which of stdin/out/err */ + char hdr_state; +#endif +#ifndef LWS_NO_CLIENT + char chunk_parser; /* enum lws_chunk_parser */ +#endif +#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT) + char reason_bf; /* internal writeable callback reason bitfield */ +#endif +#if defined(LWS_WITH_STATS) && defined(LWS_WITH_TLS) + char seen_rx; +#endif + uint8_t ws_over_h2_count; + /* volatile to make sure code is aware other thread can change */ + volatile char handling_pollout; + volatile char leave_pollout_active; +}; + +#define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap)) + +void +lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt); + +const struct lws_role_ops * +lws_role_by_name(const char *name); + +LWS_EXTERN int +lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, + const char *iface); + +#if defined(LWS_WITH_IPV6) +LWS_EXTERN unsigned long +lws_get_addr_scope(const char *ipaddr); +#endif + +LWS_EXTERN void +lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller); +LWS_EXTERN void +__lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller); + +LWS_EXTERN void +__lws_free_wsi(struct lws *wsi); + +#if LWS_MAX_SMP > 1 + +static LWS_INLINE void +lws_pt_mutex_init(struct lws_context_per_thread *pt) +{ + lws_mutex_refcount_init(&pt->mr); + pthread_mutex_init(&pt->lock_stats, NULL); +} + +static LWS_INLINE void +lws_pt_mutex_destroy(struct lws_context_per_thread *pt) +{ + pthread_mutex_destroy(&pt->lock_stats); + lws_mutex_refcount_destroy(&pt->mr); +} + +#define lws_pt_lock(pt, reason) lws_mutex_refcount_lock(&pt->mr, reason) +#define lws_pt_unlock(pt) lws_mutex_refcount_unlock(&pt->mr) + +static LWS_INLINE void +lws_pt_stats_lock(struct lws_context_per_thread *pt) +{ + pthread_mutex_lock(&pt->lock_stats); +} + +static LWS_INLINE void +lws_pt_stats_unlock(struct lws_context_per_thread *pt) +{ + pthread_mutex_unlock(&pt->lock_stats); +} +#endif + +/* + * EXTENSIONS + */ + +#if defined(LWS_WITHOUT_EXTENSIONS) +#define lws_any_extension_handled(_a, _b, _c, _d) (0) +#define lws_ext_cb_active(_a, _b, _c, _d) (0) +#define lws_ext_cb_all_exts(_a, _b, _c, _d, _e) (0) +#define lws_issue_raw_ext_access lws_issue_raw +#define lws_context_init_extensions(_a, _b) +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_client_interpret_server_handshake(struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len); + +LWS_EXTERN void +lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state, + const struct lws_role_ops *ops); + +int +lws_http_to_fallback(struct lws *wsi, unsigned char *buf, size_t len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +user_callback_handle_rxflow(lws_callback_function, struct lws *wsi, + enum lws_callback_reasons reason, void *user, + void *in, size_t len); + +LWS_EXTERN int +lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd, + int unix_skt); + +LWS_EXTERN int +lws_plat_check_connection_error(struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_header_table_attach(struct lws *wsi, int autoservice); + +LWS_EXTERN int +lws_header_table_detach(struct lws *wsi, int autoservice); +LWS_EXTERN int +__lws_header_table_detach(struct lws *wsi, int autoservice); + +LWS_EXTERN void +lws_header_table_reset(struct lws *wsi, int autoservice); + +void +__lws_header_table_reset(struct lws *wsi, int autoservice); + +LWS_EXTERN char * LWS_WARN_UNUSED_RESULT +lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ensure_user_space(struct lws *wsi); + +LWS_EXTERN int +lws_change_pollfd(struct lws *wsi, int _and, int _or); + +#ifndef LWS_NO_SERVER + int _lws_vhost_init_server(const struct lws_context_creation_info *info, + struct lws_vhost *vhost); + LWS_EXTERN struct lws_vhost * + lws_select_vhost(struct lws_context *context, int port, const char *servername); + LWS_EXTERN int LWS_WARN_UNUSED_RESULT + lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len); + LWS_EXTERN void + lws_server_get_canonical_hostname(struct lws_context *context, + const struct lws_context_creation_info *info); +#else + #define _lws_vhost_init_server(_a, _b) (0) + #define lws_parse_ws(_a, _b, _c) (0) + #define lws_server_get_canonical_hostname(_a, _b) +#endif + +LWS_EXTERN int +__remove_wsi_socket_from_fds(struct lws *wsi); +LWS_EXTERN int +lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len); + +LWS_EXTERN int +lws_service_flag_pending(struct lws_context *context, int tsi); + +LWS_EXTERN void +lws_client_stash_destroy(struct lws *wsi); + +static LWS_INLINE int +lws_has_buffered_out(struct lws *wsi) { return !!wsi->buflist_out; } + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ws_client_rx_sm(struct lws *wsi, unsigned char c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_parse(struct lws *wsi, unsigned char *buf, int *len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_parse_urldecode(struct lws *wsi, uint8_t *_c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_action(struct lws *wsi); + +LWS_EXTERN void +__lws_close_free_wsi_final(struct lws *wsi); +LWS_EXTERN void +lws_libuv_closehandle(struct lws *wsi); +LWS_EXTERN int +lws_libuv_check_watcher_active(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_plugins_init(struct lws_context * context, const char * const *d); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_plugins_destroy(struct lws_context * context); + +LWS_EXTERN void +lws_restart_ws_ping_pong_timer(struct lws *wsi); + +struct lws * +lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd); + +void +lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi); +void +lws_vhost_unbind_wsi(struct lws *wsi); + +void +__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs); +int +__lws_change_pollfd(struct lws *wsi, int _and, int _or); + + +int +lws_callback_as_writeable(struct lws *wsi); + +int +lws_role_call_client_bind(struct lws *wsi, + const struct lws_client_connect_info *i); +void +lws_remove_child_from_any_parent(struct lws *wsi); + +char * +lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1); +int +lws_client_ws_upgrade(struct lws *wsi, const char **cce); +int +lws_create_client_ws_object(const struct lws_client_connect_info *i, + struct lws *wsi); +int +lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len); +int +lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn); +int +lws_tls_server_conn_alpn(struct lws *wsi); + +int +lws_ws_client_rx_sm_block(struct lws *wsi, unsigned char **buf, size_t len); +void +lws_destroy_event_pipe(struct lws *wsi); + +/* socks */ +void socks_generate_msg(struct lws *wsi, enum socks_msg_type type, + ssize_t *msg_len); + + +void +lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs); + +LWS_EXTERN int +__lws_timed_callback_remove(struct lws_vhost *vh, struct lws_timed_vh_protocol *p); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +__insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len); + +LWS_EXTERN void +lws_remove_from_timeout_list(struct lws *wsi); + +LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_client_connect_2(struct lws *wsi); + +LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT +lws_client_reset(struct lws **wsi, int ssl, const char *address, int port, + const char *path, const char *host); + +LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi); + +LWS_EXTERN char * LWS_WARN_UNUSED_RESULT +lws_generate_client_handshake(struct lws *wsi, char *pkt); + +LWS_EXTERN int +lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); + +LWS_EXTERN struct lws * +lws_http_client_connect_via_info2(struct lws *wsi); + + +#ifndef LWS_NO_CLIENT +LWS_EXTERN int lws_client_socket_service(struct lws *wsi, + struct lws_pollfd *pollfd, + struct lws *wsi_conn); +LWS_EXTERN struct lws * +lws_client_wsi_effective(struct lws *wsi); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed_client(struct lws *wsi); +#if !defined(LWS_WITH_TLS) + #define lws_context_init_client_ssl(_a, _b) (0) +#endif +LWS_EXTERN void +lws_decode_ssl_error(void); +#else +#define lws_context_init_client_ssl(_a, _b) (0) +#endif + +LWS_EXTERN int +__lws_rx_flow_control(struct lws *wsi); + +LWS_EXTERN int +_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa); + +#ifndef LWS_NO_SERVER +LWS_EXTERN int +lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len); +#else +#define lws_server_socket_service(_b, _c) (0) +#define lws_handshake_server(_a, _b, _c) (0) +#endif + +#ifdef LWS_WITH_ACCESS_LOG +LWS_EXTERN int +lws_access_log(struct lws *wsi); +LWS_EXTERN void +lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int len, int meth); +#else +#define lws_access_log(_a) +#endif + +LWS_EXTERN int +lws_cgi_kill_terminated(struct lws_context_per_thread *pt); + +LWS_EXTERN void +lws_cgi_remove_and_kill(struct lws *wsi); + +LWS_EXTERN void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m); +LWS_EXTERN void +lws_plat_insert_socket_into_fds(struct lws_context *context, + struct lws *wsi); +LWS_EXTERN void +lws_plat_service_periodic(struct lws_context *context); + +LWS_EXTERN int +lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pfd); + + +int +lws_plat_pipe_create(struct lws *wsi); +int +lws_plat_pipe_signal(struct lws *wsi); +void +lws_plat_pipe_close(struct lws *wsi); + +LWS_EXTERN void +lws_add_wsi_to_draining_ext_list(struct lws *wsi); +LWS_EXTERN void +lws_remove_wsi_from_draining_ext_list(struct lws *wsi); +LWS_EXTERN int +lws_poll_listen_fd(struct lws_pollfd *fd); +LWS_EXTERN int +lws_plat_service(struct lws_context *context, int timeout_ms); +LWS_EXTERN LWS_VISIBLE int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi); + +LWS_EXTERN int +lws_pthread_self_to_tsi(struct lws_context *context); +LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_plat_inet_pton(int af, const char *src, void *dst); + +LWS_EXTERN void +lws_same_vh_protocol_remove(struct lws *wsi); +LWS_EXTERN void +__lws_same_vh_protocol_remove(struct lws *wsi); +LWS_EXTERN void +lws_same_vh_protocol_insert(struct lws *wsi, int n); + +LWS_EXTERN int +lws_broadcast(struct lws_context *context, int reason, void *in, size_t len); + +#if defined(LWS_WITH_STATS) + void + lws_stats_atomic_bump(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t bump); + void + lws_stats_atomic_max(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t val); +#else + static LWS_INLINE uint64_t lws_stats_atomic_bump(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t bump) { + (void)context; (void)pt; (void)index; (void)bump; return 0; } + static LWS_INLINE uint64_t lws_stats_atomic_max(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t val) { + (void)context; (void)pt; (void)index; (void)val; return 0; } +#endif + + + +#if defined(LWS_WITH_PEER_LIMITS) +void +lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer); +int +lws_peer_confirm_ah_attach_ok(struct lws_context *context, + struct lws_peer *peer); +void +lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer); +void +lws_peer_cull_peer_wait_list(struct lws_context *context); +struct lws_peer * +lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd); +void +lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer, + struct lws *wsi); +void +lws_peer_dump_from_wsi(struct lws *wsi); +#endif + +#ifdef LWS_WITH_HUBBUB +hubbub_error +html_parser_cb(const hubbub_token *token, void *pw); +#endif + +int +lws_threadpool_tsi_context(struct lws_context *context, int tsi); + +void +__lws_remove_from_timeout_list(struct lws *wsi); + +lws_usec_t +__lws_hrtimer_service(struct lws_context_per_thread *pt); + + +int +lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi, + struct lws_tokens *ebuf); +int +lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used, + int buffered); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/lib/core-net/server.c b/lib/core-net/server.c new file mode 100644 index 0000000000..3f692a9bbb --- /dev/null +++ b/lib/core-net/server.c @@ -0,0 +1,299 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + +#if defined(LWS_WITH_SERVER_STATUS) + +void +lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs) +{ + const struct lws_vhost *vh = ctx->vhost_list; + + while (vh) { + + cs->rx += vh->conn_stats.rx; + cs->tx += vh->conn_stats.tx; + cs->h1_conn += vh->conn_stats.h1_conn; + cs->h1_trans += vh->conn_stats.h1_trans; + cs->h2_trans += vh->conn_stats.h2_trans; + cs->ws_upg += vh->conn_stats.ws_upg; + cs->h2_upg += vh->conn_stats.h2_upg; + cs->h2_alpn += vh->conn_stats.h2_alpn; + cs->h2_subs += vh->conn_stats.h2_subs; + cs->rejected += vh->conn_stats.rejected; + + vh = vh->vhost_next; + } +} + +LWS_EXTERN int +lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len) +{ +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + static const char * const prots[] = { + "http://", + "https://", + "file://", + "cgi://", + ">http://", + ">https://", + "callback://" + }; +#endif + char *orig = buf, *end = buf + len - 1, first = 1; + int n = 0; + + if (len < 100) + return 0; + + buf += lws_snprintf(buf, end - buf, + "{\n \"name\":\"%s\",\n" + " \"port\":\"%d\",\n" + " \"use_ssl\":\"%d\",\n" + " \"sts\":\"%d\",\n" + " \"rx\":\"%llu\",\n" + " \"tx\":\"%llu\",\n" + " \"h1_conn\":\"%lu\",\n" + " \"h1_trans\":\"%lu\",\n" + " \"h2_trans\":\"%lu\",\n" + " \"ws_upg\":\"%lu\",\n" + " \"rejected\":\"%lu\",\n" + " \"h2_upg\":\"%lu\",\n" + " \"h2_alpn\":\"%lu\",\n" + " \"h2_subs\":\"%lu\"" + , + vh->name, vh->listen_port, +#if defined(LWS_WITH_TLS) + vh->tls.use_ssl & LCCSCF_USE_SSL, +#else + 0, +#endif + !!(vh->options & LWS_SERVER_OPTION_STS), + vh->conn_stats.rx, vh->conn_stats.tx, + vh->conn_stats.h1_conn, + vh->conn_stats.h1_trans, + vh->conn_stats.h2_trans, + vh->conn_stats.ws_upg, + vh->conn_stats.rejected, + vh->conn_stats.h2_upg, + vh->conn_stats.h2_alpn, + vh->conn_stats.h2_subs + ); +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + if (vh->http.mount_list) { + const struct lws_http_mount *m = vh->http.mount_list; + + buf += lws_snprintf(buf, end - buf, ",\n \"mounts\":["); + while (m) { + if (!first) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n \"mountpoint\":\"%s\",\n" + " \"origin\":\"%s%s\",\n" + " \"cache_max_age\":\"%d\",\n" + " \"cache_reuse\":\"%d\",\n" + " \"cache_revalidate\":\"%d\",\n" + " \"cache_intermediaries\":\"%d\"\n" + , + m->mountpoint, + prots[m->origin_protocol], + m->origin, + m->cache_max_age, + m->cache_reusable, + m->cache_revalidate, + m->cache_intermediaries); + if (m->def) + buf += lws_snprintf(buf, end - buf, + ",\n \"default\":\"%s\"", + m->def); + buf += lws_snprintf(buf, end - buf, "\n }"); + first = 0; + m = m->mount_next; + } + buf += lws_snprintf(buf, end - buf, "\n ]"); + } +#endif + if (vh->protocols) { + n = 0; + first = 1; + + buf += lws_snprintf(buf, end - buf, ",\n \"ws-protocols\":["); + while (n < vh->count_protocols) { + if (!first) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n \"%s\":{\n" + " \"status\":\"ok\"\n }\n }" + , + vh->protocols[n].name); + first = 0; + n++; + } + buf += lws_snprintf(buf, end - buf, "\n ]"); + } + + buf += lws_snprintf(buf, end - buf, "\n}"); + + return buf - orig; +} + + +LWS_EXTERN LWS_VISIBLE int +lws_json_dump_context(const struct lws_context *context, char *buf, int len, + int hide_vhosts) +{ + char *orig = buf, *end = buf + len - 1, first = 1; + const struct lws_vhost *vh = context->vhost_list; + const struct lws_context_per_thread *pt; + time_t t = time(NULL); + int n, listening = 0, cgi_count = 0; + struct lws_conn_stats cs; + double d = 0; +#ifdef LWS_WITH_CGI + struct lws_cgi * const *pcgi; +#endif + +#ifdef LWS_WITH_LIBUV + uv_uptime(&d); +#endif + + buf += lws_snprintf(buf, end - buf, "{ " + "\"version\":\"%s\",\n" + "\"uptime\":\"%ld\",\n", + lws_get_library_version(), + (long)d); + +#ifdef LWS_HAVE_GETLOADAVG + { + double d[3]; + int m; + + m = getloadavg(d, 3); + for (n = 0; n < m; n++) { + buf += lws_snprintf(buf, end - buf, + "\"l%d\":\"%.2f\",\n", + n + 1, d[n]); + } + } +#endif + + buf += lws_snprintf(buf, end - buf, "\"contexts\":[\n"); + + buf += lws_snprintf(buf, end - buf, "{ " + "\"context_uptime\":\"%ld\",\n" + "\"cgi_spawned\":\"%d\",\n" + "\"pt_fd_max\":\"%d\",\n" + "\"ah_pool_max\":\"%d\",\n" + "\"deprecated\":\"%d\",\n" + "\"wsi_alive\":\"%d\",\n", + (unsigned long)(t - context->time_up), + context->count_cgi_spawned, + context->fd_limit_per_thread, + context->max_http_header_pool, + context->deprecated, + context->count_wsi_allocated); + + buf += lws_snprintf(buf, end - buf, "\"pt\":[\n "); + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + if (n) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n" + " \"fds_count\":\"%d\",\n" + " \"ah_pool_inuse\":\"%d\",\n" + " \"ah_wait_list\":\"%d\"\n" + " }", + pt->fds_count, + pt->http.ah_count_in_use, + pt->http.ah_wait_list_length); + } + + buf += lws_snprintf(buf, end - buf, "]"); + + buf += lws_snprintf(buf, end - buf, ", \"vhosts\":[\n "); + + first = 1; + vh = context->vhost_list; + listening = 0; + cs = context->conn_stats; + lws_sum_stats(context, &cs); + while (vh) { + + if (!hide_vhosts) { + if (!first) + if(buf != end) + *buf++ = ','; + buf += lws_json_dump_vhost(vh, buf, end - buf); + first = 0; + } + if (vh->lserv_wsi) + listening++; + vh = vh->vhost_next; + } + + buf += lws_snprintf(buf, end - buf, + "],\n\"listen_wsi\":\"%d\",\n" + " \"rx\":\"%llu\",\n" + " \"tx\":\"%llu\",\n" + " \"h1_conn\":\"%lu\",\n" + " \"h1_trans\":\"%lu\",\n" + " \"h2_trans\":\"%lu\",\n" + " \"ws_upg\":\"%lu\",\n" + " \"rejected\":\"%lu\",\n" + " \"h2_alpn\":\"%lu\",\n" + " \"h2_subs\":\"%lu\",\n" + " \"h2_upg\":\"%lu\"", + listening, cs.rx, cs.tx, + cs.h1_conn, + cs.h1_trans, + cs.h2_trans, + cs.ws_upg, + cs.rejected, + cs.h2_alpn, + cs.h2_subs, + cs.h2_upg); + +#ifdef LWS_WITH_CGI + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + pcgi = &pt->http.cgi_list; + + while (*pcgi) { + pcgi = &(*pcgi)->cgi_list; + + cgi_count++; + } + } +#endif + buf += lws_snprintf(buf, end - buf, ",\n \"cgi_alive\":\"%d\"\n ", + cgi_count); + + buf += lws_snprintf(buf, end - buf, "}"); + + + buf += lws_snprintf(buf, end - buf, "]}\n "); + + return buf - orig; +} + +#endif diff --git a/lib/core/service.c b/lib/core-net/service.c similarity index 99% rename from lib/core/service.c rename to lib/core-net/service.c index c8295de61e..ac5c661bda 100644 --- a/lib/core/service.c +++ b/lib/core-net/service.c @@ -347,6 +347,7 @@ lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi) return 0; #endif +#if defined(LWS_WITH_TLS) /* 2) if we know we have non-network pending data, * do not wait in poll */ @@ -354,6 +355,7 @@ lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi) pt->context->tls_ops->fake_POLLIN_for_buffered && pt->context->tls_ops->fake_POLLIN_for_buffered(pt)) return 0; +#endif /* * 3) If there is any wsi with rxflow buffered and in a state to process @@ -921,6 +923,7 @@ lws_service_periodic_checks(struct lws_context *context, role_ops_cgi.periodic_checks(context, tsi, now); #endif +#if defined(LWS_WITH_TLS) /* * Phase 6: check the remaining cert lifetime daily */ @@ -928,6 +931,7 @@ lws_service_periodic_checks(struct lws_context *context, if (context->tls_ops && context->tls_ops->periodic_housekeeping) context->tls_ops->periodic_housekeeping(context, now); +#endif return 0; } diff --git a/lib/core-net/stats.c b/lib/core-net/stats.c new file mode 100644 index 0000000000..bb14c65046 --- /dev/null +++ b/lib/core-net/stats.c @@ -0,0 +1,263 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + + +#if defined(LWS_WITH_STATS) + +LWS_VISIBLE LWS_EXTERN uint64_t +lws_stats_get(struct lws_context *context, int index) +{ + if (index >= LWSSTATS_SIZE) + return 0; + + return context->lws_stats[index]; +} + +LWS_VISIBLE LWS_EXTERN void +lws_stats_log_dump(struct lws_context *context) +{ + struct lws_vhost *v = context->vhost_list; + int n; +#if defined(LWS_WITH_PEER_LIMITS) + int m; +#endif + + if (!context->updated) + return; + + context->updated = 0; + + lwsl_notice("\n"); + lwsl_notice("LWS internal statistics dump ----->\n"); + lwsl_notice("LWSSTATS_C_CONNECTIONS: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_CONNECTIONS)); + lwsl_notice("LWSSTATS_C_API_CLOSE: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_API_CLOSE)); + lwsl_notice("LWSSTATS_C_API_READ: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_API_READ)); + lwsl_notice("LWSSTATS_C_API_LWS_WRITE: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_API_LWS_WRITE)); + lwsl_notice("LWSSTATS_C_API_WRITE: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_API_WRITE)); + lwsl_notice("LWSSTATS_C_WRITE_PARTIALS: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_WRITE_PARTIALS)); + lwsl_notice("LWSSTATS_C_WRITEABLE_CB_REQ: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_WRITEABLE_CB_REQ)); + lwsl_notice("LWSSTATS_C_WRITEABLE_CB_EFF_REQ: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_WRITEABLE_CB_EFF_REQ)); + lwsl_notice("LWSSTATS_C_WRITEABLE_CB: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_WRITEABLE_CB)); + lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN)); + lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_FAILED: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_SSL_CONNECTIONS_FAILED)); + lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)); + lwsl_notice("LWSSTATS_C_SSL_CONNS_HAD_RX: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_SSL_CONNS_HAD_RX)); + lwsl_notice("LWSSTATS_C_PEER_LIMIT_AH_DENIED: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_PEER_LIMIT_AH_DENIED)); + lwsl_notice("LWSSTATS_C_PEER_LIMIT_WSI_DENIED: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_PEER_LIMIT_WSI_DENIED)); + + lwsl_notice("LWSSTATS_C_TIMEOUTS: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_TIMEOUTS)); + lwsl_notice("LWSSTATS_C_SERVICE_ENTRY: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_C_SERVICE_ENTRY)); + lwsl_notice("LWSSTATS_B_READ: %8llu\n", + (unsigned long long)lws_stats_get(context, LWSSTATS_B_READ)); + lwsl_notice("LWSSTATS_B_WRITE: %8llu\n", + (unsigned long long)lws_stats_get(context, LWSSTATS_B_WRITE)); + lwsl_notice("LWSSTATS_B_PARTIALS_ACCEPTED_PARTS: %8llu\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_B_PARTIALS_ACCEPTED_PARTS)); + lwsl_notice("LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY: %8llums\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY) / 1000); + if (lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)) + lwsl_notice(" Avg accept delay: %8llums\n", + (unsigned long long)(lws_stats_get(context, + LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY) / + lws_stats_get(context, + LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)) / 1000); + lwsl_notice("LWSSTATS_MS_SSL_RX_DELAY: %8llums\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_MS_SSL_RX_DELAY) / 1000); + if (lws_stats_get(context, LWSSTATS_C_SSL_CONNS_HAD_RX)) + lwsl_notice(" Avg accept-rx delay: %8llums\n", + (unsigned long long)(lws_stats_get(context, + LWSSTATS_MS_SSL_RX_DELAY) / + lws_stats_get(context, + LWSSTATS_C_SSL_CONNS_HAD_RX)) / 1000); + + lwsl_notice("LWSSTATS_MS_WRITABLE_DELAY: %8lluus\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_MS_WRITABLE_DELAY)); + lwsl_notice("LWSSTATS_MS_WORST_WRITABLE_DELAY: %8lluus\n", + (unsigned long long)lws_stats_get(context, + LWSSTATS_MS_WORST_WRITABLE_DELAY)); + if (lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB)) + lwsl_notice(" Avg writable delay: %8lluus\n", + (unsigned long long)(lws_stats_get(context, + LWSSTATS_MS_WRITABLE_DELAY) / + lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB))); + lwsl_notice("Simultaneous SSL restriction: %8d/%d\n", + context->simultaneous_ssl, + context->simultaneous_ssl_restriction); + + lwsl_notice("Live wsi: %8d\n", + context->count_wsi_allocated); + + context->updated = 1; + + while (v) { + if (v->lserv_wsi && + v->lserv_wsi->position_in_fds_table != LWS_NO_FDS_POS) { + + struct lws_context_per_thread *pt = + &context->pt[(int)v->lserv_wsi->tsi]; + struct lws_pollfd *pfd; + + pfd = &pt->fds[v->lserv_wsi->position_in_fds_table]; + + lwsl_notice(" Listen port %d actual POLLIN: %d\n", + v->listen_port, + (int)pfd->events & LWS_POLLIN); + } + + v = v->vhost_next; + } + + for (n = 0; n < context->count_threads; n++) { + struct lws_context_per_thread *pt = &context->pt[n]; + struct lws *wl; + int m = 0; + + lwsl_notice("PT %d\n", n + 1); + + lws_pt_lock(pt, __func__); + + lwsl_notice(" AH in use / max: %d / %d\n", + pt->http.ah_count_in_use, + context->max_http_header_pool); + + wl = pt->http.ah_wait_list; + while (wl) { + m++; + wl = wl->http.ah_wait_list; + } + + lwsl_notice(" AH wait list count / actual: %d / %d\n", + pt->http.ah_wait_list_length, m); + + lws_pt_unlock(pt); + } + +#if defined(LWS_WITH_PEER_LIMITS) + m = 0; + for (n = 0; n < (int)context->pl_hash_elements; n++) { + lws_start_foreach_llp(struct lws_peer **, peer, + context->pl_hash_table[n]) { + m++; + } lws_end_foreach_llp(peer, next); + } + + lwsl_notice(" Peers: total active %d\n", m); + if (m > 10) { + m = 10; + lwsl_notice(" (showing 10 peers only)\n"); + } + + if (m) { + for (n = 0; n < (int)context->pl_hash_elements; n++) { + char buf[72]; + + lws_start_foreach_llp(struct lws_peer **, peer, + context->pl_hash_table[n]) { + struct lws_peer *df = *peer; + + if (!lws_plat_inet_ntop(df->af, df->addr, buf, + sizeof(buf) - 1)) + strcpy(buf, "unknown"); +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + lwsl_notice(" peer %s: count wsi: %d, count ah: %d\n", + buf, df->count_wsi, + df->http.count_ah); +#else + lwsl_notice(" peer %s: count wsi: %d\n", + buf, df->count_wsi); +#endif + + if (!--m) + break; + } lws_end_foreach_llp(peer, next); + } + } +#endif + + lwsl_notice("\n"); +} + +void +lws_stats_atomic_bump(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t bump) +{ + lws_pt_stats_lock(pt); + context->lws_stats[index] += bump; + if (index != LWSSTATS_C_SERVICE_ENTRY) + context->updated = 1; + lws_pt_stats_unlock(pt); +} + +void +lws_stats_atomic_max(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t val) +{ + lws_pt_stats_lock(pt); + if (val > context->lws_stats[index]) { + context->lws_stats[index] = val; + context->updated = 1; + } + lws_pt_stats_unlock(pt); +} + +#endif + + diff --git a/lib/core-net/vhost.c b/lib/core-net/vhost.c new file mode 100644 index 0000000000..db16ba98e7 --- /dev/null +++ b/lib/core-net/vhost.c @@ -0,0 +1,1241 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + +const struct lws_role_ops *available_roles[] = { +#if defined(LWS_ROLE_H2) + &role_ops_h2, +#endif +#if defined(LWS_ROLE_H1) + &role_ops_h1, +#endif +#if defined(LWS_ROLE_WS) + &role_ops_ws, +#endif +#if defined(LWS_ROLE_DBUS) + &role_ops_dbus, +#endif +#if defined(LWS_ROLE_RAW_PROXY) + &role_ops_raw_proxy, +#endif + NULL +}; + +const struct lws_event_loop_ops *available_event_libs[] = { +#if defined(LWS_WITH_POLL) + &event_loop_ops_poll, +#endif +#if defined(LWS_WITH_LIBUV) + &event_loop_ops_uv, +#endif +#if defined(LWS_WITH_LIBEVENT) + &event_loop_ops_event, +#endif +#if defined(LWS_WITH_LIBEV) + &event_loop_ops_ev, +#endif + NULL +}; + +static const char * const mount_protocols[] = { + "http://", + "https://", + "file://", + "cgi://", + ">http://", + ">https://", + "callback://" +}; + +const struct lws_role_ops * +lws_role_by_name(const char *name) +{ + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) + if (!strcmp(ar->name, name)) + return ar; + LWS_FOR_EVERY_AVAILABLE_ROLE_END; + + if (!strcmp(name, role_ops_raw_skt.name)) + return &role_ops_raw_skt; + + if (!strcmp(name, role_ops_raw_file.name)) + return &role_ops_raw_file; + + return NULL; +} + +int +lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn) +{ +#if defined(LWS_WITH_TLS) + if (!alpn) + return 0; + + lwsl_info("%s: '%s'\n", __func__, alpn); + + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) + if (ar->alpn && !strcmp(ar->alpn, alpn) && ar->alpn_negotiated) + return ar->alpn_negotiated(wsi, alpn); + LWS_FOR_EVERY_AVAILABLE_ROLE_END; +#endif + return 0; +} + +//#if !defined(LWS_WITHOUT_SERVER) +int +lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot) +{ + int n; + + /* + * if the vhost is told to bind accepted sockets to a given role, + * then look it up by name and try to bind to the specific role. + */ + if (lws_check_opt(wsi->vhost->options, + LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG) && + wsi->vhost->listen_accept_role) { + const struct lws_role_ops *role = + lws_role_by_name(wsi->vhost->listen_accept_role); + + if (!prot) + prot = wsi->vhost->listen_accept_protocol; + + if (!role) + lwsl_err("%s: can't find role '%s'\n", __func__, + wsi->vhost->listen_accept_role); + + if (role && role->adoption_bind) { + n = role->adoption_bind(wsi, type, prot); + if (n < 0) + return -1; + if (n) /* did the bind */ + return 0; + } + + if (type & _LWS_ADOPT_FINISH) { + lwsl_debug("%s: leaving bound to role %s\n", __func__, + wsi->role_ops->name); + return 0; + } + + + lwsl_warn("%s: adoption bind to role '%s', " + "protocol '%s', type 0x%x, failed\n", __func__, + wsi->vhost->listen_accept_role, prot, type); + } + + /* + * Otherwise ask each of the roles in order of preference if they + * want to bind to this accepted socket + */ + + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) + if (ar->adoption_bind && ar->adoption_bind(wsi, type, prot)) + return 0; + LWS_FOR_EVERY_AVAILABLE_ROLE_END; + + /* fall back to raw socket role if, eg, h1 not configured */ + + if (role_ops_raw_skt.adoption_bind && + role_ops_raw_skt.adoption_bind(wsi, type, prot)) + return 0; + + /* fall back to raw file role if, eg, h1 not configured */ + + if (role_ops_raw_file.adoption_bind && + role_ops_raw_file.adoption_bind(wsi, type, prot)) + return 0; + + return 1; +} +//#endif + +#if !defined(LWS_WITHOUT_CLIENT) +int +lws_role_call_client_bind(struct lws *wsi, + const struct lws_client_connect_info *i) +{ + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) + if (ar->client_bind) { + int m = ar->client_bind(wsi, i); + if (m < 0) + return m; + if (m) + return 0; + } + LWS_FOR_EVERY_AVAILABLE_ROLE_END; + + /* fall back to raw socket role if, eg, h1 not configured */ + + if (role_ops_raw_skt.client_bind && + role_ops_raw_skt.client_bind(wsi, i)) + return 0; + + return 1; +} +#endif + +LWS_VISIBLE void * +lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, + const struct lws_protocols *prot, int size) +{ + int n = 0; + + /* allocate the vh priv array only on demand */ + if (!vhost->protocol_vh_privs) { + vhost->protocol_vh_privs = (void **)lws_zalloc( + vhost->count_protocols * sizeof(void *), + "protocol_vh_privs"); + if (!vhost->protocol_vh_privs) + return NULL; + } + + while (n < vhost->count_protocols && &vhost->protocols[n] != prot) + n++; + + if (n == vhost->count_protocols) { + n = 0; + while (n < vhost->count_protocols && + strcmp(vhost->protocols[n].name, prot->name)) + n++; + + if (n == vhost->count_protocols) + return NULL; + } + + vhost->protocol_vh_privs[n] = lws_zalloc(size, "vh priv"); + return vhost->protocol_vh_privs[n]; +} + +LWS_VISIBLE void * +lws_protocol_vh_priv_get(struct lws_vhost *vhost, + const struct lws_protocols *prot) +{ + int n = 0; + + if (!vhost || !vhost->protocol_vh_privs || !prot) + return NULL; + + while (n < vhost->count_protocols && &vhost->protocols[n] != prot) + n++; + + if (n == vhost->count_protocols) { + n = 0; + while (n < vhost->count_protocols && + strcmp(vhost->protocols[n].name, prot->name)) + n++; + + if (n == vhost->count_protocols) { + lwsl_err("%s: unknown protocol %p\n", __func__, prot); + return NULL; + } + } + + return vhost->protocol_vh_privs[n]; +} + +const struct lws_protocol_vhost_options * +lws_vhost_protocol_options(struct lws_vhost *vh, const char *name) +{ + const struct lws_protocol_vhost_options *pvo = vh->pvo; + + if (!name) + return NULL; + + while (pvo) { + if (!strcmp(pvo->name, name)) + return pvo; + pvo = pvo->next; + } + + return NULL; +} + +/* + * inform every vhost that hasn't already done it, that + * his protocols are initializing + */ +LWS_VISIBLE int +lws_protocol_init(struct lws_context *context) +{ + struct lws_vhost *vh = context->vhost_list; + const struct lws_protocol_vhost_options *pvo, *pvo1; + struct lws wsi; + int n, any = 0; + + if (context->doing_protocol_init) + return 0; + + context->doing_protocol_init = 1; + + memset(&wsi, 0, sizeof(wsi)); + wsi.context = context; + + lwsl_info("%s\n", __func__); + + while (vh) { + wsi.vhost = vh; + + /* only do the protocol init once for a given vhost */ + if (vh->created_vhost_protocols || + (vh->options & LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT)) + goto next; + + /* initialize supported protocols on this vhost */ + + for (n = 0; n < vh->count_protocols; n++) { + wsi.protocol = &vh->protocols[n]; + if (!vh->protocols[n].name) + continue; + pvo = lws_vhost_protocol_options(vh, + vh->protocols[n].name); + if (pvo) { + /* + * linked list of options specific to + * vh + protocol + */ + pvo1 = pvo; + pvo = pvo1->options; + + while (pvo) { + lwsl_debug( + " vhost \"%s\", " + "protocol \"%s\", " + "option \"%s\"\n", + vh->name, + vh->protocols[n].name, + pvo->name); + + if (!strcmp(pvo->name, "default")) { + lwsl_info("Setting default " + "protocol for vh %s to %s\n", + vh->name, + vh->protocols[n].name); + vh->default_protocol_index = n; + } + if (!strcmp(pvo->name, "raw")) { + lwsl_info("Setting raw " + "protocol for vh %s to %s\n", + vh->name, + vh->protocols[n].name); + vh->raw_protocol_index = n; + } + pvo = pvo->next; + } + + pvo = pvo1->options; + } + +#if defined(LWS_WITH_TLS) + any |= !!vh->tls.ssl_ctx; +#endif + + /* + * inform all the protocols that they are doing their + * one-time initialization if they want to. + * + * NOTE the wsi is all zeros except for the context, vh + * + protocol ptrs so lws_get_context(wsi) etc can work + */ + if (vh->protocols[n].callback(&wsi, + LWS_CALLBACK_PROTOCOL_INIT, NULL, + (void *)pvo, 0)) { + lws_free(vh->protocol_vh_privs[n]); + vh->protocol_vh_privs[n] = NULL; + lwsl_err("%s: protocol %s failed init\n", + __func__, vh->protocols[n].name); + + return 1; + } + } + + vh->created_vhost_protocols = 1; +next: + vh = vh->vhost_next; + } + + context->doing_protocol_init = 0; + + if (!context->protocol_init_done) + lws_finalize_startup(context); + + context->protocol_init_done = 1; + + if (any) + lws_tls_check_all_cert_lifetimes(context); + + return 0; +} + + +/* list of supported protocols and callbacks */ + +static const struct lws_protocols protocols_dummy[] = { + /* first protocol must always be HTTP handler */ + + { + "http-only", /* name */ + lws_callback_http_dummy, /* callback */ + 0, /* per_session_data_size */ + 0, /* rx_buffer_size */ + 0, /* id */ + NULL, /* user */ + 0 /* tx_packet_size */ + }, + /* + * the other protocols are provided by lws plugins + */ + { NULL, NULL, 0, 0, 0, NULL, 0} /* terminator */ +}; + + +#ifdef LWS_PLAT_OPTEE +#undef LWS_HAVE_GETENV +#endif + +LWS_VISIBLE struct lws_vhost * +lws_create_vhost(struct lws_context *context, + const struct lws_context_creation_info *info) +{ + struct lws_vhost *vh = lws_zalloc(sizeof(*vh), "create vhost"), + **vh1 = &context->vhost_list; + const struct lws_http_mount *mounts; + const struct lws_protocols *pcols = info->protocols; + const struct lws_protocol_vhost_options *pvo; +#ifdef LWS_WITH_PLUGINS + struct lws_plugin *plugin = context->plugin_list; +#endif + struct lws_protocols *lwsp; + int m, f = !info->pvo; + char buf[20]; +#if !defined(LWS_WITHOUT_CLIENT) && defined(LWS_HAVE_GETENV) + char *p; +#endif + int n; + + if (!vh) + return NULL; + +#if LWS_MAX_SMP > 1 + pthread_mutex_init(&vh->lock, NULL); +#endif + + if (!pcols) + pcols = &protocols_dummy[0]; + + vh->context = context; + if (!info->vhost_name) + vh->name = "default"; + else + vh->name = info->vhost_name; + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + vh->http.error_document_404 = info->error_document_404; +#endif + + if (info->options & LWS_SERVER_OPTION_ONLY_RAW) + lwsl_info("%s set to only support RAW\n", vh->name); + + vh->iface = info->iface; +#if !defined(LWS_WITH_ESP32) && \ + !defined(OPTEE_TA) && !defined(WIN32) + vh->bind_iface = info->bind_iface; +#endif + + for (vh->count_protocols = 0; + pcols[vh->count_protocols].callback; + vh->count_protocols++) + ; + + vh->options = info->options; + vh->pvo = info->pvo; + vh->headers = info->headers; + vh->user = info->user; + vh->finalize = info->finalize; + vh->finalize_arg = info->finalize_arg; + vh->listen_accept_role = info->listen_accept_role; + vh->listen_accept_protocol = info->listen_accept_protocol; + + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) + if (ar->init_vhost) + if (ar->init_vhost(vh, info)) + return NULL; + LWS_FOR_EVERY_AVAILABLE_ROLE_END; + + + if (info->keepalive_timeout) + vh->keepalive_timeout = info->keepalive_timeout; + else + vh->keepalive_timeout = 5; + + if (info->timeout_secs_ah_idle) + vh->timeout_secs_ah_idle = info->timeout_secs_ah_idle; + else + vh->timeout_secs_ah_idle = 10; + +#if defined(LWS_WITH_TLS) + + vh->tls.alpn = info->alpn; + vh->tls.ssl_info_event_mask = info->ssl_info_event_mask; + + if (info->ecdh_curve) + lws_strncpy(vh->tls.ecdh_curve, info->ecdh_curve, + sizeof(vh->tls.ecdh_curve)); + + /* carefully allocate and take a copy of cert + key paths if present */ + n = 0; + if (info->ssl_cert_filepath) + n += (int)strlen(info->ssl_cert_filepath) + 1; + if (info->ssl_private_key_filepath) + n += (int)strlen(info->ssl_private_key_filepath) + 1; + + if (n) { + vh->tls.key_path = vh->tls.alloc_cert_path = + lws_malloc(n, "vh paths"); + if (info->ssl_cert_filepath) { + n = (int)strlen(info->ssl_cert_filepath) + 1; + memcpy(vh->tls.alloc_cert_path, + info->ssl_cert_filepath, n); + vh->tls.key_path += n; + } + if (info->ssl_private_key_filepath) + memcpy(vh->tls.key_path, info->ssl_private_key_filepath, + strlen(info->ssl_private_key_filepath) + 1); + } +#endif + + /* + * give the vhost a unified list of protocols including the + * ones that came from plugins + */ + lwsp = lws_zalloc(sizeof(struct lws_protocols) * (vh->count_protocols + + context->plugin_protocol_count + 1), + "vhost-specific plugin table"); + if (!lwsp) { + lwsl_err("OOM\n"); + return NULL; + } + + m = vh->count_protocols; + memcpy(lwsp, pcols, sizeof(struct lws_protocols) * m); + + /* for compatibility, all protocols enabled on vhost if only + * the default vhost exists. Otherwise only vhosts who ask + * for a protocol get it enabled. + */ + + if (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) + f = 0; + (void)f; +#ifdef LWS_WITH_PLUGINS + if (plugin) { + + while (plugin) { + for (n = 0; n < plugin->caps.count_protocols; n++) { + /* + * for compatibility's sake, no pvo implies + * allow all protocols + */ + if (f || lws_vhost_protocol_options(vh, + plugin->caps.protocols[n].name)) { + memcpy(&lwsp[m], + &plugin->caps.protocols[n], + sizeof(struct lws_protocols)); + m++; + vh->count_protocols++; + } + } + plugin = plugin->list; + } + } +#endif + + if ( +#ifdef LWS_WITH_PLUGINS + (context->plugin_list) || +#endif + context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) + vh->protocols = lwsp; + else { + vh->protocols = pcols; + lws_free(lwsp); + } + + vh->same_vh_protocol_heads = (struct lws_dll_lws *) + lws_zalloc(sizeof(struct lws_dll_lws) * + vh->count_protocols, "same vh list"); +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + vh->http.mount_list = info->mounts; +#endif + +#ifdef LWS_WITH_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(vh)) { + lwsl_info("Creating Vhost '%s' path \"%s\", %d protocols\n", + vh->name, vh->iface, vh->count_protocols); + } else +#endif + { + switch(info->port) { + case CONTEXT_PORT_NO_LISTEN: + strcpy(buf, "(serving disabled)"); + break; + case CONTEXT_PORT_NO_LISTEN_SERVER: + strcpy(buf, "(no listener)"); + break; + default: + lws_snprintf(buf, sizeof(buf), "port %u", info->port); + break; + } + lwsl_info("Creating Vhost '%s' %s, %d protocols, IPv6 %s\n", + vh->name, buf, vh->count_protocols, + LWS_IPV6_ENABLED(vh) ? "on" : "off"); + } + mounts = info->mounts; + while (mounts) { + (void)mount_protocols[0]; + lwsl_info(" mounting %s%s to %s\n", + mount_protocols[mounts->origin_protocol], + mounts->origin, mounts->mountpoint); + + /* convert interpreter protocol names to pointers */ + pvo = mounts->interpret; + while (pvo) { + for (n = 0; n < vh->count_protocols; n++) { + if (strcmp(pvo->value, vh->protocols[n].name)) + continue; + ((struct lws_protocol_vhost_options *)pvo)-> + value = (const char *)(lws_intptr_t)n; + break; + } + if (n == vh->count_protocols) + lwsl_err("ignoring unknown interp pr %s\n", + pvo->value); + pvo = pvo->next; + } + + mounts = mounts->mount_next; + } + + vh->listen_port = info->port; +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + vh->http.http_proxy_port = 0; + vh->http.http_proxy_address[0] = '\0'; +#endif +#if defined(LWS_WITH_SOCKS5) + vh->socks_proxy_port = 0; + vh->socks_proxy_address[0] = '\0'; +#endif + +#if !defined(LWS_WITHOUT_CLIENT) + /* either use proxy from info, or try get it from env var */ +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + /* http proxy */ + if (info->http_proxy_address) { + /* override for backwards compatibility */ + if (info->http_proxy_port) + vh->http.http_proxy_port = info->http_proxy_port; + lws_set_proxy(vh, info->http_proxy_address); + } else +#endif + { +#ifdef LWS_HAVE_GETENV + p = getenv("http_proxy"); + if (p) + lws_set_proxy(vh, p); +#endif + } +#endif +#if defined(LWS_WITH_SOCKS5) + /* socks proxy */ + if (info->socks_proxy_address) { + /* override for backwards compatibility */ + if (info->socks_proxy_port) + vh->socks_proxy_port = info->socks_proxy_port; + lws_set_socks(vh, info->socks_proxy_address); + } else { +#ifdef LWS_HAVE_GETENV + p = getenv("socks_proxy"); + if (p) + lws_set_socks(vh, p); +#endif + } +#endif + + vh->ka_time = info->ka_time; + vh->ka_interval = info->ka_interval; + vh->ka_probes = info->ka_probes; + + if (vh->options & LWS_SERVER_OPTION_STS) + lwsl_notice(" STS enabled\n"); + +#ifdef LWS_WITH_ACCESS_LOG + if (info->log_filepath) { + vh->log_fd = lws_open(info->log_filepath, + O_CREAT | O_APPEND | O_RDWR, 0600); + if (vh->log_fd == (int)LWS_INVALID_FILE) { + lwsl_err("unable to open log filepath %s\n", + info->log_filepath); + goto bail; + } +#ifndef WIN32 + if (context->uid != -1) + if (chown(info->log_filepath, context->uid, + context->gid) == -1) + lwsl_err("unable to chown log file %s\n", + info->log_filepath); +#endif + } else + vh->log_fd = (int)LWS_INVALID_FILE; +#endif + if (lws_context_init_server_ssl(info, vh)) { + lwsl_err("%s: lws_context_init_server_ssl failed\n", __func__); + goto bail1; + } + if (lws_context_init_client_ssl(info, vh)) { + lwsl_err("%s: lws_context_init_client_ssl failed\n", __func__); + goto bail1; + } + lws_context_lock(context, "create_vhost"); + n = _lws_vhost_init_server(info, vh); + lws_context_unlock(context); + if (n < 0) { + lwsl_err("init server failed\n"); + goto bail1; + } + + while (1) { + if (!(*vh1)) { + *vh1 = vh; + break; + } + vh1 = &(*vh1)->vhost_next; + }; + + /* for the case we are adding a vhost much later, after server init */ + + if (context->protocol_init_done) + if (lws_protocol_init(context)) { + lwsl_err("%s: lws_protocol_init failed\n", __func__); + goto bail1; + } + + return vh; + +bail1: + lws_vhost_destroy(vh); + + return NULL; + +#ifdef LWS_WITH_ACCESS_LOG +bail: + lws_free(vh); +#endif + + return NULL; +} + +LWS_VISIBLE int +lws_init_vhost_client_ssl(const struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ + struct lws_context_creation_info i; + + memcpy(&i, info, sizeof(i)); + i.port = CONTEXT_PORT_NO_LISTEN; + + return lws_context_init_client_ssl(&i, vhost); +} + +LWS_VISIBLE void +lws_cancel_service_pt(struct lws *wsi) +{ + lws_plat_pipe_signal(wsi); +} + +LWS_VISIBLE void +lws_cancel_service(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + short m = context->count_threads; + + if (context->being_destroyed1) + return; + + lwsl_info("%s\n", __func__); + + while (m--) { + if (pt->pipe_wsi) + lws_plat_pipe_signal(pt->pipe_wsi); + pt++; + } +} + +int +lws_create_event_pipes(struct lws_context *context) +{ + struct lws *wsi; + int n; + + /* + * Create the pt event pipes... these are unique in that they are + * not bound to a vhost or protocol (both are NULL) + */ + + for (n = 0; n < context->count_threads; n++) { + if (context->pt[n].pipe_wsi) + continue; + + wsi = lws_zalloc(sizeof(*wsi), "event pipe wsi"); + if (!wsi) { + lwsl_err("%s: Out of mem\n", __func__); + return 1; + } + wsi->context = context; + lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_pipe); + wsi->protocol = NULL; + wsi->tsi = n; + wsi->vhost = NULL; + wsi->event_pipe = 1; + wsi->desc.sockfd = LWS_SOCK_INVALID; + context->pt[n].pipe_wsi = wsi; + context->count_wsi_allocated++; + + if (lws_plat_pipe_create(wsi)) + /* + * platform code returns 0 if it actually created pipes + * and initialized pt->dummy_pipe_fds[]. If it used + * some other mechanism outside of signaling in the + * normal event loop, we skip treating the pipe as + * related to dummy_pipe_fds[], adding it to the fds, + * etc. + */ + continue; + + wsi->desc.sockfd = context->pt[n].dummy_pipe_fds[0]; + lwsl_debug("event pipe fd %d\n", wsi->desc.sockfd); + + if (context->event_loop_ops->accept) + if (context->event_loop_ops->accept(wsi)) + return 1; + + if (__insert_wsi_socket_into_fds(context, wsi)) + return 1; + } + + return 0; +} + +void +lws_destroy_event_pipe(struct lws *wsi) +{ + lwsl_info("%s\n", __func__); + __remove_wsi_socket_from_fds(wsi); + + if (wsi->context->event_loop_ops->wsi_logical_close) { + wsi->context->event_loop_ops->wsi_logical_close(wsi); + lws_plat_pipe_close(wsi); + wsi->context->count_wsi_allocated--; + return; + } + + if (wsi->context->event_loop_ops->destroy_wsi) + wsi->context->event_loop_ops->destroy_wsi(wsi); + lws_plat_pipe_close(wsi); + wsi->context->count_wsi_allocated--; + lws_free(wsi); +} + + +void +lws_vhost_destroy1(struct lws_vhost *vh) +{ + struct lws_context *context = vh->context; + + lwsl_info("%s\n", __func__); + + lws_context_lock(context, "vhost destroy 1"); /* ---------- context { */ + + if (vh->being_destroyed) + goto out; + + lws_vhost_lock(vh); /* -------------- vh { */ + + vh->being_destroyed = 1; +#if defined(LWS_WITH_NETWORK) + /* + * PHASE 1: take down or reassign any listen wsi + * + * Are there other vhosts that are piggybacking on our listen socket? + * If so we need to hand the listen socket off to one of the others + * so it will remain open. + * + * If not, leave it attached to the closing vhost, the vh being marked + * being_destroyed will defeat any service and it will get closed in + * later phases. + */ + + if (vh->lserv_wsi) + lws_start_foreach_ll(struct lws_vhost *, v, + context->vhost_list) { + if (v != vh && + !v->being_destroyed && + v->listen_port == vh->listen_port && + ((!v->iface && !vh->iface) || + (v->iface && vh->iface && + !strcmp(v->iface, vh->iface)))) { + /* + * this can only be a listen wsi, which is + * restricted... it has no protocol or other + * bindings or states. So we can simply + * swap it to a vhost that has the same + * iface + port, but is not closing. + */ + assert(v->lserv_wsi == NULL); + v->lserv_wsi = vh->lserv_wsi; + + lwsl_notice("%s: listen skt from %s to %s\n", + __func__, vh->name, v->name); + + if (v->lserv_wsi) { + lws_vhost_unbind_wsi(vh->lserv_wsi); + lws_vhost_bind_wsi(v, v->lserv_wsi); + } + + break; + } + } lws_end_foreach_ll(v, vhost_next); + +#endif + + lws_vhost_unlock(vh); /* } vh -------------- */ + + /* + * lws_check_deferred_free() will notice there is a vhost that is + * marked for destruction during the next 1s, for all tsi. + * + * It will start closing all wsi on this vhost. When the last wsi + * is closed, it will trigger lws_vhost_destroy2() + */ + +out: + lws_context_unlock(context); /* --------------------------- context { */ +} + +void +__lws_vhost_destroy2(struct lws_vhost *vh) +{ + const struct lws_protocols *protocol = NULL; + struct lws_context *context = vh->context; + struct lws_deferred_free *df; + struct lws wsi; + int n; + + /* + * destroy any pending timed events + */ + + while (vh->timed_vh_protocol_list) + __lws_timed_callback_remove(vh, vh->timed_vh_protocol_list); + + /* + * let the protocols destroy the per-vhost protocol objects + */ + + memset(&wsi, 0, sizeof(wsi)); + wsi.context = vh->context; + wsi.vhost = vh; /* not a real bound wsi */ + protocol = vh->protocols; + if (protocol && vh->created_vhost_protocols) { + n = 0; + while (n < vh->count_protocols) { + wsi.protocol = protocol; + protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY, + NULL, NULL, 0); + protocol++; + n++; + } + } + + /* + * remove vhost from context list of vhosts + */ + + lws_start_foreach_llp(struct lws_vhost **, pv, context->vhost_list) { + if (*pv == vh) { + *pv = vh->vhost_next; + break; + } + } lws_end_foreach_llp(pv, vhost_next); + + /* add ourselves to the pending destruction list */ + + vh->vhost_next = vh->context->vhost_pending_destruction_list; + vh->context->vhost_pending_destruction_list = vh; + + lwsl_info("%s: %p\n", __func__, vh); + + /* if we are still on deferred free list, remove ourselves */ + + lws_start_foreach_llp(struct lws_deferred_free **, pdf, + context->deferred_free_list) { + if ((*pdf)->payload == vh) { + df = *pdf; + *pdf = df->next; + lws_free(df); + break; + } + } lws_end_foreach_llp(pdf, next); + + /* remove ourselves from the pending destruction list */ + + lws_start_foreach_llp(struct lws_vhost **, pv, + context->vhost_pending_destruction_list) { + if ((*pv) == vh) { + *pv = (*pv)->vhost_next; + break; + } + } lws_end_foreach_llp(pv, vhost_next); + + /* + * Free all the allocations associated with the vhost + */ + + protocol = vh->protocols; + if (protocol) { + n = 0; + while (n < vh->count_protocols) { + if (vh->protocol_vh_privs && + vh->protocol_vh_privs[n]) { + lws_free(vh->protocol_vh_privs[n]); + vh->protocol_vh_privs[n] = NULL; + } + protocol++; + n++; + } + } + if (vh->protocol_vh_privs) + lws_free(vh->protocol_vh_privs); + lws_ssl_SSL_CTX_destroy(vh); + lws_free(vh->same_vh_protocol_heads); + + if (context->plugin_list || + (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + lws_free((void *)vh->protocols); +#if defined(LWS_WITH_NETWORK) + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) + if (ar->destroy_vhost) + ar->destroy_vhost(vh); + LWS_FOR_EVERY_AVAILABLE_ROLE_END; +#endif + +#ifdef LWS_WITH_ACCESS_LOG + if (vh->log_fd != (int)LWS_INVALID_FILE) + close(vh->log_fd); +#endif + +#if defined (LWS_WITH_TLS) + lws_free_set_NULL(vh->tls.alloc_cert_path); +#endif + +#if LWS_MAX_SMP > 1 + pthread_mutex_destroy(&vh->lock); +#endif + +#if defined(LWS_WITH_UNIX_SOCK) + if (LWS_UNIX_SOCK_ENABLED(vh)) { + n = unlink(vh->iface); + if (n) + lwsl_info("Closing unix socket %s: errno %d\n", + vh->iface, errno); + } +#endif + /* + * although async event callbacks may still come for wsi handles with + * pending close in the case of asycn event library like libuv, + * they do not refer to the vhost. So it's safe to free. + */ + + if (vh->finalize) + vh->finalize(vh, vh->finalize_arg); + + lwsl_info(" %s: Freeing vhost %p\n", __func__, vh); + + memset(vh, 0, sizeof(*vh)); + lws_free(vh); +} + +/* + * each service thread calls this once a second or so + */ + +int +lws_check_deferred_free(struct lws_context *context, int tsi, int force) +{ + struct lws_context_per_thread *pt; + int n; + + /* + * If we see a vhost is being destroyed, forcibly close every wsi on + * this tsi associated with this vhost. That will include the listen + * socket if it is still associated with the closing vhost. + * + * For SMP, we do this once per tsi per destroyed vhost. The reference + * counting on the vhost as the bound wsi close will notice that there + * are no bound wsi left, that vhost destruction can complete, + * and perform it. It doesn't matter which service thread does that + * because there is nothing left using the vhost to conflict. + */ + + lws_context_lock(context, "check deferred free"); /* ------ context { */ + + lws_start_foreach_ll_safe(struct lws_vhost *, v, context->vhost_list, vhost_next) { + if (v->being_destroyed +#if LWS_MAX_SMP > 1 + && !v->close_flow_vs_tsi[tsi] +#endif + ) { + + pt = &context->pt[tsi]; + + lws_pt_lock(pt, "vhost removal"); /* -------------- pt { */ + +#if LWS_MAX_SMP > 1 + v->close_flow_vs_tsi[tsi] = 1; +#endif + + for (n = 0; (unsigned int)n < pt->fds_count; n++) { + struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->vhost != v) + continue; + + __lws_close_free_wsi(wsi, + LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY, + "vh destroy" + /* no protocol close */); + n--; + } + + lws_pt_unlock(pt); /* } pt -------------- */ + } + } lws_end_foreach_ll_safe(v); + + + lws_context_unlock(context); /* } context ------------------- */ + + return 0; +} + + +LWS_VISIBLE void +lws_vhost_destroy(struct lws_vhost *vh) +{ + struct lws_deferred_free *df = lws_malloc(sizeof(*df), "deferred free"); + struct lws_context *context = vh->context; + + if (!df) + return; + + lws_context_lock(context, __func__); /* ------ context { */ + + lws_vhost_destroy1(vh); + + if (!vh->count_bound_wsi) { + /* + * After listen handoff, there are already no wsi bound to this + * vhost by any pt: nothing can be servicing any wsi belonging + * to it any more. + * + * Finalize the vh destruction immediately + */ + __lws_vhost_destroy2(vh); + lws_free(df); + + goto out; + } + + /* part 2 is deferred to allow all the handle closes to complete */ + + df->next = vh->context->deferred_free_list; + df->deadline = lws_now_secs(); + df->payload = vh; + vh->context->deferred_free_list = df; + +out: + lws_context_unlock(context); /* } context ------------------- */ +} + + +LWS_EXTERN void * +lws_vhost_user(struct lws_vhost *vhost) +{ + return vhost->user; +} + +LWS_VISIBLE LWS_EXTERN int +lws_get_vhost_listen_port(struct lws_vhost *vhost) +{ + return vhost->listen_port; +} + + +LWS_VISIBLE LWS_EXTERN void +lws_context_deprecate(struct lws_context *context, lws_reload_func cb) +{ + struct lws_vhost *vh = context->vhost_list, *vh1; + + /* + * "deprecation" means disable the context from accepting any new + * connections and free up listen sockets to be used by a replacement + * context. + * + * Otherwise the deprecated context remains operational, until its + * number of connected sockets falls to zero, when it is deleted. + */ + + /* for each vhost, close his listen socket */ + + while (vh) { + struct lws *wsi = vh->lserv_wsi; + + if (wsi) { + wsi->socket_is_permanently_unusable = 1; + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "ctx deprecate"); + wsi->context->deprecation_pending_listen_close_count++; + /* + * other vhosts can share the listen port, they + * point to the same wsi. So zap those too. + */ + vh1 = context->vhost_list; + while (vh1) { + if (vh1->lserv_wsi == wsi) + vh1->lserv_wsi = NULL; + vh1 = vh1->vhost_next; + } + } + vh = vh->vhost_next; + } + + context->deprecated = 1; + context->deprecation_cb = cb; +} diff --git a/lib/core-net/wsi-timeout.c b/lib/core-net/wsi-timeout.c new file mode 100644 index 0000000000..bf41047256 --- /dev/null +++ b/lib/core-net/wsi-timeout.c @@ -0,0 +1,251 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + + +void +__lws_remove_from_timeout_list(struct lws *wsi) +{ + lws_dll_lws_remove(&wsi->dll_timeout); +} + +void +lws_remove_from_timeout_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + lws_pt_lock(pt, __func__); + __lws_remove_from_timeout_list(wsi); + lws_pt_unlock(pt); +} + + +void +__lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + struct lws_dll_lws *dd = &pt->dll_head_hrtimer; + struct timeval now; + struct lws *wsi1; + int bef = 0; + + lws_dll_lws_remove(&wsi->dll_hrtimer); + + if (usecs == LWS_SET_TIMER_USEC_CANCEL) + return; + + gettimeofday(&now, NULL); + wsi->pending_timer = ((now.tv_sec * 1000000ll) + now.tv_usec) + usecs; + + /* + * we sort the hrtimer list with the earliest timeout first + */ + + lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, + pt->dll_head_hrtimer.next) { + dd = d; + wsi1 = lws_container_of(d, struct lws, dll_hrtimer); + + if (wsi1->pending_timer >= wsi->pending_timer) { + /* d, dprev's next, is >= our time */ + bef = 1; + break; + } + } lws_end_foreach_dll_safe(d, d1); + + if (bef) { + /* + * we go before dd + * DDp <-> DD <-> DDn --> DDp <-> us <-> DD <-> DDn + */ + /* we point forward to dd */ + wsi->dll_hrtimer.next = dd; + /* we point back to what dd used to point back to */ + wsi->dll_hrtimer.prev = dd->prev; + /* DDp points forward to us now */ + dd->prev->next = &wsi->dll_hrtimer; + /* DD points back to us now */ + dd->prev = &wsi->dll_hrtimer; + } else { + /* + * we go after dd + * DDp <-> DD <-> DDn --> DDp <-> DD <-> us <-> DDn + */ + /* we point forward to what dd used to point forward to */ + wsi->dll_hrtimer.next = dd->next; + /* we point back to dd */ + wsi->dll_hrtimer.prev = dd; + /* DDn points back to us */ + if (dd->next) + dd->next->prev = &wsi->dll_hrtimer; + /* DD points forward to us */ + dd->next = &wsi->dll_hrtimer; + } + +// lws_dll_dump(&pt->dll_head_hrtimer, "after set_timer_usec"); +} + +LWS_VISIBLE void +lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs) +{ + __lws_set_timer_usecs(wsi, usecs); +} + + +lws_usec_t +__lws_hrtimer_service(struct lws_context_per_thread *pt) +{ + struct timeval now; + struct lws *wsi; + lws_usec_t t; + + gettimeofday(&now, NULL); + t = (now.tv_sec * 1000000ll) + now.tv_usec; + + lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, + pt->dll_head_hrtimer.next) { + wsi = lws_container_of(d, struct lws, dll_hrtimer); + + /* + * if we met one in the future, we are done, because the list + * is sorted by time in the future. + */ + if (wsi->pending_timer > t) + break; + + lws_set_timer_usecs(wsi, LWS_SET_TIMER_USEC_CANCEL); + + /* it's time for the timer to be serviced */ + + if (wsi->protocol && + wsi->protocol->callback(wsi, LWS_CALLBACK_TIMER, + wsi->user_space, NULL, 0)) + __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, + "timer cb errored"); + } lws_end_foreach_dll_safe(d, d1); + + /* return an estimate how many us until next timer hit */ + + if (!pt->dll_head_hrtimer.next) + return LWS_HRTIMER_NOWAIT; + + wsi = lws_container_of(pt->dll_head_hrtimer.next, struct lws, + dll_hrtimer); + + gettimeofday(&now, NULL); + t = (now.tv_sec * 1000000ll) + now.tv_usec; + + if (wsi->pending_timer < t) + return 0; + + return wsi->pending_timer - t; +} + +void +__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + time_t now; + + time(&now); + + lwsl_debug("%s: %p: %d secs (reason %d)\n", __func__, wsi, secs, reason); + wsi->pending_timeout_limit = secs; + wsi->pending_timeout_set = now; + wsi->pending_timeout = reason; + + if (!reason) + lws_dll_lws_remove(&wsi->dll_timeout); + else + lws_dll_lws_add_front(&wsi->dll_timeout, &pt->dll_head_timeout); +} + +LWS_VISIBLE void +lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + if (secs == LWS_TO_KILL_SYNC) { + lws_remove_from_timeout_list(wsi); + lwsl_debug("synchronously killing %p\n", wsi); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, + "to sync kill"); + return; + } + + if (secs == LWS_TO_KILL_ASYNC) + secs = 0; + + lws_pt_lock(pt, __func__); + __lws_set_timeout(wsi, reason, secs); + lws_pt_unlock(pt); +} + +/* requires context + vh lock */ + +int +__lws_timed_callback_remove(struct lws_vhost *vh, struct lws_timed_vh_protocol *p) +{ + lws_start_foreach_llp(struct lws_timed_vh_protocol **, pt, + vh->timed_vh_protocol_list) { + if (*pt == p) { + *pt = p->next; + lws_free(p); + + return 0; + } + } lws_end_foreach_llp(pt, next); + + return 1; +} + + +LWS_VISIBLE LWS_EXTERN int +lws_timed_callback_vh_protocol(struct lws_vhost *vh, + const struct lws_protocols *prot, int reason, + int secs) +{ + struct lws_timed_vh_protocol *p = (struct lws_timed_vh_protocol *) + lws_malloc(sizeof(*p), "timed_vh"); + + if (!p) + return 1; + + p->tsi_req = lws_pthread_self_to_tsi(vh->context); + if (p->tsi_req < 0) /* not called from a service thread --> tsi 0 */ + p->tsi_req = 0; + + lws_context_lock(vh->context, __func__); /* context ----------------- */ + + p->protocol = prot; + p->reason = reason; + p->time = lws_now_secs() + secs; + + lws_vhost_lock(vh); /* vhost ---------------------------------------- */ + p->next = vh->timed_vh_protocol_list; + vh->timed_vh_protocol_list = p; + lws_vhost_unlock(vh); /* -------------------------------------- vhost */ + + lws_context_unlock(vh->context); /* ------------------------- context */ + + return 0; +} diff --git a/lib/core-net/wsi.c b/lib/core-net/wsi.c new file mode 100644 index 0000000000..2b93331a85 --- /dev/null +++ b/lib/core-net/wsi.c @@ -0,0 +1,883 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + +#if defined (_DEBUG) +void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role) +{ + wsi->wsistate = (wsi->wsistate & (~LWSI_ROLE_MASK)) | role; + + lwsl_debug("lwsi_set_role(%p, 0x%x)\n", wsi, wsi->wsistate); +} + +void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs) +{ + wsi->wsistate = (wsi->wsistate & (~LRS_MASK)) | lrs; + + lwsl_debug("lwsi_set_state(%p, 0x%x)\n", wsi, wsi->wsistate); +} +#endif + + +void +lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi) +{ + if (wsi->vhost == vh) + return; + lws_context_lock(vh->context, __func__); /* ---------- context { */ + wsi->vhost = vh; + vh->count_bound_wsi++; + lws_context_unlock(vh->context); /* } context ---------- */ + lwsl_info("%s: vh %s: count_bound_wsi %d\n", + __func__, vh->name, vh->count_bound_wsi); + assert(wsi->vhost->count_bound_wsi > 0); +} + +void +lws_vhost_unbind_wsi(struct lws *wsi) +{ + if (!wsi->vhost) + return; + + lws_context_lock(wsi->context, __func__); /* ---------- context { */ + + assert(wsi->vhost->count_bound_wsi > 0); + wsi->vhost->count_bound_wsi--; + lwsl_info("%s: vh %s: count_bound_wsi %d\n", __func__, + wsi->vhost->name, wsi->vhost->count_bound_wsi); + + if (!wsi->vhost->count_bound_wsi && + wsi->vhost->being_destroyed) { + /* + * We have closed all wsi that were bound to this vhost + * by any pt: nothing can be servicing any wsi belonging + * to it any more. + * + * Finalize the vh destruction + */ + __lws_vhost_destroy2(wsi->vhost); + } + wsi->vhost = NULL; + + lws_context_unlock(wsi->context); /* } context ---------- */ +} + +LWS_VISIBLE struct lws * +lws_get_network_wsi(struct lws *wsi) +{ + if (!wsi) + return NULL; + +#if defined(LWS_WITH_HTTP2) + if (!wsi->http2_substream +#if !defined(LWS_NO_CLIENT) + && !wsi->client_h2_substream +#endif + ) + return wsi; + + while (wsi->h2.parent_wsi) + wsi = wsi->h2.parent_wsi; +#endif + + return wsi; +} + + +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name) +{ + int n; + + for (n = 0; n < vh->count_protocols; n++) + if (!strcmp(name, vh->protocols[n].name)) + return &vh->protocols[n]; + + return NULL; +} + +LWS_VISIBLE int +lws_callback_all_protocol(struct lws_context *context, + const struct lws_protocols *protocol, int reason) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + unsigned int n, m = context->count_threads; + struct lws *wsi; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->protocol == protocol) + protocol->callback(wsi, reason, wsi->user_space, + NULL, 0); + } + pt++; + } + + return 0; +} + +LWS_VISIBLE int +lws_callback_all_protocol_vhost_args(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason, + void *argp, size_t len) +{ + struct lws_context *context = vh->context; + struct lws_context_per_thread *pt = &context->pt[0]; + unsigned int n, m = context->count_threads; + struct lws *wsi; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->vhost == vh && (wsi->protocol == protocol || + !protocol)) + wsi->protocol->callback(wsi, reason, + wsi->user_space, argp, len); + } + pt++; + } + + return 0; +} + +LWS_VISIBLE int +lws_callback_all_protocol_vhost(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason) +{ + return lws_callback_all_protocol_vhost_args(vh, protocol, reason, NULL, 0); +} + +LWS_VISIBLE LWS_EXTERN int +lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len) +{ + int n; + + for (n = 0; n < wsi->vhost->count_protocols; n++) + if (wsi->vhost->protocols[n].callback(wsi, reason, NULL, in, len)) + return 1; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_callback_vhost_protocols_vhost(struct lws_vhost *vh, int reason, void *in, + size_t len) +{ + int n; + struct lws *wsi = lws_zalloc(sizeof(*wsi), "fake wsi"); + + wsi->context = vh->context; + lws_vhost_bind_wsi(vh, wsi); + + for (n = 0; n < wsi->vhost->count_protocols; n++) { + wsi->protocol = &vh->protocols[n]; + if (wsi->protocol->callback(wsi, reason, NULL, in, len)) { + lws_free(wsi); + return 1; + } + } + + lws_free(wsi); + + return 0; +} + + +LWS_VISIBLE int +lws_rx_flow_control(struct lws *wsi, int _enable) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int en = _enable; + + // h2 ignores rx flow control atm + if (lwsi_role_h2(wsi) || wsi->http2_substream || + lwsi_role_h2_ENCAPSULATION(wsi)) + return 0; // !!! + + lwsl_info("%s: %p 0x%x\n", __func__, wsi, _enable); + + if (!(_enable & LWS_RXFLOW_REASON_APPLIES)) { + /* + * convert user bool style to bitmap style... in user simple + * bool style _enable = 0 = flow control it, = 1 = allow rx + */ + en = LWS_RXFLOW_REASON_APPLIES | LWS_RXFLOW_REASON_USER_BOOL; + if (_enable & 1) + en |= LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT; + } + + lws_pt_lock(pt, __func__); + + /* any bit set in rxflow_bitmap DISABLEs rxflow control */ + if (en & LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT) + wsi->rxflow_bitmap &= ~(en & 0xff); + else + wsi->rxflow_bitmap |= en & 0xff; + + if ((LWS_RXFLOW_PENDING_CHANGE | (!wsi->rxflow_bitmap)) == + wsi->rxflow_change_to) + goto skip; + + wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | + (!wsi->rxflow_bitmap); + + lwsl_info("%s: %p: bitmap 0x%x: en 0x%x, ch 0x%x\n", __func__, wsi, + wsi->rxflow_bitmap, en, wsi->rxflow_change_to); + + if (_enable & LWS_RXFLOW_REASON_FLAG_PROCESS_NOW || + !wsi->rxflow_will_be_applied) { + en = __lws_rx_flow_control(wsi); + lws_pt_unlock(pt); + + return en; + } + +skip: + lws_pt_unlock(pt); + + return 0; +} + +LWS_VISIBLE void +lws_rx_flow_allow_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol) +{ + const struct lws_context_per_thread *pt = &context->pt[0]; + struct lws *wsi; + unsigned int n, m = context->count_threads; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->protocol == protocol) + lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW); + } + pt++; + } +} + +int user_callback_handle_rxflow(lws_callback_function callback_function, + struct lws *wsi, + enum lws_callback_reasons reason, void *user, + void *in, size_t len) +{ + int n; + + wsi->rxflow_will_be_applied = 1; + n = callback_function(wsi, reason, user, in, len); + wsi->rxflow_will_be_applied = 0; + if (!n) + n = __lws_rx_flow_control(wsi); + + return n; +} + +LWS_EXTERN int +__lws_rx_flow_control(struct lws *wsi) +{ + struct lws *wsic = wsi->child_list; + + // h2 ignores rx flow control atm + if (lwsi_role_h2(wsi) || wsi->http2_substream || + lwsi_role_h2_ENCAPSULATION(wsi)) + return 0; // !!! + + /* if he has children, do those if they were changed */ + while (wsic) { + if (wsic->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE) + __lws_rx_flow_control(wsic); + + wsic = wsic->sibling_list; + } + + /* there is no pending change */ + if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) + return 0; + + /* stuff is still buffered, not ready to really accept new input */ + if (lws_buflist_next_segment_len(&wsi->buflist, NULL)) { + /* get ourselves called back to deal with stashed buffer */ + lws_callback_on_writable(wsi); + return 0; + } + + /* now the pending is cleared, we can change rxflow state */ + + wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE; + + lwsl_info("rxflow: wsi %p change_to %d\n", wsi, + wsi->rxflow_change_to & LWS_RXFLOW_ALLOW); + + /* adjust the pollfd for this wsi */ + + if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) { + if (__lws_change_pollfd(wsi, 0, LWS_POLLIN)) { + lwsl_info("%s: fail\n", __func__); + return -1; + } + } else + if (__lws_change_pollfd(wsi, LWS_POLLIN, 0)) + return -1; + + return 0; +} + + +LWS_VISIBLE const struct lws_protocols * +lws_get_protocol(struct lws *wsi) +{ + return wsi->protocol; +} + + +int +lws_ensure_user_space(struct lws *wsi) +{ + if (!wsi->protocol) + return 0; + + /* allocate the per-connection user memory (if any) */ + + if (wsi->protocol->per_session_data_size && !wsi->user_space) { + wsi->user_space = lws_zalloc( + wsi->protocol->per_session_data_size, "user space"); + if (wsi->user_space == NULL) { + lwsl_err("%s: OOM\n", __func__); + return 1; + } + } else + lwsl_debug("%s: %p protocol pss %lu, user_space=%p\n", __func__, + wsi, (long)wsi->protocol->per_session_data_size, + wsi->user_space); + return 0; +} + +LWS_VISIBLE void * +lws_adjust_protocol_psds(struct lws *wsi, size_t new_size) +{ + ((struct lws_protocols *)lws_get_protocol(wsi))->per_session_data_size = + new_size; + + if (lws_ensure_user_space(wsi)) + return NULL; + + return wsi->user_space; +} + + + +LWS_VISIBLE int +lws_is_ssl(struct lws *wsi) +{ +#if defined(LWS_WITH_TLS) + return wsi->tls.use_ssl & LCCSCF_USE_SSL; +#else + (void)wsi; + return 0; +#endif +} + +#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS) +LWS_VISIBLE lws_tls_conn* +lws_get_ssl(struct lws *wsi) +{ + return wsi->tls.ssl; +} +#endif + +LWS_VISIBLE int +lws_partial_buffered(struct lws *wsi) +{ + return lws_has_buffered_out(wsi); +} + +LWS_VISIBLE lws_fileofs_t +lws_get_peer_write_allowance(struct lws *wsi) +{ + if (!wsi->role_ops->tx_credit) + return -1; + return wsi->role_ops->tx_credit(wsi); +} + +LWS_VISIBLE void +lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state, + const struct lws_role_ops *ops) +{ +#if defined(_DEBUG) + const char *name = "(unset)"; +#endif + wsi->wsistate = role | state; + if (ops) + wsi->role_ops = ops; +#if defined(_DEBUG) + if (wsi->role_ops) + name = wsi->role_ops->name; + lwsl_debug("%s: %p: wsistate 0x%x, ops %s\n", __func__, wsi, + wsi->wsistate, name); +#endif +} + +LWS_VISIBLE LWS_EXTERN int +lws_parse_uri(char *p, const char **prot, const char **ads, int *port, + const char **path) +{ + const char *end; + char unix_skt = 0; + + /* cut up the location into address, port and path */ + *prot = p; + while (*p && (*p != ':' || p[1] != '/' || p[2] != '/')) + p++; + if (!*p) { + end = p; + p = (char *)*prot; + *prot = end; + } else { + *p = '\0'; + p += 3; + } + if (*p == '+') /* unix skt */ + unix_skt = 1; + + *ads = p; + if (!strcmp(*prot, "http") || !strcmp(*prot, "ws")) + *port = 80; + else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss")) + *port = 443; + + if (*p == '[') { + ++(*ads); + while (*p && *p != ']') + p++; + if (*p) + *p++ = '\0'; + } else + while (*p && *p != ':' && (unix_skt || *p != '/')) + p++; + + if (*p == ':') { + *p++ = '\0'; + *port = atoi(p); + while (*p && *p != '/') + p++; + } + *path = "/"; + if (*p) { + *p++ = '\0'; + if (*p) + *path = p; + } + + return 0; +} + +/* ... */ + +LWS_VISIBLE LWS_EXTERN const char * +lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len) +{ + int n = 0, sl = (int)strlen(name); + + while (lws_hdr_copy_fragment(wsi, buf, len, + WSI_TOKEN_HTTP_URI_ARGS, n) >= 0) { + + if (!strncmp(buf, name, sl)) + return buf + sl; + + n++; + } + + return NULL; +} + + +#if defined(LWS_WITHOUT_EXTENSIONS) + +/* we need to provide dummy callbacks for internal exts + * so user code runs when faced with a lib compiled with + * extensions disabled. + */ + +LWS_VISIBLE int +lws_extension_callback_pm_deflate(struct lws_context *context, + const struct lws_extension *ext, + struct lws *wsi, + enum lws_extension_callback_reasons reason, + void *user, void *in, size_t len) +{ + (void)context; + (void)ext; + (void)wsi; + (void)reason; + (void)user; + (void)in; + (void)len; + + return 0; +} + +LWS_EXTERN int +lws_set_extension_option(struct lws *wsi, const char *ext_name, + const char *opt_name, const char *opt_val) +{ + return -1; +} +#endif + +LWS_VISIBLE LWS_EXTERN int +lws_is_cgi(struct lws *wsi) { +#ifdef LWS_WITH_CGI + return !!wsi->http.cgi; +#else + return 0; +#endif +} + +const struct lws_protocol_vhost_options * +lws_pvo_search(const struct lws_protocol_vhost_options *pvo, const char *name) +{ + while (pvo) { + if (!strcmp(pvo->name, name)) + break; + + pvo = pvo->next; + } + + return pvo; +} + +int +lws_pvo_get_str(void *in, const char *name, const char **result) +{ + const struct lws_protocol_vhost_options *pv = + lws_pvo_search((const struct lws_protocol_vhost_options *)in, + name); + + if (!pv) + return 1; + + *result = (const char *)pv->value; + + return 0; +} + +int +lws_broadcast(struct lws_context *context, int reason, void *in, size_t len) +{ + struct lws_vhost *v = context->vhost_list; + struct lws wsi; + int n, ret = 0; + + memset(&wsi, 0, sizeof(wsi)); + wsi.context = context; + + while (v) { + const struct lws_protocols *p = v->protocols; + wsi.vhost = v; /* not a real bound wsi */ + + for (n = 0; n < v->count_protocols; n++) { + wsi.protocol = p; + if (p->callback && + p->callback(&wsi, reason, NULL, in, len)) + ret |= 1; + p++; + } + v = v->vhost_next; + } + + return ret; +} + +LWS_VISIBLE LWS_EXTERN void * +lws_wsi_user(struct lws *wsi) +{ + return wsi->user_space; +} + +LWS_VISIBLE LWS_EXTERN void +lws_set_wsi_user(struct lws *wsi, void *data) +{ + if (wsi->user_space_externally_allocated) + wsi->user_space = data; + else + lwsl_err("%s: Cannot set internally-allocated user_space\n", + __func__); +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_get_parent(const struct lws *wsi) +{ + return wsi->parent; +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_get_child(const struct lws *wsi) +{ + return wsi->child_list; +} + +LWS_VISIBLE LWS_EXTERN void * +lws_get_opaque_parent_data(const struct lws *wsi) +{ + return wsi->opaque_parent_data; +} + +LWS_VISIBLE LWS_EXTERN void +lws_set_opaque_parent_data(struct lws *wsi, void *data) +{ + wsi->opaque_parent_data = data; +} + +LWS_VISIBLE LWS_EXTERN void * +lws_get_opaque_user_data(const struct lws *wsi) +{ + return wsi->opaque_user_data; +} + +LWS_VISIBLE LWS_EXTERN void +lws_set_opaque_user_data(struct lws *wsi, void *data) +{ + wsi->opaque_user_data = data; +} + +LWS_VISIBLE LWS_EXTERN int +lws_get_child_pending_on_writable(const struct lws *wsi) +{ + return wsi->parent_pending_cb_on_writable; +} + +LWS_VISIBLE LWS_EXTERN void +lws_clear_child_pending_on_writable(struct lws *wsi) +{ + wsi->parent_pending_cb_on_writable = 0; +} + + + +LWS_VISIBLE LWS_EXTERN const char * +lws_get_vhost_name(struct lws_vhost *vhost) +{ + return vhost->name; +} + +LWS_VISIBLE LWS_EXTERN int +lws_get_vhost_port(struct lws_vhost *vhost) +{ + return vhost->listen_port; +} + +LWS_VISIBLE LWS_EXTERN void * +lws_get_vhost_user(struct lws_vhost *vhost) +{ + return vhost->user; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_get_vhost_iface(struct lws_vhost *vhost) +{ + return vhost->iface; +} + +LWS_VISIBLE lws_sockfd_type +lws_get_socket_fd(struct lws *wsi) +{ + if (!wsi) + return -1; + return wsi->desc.sockfd; +} + + +LWS_VISIBLE struct lws_vhost * +lws_vhost_get(struct lws *wsi) +{ + return wsi->vhost; +} + +LWS_VISIBLE struct lws_vhost * +lws_get_vhost(struct lws *wsi) +{ + return wsi->vhost; +} + +LWS_VISIBLE const struct lws_protocols * +lws_protocol_get(struct lws *wsi) +{ + return wsi->protocol; +} + +LWS_VISIBLE const struct lws_udp * +lws_get_udp(const struct lws *wsi) +{ + return wsi->udp; +} + +LWS_VISIBLE LWS_EXTERN struct lws_context * +lws_get_context(const struct lws *wsi) +{ + return wsi->context; +} + +#ifdef LWS_LATENCY +void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completed) +{ + unsigned long long u; + char buf[256]; + + u = lws_time_in_microseconds(); + + if (!action) { + wsi->latency_start = u; + if (!wsi->action_start) + wsi->action_start = u; + return; + } + if (completed) { + if (wsi->action_start == wsi->latency_start) + sprintf(buf, + "Completion first try lat %lluus: %p: ret %d: %s\n", + u - wsi->latency_start, + (void *)wsi, ret, action); + else + sprintf(buf, + "Completion %lluus: lat %lluus: %p: ret %d: %s\n", + u - wsi->action_start, + u - wsi->latency_start, + (void *)wsi, ret, action); + wsi->action_start = 0; + } else + sprintf(buf, "lat %lluus: %p: ret %d: %s\n", + u - wsi->latency_start, (void *)wsi, ret, action); + + if (u - wsi->latency_start > context->worst_latency) { + context->worst_latency = u - wsi->latency_start; + strcpy(context->worst_latency_info, buf); + } + lwsl_latency("%s", buf); +} +#endif + +LWS_VISIBLE int LWS_WARN_UNUSED_RESULT +lws_raw_transaction_completed(struct lws *wsi) +{ + if (lws_has_buffered_out(wsi)) { + /* + * ...so he tried to send something large, but it went out + * as a partial, but he immediately called us to say he wants + * to close the connection. + * + * Defer the close until the last part of the partial is sent. + * + */ + lwsl_debug("%s: %p: deferring due to partial\n", __func__, wsi); + wsi->close_when_buffered_out_drained = 1; + lws_callback_on_writable(wsi); + + return 0; + } + + return -1; +} + +void +lws_dll_dump(struct lws_dll_lws *head, const char *title) +{ + int n = 0; + + (void)n; + lwsl_notice("%s: %s (head.next %p)\n", __func__, title, head->next); + + lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, head->next) { + struct lws *wsi = lws_container_of(d, struct lws, dll_hrtimer); + + (void)wsi; + + lwsl_notice(" %d: wsi %p: %llu\n", n++, wsi, + (unsigned long long)wsi->pending_timer); + } lws_end_foreach_dll_safe(d, d1); +} + + +int +lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p, + const char *reason) +{ +// if (wsi->protocol == p) +// return 0; + const struct lws_protocols *vp = wsi->vhost->protocols, *vpo; + + if (wsi->protocol && wsi->protocol_bind_balance) { + wsi->protocol->callback(wsi, + wsi->role_ops->protocol_unbind_cb[!!lwsi_role_server(wsi)], + wsi->user_space, (void *)reason, 0); + wsi->protocol_bind_balance = 0; + } + if (!wsi->user_space_externally_allocated) + lws_free_set_NULL(wsi->user_space); + + lws_same_vh_protocol_remove(wsi); + + wsi->protocol = p; + if (!p) + return 0; + + if (lws_ensure_user_space(wsi)) + return 1; + + if (p > vp && p < &vp[wsi->vhost->count_protocols]) + lws_same_vh_protocol_insert(wsi, (int)(p - vp)); + else { + int n = wsi->vhost->count_protocols; + int hit = 0; + + vpo = vp; + + while (n--) { + if (p->name && vp->name && !strcmp(p->name, vp->name)) { + hit = 1; + lws_same_vh_protocol_insert(wsi, (int)(vp - vpo)); + break; + } + vp++; + } + if (!hit) + lwsl_err("%s: %p is not in vhost '%s' protocols list\n", + __func__, p, wsi->vhost->name); + } + + if (wsi->protocol->callback(wsi, wsi->role_ops->protocol_bind_cb[ + !!lwsi_role_server(wsi)], + wsi->user_space, NULL, 0)) + return 1; + + wsi->protocol_bind_balance = 1; + + return 0; +} diff --git a/lib/core/context.c b/lib/core/context.c index ed7d82632d..722ea15dcf 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010-2018 Andy Green + * Copyright (C) 2010-2019 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,40 +25,6 @@ #define LWS_BUILD_HASH "unknown-build-hash" #endif -const struct lws_role_ops *available_roles[] = { -#if defined(LWS_ROLE_H2) - &role_ops_h2, -#endif -#if defined(LWS_ROLE_H1) - &role_ops_h1, -#endif -#if defined(LWS_ROLE_WS) - &role_ops_ws, -#endif -#if defined(LWS_ROLE_DBUS) - &role_ops_dbus, -#endif -#if defined(LWS_ROLE_RAW_PROXY) - &role_ops_raw_proxy, -#endif - NULL -}; - -const struct lws_event_loop_ops *available_event_libs[] = { -#if defined(LWS_WITH_POLL) - &event_loop_ops_poll, -#endif -#if defined(LWS_WITH_LIBUV) - &event_loop_ops_uv, -#endif -#if defined(LWS_WITH_LIBEVENT) - &event_loop_ops_event, -#endif -#if defined(LWS_WITH_LIBEV) - &event_loop_ops_ev, -#endif - NULL -}; static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH; @@ -75,813 +41,6 @@ lws_get_library_version(void) return library_version; } -const struct lws_role_ops * -lws_role_by_name(const char *name) -{ - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (!strcmp(ar->name, name)) - return ar; - LWS_FOR_EVERY_AVAILABLE_ROLE_END; - - if (!strcmp(name, role_ops_raw_skt.name)) - return &role_ops_raw_skt; - - if (!strcmp(name, role_ops_raw_file.name)) - return &role_ops_raw_file; - - return NULL; -} - -int -lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn) -{ -#if defined(LWS_WITH_TLS) - if (!alpn) - return 0; - - lwsl_info("%s: '%s'\n", __func__, alpn); - - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (ar->alpn && !strcmp(ar->alpn, alpn) && ar->alpn_negotiated) - return ar->alpn_negotiated(wsi, alpn); - LWS_FOR_EVERY_AVAILABLE_ROLE_END; -#endif - return 0; -} - -//#if !defined(LWS_WITHOUT_SERVER) -int -lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot) -{ - int n; - - /* - * if the vhost is told to bind accepted sockets to a given role, - * then look it up by name and try to bind to the specific role. - */ - if (lws_check_opt(wsi->vhost->options, - LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG) && - wsi->vhost->listen_accept_role) { - const struct lws_role_ops *role = - lws_role_by_name(wsi->vhost->listen_accept_role); - - if (!prot) - prot = wsi->vhost->listen_accept_protocol; - - if (!role) - lwsl_err("%s: can't find role '%s'\n", __func__, - wsi->vhost->listen_accept_role); - - if (role && role->adoption_bind) { - n = role->adoption_bind(wsi, type, prot); - if (n < 0) - return -1; - if (n) /* did the bind */ - return 0; - } - - if (type & _LWS_ADOPT_FINISH) { - lwsl_debug("%s: leaving bound to role %s\n", __func__, - wsi->role_ops->name); - return 0; - } - - - lwsl_warn("%s: adoption bind to role '%s', " - "protocol '%s', type 0x%x, failed\n", __func__, - wsi->vhost->listen_accept_role, prot, type); - } - - /* - * Otherwise ask each of the roles in order of preference if they - * want to bind to this accepted socket - */ - - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (ar->adoption_bind && ar->adoption_bind(wsi, type, prot)) - return 0; - LWS_FOR_EVERY_AVAILABLE_ROLE_END; - - /* fall back to raw socket role if, eg, h1 not configured */ - - if (role_ops_raw_skt.adoption_bind && - role_ops_raw_skt.adoption_bind(wsi, type, prot)) - return 0; - - /* fall back to raw file role if, eg, h1 not configured */ - - if (role_ops_raw_file.adoption_bind && - role_ops_raw_file.adoption_bind(wsi, type, prot)) - return 0; - - return 1; -} -//#endif - -#if !defined(LWS_WITHOUT_CLIENT) -int -lws_role_call_client_bind(struct lws *wsi, - const struct lws_client_connect_info *i) -{ - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (ar->client_bind) { - int m = ar->client_bind(wsi, i); - if (m < 0) - return m; - if (m) - return 0; - } - LWS_FOR_EVERY_AVAILABLE_ROLE_END; - - /* fall back to raw socket role if, eg, h1 not configured */ - - if (role_ops_raw_skt.client_bind && - role_ops_raw_skt.client_bind(wsi, i)) - return 0; - - return 1; -} -#endif - -static const char * const mount_protocols[] = { - "http://", - "https://", - "file://", - "cgi://", - ">http://", - ">https://", - "callback://" -}; - -LWS_VISIBLE void * -lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, - const struct lws_protocols *prot, int size) -{ - int n = 0; - - /* allocate the vh priv array only on demand */ - if (!vhost->protocol_vh_privs) { - vhost->protocol_vh_privs = (void **)lws_zalloc( - vhost->count_protocols * sizeof(void *), - "protocol_vh_privs"); - if (!vhost->protocol_vh_privs) - return NULL; - } - - while (n < vhost->count_protocols && &vhost->protocols[n] != prot) - n++; - - if (n == vhost->count_protocols) { - n = 0; - while (n < vhost->count_protocols && - strcmp(vhost->protocols[n].name, prot->name)) - n++; - - if (n == vhost->count_protocols) - return NULL; - } - - vhost->protocol_vh_privs[n] = lws_zalloc(size, "vh priv"); - return vhost->protocol_vh_privs[n]; -} - -LWS_VISIBLE void * -lws_protocol_vh_priv_get(struct lws_vhost *vhost, - const struct lws_protocols *prot) -{ - int n = 0; - - if (!vhost || !vhost->protocol_vh_privs || !prot) - return NULL; - - while (n < vhost->count_protocols && &vhost->protocols[n] != prot) - n++; - - if (n == vhost->count_protocols) { - n = 0; - while (n < vhost->count_protocols && - strcmp(vhost->protocols[n].name, prot->name)) - n++; - - if (n == vhost->count_protocols) { - lwsl_err("%s: unknown protocol %p\n", __func__, prot); - return NULL; - } - } - - return vhost->protocol_vh_privs[n]; -} - -const struct lws_protocol_vhost_options * -lws_vhost_protocol_options(struct lws_vhost *vh, const char *name) -{ - const struct lws_protocol_vhost_options *pvo = vh->pvo; - - if (!name) - return NULL; - - while (pvo) { - if (!strcmp(pvo->name, name)) - return pvo; - pvo = pvo->next; - } - - return NULL; -} - -/* - * inform every vhost that hasn't already done it, that - * his protocols are initializing - */ -LWS_VISIBLE int -lws_protocol_init(struct lws_context *context) -{ - struct lws_vhost *vh = context->vhost_list; - const struct lws_protocol_vhost_options *pvo, *pvo1; - struct lws wsi; - int n, any = 0; - - if (context->doing_protocol_init) - return 0; - - context->doing_protocol_init = 1; - - memset(&wsi, 0, sizeof(wsi)); - wsi.context = context; - - lwsl_info("%s\n", __func__); - - while (vh) { - wsi.vhost = vh; - - /* only do the protocol init once for a given vhost */ - if (vh->created_vhost_protocols || - (vh->options & LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT)) - goto next; - - /* initialize supported protocols on this vhost */ - - for (n = 0; n < vh->count_protocols; n++) { - wsi.protocol = &vh->protocols[n]; - if (!vh->protocols[n].name) - continue; - pvo = lws_vhost_protocol_options(vh, - vh->protocols[n].name); - if (pvo) { - /* - * linked list of options specific to - * vh + protocol - */ - pvo1 = pvo; - pvo = pvo1->options; - - while (pvo) { - lwsl_debug( - " vhost \"%s\", " - "protocol \"%s\", " - "option \"%s\"\n", - vh->name, - vh->protocols[n].name, - pvo->name); - - if (!strcmp(pvo->name, "default")) { - lwsl_info("Setting default " - "protocol for vh %s to %s\n", - vh->name, - vh->protocols[n].name); - vh->default_protocol_index = n; - } - if (!strcmp(pvo->name, "raw")) { - lwsl_info("Setting raw " - "protocol for vh %s to %s\n", - vh->name, - vh->protocols[n].name); - vh->raw_protocol_index = n; - } - pvo = pvo->next; - } - - pvo = pvo1->options; - } - -#if defined(LWS_WITH_TLS) - any |= !!vh->tls.ssl_ctx; -#endif - - /* - * inform all the protocols that they are doing their - * one-time initialization if they want to. - * - * NOTE the wsi is all zeros except for the context, vh - * + protocol ptrs so lws_get_context(wsi) etc can work - */ - if (vh->protocols[n].callback(&wsi, - LWS_CALLBACK_PROTOCOL_INIT, NULL, - (void *)pvo, 0)) { - lws_free(vh->protocol_vh_privs[n]); - vh->protocol_vh_privs[n] = NULL; - lwsl_err("%s: protocol %s failed init\n", - __func__, vh->protocols[n].name); - - return 1; - } - } - - vh->created_vhost_protocols = 1; -next: - vh = vh->vhost_next; - } - - context->doing_protocol_init = 0; - - if (!context->protocol_init_done) - lws_finalize_startup(context); - - context->protocol_init_done = 1; - - if (any) - lws_tls_check_all_cert_lifetimes(context); - - return 0; -} - - -/* list of supported protocols and callbacks */ - -static const struct lws_protocols protocols_dummy[] = { - /* first protocol must always be HTTP handler */ - - { - "http-only", /* name */ - lws_callback_http_dummy, /* callback */ - 0, /* per_session_data_size */ - 0, /* rx_buffer_size */ - 0, /* id */ - NULL, /* user */ - 0 /* tx_packet_size */ - }, - /* - * the other protocols are provided by lws plugins - */ - { NULL, NULL, 0, 0, 0, NULL, 0} /* terminator */ -}; - -#ifdef LWS_PLAT_OPTEE -#undef LWS_HAVE_GETENV -#endif - -LWS_VISIBLE struct lws_vhost * -lws_create_vhost(struct lws_context *context, - const struct lws_context_creation_info *info) -{ - struct lws_vhost *vh = lws_zalloc(sizeof(*vh), "create vhost"), - **vh1 = &context->vhost_list; - const struct lws_http_mount *mounts; - const struct lws_protocols *pcols = info->protocols; - const struct lws_protocol_vhost_options *pvo; -#ifdef LWS_WITH_PLUGINS - struct lws_plugin *plugin = context->plugin_list; -#endif - struct lws_protocols *lwsp; - int m, f = !info->pvo; - char buf[20]; -#if !defined(LWS_WITHOUT_CLIENT) && defined(LWS_HAVE_GETENV) - char *p; -#endif - int n; - - if (!vh) - return NULL; - -#if LWS_MAX_SMP > 1 - pthread_mutex_init(&vh->lock, NULL); -#endif - - if (!pcols) - pcols = &protocols_dummy[0]; - - vh->context = context; - if (!info->vhost_name) - vh->name = "default"; - else - vh->name = info->vhost_name; - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - vh->http.error_document_404 = info->error_document_404; -#endif - - if (info->options & LWS_SERVER_OPTION_ONLY_RAW) - lwsl_info("%s set to only support RAW\n", vh->name); - - vh->iface = info->iface; -#if !defined(LWS_WITH_ESP32) && \ - !defined(OPTEE_TA) && !defined(WIN32) - vh->bind_iface = info->bind_iface; -#endif - - for (vh->count_protocols = 0; - pcols[vh->count_protocols].callback; - vh->count_protocols++) - ; - - vh->options = info->options; - vh->pvo = info->pvo; - vh->headers = info->headers; - vh->user = info->user; - vh->finalize = info->finalize; - vh->finalize_arg = info->finalize_arg; - vh->listen_accept_role = info->listen_accept_role; - vh->listen_accept_protocol = info->listen_accept_protocol; - - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (ar->init_vhost) - if (ar->init_vhost(vh, info)) - return NULL; - LWS_FOR_EVERY_AVAILABLE_ROLE_END; - - - if (info->keepalive_timeout) - vh->keepalive_timeout = info->keepalive_timeout; - else - vh->keepalive_timeout = 5; - - if (info->timeout_secs_ah_idle) - vh->timeout_secs_ah_idle = info->timeout_secs_ah_idle; - else - vh->timeout_secs_ah_idle = 10; - -#if defined(LWS_WITH_TLS) - - vh->tls.alpn = info->alpn; - vh->tls.ssl_info_event_mask = info->ssl_info_event_mask; - - if (info->ecdh_curve) - lws_strncpy(vh->tls.ecdh_curve, info->ecdh_curve, - sizeof(vh->tls.ecdh_curve)); - - /* carefully allocate and take a copy of cert + key paths if present */ - n = 0; - if (info->ssl_cert_filepath) - n += (int)strlen(info->ssl_cert_filepath) + 1; - if (info->ssl_private_key_filepath) - n += (int)strlen(info->ssl_private_key_filepath) + 1; - - if (n) { - vh->tls.key_path = vh->tls.alloc_cert_path = - lws_malloc(n, "vh paths"); - if (info->ssl_cert_filepath) { - n = (int)strlen(info->ssl_cert_filepath) + 1; - memcpy(vh->tls.alloc_cert_path, - info->ssl_cert_filepath, n); - vh->tls.key_path += n; - } - if (info->ssl_private_key_filepath) - memcpy(vh->tls.key_path, info->ssl_private_key_filepath, - strlen(info->ssl_private_key_filepath) + 1); - } -#endif - - /* - * give the vhost a unified list of protocols including the - * ones that came from plugins - */ - lwsp = lws_zalloc(sizeof(struct lws_protocols) * (vh->count_protocols + - context->plugin_protocol_count + 1), - "vhost-specific plugin table"); - if (!lwsp) { - lwsl_err("OOM\n"); - return NULL; - } - - m = vh->count_protocols; - memcpy(lwsp, pcols, sizeof(struct lws_protocols) * m); - - /* for compatibility, all protocols enabled on vhost if only - * the default vhost exists. Otherwise only vhosts who ask - * for a protocol get it enabled. - */ - - if (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) - f = 0; - (void)f; -#ifdef LWS_WITH_PLUGINS - if (plugin) { - - while (plugin) { - for (n = 0; n < plugin->caps.count_protocols; n++) { - /* - * for compatibility's sake, no pvo implies - * allow all protocols - */ - if (f || lws_vhost_protocol_options(vh, - plugin->caps.protocols[n].name)) { - memcpy(&lwsp[m], - &plugin->caps.protocols[n], - sizeof(struct lws_protocols)); - m++; - vh->count_protocols++; - } - } - plugin = plugin->list; - } - } -#endif - - if ( -#ifdef LWS_WITH_PLUGINS - (context->plugin_list) || -#endif - context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) - vh->protocols = lwsp; - else { - vh->protocols = pcols; - lws_free(lwsp); - } - - vh->same_vh_protocol_heads = (struct lws_dll_lws *) - lws_zalloc(sizeof(struct lws_dll_lws) * - vh->count_protocols, "same vh list"); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - vh->http.mount_list = info->mounts; -#endif - -#ifdef LWS_WITH_UNIX_SOCK - if (LWS_UNIX_SOCK_ENABLED(vh)) { - lwsl_info("Creating Vhost '%s' path \"%s\", %d protocols\n", - vh->name, vh->iface, vh->count_protocols); - } else -#endif - { - switch(info->port) { - case CONTEXT_PORT_NO_LISTEN: - strcpy(buf, "(serving disabled)"); - break; - case CONTEXT_PORT_NO_LISTEN_SERVER: - strcpy(buf, "(no listener)"); - break; - default: - lws_snprintf(buf, sizeof(buf), "port %u", info->port); - break; - } - lwsl_info("Creating Vhost '%s' %s, %d protocols, IPv6 %s\n", - vh->name, buf, vh->count_protocols, - LWS_IPV6_ENABLED(vh) ? "on" : "off"); - } - mounts = info->mounts; - while (mounts) { - (void)mount_protocols[0]; - lwsl_info(" mounting %s%s to %s\n", - mount_protocols[mounts->origin_protocol], - mounts->origin, mounts->mountpoint); - - /* convert interpreter protocol names to pointers */ - pvo = mounts->interpret; - while (pvo) { - for (n = 0; n < vh->count_protocols; n++) { - if (strcmp(pvo->value, vh->protocols[n].name)) - continue; - ((struct lws_protocol_vhost_options *)pvo)-> - value = (const char *)(lws_intptr_t)n; - break; - } - if (n == vh->count_protocols) - lwsl_err("ignoring unknown interp pr %s\n", - pvo->value); - pvo = pvo->next; - } - - mounts = mounts->mount_next; - } - - vh->listen_port = info->port; -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - vh->http.http_proxy_port = 0; - vh->http.http_proxy_address[0] = '\0'; -#endif -#if defined(LWS_WITH_SOCKS5) - vh->socks_proxy_port = 0; - vh->socks_proxy_address[0] = '\0'; -#endif - -#if !defined(LWS_WITHOUT_CLIENT) - /* either use proxy from info, or try get it from env var */ -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - /* http proxy */ - if (info->http_proxy_address) { - /* override for backwards compatibility */ - if (info->http_proxy_port) - vh->http.http_proxy_port = info->http_proxy_port; - lws_set_proxy(vh, info->http_proxy_address); - } else -#endif - { -#ifdef LWS_HAVE_GETENV - p = getenv("http_proxy"); - if (p) - lws_set_proxy(vh, p); -#endif - } -#endif -#if defined(LWS_WITH_SOCKS5) - /* socks proxy */ - if (info->socks_proxy_address) { - /* override for backwards compatibility */ - if (info->socks_proxy_port) - vh->socks_proxy_port = info->socks_proxy_port; - lws_set_socks(vh, info->socks_proxy_address); - } else { -#ifdef LWS_HAVE_GETENV - p = getenv("socks_proxy"); - if (p) - lws_set_socks(vh, p); -#endif - } -#endif - - vh->ka_time = info->ka_time; - vh->ka_interval = info->ka_interval; - vh->ka_probes = info->ka_probes; - - if (vh->options & LWS_SERVER_OPTION_STS) - lwsl_notice(" STS enabled\n"); - -#ifdef LWS_WITH_ACCESS_LOG - if (info->log_filepath) { - vh->log_fd = lws_open(info->log_filepath, - O_CREAT | O_APPEND | O_RDWR, 0600); - if (vh->log_fd == (int)LWS_INVALID_FILE) { - lwsl_err("unable to open log filepath %s\n", - info->log_filepath); - goto bail; - } -#ifndef WIN32 - if (context->uid != -1) - if (chown(info->log_filepath, context->uid, - context->gid) == -1) - lwsl_err("unable to chown log file %s\n", - info->log_filepath); -#endif - } else - vh->log_fd = (int)LWS_INVALID_FILE; -#endif - if (lws_context_init_server_ssl(info, vh)) { - lwsl_err("%s: lws_context_init_server_ssl failed\n", __func__); - goto bail1; - } - if (lws_context_init_client_ssl(info, vh)) { - lwsl_err("%s: lws_context_init_client_ssl failed\n", __func__); - goto bail1; - } - lws_context_lock(context, "create_vhost"); - n = _lws_vhost_init_server(info, vh); - lws_context_unlock(context); - if (n < 0) { - lwsl_err("init server failed\n"); - goto bail1; - } - - while (1) { - if (!(*vh1)) { - *vh1 = vh; - break; - } - vh1 = &(*vh1)->vhost_next; - }; - - /* for the case we are adding a vhost much later, after server init */ - - if (context->protocol_init_done) - if (lws_protocol_init(context)) { - lwsl_err("%s: lws_protocol_init failed\n", __func__); - goto bail1; - } - - return vh; - -bail1: - lws_vhost_destroy(vh); - - return NULL; - -#ifdef LWS_WITH_ACCESS_LOG -bail: - lws_free(vh); -#endif - - return NULL; -} - -LWS_VISIBLE int -lws_init_vhost_client_ssl(const struct lws_context_creation_info *info, - struct lws_vhost *vhost) -{ - struct lws_context_creation_info i; - - memcpy(&i, info, sizeof(i)); - i.port = CONTEXT_PORT_NO_LISTEN; - - return lws_context_init_client_ssl(&i, vhost); -} - -LWS_VISIBLE void -lws_cancel_service_pt(struct lws *wsi) -{ - lws_plat_pipe_signal(wsi); -} - -LWS_VISIBLE void -lws_cancel_service(struct lws_context *context) -{ - struct lws_context_per_thread *pt = &context->pt[0]; - short m = context->count_threads; - - if (context->being_destroyed1) - return; - - lwsl_info("%s\n", __func__); - - while (m--) { - if (pt->pipe_wsi) - lws_plat_pipe_signal(pt->pipe_wsi); - pt++; - } -} - -int -lws_create_event_pipes(struct lws_context *context) -{ - struct lws *wsi; - int n; - - /* - * Create the pt event pipes... these are unique in that they are - * not bound to a vhost or protocol (both are NULL) - */ - - for (n = 0; n < context->count_threads; n++) { - if (context->pt[n].pipe_wsi) - continue; - - wsi = lws_zalloc(sizeof(*wsi), "event pipe wsi"); - if (!wsi) { - lwsl_err("%s: Out of mem\n", __func__); - return 1; - } - wsi->context = context; - lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_pipe); - wsi->protocol = NULL; - wsi->tsi = n; - wsi->vhost = NULL; - wsi->event_pipe = 1; - wsi->desc.sockfd = LWS_SOCK_INVALID; - context->pt[n].pipe_wsi = wsi; - context->count_wsi_allocated++; - - if (lws_plat_pipe_create(wsi)) - /* - * platform code returns 0 if it actually created pipes - * and initialized pt->dummy_pipe_fds[]. If it used - * some other mechanism outside of signaling in the - * normal event loop, we skip treating the pipe as - * related to dummy_pipe_fds[], adding it to the fds, - * etc. - */ - continue; - - wsi->desc.sockfd = context->pt[n].dummy_pipe_fds[0]; - lwsl_debug("event pipe fd %d\n", wsi->desc.sockfd); - - if (context->event_loop_ops->accept) - if (context->event_loop_ops->accept(wsi)) - return 1; - - if (__insert_wsi_socket_into_fds(context, wsi)) - return 1; - } - - return 0; -} - -void -lws_destroy_event_pipe(struct lws *wsi) -{ - lwsl_info("%s\n", __func__); - __remove_wsi_socket_from_fds(wsi); - - if (wsi->context->event_loop_ops->wsi_logical_close) { - wsi->context->event_loop_ops->wsi_logical_close(wsi); - lws_plat_pipe_close(wsi); - wsi->context->count_wsi_allocated--; - return; - } - - if (wsi->context->event_loop_ops->destroy_wsi) - wsi->context->event_loop_ops->destroy_wsi(wsi); - lws_plat_pipe_close(wsi); - wsi->context->count_wsi_allocated--; - lws_free(wsi); -} - LWS_VISIBLE struct lws_context * lws_create_context(const struct lws_context_creation_info *info) { @@ -890,7 +49,9 @@ lws_create_context(const struct lws_context_creation_info *info) #ifndef LWS_NO_DAEMONIZE int pid_daemon = get_daemonize_pid(); #endif +#if defined(LWS_WITH_NETWORK) int n; +#endif #if defined(__ANDROID__) struct rlimit rt; #endif @@ -929,7 +90,7 @@ lws_create_context(const struct lws_context_creation_info *info) return NULL; } -#if defined(LWS_WITH_TLS) +#if defined(LWS_WITH_TLS) && defined(LWS_WITH_NETWORK) #if defined(LWS_WITH_MBEDTLS) context->tls_ops = &tls_ops_mbedtls; #else @@ -1025,6 +186,8 @@ lws_create_context(const struct lws_context_creation_info *info) context->options = info->options; +#if defined(LWS_WITH_NETWORK) + /* * set the context event loops ops struct * @@ -1060,8 +223,9 @@ lws_create_context(const struct lws_context_creation_info *info) goto fail_event_libs; lwsl_info("Using event loop: %s\n", context->event_loop_ops->name); +#endif -#if defined(LWS_WITH_TLS) +#if defined(LWS_WITH_TLS) && defined(LWS_WITH_NETWORK) time(&context->tls.last_cert_check_s); if (info->alpn) context->tls.alpn_default = info->alpn; @@ -1119,6 +283,7 @@ lws_create_context(const struct lws_context_creation_info *info) context->fd_limit_per_thread = context->max_fds / context->count_threads; +#if defined(LWS_WITH_NETWORK) /* * Allocate the per-thread storage for scratchpad buffers, * and header data pool @@ -1188,7 +353,7 @@ lws_create_context(const struct lws_context_creation_info *info) goto bail; } lwsl_info(" mem: pollfd map: %5u\n", n); - +#endif if (info->server_string) { context->server_string = info->server_string; context->server_string_len = (short) @@ -1205,6 +370,7 @@ lws_create_context(const struct lws_context_creation_info *info) if (lws_plat_init(context, info)) goto bail; +#if defined(LWS_WITH_NETWORK) if (context->event_loop_ops->init_context) if (context->event_loop_ops->init_context(context, info)) goto bail; @@ -1223,11 +389,12 @@ lws_create_context(const struct lws_context_creation_info *info) if (lws_create_event_pipes(context)) goto bail; +#endif lws_context_init_ssl_library(info); context->user_space = info->user; - +#if defined(LWS_WITH_NETWORK) /* * if he's not saying he'll make his own vhosts later then act * compatibly and make a default vhost using the data in the info @@ -1250,9 +417,11 @@ lws_create_context(const struct lws_context_creation_info *info) lwsl_info(" mem: per-conn: %5lu bytes + protocol rx buf\n", (unsigned long)sizeof(struct lws)); - +#endif strcpy(context->canonical_hostname, "unknown"); +#if defined(LWS_WITH_NETWORK) lws_server_get_canonical_hostname(context, info); +#endif context->uid = info->uid; context->gid = info->gid; @@ -1270,8 +439,10 @@ lws_create_context(const struct lws_context_creation_info *info) if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) lws_plat_drop_app_privileges(info); +#if defined(LWS_WITH_NETWORK) /* expedite post-context init (eg, protocols) */ lws_cancel_service(context); +#endif return context; @@ -1280,9 +451,11 @@ lws_create_context(const struct lws_context_creation_info *info) return NULL; +#if defined(LWS_WITH_NETWORK) fail_event_libs: lwsl_err("Requested event library support not configured, available:\n"); { + extern const struct lws_event_loop_ops *available_event_libs[]; const struct lws_event_loop_ops **elops = available_event_libs; while (*elops) { @@ -1290,374 +463,18 @@ lws_create_context(const struct lws_context_creation_info *info) elops++; } } +#endif lws_free(context); return NULL; } -LWS_VISIBLE LWS_EXTERN void -lws_context_deprecate(struct lws_context *context, lws_reload_func cb) -{ - struct lws_vhost *vh = context->vhost_list, *vh1; - - /* - * "deprecation" means disable the context from accepting any new - * connections and free up listen sockets to be used by a replacement - * context. - * - * Otherwise the deprecated context remains operational, until its - * number of connected sockets falls to zero, when it is deleted. - */ - - /* for each vhost, close his listen socket */ - - while (vh) { - struct lws *wsi = vh->lserv_wsi; - - if (wsi) { - wsi->socket_is_permanently_unusable = 1; - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "ctx deprecate"); - wsi->context->deprecation_pending_listen_close_count++; - /* - * other vhosts can share the listen port, they - * point to the same wsi. So zap those too. - */ - vh1 = context->vhost_list; - while (vh1) { - if (vh1->lserv_wsi == wsi) - vh1->lserv_wsi = NULL; - vh1 = vh1->vhost_next; - } - } - vh = vh->vhost_next; - } - - context->deprecated = 1; - context->deprecation_cb = cb; -} - LWS_VISIBLE LWS_EXTERN int lws_context_is_deprecated(struct lws_context *context) { return context->deprecated; } -void -lws_vhost_destroy1(struct lws_vhost *vh) -{ - struct lws_context *context = vh->context; - - lwsl_info("%s\n", __func__); - - lws_context_lock(context, "vhost destroy 1"); /* ---------- context { */ - - if (vh->being_destroyed) - goto out; - - lws_vhost_lock(vh); /* -------------- vh { */ - - vh->being_destroyed = 1; - - /* - * PHASE 1: take down or reassign any listen wsi - * - * Are there other vhosts that are piggybacking on our listen socket? - * If so we need to hand the listen socket off to one of the others - * so it will remain open. - * - * If not, leave it attached to the closing vhost, the vh being marked - * being_destroyed will defeat any service and it will get closed in - * later phases. - */ - - if (vh->lserv_wsi) - lws_start_foreach_ll(struct lws_vhost *, v, - context->vhost_list) { - if (v != vh && - !v->being_destroyed && - v->listen_port == vh->listen_port && - ((!v->iface && !vh->iface) || - (v->iface && vh->iface && - !strcmp(v->iface, vh->iface)))) { - /* - * this can only be a listen wsi, which is - * restricted... it has no protocol or other - * bindings or states. So we can simply - * swap it to a vhost that has the same - * iface + port, but is not closing. - */ - assert(v->lserv_wsi == NULL); - v->lserv_wsi = vh->lserv_wsi; - - lwsl_notice("%s: listen skt from %s to %s\n", - __func__, vh->name, v->name); - - if (v->lserv_wsi) { - lws_vhost_unbind_wsi(vh->lserv_wsi); - lws_vhost_bind_wsi(v, v->lserv_wsi); - } - - break; - } - } lws_end_foreach_ll(v, vhost_next); - - lws_vhost_unlock(vh); /* } vh -------------- */ - - /* - * lws_check_deferred_free() will notice there is a vhost that is - * marked for destruction during the next 1s, for all tsi. - * - * It will start closing all wsi on this vhost. When the last wsi - * is closed, it will trigger lws_vhost_destroy2() - */ - -out: - lws_context_unlock(context); /* --------------------------- context { */ -} - -void -__lws_vhost_destroy2(struct lws_vhost *vh) -{ - const struct lws_protocols *protocol = NULL; - struct lws_context *context = vh->context; - struct lws_deferred_free *df; - struct lws wsi; - int n; - - /* - * destroy any pending timed events - */ - - while (vh->timed_vh_protocol_list) - __lws_timed_callback_remove(vh, vh->timed_vh_protocol_list); - - /* - * let the protocols destroy the per-vhost protocol objects - */ - - memset(&wsi, 0, sizeof(wsi)); - wsi.context = vh->context; - wsi.vhost = vh; /* not a real bound wsi */ - protocol = vh->protocols; - if (protocol && vh->created_vhost_protocols) { - n = 0; - while (n < vh->count_protocols) { - wsi.protocol = protocol; - protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY, - NULL, NULL, 0); - protocol++; - n++; - } - } - - /* - * remove vhost from context list of vhosts - */ - - lws_start_foreach_llp(struct lws_vhost **, pv, context->vhost_list) { - if (*pv == vh) { - *pv = vh->vhost_next; - break; - } - } lws_end_foreach_llp(pv, vhost_next); - - /* add ourselves to the pending destruction list */ - - vh->vhost_next = vh->context->vhost_pending_destruction_list; - vh->context->vhost_pending_destruction_list = vh; - - lwsl_info("%s: %p\n", __func__, vh); - - /* if we are still on deferred free list, remove ourselves */ - - lws_start_foreach_llp(struct lws_deferred_free **, pdf, - context->deferred_free_list) { - if ((*pdf)->payload == vh) { - df = *pdf; - *pdf = df->next; - lws_free(df); - break; - } - } lws_end_foreach_llp(pdf, next); - - /* remove ourselves from the pending destruction list */ - - lws_start_foreach_llp(struct lws_vhost **, pv, - context->vhost_pending_destruction_list) { - if ((*pv) == vh) { - *pv = (*pv)->vhost_next; - break; - } - } lws_end_foreach_llp(pv, vhost_next); - - /* - * Free all the allocations associated with the vhost - */ - - protocol = vh->protocols; - if (protocol) { - n = 0; - while (n < vh->count_protocols) { - if (vh->protocol_vh_privs && - vh->protocol_vh_privs[n]) { - lws_free(vh->protocol_vh_privs[n]); - vh->protocol_vh_privs[n] = NULL; - } - protocol++; - n++; - } - } - if (vh->protocol_vh_privs) - lws_free(vh->protocol_vh_privs); - lws_ssl_SSL_CTX_destroy(vh); - lws_free(vh->same_vh_protocol_heads); - - if (context->plugin_list || - (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) - lws_free((void *)vh->protocols); - - LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (ar->destroy_vhost) - ar->destroy_vhost(vh); - LWS_FOR_EVERY_AVAILABLE_ROLE_END; - -#ifdef LWS_WITH_ACCESS_LOG - if (vh->log_fd != (int)LWS_INVALID_FILE) - close(vh->log_fd); -#endif - -#if defined (LWS_WITH_TLS) - lws_free_set_NULL(vh->tls.alloc_cert_path); -#endif - -#if LWS_MAX_SMP > 1 - pthread_mutex_destroy(&vh->lock); -#endif - -#if defined(LWS_WITH_UNIX_SOCK) - if (LWS_UNIX_SOCK_ENABLED(vh)) { - n = unlink(vh->iface); - if (n) - lwsl_info("Closing unix socket %s: errno %d\n", - vh->iface, errno); - } -#endif - /* - * although async event callbacks may still come for wsi handles with - * pending close in the case of asycn event library like libuv, - * they do not refer to the vhost. So it's safe to free. - */ - - if (vh->finalize) - vh->finalize(vh, vh->finalize_arg); - - lwsl_info(" %s: Freeing vhost %p\n", __func__, vh); - - memset(vh, 0, sizeof(*vh)); - lws_free(vh); -} - -/* - * each service thread calls this once a second or so - */ - -int -lws_check_deferred_free(struct lws_context *context, int tsi, int force) -{ - struct lws_context_per_thread *pt; - int n; - - /* - * If we see a vhost is being destroyed, forcibly close every wsi on - * this tsi associated with this vhost. That will include the listen - * socket if it is still associated with the closing vhost. - * - * For SMP, we do this once per tsi per destroyed vhost. The reference - * counting on the vhost as the bound wsi close will notice that there - * are no bound wsi left, that vhost destruction can complete, - * and perform it. It doesn't matter which service thread does that - * because there is nothing left using the vhost to conflict. - */ - - lws_context_lock(context, "check deferred free"); /* ------ context { */ - - lws_start_foreach_ll_safe(struct lws_vhost *, v, context->vhost_list, vhost_next) { - if (v->being_destroyed -#if LWS_MAX_SMP > 1 - && !v->close_flow_vs_tsi[tsi] -#endif - ) { - - pt = &context->pt[tsi]; - - lws_pt_lock(pt, "vhost removal"); /* -------------- pt { */ - -#if LWS_MAX_SMP > 1 - v->close_flow_vs_tsi[tsi] = 1; -#endif - - for (n = 0; (unsigned int)n < pt->fds_count; n++) { - struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd); - if (!wsi) - continue; - if (wsi->vhost != v) - continue; - - __lws_close_free_wsi(wsi, - LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY, - "vh destroy" - /* no protocol close */); - n--; - } - - lws_pt_unlock(pt); /* } pt -------------- */ - } - } lws_end_foreach_ll_safe(v); - - - lws_context_unlock(context); /* } context ------------------- */ - - return 0; -} - -LWS_VISIBLE void -lws_vhost_destroy(struct lws_vhost *vh) -{ - struct lws_deferred_free *df = lws_malloc(sizeof(*df), "deferred free"); - struct lws_context *context = vh->context; - - if (!df) - return; - - lws_context_lock(context, __func__); /* ------ context { */ - - lws_vhost_destroy1(vh); - - if (!vh->count_bound_wsi) { - /* - * After listen handoff, there are already no wsi bound to this - * vhost by any pt: nothing can be servicing any wsi belonging - * to it any more. - * - * Finalize the vh destruction immediately - */ - __lws_vhost_destroy2(vh); - lws_free(df); - - goto out; - } - - /* part 2 is deferred to allow all the handle closes to complete */ - - df->next = vh->context->deferred_free_list; - df->deadline = lws_now_secs(); - df->payload = vh; - vh->context->deferred_free_list = df; - -out: - lws_context_unlock(context); /* } context ------------------- */ -} - /* * When using an event loop, the context destruction is in three separate * parts. This is to cover both internal and foreign event loops cleanly. @@ -1689,6 +506,7 @@ static void lws_context_destroy3(struct lws_context *context) { struct lws_context **pcontext_finalize = context->pcontext_finalize; +#if defined(LWS_WITH_NETWORK) int n; for (n = 0; n < context->count_threads; n++) { @@ -1709,7 +527,7 @@ lws_context_destroy3(struct lws_context *context) if (context->pt[0].fds) lws_free_set_NULL(context->pt[0].fds); - +#endif lws_free(context); lwsl_info("%s: ctx %p freed\n", __func__, context); @@ -1724,7 +542,9 @@ lws_context_destroy3(struct lws_context *context) void lws_context_destroy2(struct lws_context *context) { +#if defined(LWS_WITH_NETWORK) struct lws_vhost *vh = NULL, *vh1; +#endif #if defined(LWS_WITH_PEER_LIMITS) uint32_t nu; #endif @@ -1734,7 +554,7 @@ lws_context_destroy2(struct lws_context *context) lws_context_lock(context, "context destroy 2"); /* ------ context { */ context->being_destroyed2 = 1; - +#if defined(LWS_WITH_NETWORK) /* * free all the per-vhost allocations */ @@ -1751,11 +571,12 @@ lws_context_destroy2(struct lws_context *context) while (context->vhost_pending_destruction_list) /* removes itself from list */ __lws_vhost_destroy2(context->vhost_pending_destruction_list); - +#endif lws_stats_log_dump(context); - +#if defined(LWS_WITH_NETWORK) lws_ssl_context_destroy(context); +#endif lws_plat_context_late_destroy(context); #if defined(LWS_WITH_PEER_LIMITS) @@ -1774,12 +595,14 @@ lws_context_destroy2(struct lws_context *context) if (context->external_baggage_free_on_destroy) free(context->external_baggage_free_on_destroy); +#if defined(LWS_WITH_NETWORK) lws_check_deferred_free(context, 0, 1); +#endif #if LWS_MAX_SMP > 1 lws_mutex_refcount_destroy(&context->mr); #endif - +#if defined(LWS_WITH_NETWORK) if (context->event_loop_ops->destroy_context2) if (context->event_loop_ops->destroy_context2(context)) { lws_context_unlock(context); /* } context ----------- */ @@ -1795,7 +618,7 @@ lws_context_destroy2(struct lws_context *context) return; } } - +#endif lws_context_unlock(context); /* } context ------------------- */ lws_context_destroy3(context); @@ -1808,24 +631,25 @@ lws_context_destroy2(struct lws_context *context) LWS_VISIBLE void lws_context_destroy(struct lws_context *context) { +#if defined(LWS_WITH_NETWORK) volatile struct lws_foreign_thread_pollfd *ftp, *next; volatile struct lws_context_per_thread *vpt; struct lws_vhost *vh = NULL; struct lws wsi; int n, m; +#endif if (!context) return; - +#if defined(LWS_WITH_NETWORK) if (context->finalize_destroy_after_internal_loops_stopped) { if (context->event_loop_ops->destroy_context2) context->event_loop_ops->destroy_context2(context); - lws_context_destroy3(context); return; } - +#endif if (context->being_destroyed1) { if (!context->being_destroyed2) { lws_context_destroy2(context); @@ -1841,11 +665,12 @@ lws_context_destroy(struct lws_context *context) lwsl_info("%s: ctx %p\n", __func__, context); - m = context->count_threads; context->being_destroyed = 1; context->being_destroyed1 = 1; context->requested_kill = 1; +#if defined(LWS_WITH_NETWORK) + m = context->count_threads; memset(&wsi, 0, sizeof(wsi)); wsi.context = context; @@ -1896,9 +721,12 @@ lws_context_destroy(struct lws_context *context) lws_vhost_destroy1(vh); vh = vhn; } +#endif lws_plat_context_early_destroy(context); +#if defined(LWS_WITH_NETWORK) + /* * We face two different needs depending if foreign loop or not. * @@ -1921,6 +749,7 @@ lws_context_destroy(struct lws_context *context) return; } +#endif lws_context_destroy2(context); } diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c index da8f9bbcef..b18fb36467 100644 --- a/lib/core/libwebsockets.c +++ b/lib/core/libwebsockets.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010-2017 Andy Green + * Copyright (C) 2010-2019 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -34,46 +34,6 @@ #endif #endif -int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE; -static void (*lwsl_emit)(int level, const char *line) -#ifndef LWS_PLAT_OPTEE - = lwsl_emit_stderr -#endif - ; -#ifndef LWS_PLAT_OPTEE -static const char * const log_level_names[] = { - "ERR", - "WARN", - "NOTICE", - "INFO", - "DEBUG", - "PARSER", - "HEADER", - "EXTENSION", - "CLIENT", - "LATENCY", - "USER", - "THREAD", - "?", - "?" -}; -#endif - -#if defined (_DEBUG) -void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role) -{ - wsi->wsistate = (wsi->wsistate & (~LWSI_ROLE_MASK)) | role; - - lwsl_debug("lwsi_set_role(%p, 0x%x)\n", wsi, wsi->wsistate); -} - -void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs) -{ - wsi->wsistate = (wsi->wsistate & (~LRS_MASK)) | lrs; - - lwsl_debug("lwsi_set_state(%p, 0x%x)\n", wsi, wsi->wsistate); -} -#endif signed char char_to_hex(const char c) { @@ -115,108 +75,7 @@ int lws_open(const char *__file, int __oflag, ...) return n; } -void -lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi) -{ - if (wsi->vhost == vh) - return; - lws_context_lock(vh->context, __func__); /* ---------- context { */ - wsi->vhost = vh; - vh->count_bound_wsi++; - lws_context_unlock(vh->context); /* } context ---------- */ - lwsl_info("%s: vh %s: count_bound_wsi %d\n", - __func__, vh->name, vh->count_bound_wsi); - assert(wsi->vhost->count_bound_wsi > 0); -} - -void -lws_vhost_unbind_wsi(struct lws *wsi) -{ - if (!wsi->vhost) - return; - - lws_context_lock(wsi->context, __func__); /* ---------- context { */ - - assert(wsi->vhost->count_bound_wsi > 0); - wsi->vhost->count_bound_wsi--; - lwsl_info("%s: vh %s: count_bound_wsi %d\n", __func__, - wsi->vhost->name, wsi->vhost->count_bound_wsi); - - if (!wsi->vhost->count_bound_wsi && - wsi->vhost->being_destroyed) { - /* - * We have closed all wsi that were bound to this vhost - * by any pt: nothing can be servicing any wsi belonging - * to it any more. - * - * Finalize the vh destruction - */ - __lws_vhost_destroy2(wsi->vhost); - } - wsi->vhost = NULL; - - lws_context_unlock(wsi->context); /* } context ---------- */ -} - -void -__lws_free_wsi(struct lws *wsi) -{ - if (!wsi) - return; - - /* - * Protocol user data may be allocated either internally by lws - * or by specified the user. We should only free what we allocated. - */ - if (wsi->protocol && wsi->protocol->per_session_data_size && - wsi->user_space && !wsi->user_space_externally_allocated) - lws_free(wsi->user_space); - - lws_buflist_destroy_all_segments(&wsi->buflist); - lws_buflist_destroy_all_segments(&wsi->buflist_out); - lws_free_set_NULL(wsi->udp); - - if (wsi->vhost && wsi->vhost->lserv_wsi == wsi) - wsi->vhost->lserv_wsi = NULL; -#if !defined(LWS_NO_CLIENT) - lws_dll_lws_remove(&wsi->dll_active_client_conns); -#endif - wsi->context->count_wsi_allocated--; - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - __lws_header_table_detach(wsi, 0); -#endif - __lws_same_vh_protocol_remove(wsi); -#if !defined(LWS_NO_CLIENT) - lws_client_stash_destroy(wsi); - lws_free_set_NULL(wsi->client_hostname_copy); -#endif - - if (wsi->role_ops->destroy_role) - wsi->role_ops->destroy_role(wsi); - -#if defined(LWS_WITH_PEER_LIMITS) - lws_peer_track_wsi_close(wsi->context, wsi->peer); - wsi->peer = NULL; -#endif - - /* since we will destroy the wsi, make absolutely sure now */ - -#if defined(LWS_WITH_OPENSSL) - __lws_ssl_remove_wsi_from_buffered_list(wsi); -#endif - __lws_remove_from_timeout_list(wsi); - - if (wsi->context->event_loop_ops->destroy_wsi) - wsi->context->event_loop_ops->destroy_wsi(wsi); - - lws_vhost_unbind_wsi(wsi); - lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi, - wsi->context->count_wsi_allocated); - - lws_free(wsi); -} void lws_dll_add_front(struct lws_dll *d, struct lws_dll *phead) @@ -288,105 +147,6 @@ lws_dll_remove(struct lws_dll *d) d->next = NULL; } -void -__lws_remove_from_timeout_list(struct lws *wsi) -{ - lws_dll_lws_remove(&wsi->dll_timeout); -} - -void -lws_remove_from_timeout_list(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - lws_pt_lock(pt, __func__); - __lws_remove_from_timeout_list(wsi); - lws_pt_unlock(pt); -} - -void -lws_dll_dump(struct lws_dll_lws *head, const char *title) -{ - int n = 0; - - (void)n; - lwsl_notice("%s: %s (head.next %p)\n", __func__, title, head->next); - - lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, head->next) { - struct lws *wsi = lws_container_of(d, struct lws, dll_hrtimer); - - (void)wsi; - - lwsl_notice(" %d: wsi %p: %llu\n", n++, wsi, - (unsigned long long)wsi->pending_timer); - } lws_end_foreach_dll_safe(d, d1); -} - -void -__lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - struct lws_dll_lws *dd = &pt->dll_head_hrtimer; - struct timeval now; - struct lws *wsi1; - int bef = 0; - - lws_dll_lws_remove(&wsi->dll_hrtimer); - - if (usecs == LWS_SET_TIMER_USEC_CANCEL) - return; - - gettimeofday(&now, NULL); - wsi->pending_timer = ((now.tv_sec * 1000000ll) + now.tv_usec) + usecs; - - /* - * we sort the hrtimer list with the earliest timeout first - */ - - lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, - pt->dll_head_hrtimer.next) { - dd = d; - wsi1 = lws_container_of(d, struct lws, dll_hrtimer); - - if (wsi1->pending_timer >= wsi->pending_timer) { - /* d, dprev's next, is >= our time */ - bef = 1; - break; - } - } lws_end_foreach_dll_safe(d, d1); - - if (bef) { - /* - * we go before dd - * DDp <-> DD <-> DDn --> DDp <-> us <-> DD <-> DDn - */ - /* we point forward to dd */ - wsi->dll_hrtimer.next = dd; - /* we point back to what dd used to point back to */ - wsi->dll_hrtimer.prev = dd->prev; - /* DDp points forward to us now */ - dd->prev->next = &wsi->dll_hrtimer; - /* DD points back to us now */ - dd->prev = &wsi->dll_hrtimer; - } else { - /* - * we go after dd - * DDp <-> DD <-> DDn --> DDp <-> DD <-> us <-> DDn - */ - /* we point forward to what dd used to point forward to */ - wsi->dll_hrtimer.next = dd->next; - /* we point back to dd */ - wsi->dll_hrtimer.prev = dd; - /* DDn points back to us */ - if (dd->next) - dd->next->prev = &wsi->dll_hrtimer; - /* DD points forward to us */ - dd->next = &wsi->dll_hrtimer; - } - -// lws_dll_dump(&pt->dll_head_hrtimer, "after set_timer_usec"); -} - LWS_VISIBLE lws_usec_t lws_now_usecs(void) { @@ -396,119 +156,6 @@ lws_now_usecs(void) return (now.tv_sec * 1000000ll) + now.tv_usec; } -LWS_VISIBLE void -lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs) -{ - __lws_set_timer_usecs(wsi, usecs); -} - -lws_usec_t -__lws_hrtimer_service(struct lws_context_per_thread *pt) -{ - struct timeval now; - struct lws *wsi; - lws_usec_t t; - - gettimeofday(&now, NULL); - t = (now.tv_sec * 1000000ll) + now.tv_usec; - - lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, - pt->dll_head_hrtimer.next) { - wsi = lws_container_of(d, struct lws, dll_hrtimer); - - /* - * if we met one in the future, we are done, because the list - * is sorted by time in the future. - */ - if (wsi->pending_timer > t) - break; - - lws_set_timer_usecs(wsi, LWS_SET_TIMER_USEC_CANCEL); - - /* it's time for the timer to be serviced */ - - if (wsi->protocol && - wsi->protocol->callback(wsi, LWS_CALLBACK_TIMER, - wsi->user_space, NULL, 0)) - __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "timer cb errored"); - } lws_end_foreach_dll_safe(d, d1); - - /* return an estimate how many us until next timer hit */ - - if (!pt->dll_head_hrtimer.next) - return LWS_HRTIMER_NOWAIT; - - wsi = lws_container_of(pt->dll_head_hrtimer.next, struct lws, - dll_hrtimer); - - gettimeofday(&now, NULL); - t = (now.tv_sec * 1000000ll) + now.tv_usec; - - if (wsi->pending_timer < t) - return 0; - - return wsi->pending_timer - t; -} - -void -__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - time_t now; - - time(&now); - - lwsl_debug("%s: %p: %d secs (reason %d)\n", __func__, wsi, secs, reason); - wsi->pending_timeout_limit = secs; - wsi->pending_timeout_set = now; - wsi->pending_timeout = reason; - - if (!reason) - lws_dll_lws_remove(&wsi->dll_timeout); - else - lws_dll_lws_add_front(&wsi->dll_timeout, &pt->dll_head_timeout); -} - -LWS_VISIBLE void -lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - if (secs == LWS_TO_KILL_SYNC) { - lws_remove_from_timeout_list(wsi); - lwsl_debug("synchronously killing %p\n", wsi); - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "to sync kill"); - return; - } - - if (secs == LWS_TO_KILL_ASYNC) - secs = 0; - - lws_pt_lock(pt, __func__); - __lws_set_timeout(wsi, reason, secs); - lws_pt_unlock(pt); -} - -/* requires context + vh lock */ - -int -__lws_timed_callback_remove(struct lws_vhost *vh, struct lws_timed_vh_protocol *p) -{ - lws_start_foreach_llp(struct lws_timed_vh_protocol **, pt, - vh->timed_vh_protocol_list) { - if (*pt == p) { - *pt = p->next; - lws_free(p); - - return 0; - } - } lws_end_foreach_llp(pt, next); - - return 1; -} - int lws_pthread_self_to_tsi(struct lws_context *context) { @@ -529,653 +176,135 @@ lws_pthread_self_to_tsi(struct lws_context *context) #endif } -LWS_VISIBLE LWS_EXTERN int -lws_timed_callback_vh_protocol(struct lws_vhost *vh, - const struct lws_protocols *prot, int reason, - int secs) + +/* lws_buflist */ + +int +lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf, + size_t len) { - struct lws_timed_vh_protocol *p = (struct lws_timed_vh_protocol *) - lws_malloc(sizeof(*p), "timed_vh"); + struct lws_buflist *nbuf; + int first = !*head; + void *p = *head; + int sanity = 1024; - if (!p) - return 1; + assert(buf); + assert(len); + + /* append at the tail */ + while (*head) { + if (!--sanity) { + lwsl_err("%s: buflist reached sanity limit\n", __func__); + return -1; + } + if (*head == (*head)->next) { + lwsl_err("%s: corrupt list points to self\n", __func__); + return -1; + } + head = &((*head)->next); + } - p->tsi_req = lws_pthread_self_to_tsi(vh->context); - if (p->tsi_req < 0) /* not called from a service thread --> tsi 0 */ - p->tsi_req = 0; + lwsl_info("%s: len %u first %d %p\n", __func__, (uint32_t)len, first, p); - lws_context_lock(vh->context, __func__); /* context ----------------- */ + nbuf = (struct lws_buflist *)lws_malloc(sizeof(**head) + len, __func__); + if (!nbuf) { + lwsl_err("%s: OOM\n", __func__); + return -1; + } - p->protocol = prot; - p->reason = reason; - p->time = lws_now_secs() + secs; + nbuf->len = len; + nbuf->pos = 0; + nbuf->next = NULL; - lws_vhost_lock(vh); /* vhost ---------------------------------------- */ - p->next = vh->timed_vh_protocol_list; - vh->timed_vh_protocol_list = p; - lws_vhost_unlock(vh); /* -------------------------------------- vhost */ + p = (void *)nbuf->buf; + memcpy(p, buf, len); - lws_context_unlock(vh->context); /* ------------------------- context */ + *head = nbuf; - return 0; + return first; /* returns 1 if first segment just created */ } -void -lws_remove_child_from_any_parent(struct lws *wsi) +static int +lws_buflist_destroy_segment(struct lws_buflist **head) { - struct lws **pwsi; - int seen = 0; + struct lws_buflist *old = *head; - if (!wsi->parent) - return; + assert(*head); + *head = old->next; + old->next = NULL; + lws_free(old); - /* detach ourselves from parent's child list */ - pwsi = &wsi->parent->child_list; - while (*pwsi) { - if (*pwsi == wsi) { - lwsl_info("%s: detach %p from parent %p\n", __func__, - wsi, wsi->parent); + return !*head; /* returns 1 if last segment just destroyed */ +} - if (wsi->parent->protocol) - wsi->parent->protocol->callback(wsi, - LWS_CALLBACK_CHILD_CLOSING, - wsi->parent->user_space, wsi, 0); +void +lws_buflist_destroy_all_segments(struct lws_buflist **head) +{ + struct lws_buflist *p = *head, *p1; - *pwsi = wsi->sibling_list; - seen = 1; - break; - } - pwsi = &(*pwsi)->sibling_list; + while (p) { + p1 = p->next; + p->next = NULL; + lws_free(p); + p = p1; } - if (!seen) - lwsl_err("%s: failed to detach from parent\n", __func__); - wsi->parent = NULL; + *head = NULL; } -int -lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p, - const char *reason) +size_t +lws_buflist_next_segment_len(struct lws_buflist **head, uint8_t **buf) { -// if (wsi->protocol == p) -// return 0; - const struct lws_protocols *vp = wsi->vhost->protocols, *vpo; - - if (wsi->protocol && wsi->protocol_bind_balance) { - wsi->protocol->callback(wsi, - wsi->role_ops->protocol_unbind_cb[!!lwsi_role_server(wsi)], - wsi->user_space, (void *)reason, 0); - wsi->protocol_bind_balance = 0; - } - if (!wsi->user_space_externally_allocated) - lws_free_set_NULL(wsi->user_space); - - lws_same_vh_protocol_remove(wsi); + if (!*head) { + if (buf) + *buf = NULL; - wsi->protocol = p; - if (!p) return 0; + } - if (lws_ensure_user_space(wsi)) - return 1; - - if (p > vp && p < &vp[wsi->vhost->count_protocols]) - lws_same_vh_protocol_insert(wsi, (int)(p - vp)); - else { - int n = wsi->vhost->count_protocols; - int hit = 0; + if (!(*head)->len && (*head)->next) + lws_buflist_destroy_segment(head); - vpo = vp; + if (!*head) { + if (buf) + *buf = NULL; - while (n--) { - if (p->name && vp->name && !strcmp(p->name, vp->name)) { - hit = 1; - lws_same_vh_protocol_insert(wsi, (int)(vp - vpo)); - break; - } - vp++; - } - if (!hit) - lwsl_err("%s: %p is not in vhost '%s' protocols list\n", - __func__, p, wsi->vhost->name); + return 0; } - if (wsi->protocol->callback(wsi, wsi->role_ops->protocol_bind_cb[ - !!lwsi_role_server(wsi)], - wsi->user_space, NULL, 0)) - return 1; + assert((*head)->pos < (*head)->len); - wsi->protocol_bind_balance = 1; + if (buf) + *buf = (*head)->buf + (*head)->pos; - return 0; + return (*head)->len - (*head)->pos; } -void -__lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, - const char *caller) +int +lws_buflist_use_segment(struct lws_buflist **head, size_t len) { - struct lws_context_per_thread *pt; - struct lws *wsi1, *wsi2; - struct lws_context *context; - int n; - - lwsl_info("%s: %p: caller: %s\n", __func__, wsi, caller); + assert(*head); + assert(len); + assert((*head)->pos + len <= (*head)->len); - if (!wsi) - return; + (*head)->pos += len; + if ((*head)->pos == (*head)->len) + lws_buflist_destroy_segment(head); - lws_access_log(wsi); + if (!*head) + return 0; - context = wsi->context; - pt = &context->pt[(int)wsi->tsi]; - lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_CLOSE, 1); + return (int)((*head)->len - (*head)->pos); +} -#if !defined(LWS_NO_CLIENT) +void +lws_buflist_describe(struct lws_buflist **head, void *id) +{ + struct lws_buflist *old; + int n = 0; - lws_free_set_NULL(wsi->client_hostname_copy); - /* we are no longer an active client connection that can piggyback */ - lws_dll_lws_remove(&wsi->dll_active_client_conns); - - /* - * if we have wsi in our transaction queue, if we are closing we - * must go through and close all those first - */ - if (wsi->vhost) { - if ((int)reason != -1) - lws_vhost_lock(wsi->vhost); - lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, - wsi->dll_client_transaction_queue_head.next) { - struct lws *w = lws_container_of(d, struct lws, - dll_client_transaction_queue); - - __lws_close_free_wsi(w, -1, "trans q leader closing"); - } lws_end_foreach_dll_safe(d, d1); - - /* - * !!! If we are closing, but we have pending pipelined - * transaction results we already sent headers for, that's going - * to destroy sync for HTTP/1 and leave H2 stream with no live - * swsi. - * - * However this is normal if we are being closed because the - * transaction queue leader is closing. - */ - lws_dll_lws_remove(&wsi->dll_client_transaction_queue); - if ((int)reason !=-1) - lws_vhost_unlock(wsi->vhost); - } -#endif - - /* if we have children, close them first */ - if (wsi->child_list) { - wsi2 = wsi->child_list; - while (wsi2) { - wsi1 = wsi2->sibling_list; - wsi2->parent = NULL; - /* stop it doing shutdown processing */ - wsi2->socket_is_permanently_unusable = 1; - __lws_close_free_wsi(wsi2, reason, - "general child recurse"); - wsi2 = wsi1; - } - wsi->child_list = NULL; - } - - if (wsi->role_ops == &role_ops_raw_file) { - lws_remove_child_from_any_parent(wsi); - __remove_wsi_socket_from_fds(wsi); - wsi->protocol->callback(wsi, wsi->role_ops->close_cb[0], - wsi->user_space, NULL, 0); - goto async_close; - } - - wsi->wsistate_pre_close = wsi->wsistate; - -#ifdef LWS_WITH_CGI - if (wsi->role_ops == &role_ops_cgi) { - /* we are not a network connection, but a handler for CGI io */ - if (wsi->parent && wsi->parent->http.cgi) { - - if (wsi->cgi_channel == LWS_STDOUT) - lws_cgi_remove_and_kill(wsi->parent); - - /* end the binding between us and master */ - wsi->parent->http.cgi->stdwsi[(int)wsi->cgi_channel] = - NULL; - } - wsi->socket_is_permanently_unusable = 1; - - goto just_kill_connection; - } - - if (wsi->http.cgi) - lws_cgi_remove_and_kill(wsi); -#endif - -#if !defined(LWS_NO_CLIENT) - lws_client_stash_destroy(wsi); -#endif - - if (wsi->role_ops == &role_ops_raw_skt) { - wsi->socket_is_permanently_unusable = 1; - goto just_kill_connection; - } -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - if (lwsi_role_http(wsi) && lwsi_role_server(wsi) && - wsi->http.fop_fd != NULL) - lws_vfs_file_close(&wsi->http.fop_fd); -#endif - - if (lwsi_state(wsi) == LRS_DEAD_SOCKET) - return; - - if (wsi->socket_is_permanently_unusable || - reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY || - lwsi_state(wsi) == LRS_SHUTDOWN) - goto just_kill_connection; - - switch (lwsi_state_PRE_CLOSE(wsi)) { - case LRS_DEAD_SOCKET: - return; - - /* we tried the polite way... */ - case LRS_WAITING_TO_SEND_CLOSE: - case LRS_AWAITING_CLOSE_ACK: - case LRS_RETURNED_CLOSE: - goto just_kill_connection; - - case LRS_FLUSHING_BEFORE_CLOSE: - if (lws_has_buffered_out(wsi) -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - || wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more -#endif - ) { - lws_callback_on_writable(wsi); - return; - } - lwsl_info("%p: end LRS_FLUSHING_BEFORE_CLOSE\n", wsi); - goto just_kill_connection; - default: - if (lws_has_buffered_out(wsi) -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - || wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more -#endif - ) { - lwsl_info("%p: LRS_FLUSHING_BEFORE_CLOSE\n", wsi); - lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE); - __lws_set_timeout(wsi, - PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5); - return; - } - break; - } - - if (lwsi_state(wsi) == LRS_WAITING_CONNECT || - lwsi_state(wsi) == LRS_H1C_ISSUE_HANDSHAKE) - goto just_kill_connection; - - if (!wsi->told_user_closed && wsi->user_space && wsi->protocol && - wsi->protocol_bind_balance) { - wsi->protocol->callback(wsi, - wsi->role_ops->protocol_unbind_cb[ - !!lwsi_role_server(wsi)], - wsi->user_space, (void *)__func__, 0); - wsi->protocol_bind_balance = 0; - } - - /* - * signal we are closing, lws_write will - * add any necessary version-specific stuff. If the write fails, - * no worries we are closing anyway. If we didn't initiate this - * close, then our state has been changed to - * LRS_RETURNED_CLOSE and we will skip this. - * - * Likewise if it's a second call to close this connection after we - * sent the close indication to the peer already, we are in state - * LRS_AWAITING_CLOSE_ACK and will skip doing this a second time. - */ - - if (wsi->role_ops->close_via_role_protocol && - wsi->role_ops->close_via_role_protocol(wsi, reason)) - return; - -just_kill_connection: - - if (wsi->role_ops->close_kill_connection) - wsi->role_ops->close_kill_connection(wsi, reason); - - lws_remove_child_from_any_parent(wsi); - n = 0; - - if (!wsi->told_user_closed && wsi->user_space && - wsi->protocol_bind_balance) { - lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi, - wsi->protocol->name); - wsi->protocol->callback(wsi, - wsi->role_ops->protocol_unbind_cb[ - !!lwsi_role_server(wsi)], - wsi->user_space, (void *)__func__, 0); - wsi->protocol_bind_balance = 0; - } - - if ((lwsi_state(wsi) == LRS_WAITING_SERVER_REPLY || - lwsi_state(wsi) == LRS_WAITING_CONNECT) && !wsi->already_did_cce) - wsi->protocol->callback(wsi, - LWS_CALLBACK_CLIENT_CONNECTION_ERROR, - wsi->user_space, NULL, 0); - - /* - * Testing with ab shows that we have to stage the socket close when - * the system is under stress... shutdown any further TX, change the - * state to one that won't emit anything more, and wait with a timeout - * for the POLLIN to show a zero-size rx before coming back and doing - * the actual close. - */ - if (wsi->role_ops != &role_ops_raw_skt && !lwsi_role_client(wsi) && - lwsi_state(wsi) != LRS_SHUTDOWN && - lwsi_state(wsi) != LRS_UNCONNECTED && - reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY && - !wsi->socket_is_permanently_unusable) { - -#if defined(LWS_WITH_TLS) - if (lws_is_ssl(wsi) && wsi->tls.ssl) { - n = 0; - switch (__lws_tls_shutdown(wsi)) { - case LWS_SSL_CAPABLE_DONE: - case LWS_SSL_CAPABLE_ERROR: - case LWS_SSL_CAPABLE_MORE_SERVICE_READ: - case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE: - case LWS_SSL_CAPABLE_MORE_SERVICE: - break; - } - } else -#endif - { - lwsl_info("%s: shutdown conn: %p (sk %d, state 0x%x)\n", - __func__, wsi, (int)(long)wsi->desc.sockfd, - lwsi_state(wsi)); - if (!wsi->socket_is_permanently_unusable && - lws_socket_is_valid(wsi->desc.sockfd)) { - wsi->socket_is_permanently_unusable = 1; - n = shutdown(wsi->desc.sockfd, SHUT_WR); - } - } - if (n) - lwsl_debug("closing: shutdown (state 0x%x) ret %d\n", - lwsi_state(wsi), LWS_ERRNO); - - /* - * This causes problems on WINCE / ESP32 with disconnection - * when the events are half closing connection - */ -#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP32) - /* libuv: no event available to guarantee completion */ - if (!wsi->socket_is_permanently_unusable && - lws_socket_is_valid(wsi->desc.sockfd) && - lwsi_state(wsi) != LRS_SHUTDOWN && - context->event_loop_ops->periodic_events_available) { - __lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN); - lwsi_set_state(wsi, LRS_SHUTDOWN); - __lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH, - context->timeout_secs); - - return; - } -#endif - } - - lwsl_debug("%s: real just_kill_connection: %p (sockfd %d)\n", __func__, - wsi, wsi->desc.sockfd); - -#ifdef LWS_WITH_HUBBUB - if (wsi->http.rw) { - lws_rewrite_destroy(wsi->http.rw); - wsi->http.rw = NULL; - } -#endif - - if (wsi->http.pending_return_headers) - lws_free_set_NULL(wsi->http.pending_return_headers); - - /* - * we won't be servicing or receiving anything further from this guy - * delete socket from the internal poll list if still present - */ - __lws_ssl_remove_wsi_from_buffered_list(wsi); - __lws_remove_from_timeout_list(wsi); - lws_dll_lws_remove(&wsi->dll_hrtimer); - - /* don't repeat event loop stuff */ - if (wsi->told_event_loop_closed) - return; - - /* checking return redundant since we anyway close */ - if (wsi->desc.sockfd != LWS_SOCK_INVALID) - __remove_wsi_socket_from_fds(wsi); - else - __lws_same_vh_protocol_remove(wsi); - - lwsi_set_state(wsi, LRS_DEAD_SOCKET); - lws_buflist_destroy_all_segments(&wsi->buflist); - lws_dll_lws_remove(&wsi->dll_buflist); - - if (wsi->role_ops->close_role) - wsi->role_ops->close_role(pt, wsi); - - /* tell the user it's all over for this guy */ - - if ((lwsi_state_est_PRE_CLOSE(wsi) || - lwsi_state_PRE_CLOSE(wsi) == LRS_WAITING_SERVER_REPLY) && - !wsi->told_user_closed && - wsi->role_ops->close_cb[lwsi_role_server(wsi)]) { - const struct lws_protocols *pro = wsi->protocol; - - if (!wsi->protocol) - pro = &wsi->vhost->protocols[0]; - - if (!wsi->upgraded_to_http2 || !lwsi_role_client(wsi)) - /* - * The network wsi for a client h2 connection shouldn't - * call back for its role: the child stream connections - * own the role. Otherwise h2 will call back closed - * one too many times as the children do it and then - * the closing network stream. - */ - pro->callback(wsi, - wsi->role_ops->close_cb[lwsi_role_server(wsi)], - wsi->user_space, NULL, 0); - wsi->told_user_closed = 1; - } - -async_close: - wsi->socket_is_permanently_unusable = 1; - - if (wsi->context->event_loop_ops->wsi_logical_close) - if (wsi->context->event_loop_ops->wsi_logical_close(wsi)) - return; - - __lws_close_free_wsi_final(wsi); -} - -void -__lws_close_free_wsi_final(struct lws *wsi) -{ - int n; - - if (!wsi->shadow && - lws_socket_is_valid(wsi->desc.sockfd) && !lws_ssl_close(wsi)) { - lwsl_debug("%s: wsi %p: fd %d\n", __func__, wsi, wsi->desc.sockfd); - n = compatible_close(wsi->desc.sockfd); - if (n) - lwsl_debug("closing: close ret %d\n", LWS_ERRNO); - - wsi->desc.sockfd = LWS_SOCK_INVALID; - } - - /* outermost destroy notification for wsi (user_space still intact) */ - if (wsi->vhost) - wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, - wsi->user_space, NULL, 0); - -#ifdef LWS_WITH_CGI - if (wsi->http.cgi) { - - for (n = 0; n < 3; n++) { - if (wsi->http.cgi->pipe_fds[n][!!(n == 0)] == 0) - lwsl_err("ZERO FD IN CGI CLOSE"); - - if (wsi->http.cgi->pipe_fds[n][!!(n == 0)] >= 0) - close(wsi->http.cgi->pipe_fds[n][!!(n == 0)]); - } - - lws_free(wsi->http.cgi); - } -#endif - - __lws_free_wsi(wsi); -} - - -void -lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *caller) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - lws_pt_lock(pt, __func__); - __lws_close_free_wsi(wsi, reason, caller); - lws_pt_unlock(pt); -} - -/* lws_buflist */ - -int -lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf, - size_t len) -{ - struct lws_buflist *nbuf; - int first = !*head; - void *p = *head; - int sanity = 1024; - - assert(buf); - assert(len); - - /* append at the tail */ - while (*head) { - if (!--sanity) { - lwsl_err("%s: buflist reached sanity limit\n", __func__); - return -1; - } - if (*head == (*head)->next) { - lwsl_err("%s: corrupt list points to self\n", __func__); - return -1; - } - head = &((*head)->next); - } - - lwsl_info("%s: len %u first %d %p\n", __func__, (uint32_t)len, first, p); - - nbuf = (struct lws_buflist *)lws_malloc(sizeof(**head) + len, __func__); - if (!nbuf) { - lwsl_err("%s: OOM\n", __func__); - return -1; - } - - nbuf->len = len; - nbuf->pos = 0; - nbuf->next = NULL; - - p = (void *)nbuf->buf; - memcpy(p, buf, len); - - *head = nbuf; - - return first; /* returns 1 if first segment just created */ -} - -static int -lws_buflist_destroy_segment(struct lws_buflist **head) -{ - struct lws_buflist *old = *head; - - assert(*head); - *head = old->next; - old->next = NULL; - lws_free(old); - - return !*head; /* returns 1 if last segment just destroyed */ -} - -void -lws_buflist_destroy_all_segments(struct lws_buflist **head) -{ - struct lws_buflist *p = *head, *p1; - - while (p) { - p1 = p->next; - p->next = NULL; - lws_free(p); - p = p1; - } - - *head = NULL; -} - -size_t -lws_buflist_next_segment_len(struct lws_buflist **head, uint8_t **buf) -{ - if (!*head) { - if (buf) - *buf = NULL; - - return 0; - } - - if (!(*head)->len && (*head)->next) - lws_buflist_destroy_segment(head); - - if (!*head) { - if (buf) - *buf = NULL; - - return 0; - } - - assert((*head)->pos < (*head)->len); - - if (buf) - *buf = (*head)->buf + (*head)->pos; - - return (*head)->len - (*head)->pos; -} - -int -lws_buflist_use_segment(struct lws_buflist **head, size_t len) -{ - assert(*head); - assert(len); - assert((*head)->pos + len <= (*head)->len); - - (*head)->pos += len; - if ((*head)->pos == (*head)->len) - lws_buflist_destroy_segment(head); - - if (!*head) - return 0; - - return (int)((*head)->len - (*head)->pos); -} - -void -lws_buflist_describe(struct lws_buflist **head, void *id) -{ - struct lws_buflist *old; - int n = 0; - - if (*head == NULL) - lwsl_notice("%p: buflist empty\n", id); + if (*head == NULL) + lwsl_notice("%p: buflist empty\n", id); while (*head) { lwsl_notice("%p: %d: %llu / %llu (%llu left)\n", id, n, @@ -1192,779 +321,53 @@ lws_buflist_describe(struct lws_buflist **head, void *id) } } -/* ... */ - -LWS_VISIBLE LWS_EXTERN const char * -lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len) -{ - int n = 0, sl = (int)strlen(name); - - while (lws_hdr_copy_fragment(wsi, buf, len, - WSI_TOKEN_HTTP_URI_ARGS, n) >= 0) { - - if (!strncmp(buf, name, sl)) - return buf + sl; - - n++; - } - - return NULL; -} - -#if !defined(LWS_WITH_ESP32) && !defined(LWS_PLAT_OPTEE) -LWS_VISIBLE int -interface_to_sa(struct lws_vhost *vh, const char *ifname, - struct sockaddr_in *addr, size_t addrlen) -{ - int ipv6 = 0; -#ifdef LWS_WITH_IPV6 - ipv6 = LWS_IPV6_ENABLED(vh); -#endif - (void)vh; - - return lws_interface_to_sa(ipv6, ifname, addr, addrlen); -} -#endif - -#ifndef LWS_PLAT_OPTEE -static int -lws_get_addresses(struct lws_vhost *vh, void *ads, char *name, - int name_len, char *rip, int rip_len) -{ - struct addrinfo ai, *res; - struct sockaddr_in addr4; - - rip[0] = '\0'; - name[0] = '\0'; - addr4.sin_family = AF_UNSPEC; - -#ifdef LWS_WITH_IPV6 - if (LWS_IPV6_ENABLED(vh)) { - if (!lws_plat_inet_ntop(AF_INET6, - &((struct sockaddr_in6 *)ads)->sin6_addr, - rip, rip_len)) { - lwsl_err("inet_ntop: %s", strerror(LWS_ERRNO)); - return -1; - } - - // Strip off the IPv4 to IPv6 header if one exists - if (strncmp(rip, "::ffff:", 7) == 0) - memmove(rip, rip + 7, strlen(rip) - 6); - - getnameinfo((struct sockaddr *)ads, sizeof(struct sockaddr_in6), - name, name_len, NULL, 0, 0); - - return 0; - } else -#endif - { - struct addrinfo *result; - - memset(&ai, 0, sizeof ai); - ai.ai_family = PF_UNSPEC; - ai.ai_socktype = SOCK_STREAM; -#if !defined(LWS_WITH_ESP32) - if (getnameinfo((struct sockaddr *)ads, - sizeof(struct sockaddr_in), - name, name_len, NULL, 0, 0)) - return -1; -#endif - - if (getaddrinfo(name, NULL, &ai, &result)) - return -1; - - res = result; - while (addr4.sin_family == AF_UNSPEC && res) { - switch (res->ai_family) { - case AF_INET: - addr4.sin_addr = - ((struct sockaddr_in *)res->ai_addr)->sin_addr; - addr4.sin_family = AF_INET; - break; - } - - res = res->ai_next; - } - freeaddrinfo(result); - } - - if (addr4.sin_family == AF_UNSPEC) - return -1; - - if (lws_plat_inet_ntop(AF_INET, &addr4.sin_addr, rip, rip_len) == NULL) - return -1; - - return 0; -} - - -LWS_VISIBLE const char * -lws_get_peer_simple(struct lws *wsi, char *name, int namelen) -{ - socklen_t len, olen; -#ifdef LWS_WITH_IPV6 - struct sockaddr_in6 sin6; -#endif - struct sockaddr_in sin4; - int af = AF_INET; - void *p, *q; - - wsi = lws_get_network_wsi(wsi); - -#ifdef LWS_WITH_IPV6 - if (LWS_IPV6_ENABLED(wsi->vhost)) { - len = sizeof(sin6); - p = &sin6; - af = AF_INET6; - q = &sin6.sin6_addr; - } else -#endif - { - len = sizeof(sin4); - p = &sin4; - q = &sin4.sin_addr; - } - - olen = len; - if (getpeername(wsi->desc.sockfd, p, &len) < 0 || len > olen) { - lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); - return NULL; - } - - return lws_plat_inet_ntop(af, q, name, namelen); -} -#endif - -LWS_VISIBLE void -lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name, - int name_len, char *rip, int rip_len) -{ -#ifndef LWS_PLAT_OPTEE - socklen_t len; -#ifdef LWS_WITH_IPV6 - struct sockaddr_in6 sin6; -#endif - struct sockaddr_in sin4; - struct lws_context *context = wsi->context; - int ret = -1; - void *p; - - rip[0] = '\0'; - name[0] = '\0'; - - lws_latency_pre(context, wsi); - -#ifdef LWS_WITH_IPV6 - if (LWS_IPV6_ENABLED(wsi->vhost)) { - len = sizeof(sin6); - p = &sin6; - } else -#endif - { - len = sizeof(sin4); - p = &sin4; - } - - if (getpeername(fd, p, &len) < 0) { - lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); - goto bail; - } - - ret = lws_get_addresses(wsi->vhost, p, name, name_len, rip, rip_len); - -bail: - lws_latency(context, wsi, "lws_get_peer_addresses", ret, 1); -#endif - (void)wsi; - (void)fd; - (void)name; - (void)name_len; - (void)rip; - (void)rip_len; - -} - -LWS_EXTERN void * -lws_vhost_user(struct lws_vhost *vhost) -{ - return vhost->user; -} - -LWS_EXTERN void * -lws_context_user(struct lws_context *context) -{ - return context->user_space; -} - -LWS_VISIBLE struct lws_vhost * -lws_vhost_get(struct lws *wsi) -{ - return wsi->vhost; -} - -LWS_VISIBLE struct lws_vhost * -lws_get_vhost(struct lws *wsi) -{ - return wsi->vhost; -} - -LWS_VISIBLE const struct lws_protocols * -lws_protocol_get(struct lws *wsi) -{ - return wsi->protocol; -} - -LWS_VISIBLE const struct lws_udp * -lws_get_udp(const struct lws *wsi) -{ - return wsi->udp; -} - -LWS_VISIBLE struct lws * -lws_get_network_wsi(struct lws *wsi) -{ - if (!wsi) - return NULL; - -#if defined(LWS_WITH_HTTP2) - if (!wsi->http2_substream -#if !defined(LWS_NO_CLIENT) - && !wsi->client_h2_substream -#endif - ) - return wsi; - - while (wsi->h2.parent_wsi) - wsi = wsi->h2.parent_wsi; -#endif - - return wsi; -} - -LWS_VISIBLE void -lws_explicit_bzero(void *p, size_t len) -{ - volatile uint8_t *vp = p; - - while (len--) - *vp++ = 0; -} - - -LWS_VISIBLE int LWS_WARN_UNUSED_RESULT -lws_raw_transaction_completed(struct lws *wsi) -{ - if (lws_has_buffered_out(wsi)) { - /* - * ...so he tried to send something large, but it went out - * as a partial, but he immediately called us to say he wants - * to close the connection. - * - * Defer the close until the last part of the partial is sent. - * - */ - lwsl_debug("%s: %p: deferring due to partial\n", __func__, wsi); - wsi->close_when_buffered_out_drained = 1; - lws_callback_on_writable(wsi); - - return 0; - } - - return -1; -} - -LWS_VISIBLE LWS_EXTERN const struct lws_protocols * -lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name) -{ - int n; - - for (n = 0; n < vh->count_protocols; n++) - if (!strcmp(name, vh->protocols[n].name)) - return &vh->protocols[n]; - - return NULL; -} - -LWS_VISIBLE int -lws_callback_all_protocol(struct lws_context *context, - const struct lws_protocols *protocol, int reason) -{ - struct lws_context_per_thread *pt = &context->pt[0]; - unsigned int n, m = context->count_threads; - struct lws *wsi; - - while (m--) { - for (n = 0; n < pt->fds_count; n++) { - wsi = wsi_from_fd(context, pt->fds[n].fd); - if (!wsi) - continue; - if (wsi->protocol == protocol) - protocol->callback(wsi, reason, wsi->user_space, - NULL, 0); - } - pt++; - } - - return 0; -} - -LWS_VISIBLE int -lws_callback_all_protocol_vhost_args(struct lws_vhost *vh, - const struct lws_protocols *protocol, int reason, - void *argp, size_t len) -{ - struct lws_context *context = vh->context; - struct lws_context_per_thread *pt = &context->pt[0]; - unsigned int n, m = context->count_threads; - struct lws *wsi; - - while (m--) { - for (n = 0; n < pt->fds_count; n++) { - wsi = wsi_from_fd(context, pt->fds[n].fd); - if (!wsi) - continue; - if (wsi->vhost == vh && (wsi->protocol == protocol || - !protocol)) - wsi->protocol->callback(wsi, reason, - wsi->user_space, argp, len); - } - pt++; - } - - return 0; -} - -LWS_VISIBLE int -lws_callback_all_protocol_vhost(struct lws_vhost *vh, - const struct lws_protocols *protocol, int reason) -{ - return lws_callback_all_protocol_vhost_args(vh, protocol, reason, NULL, 0); -} - -LWS_VISIBLE LWS_EXTERN int -lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len) -{ - int n; - - for (n = 0; n < wsi->vhost->count_protocols; n++) - if (wsi->vhost->protocols[n].callback(wsi, reason, NULL, in, len)) - return 1; - - return 0; -} - -LWS_VISIBLE LWS_EXTERN int -lws_callback_vhost_protocols_vhost(struct lws_vhost *vh, int reason, void *in, - size_t len) -{ - int n; - struct lws *wsi = lws_zalloc(sizeof(*wsi), "fake wsi"); - - wsi->context = vh->context; - lws_vhost_bind_wsi(vh, wsi); - - for (n = 0; n < wsi->vhost->count_protocols; n++) { - wsi->protocol = &vh->protocols[n]; - if (wsi->protocol->callback(wsi, reason, NULL, in, len)) { - lws_free(wsi); - return 1; - } - } - - lws_free(wsi); - - return 0; -} - -LWS_VISIBLE LWS_EXTERN void -lws_set_fops(struct lws_context *context, const struct lws_plat_file_ops *fops) -{ - context->fops = fops; -} - -LWS_VISIBLE LWS_EXTERN lws_filepos_t -lws_vfs_tell(lws_fop_fd_t fop_fd) -{ - return fop_fd->pos; -} - -LWS_VISIBLE LWS_EXTERN lws_filepos_t -lws_vfs_get_length(lws_fop_fd_t fop_fd) -{ - return fop_fd->len; -} - -LWS_VISIBLE LWS_EXTERN uint32_t -lws_vfs_get_mod_time(lws_fop_fd_t fop_fd) -{ - return fop_fd->mod_time; -} - -LWS_VISIBLE lws_fileofs_t -lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset) -{ - lws_fileofs_t ofs; - - ofs = fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, offset - fop_fd->pos); - - return ofs; -} - - -LWS_VISIBLE lws_fileofs_t -lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset) -{ - return fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, fop_fd->len + - fop_fd->pos + offset); -} - - -const struct lws_plat_file_ops * -lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path, - const char **vpath) -{ - const struct lws_plat_file_ops *pf; - const char *p = vfs_path; - int n; - - *vpath = NULL; - - /* no non-platform fops, just use that */ - - if (!fops->next) - return fops; - - /* - * scan the vfs path looking for indications we are to be - * handled by a specific fops - */ - - while (p && *p) { - if (*p != '/') { - p++; - continue; - } - /* the first one is always platform fops, so skip */ - pf = fops->next; - while (pf) { - n = 0; - while (n < (int)LWS_ARRAY_SIZE(pf->fi) && pf->fi[n].sig) { - if (p >= vfs_path + pf->fi[n].len) - if (!strncmp(p - (pf->fi[n].len - 1), - pf->fi[n].sig, - pf->fi[n].len - 1)) { - *vpath = p + 1; - return pf; - } - - n++; - } - pf = pf->next; - } - p++; - } - - return fops; -} - -LWS_VISIBLE LWS_EXTERN lws_fop_fd_t LWS_WARN_UNUSED_RESULT -lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path, - lws_fop_flags_t *flags) -{ - const char *vpath = ""; - const struct lws_plat_file_ops *selected; - - selected = lws_vfs_select_fops(fops, vfs_path, &vpath); - - return selected->LWS_FOP_OPEN(fops, vfs_path, vpath, flags); -} - - -/** - * lws_now_secs() - seconds since 1970-1-1 - * - */ -LWS_VISIBLE LWS_EXTERN unsigned long -lws_now_secs(void) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - return tv.tv_sec; -} - -LWS_VISIBLE LWS_EXTERN int -lws_compare_time_t(struct lws_context *context, time_t t1, time_t t2) -{ - if (t1 < context->time_discontiguity) - t1 += context->time_fixup; - - if (t2 < context->time_discontiguity) - t2 += context->time_fixup; - - return (int)(t1 - t2); -} - -LWS_VISIBLE lws_sockfd_type -lws_get_socket_fd(struct lws *wsi) -{ - if (!wsi) - return -1; - return wsi->desc.sockfd; -} - -#ifdef LWS_LATENCY -void -lws_latency(struct lws_context *context, struct lws *wsi, const char *action, - int ret, int completed) -{ - unsigned long long u; - char buf[256]; - - u = lws_time_in_microseconds(); - - if (!action) { - wsi->latency_start = u; - if (!wsi->action_start) - wsi->action_start = u; - return; - } - if (completed) { - if (wsi->action_start == wsi->latency_start) - sprintf(buf, - "Completion first try lat %lluus: %p: ret %d: %s\n", - u - wsi->latency_start, - (void *)wsi, ret, action); - else - sprintf(buf, - "Completion %lluus: lat %lluus: %p: ret %d: %s\n", - u - wsi->action_start, - u - wsi->latency_start, - (void *)wsi, ret, action); - wsi->action_start = 0; - } else - sprintf(buf, "lat %lluus: %p: ret %d: %s\n", - u - wsi->latency_start, (void *)wsi, ret, action); - - if (u - wsi->latency_start > context->worst_latency) { - context->worst_latency = u - wsi->latency_start; - strcpy(context->worst_latency_info, buf); - } - lwsl_latency("%s", buf); -} -#endif - -LWS_VISIBLE int -lws_rx_flow_control(struct lws *wsi, int _enable) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - int en = _enable; - - // h2 ignores rx flow control atm - if (lwsi_role_h2(wsi) || wsi->http2_substream || - lwsi_role_h2_ENCAPSULATION(wsi)) - return 0; // !!! - - lwsl_info("%s: %p 0x%x\n", __func__, wsi, _enable); - - if (!(_enable & LWS_RXFLOW_REASON_APPLIES)) { - /* - * convert user bool style to bitmap style... in user simple - * bool style _enable = 0 = flow control it, = 1 = allow rx - */ - en = LWS_RXFLOW_REASON_APPLIES | LWS_RXFLOW_REASON_USER_BOOL; - if (_enable & 1) - en |= LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT; - } - - lws_pt_lock(pt, __func__); - - /* any bit set in rxflow_bitmap DISABLEs rxflow control */ - if (en & LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT) - wsi->rxflow_bitmap &= ~(en & 0xff); - else - wsi->rxflow_bitmap |= en & 0xff; - - if ((LWS_RXFLOW_PENDING_CHANGE | (!wsi->rxflow_bitmap)) == - wsi->rxflow_change_to) - goto skip; - - wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | - (!wsi->rxflow_bitmap); - - lwsl_info("%s: %p: bitmap 0x%x: en 0x%x, ch 0x%x\n", __func__, wsi, - wsi->rxflow_bitmap, en, wsi->rxflow_change_to); - - if (_enable & LWS_RXFLOW_REASON_FLAG_PROCESS_NOW || - !wsi->rxflow_will_be_applied) { - en = __lws_rx_flow_control(wsi); - lws_pt_unlock(pt); - - return en; - } - -skip: - lws_pt_unlock(pt); - - return 0; -} - -LWS_VISIBLE void -lws_rx_flow_allow_all_protocol(const struct lws_context *context, - const struct lws_protocols *protocol) -{ - const struct lws_context_per_thread *pt = &context->pt[0]; - struct lws *wsi; - unsigned int n, m = context->count_threads; - - while (m--) { - for (n = 0; n < pt->fds_count; n++) { - wsi = wsi_from_fd(context, pt->fds[n].fd); - if (!wsi) - continue; - if (wsi->protocol == protocol) - lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW); - } - pt++; - } -} - -int -lws_broadcast(struct lws_context *context, int reason, void *in, size_t len) -{ - struct lws_vhost *v = context->vhost_list; - struct lws wsi; - int n, ret = 0; - - memset(&wsi, 0, sizeof(wsi)); - wsi.context = context; - - while (v) { - const struct lws_protocols *p = v->protocols; - wsi.vhost = v; /* not a real bound wsi */ - - for (n = 0; n < v->count_protocols; n++) { - wsi.protocol = p; - if (p->callback && - p->callback(&wsi, reason, NULL, in, len)) - ret |= 1; - p++; - } - v = v->vhost_next; - } - - return ret; -} - -LWS_VISIBLE extern const char * -lws_canonical_hostname(struct lws_context *context) -{ - return (const char *)context->canonical_hostname; -} - -LWS_VISIBLE LWS_EXTERN const char * -lws_get_vhost_name(struct lws_vhost *vhost) -{ - return vhost->name; -} - -LWS_VISIBLE LWS_EXTERN int -lws_get_vhost_port(struct lws_vhost *vhost) -{ - return vhost->listen_port; -} - -LWS_VISIBLE LWS_EXTERN void * -lws_get_vhost_user(struct lws_vhost *vhost) -{ - return vhost->user; -} - -LWS_VISIBLE LWS_EXTERN const char * -lws_get_vhost_iface(struct lws_vhost *vhost) -{ - return vhost->iface; -} - -int user_callback_handle_rxflow(lws_callback_function callback_function, - struct lws *wsi, - enum lws_callback_reasons reason, void *user, - void *in, size_t len) +LWS_EXTERN void * +lws_context_user(struct lws_context *context) { - int n; - - wsi->rxflow_will_be_applied = 1; - n = callback_function(wsi, reason, user, in, len); - wsi->rxflow_will_be_applied = 0; - if (!n) - n = __lws_rx_flow_control(wsi); - - return n; + return context->user_space; } -#if !defined(LWS_WITHOUT_CLIENT) -LWS_VISIBLE int -lws_set_proxy(struct lws_vhost *vhost, const char *proxy) +LWS_VISIBLE void +lws_explicit_bzero(void *p, size_t len) { - char *p; - char authstring[96]; - - if (!proxy) - return -1; - - /* we have to deal with a possible redundant leading http:// */ - if (!strncmp(proxy, "http://", 7)) - proxy += 7; - - p = strrchr(proxy, '@'); - if (p) { /* auth is around */ - - if ((unsigned int)(p - proxy) > sizeof(authstring) - 1) - goto auth_too_long; + volatile uint8_t *vp = p; - lws_strncpy(authstring, proxy, p - proxy + 1); - // null termination not needed on input - if (lws_b64_encode_string(authstring, lws_ptr_diff(p, proxy), - vhost->proxy_basic_auth_token, - sizeof vhost->proxy_basic_auth_token) < 0) - goto auth_too_long; + while (len--) + *vp++ = 0; +} - lwsl_info(" Proxy auth in use\n"); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - proxy = p + 1; -#endif - } else - vhost->proxy_basic_auth_token[0] = '\0'; +/** + * lws_now_secs() - seconds since 1970-1-1 + * + */ +LWS_VISIBLE LWS_EXTERN unsigned long +lws_now_secs(void) +{ + struct timeval tv; -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - lws_strncpy(vhost->http.http_proxy_address, proxy, - sizeof(vhost->http.http_proxy_address)); + gettimeofday(&tv, NULL); - p = strchr(vhost->http.http_proxy_address, ':'); - if (!p && !vhost->http.http_proxy_port) { - lwsl_err("http_proxy needs to be ads:port\n"); + return tv.tv_sec; +} - return -1; - } else { - if (p) { - *p = '\0'; - vhost->http.http_proxy_port = atoi(p + 1); - } - } +LWS_VISIBLE LWS_EXTERN int +lws_compare_time_t(struct lws_context *context, time_t t1, time_t t2) +{ + if (t1 < context->time_discontiguity) + t1 += context->time_fixup; - lwsl_info(" Proxy %s:%u\n", vhost->http.http_proxy_address, - vhost->http.http_proxy_port); -#endif - return 0; + if (t2 < context->time_discontiguity) + t2 += context->time_fixup; -auth_too_long: - lwsl_err("proxy auth too long\n"); + return (int)(t1 - t2); +} - return -1; +LWS_VISIBLE extern const char * +lws_canonical_hostname(struct lws_context *context) +{ + return (const char *)context->canonical_hostname; } -#endif #if defined(LWS_WITH_SOCKS5) LWS_VISIBLE int @@ -2015,472 +418,33 @@ lws_set_socks(struct lws_vhost *vhost, const char *socks) lws_strncpy(vhost->socks_proxy_address, socks, sizeof(vhost->socks_proxy_address)); - p_colon = strchr(vhost->socks_proxy_address, ':'); - if (!p_colon && !vhost->socks_proxy_port) { - lwsl_err("socks_proxy needs to be address:port\n"); - return -1; - } else { - if (p_colon) { - *p_colon = '\0'; - vhost->socks_proxy_port = atoi(p_colon + 1); - } - } - - lwsl_info(" Socks %s:%u\n", vhost->socks_proxy_address, - vhost->socks_proxy_port); - - return 0; - -bail: - return -1; -} -#endif - -LWS_VISIBLE const struct lws_protocols * -lws_get_protocol(struct lws *wsi) -{ - return wsi->protocol; -} - - -int -lws_ensure_user_space(struct lws *wsi) -{ - if (!wsi->protocol) - return 0; - - /* allocate the per-connection user memory (if any) */ - - if (wsi->protocol->per_session_data_size && !wsi->user_space) { - wsi->user_space = lws_zalloc( - wsi->protocol->per_session_data_size, "user space"); - if (wsi->user_space == NULL) { - lwsl_err("%s: OOM\n", __func__); - return 1; - } - } else - lwsl_debug("%s: %p protocol pss %lu, user_space=%p\n", __func__, - wsi, (long)wsi->protocol->per_session_data_size, - wsi->user_space); - return 0; -} - -LWS_VISIBLE void * -lws_adjust_protocol_psds(struct lws *wsi, size_t new_size) -{ - ((struct lws_protocols *)lws_get_protocol(wsi))->per_session_data_size = - new_size; - - if (lws_ensure_user_space(wsi)) - return NULL; - - return wsi->user_space; -} - -LWS_VISIBLE int -lwsl_timestamp(int level, char *p, int len) -{ -#ifndef LWS_PLAT_OPTEE -#ifndef _WIN32_WCE - time_t o_now = time(NULL); -#endif - unsigned long long now; - struct tm *ptm = NULL; -#ifndef WIN32 - struct tm tm; -#endif - int n; - -#ifndef _WIN32_WCE -#ifdef WIN32 - ptm = localtime(&o_now); -#else - if (localtime_r(&o_now, &tm)) - ptm = &tm; -#endif -#endif - p[0] = '\0'; - for (n = 0; n < LLL_COUNT; n++) { - if (level != (1 << n)) - continue; - now = lws_time_in_microseconds() / 100; - if (ptm) - n = lws_snprintf(p, len, - "[%04d/%02d/%02d %02d:%02d:%02d:%04d] %s: ", - ptm->tm_year + 1900, - ptm->tm_mon + 1, - ptm->tm_mday, - ptm->tm_hour, - ptm->tm_min, - ptm->tm_sec, - (int)(now % 10000), log_level_names[n]); - else - n = lws_snprintf(p, len, "[%llu:%04d] %s: ", - (unsigned long long) now / 10000, - (int)(now % 10000), log_level_names[n]); - return n; - } -#else - p[0] = '\0'; -#endif - - return 0; -} - -#ifndef LWS_PLAT_OPTEE -static const char * const colours[] = { - "[31;1m", /* LLL_ERR */ - "[36;1m", /* LLL_WARN */ - "[35;1m", /* LLL_NOTICE */ - "[32;1m", /* LLL_INFO */ - "[34;1m", /* LLL_DEBUG */ - "[33;1m", /* LLL_PARSER */ - "[33m", /* LLL_HEADER */ - "[33m", /* LLL_EXT */ - "[33m", /* LLL_CLIENT */ - "[33;1m", /* LLL_LATENCY */ - "[30;1m", /* LLL_USER */ - "[31m", /* LLL_THREAD */ -}; - -static char tty; - -LWS_VISIBLE void -lwsl_emit_stderr(int level, const char *line) -{ - char buf[50]; - int n, m = LWS_ARRAY_SIZE(colours) - 1; - - if (!tty) - tty = isatty(2) | 2; - lwsl_timestamp(level, buf, sizeof(buf)); - - if (tty == 3) { - n = 1 << (LWS_ARRAY_SIZE(colours) - 1); - while (n) { - if (level & n) - break; - m--; - n >>= 1; - } - fprintf(stderr, "%c%s%s%s%c[0m", 27, colours[m], buf, line, 27); - } else - fprintf(stderr, "%s%s", buf, line); -} - -LWS_VISIBLE void -lwsl_emit_stderr_notimestamp(int level, const char *line) -{ - int n, m = LWS_ARRAY_SIZE(colours) - 1; - - if (!tty) - tty = isatty(2) | 2; - - if (tty == 3) { - n = 1 << (LWS_ARRAY_SIZE(colours) - 1); - while (n) { - if (level & n) - break; - m--; - n >>= 1; - } - fprintf(stderr, "%c%s%s%c[0m", 27, colours[m], line, 27); - } else - fprintf(stderr, "%s", line); -} - -#endif - -LWS_VISIBLE void _lws_logv(int filter, const char *format, va_list vl) -{ - char buf[256]; - int n; - - if (!(log_level & filter)) - return; - - n = vsnprintf(buf, sizeof(buf) - 1, format, vl); - (void)n; - /* vnsprintf returns what it would have written, even if truncated */ - if (n > (int)sizeof(buf) - 1) { - n = sizeof(buf) - 5; - buf[n++] = '.'; - buf[n++] = '.'; - buf[n++] = '.'; - buf[n++] = '\n'; - buf[n] = '\0'; - } - if (n > 0) - buf[n] = '\0'; - - lwsl_emit(filter, buf); -} - -LWS_VISIBLE void _lws_log(int filter, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - _lws_logv(filter, format, ap); - va_end(ap); -} - -LWS_VISIBLE void lws_set_log_level(int level, - void (*func)(int level, const char *line)) -{ - log_level = level; - if (func) - lwsl_emit = func; -} - -LWS_VISIBLE int lwsl_visible(int level) -{ - return log_level & level; -} - -LWS_VISIBLE void -lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len) -{ - unsigned char *buf = (unsigned char *)vbuf; - unsigned int n; - - if (!lwsl_visible(hexdump_level)) - return; - - if (!len) { - _lws_log(hexdump_level, "(hexdump: zero length)\n"); - return; - } - - if (!vbuf) { - _lws_log(hexdump_level, "(hexdump: trying to dump %d at NULL)\n", - (int)len); - return; - } - - _lws_log(hexdump_level, "\n"); - - for (n = 0; n < len;) { - unsigned int start = n, m; - char line[80], *p = line; - - p += sprintf(p, "%04X: ", start); - - for (m = 0; m < 16 && n < len; m++) - p += sprintf(p, "%02X ", buf[n++]); - while (m++ < 16) - p += sprintf(p, " "); - - p += sprintf(p, " "); - - for (m = 0; m < 16 && (start + m) < len; m++) { - if (buf[start + m] >= ' ' && buf[start + m] < 127) - *p++ = buf[start + m]; - else - *p++ = '.'; - } - while (m++ < 16) - *p++ = ' '; - - *p++ = '\n'; - *p = '\0'; - _lws_log(hexdump_level, "%s", line); - (void)line; - } - - _lws_log(hexdump_level, "\n"); -} - -LWS_VISIBLE void -lwsl_hexdump(const void *vbuf, size_t len) -{ -#if defined(_DEBUG) - lwsl_hexdump_level(LLL_DEBUG, vbuf, len); -#endif -} - -LWS_VISIBLE int -lws_is_ssl(struct lws *wsi) -{ -#if defined(LWS_WITH_TLS) - return wsi->tls.use_ssl & LCCSCF_USE_SSL; -#else - (void)wsi; - return 0; -#endif -} - -#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS) -LWS_VISIBLE lws_tls_conn* -lws_get_ssl(struct lws *wsi) -{ - return wsi->tls.ssl; -} -#endif - -LWS_VISIBLE int -lws_partial_buffered(struct lws *wsi) -{ - return lws_has_buffered_out(wsi); -} - -LWS_VISIBLE lws_fileofs_t -lws_get_peer_write_allowance(struct lws *wsi) -{ - if (!wsi->role_ops->tx_credit) - return -1; - return wsi->role_ops->tx_credit(wsi); -} - -LWS_VISIBLE void -lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state, - const struct lws_role_ops *ops) -{ -#if defined(_DEBUG) - const char *name = "(unset)"; -#endif - wsi->wsistate = role | state; - if (ops) - wsi->role_ops = ops; -#if defined(_DEBUG) - if (wsi->role_ops) - name = wsi->role_ops->name; - lwsl_debug("%s: %p: wsistate 0x%x, ops %s\n", __func__, wsi, - wsi->wsistate, name); -#endif -} - -LWS_VISIBLE struct lws_plat_file_ops * -lws_get_fops(struct lws_context *context) -{ - return (struct lws_plat_file_ops *)context->fops; -} - -LWS_VISIBLE LWS_EXTERN struct lws_context * -lws_get_context(const struct lws *wsi) -{ - return wsi->context; -} - -LWS_VISIBLE LWS_EXTERN int -lws_get_count_threads(struct lws_context *context) -{ - return context->count_threads; -} - -LWS_VISIBLE LWS_EXTERN void * -lws_wsi_user(struct lws *wsi) -{ - return wsi->user_space; -} - -LWS_VISIBLE LWS_EXTERN void -lws_set_wsi_user(struct lws *wsi, void *data) -{ - if (wsi->user_space_externally_allocated) - wsi->user_space = data; - else - lwsl_err("%s: Cannot set internally-allocated user_space\n", - __func__); -} - -LWS_VISIBLE LWS_EXTERN struct lws * -lws_get_parent(const struct lws *wsi) -{ - return wsi->parent; -} - -LWS_VISIBLE LWS_EXTERN struct lws * -lws_get_child(const struct lws *wsi) -{ - return wsi->child_list; -} - -LWS_VISIBLE LWS_EXTERN void * -lws_get_opaque_parent_data(const struct lws *wsi) -{ - return wsi->opaque_parent_data; -} - -LWS_VISIBLE LWS_EXTERN void -lws_set_opaque_parent_data(struct lws *wsi, void *data) -{ - wsi->opaque_parent_data = data; -} - -LWS_VISIBLE LWS_EXTERN void * -lws_get_opaque_user_data(const struct lws *wsi) -{ - return wsi->opaque_user_data; -} - -LWS_VISIBLE LWS_EXTERN void -lws_set_opaque_user_data(struct lws *wsi, void *data) -{ - wsi->opaque_user_data = data; -} - -LWS_VISIBLE LWS_EXTERN int -lws_get_child_pending_on_writable(const struct lws *wsi) -{ - return wsi->parent_pending_cb_on_writable; -} - -LWS_VISIBLE LWS_EXTERN void -lws_clear_child_pending_on_writable(struct lws *wsi) -{ - wsi->parent_pending_cb_on_writable = 0; -} - - -LWS_EXTERN int -__lws_rx_flow_control(struct lws *wsi) -{ - struct lws *wsic = wsi->child_list; - - // h2 ignores rx flow control atm - if (lwsi_role_h2(wsi) || wsi->http2_substream || - lwsi_role_h2_ENCAPSULATION(wsi)) - return 0; // !!! - - /* if he has children, do those if they were changed */ - while (wsic) { - if (wsic->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE) - __lws_rx_flow_control(wsic); - - wsic = wsic->sibling_list; - } - - /* there is no pending change */ - if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) - return 0; - - /* stuff is still buffered, not ready to really accept new input */ - if (lws_buflist_next_segment_len(&wsi->buflist, NULL)) { - /* get ourselves called back to deal with stashed buffer */ - lws_callback_on_writable(wsi); - return 0; + p_colon = strchr(vhost->socks_proxy_address, ':'); + if (!p_colon && !vhost->socks_proxy_port) { + lwsl_err("socks_proxy needs to be address:port\n"); + return -1; + } else { + if (p_colon) { + *p_colon = '\0'; + vhost->socks_proxy_port = atoi(p_colon + 1); + } } - /* now the pending is cleared, we can change rxflow state */ + lwsl_info(" Socks %s:%u\n", vhost->socks_proxy_address, + vhost->socks_proxy_port); - wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE; + return 0; - lwsl_info("rxflow: wsi %p change_to %d\n", wsi, - wsi->rxflow_change_to & LWS_RXFLOW_ALLOW); +bail: + return -1; +} +#endif - /* adjust the pollfd for this wsi */ - if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) { - if (__lws_change_pollfd(wsi, 0, LWS_POLLIN)) { - lwsl_info("%s: fail\n", __func__); - return -1; - } - } else - if (__lws_change_pollfd(wsi, LWS_POLLIN, 0)) - return -1; - return 0; +LWS_VISIBLE LWS_EXTERN int +lws_get_count_threads(struct lws_context *context) +{ + return context->count_threads; } static const unsigned char e0f4[] = { @@ -2564,59 +528,6 @@ lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len) return 0; } -LWS_VISIBLE LWS_EXTERN int -lws_parse_uri(char *p, const char **prot, const char **ads, int *port, - const char **path) -{ - const char *end; - char unix_skt = 0; - - /* cut up the location into address, port and path */ - *prot = p; - while (*p && (*p != ':' || p[1] != '/' || p[2] != '/')) - p++; - if (!*p) { - end = p; - p = (char *)*prot; - *prot = end; - } else { - *p = '\0'; - p += 3; - } - if (*p == '+') /* unix skt */ - unix_skt = 1; - - *ads = p; - if (!strcmp(*prot, "http") || !strcmp(*prot, "ws")) - *port = 80; - else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss")) - *port = 443; - - if (*p == '[') { - ++(*ads); - while (*p && *p != ']') - p++; - if (*p) - *p++ = '\0'; - } else - while (*p && *p != ':' && (unix_skt || *p != '/')) - p++; - - if (*p == ':') { - *p++ = '\0'; - *port = atoi(p); - while (*p && *p != '/') - p++; - } - *path = "/"; - if (*p) { - *p++ = '\0'; - if (*p) - *path = p; - } - - return 0; -} char * lws_strdup(const char *s) @@ -2629,303 +540,6 @@ lws_strdup(const char *s) return d; } -#if defined(LWS_WITHOUT_EXTENSIONS) - -/* we need to provide dummy callbacks for internal exts - * so user code runs when faced with a lib compiled with - * extensions disabled. - */ - -LWS_VISIBLE int -lws_extension_callback_pm_deflate(struct lws_context *context, - const struct lws_extension *ext, - struct lws *wsi, - enum lws_extension_callback_reasons reason, - void *user, void *in, size_t len) -{ - (void)context; - (void)ext; - (void)wsi; - (void)reason; - (void)user; - (void)in; - (void)len; - - return 0; -} - -LWS_EXTERN int -lws_set_extension_option(struct lws *wsi, const char *ext_name, - const char *opt_name, const char *opt_val) -{ - return -1; -} -#endif - -/* note: this returns a random port, or one of these <= 0 return codes: - * - * LWS_ITOSA_USABLE: the interface is usable, returned if so and sockfd invalid - * LWS_ITOSA_NOT_EXIST: the requested iface does not even exist - * LWS_ITOSA_NOT_USABLE: the requested iface exists but is not usable (eg, no IP) - * LWS_ITOSA_BUSY: the port at the requested iface + port is already in use - */ - -LWS_EXTERN int -lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, - const char *iface) -{ -#ifdef LWS_WITH_UNIX_SOCK - struct sockaddr_un serv_unix; -#endif -#ifdef LWS_WITH_IPV6 - struct sockaddr_in6 serv_addr6; -#endif - struct sockaddr_in serv_addr4; -#ifndef LWS_PLAT_OPTEE - socklen_t len = sizeof(struct sockaddr_storage); -#endif - int n; -#if !defined(LWS_WITH_ESP32) && !defined(LWS_PLAT_OPTEE) - int m; -#endif - struct sockaddr_storage sin; - struct sockaddr *v; - - memset(&sin, 0, sizeof(sin)); - -#if defined(LWS_WITH_UNIX_SOCK) - if (LWS_UNIX_SOCK_ENABLED(vhost)) { - v = (struct sockaddr *)&serv_unix; - n = sizeof(struct sockaddr_un); - bzero((char *) &serv_unix, sizeof(serv_unix)); - serv_unix.sun_family = AF_UNIX; - if (!iface) - return LWS_ITOSA_NOT_EXIST; - if (sizeof(serv_unix.sun_path) <= strlen(iface)) { - lwsl_err("\"%s\" too long for UNIX domain socket\n", - iface); - return LWS_ITOSA_NOT_EXIST; - } - strcpy(serv_unix.sun_path, iface); - if (serv_unix.sun_path[0] == '@') - serv_unix.sun_path[0] = '\0'; - else - unlink(serv_unix.sun_path); - - } else -#endif -#if defined(LWS_WITH_IPV6) && !defined(LWS_WITH_ESP32) - if (LWS_IPV6_ENABLED(vhost)) { - v = (struct sockaddr *)&serv_addr6; - n = sizeof(struct sockaddr_in6); - bzero((char *) &serv_addr6, sizeof(serv_addr6)); - if (iface) { - m = interface_to_sa(vhost, iface, - (struct sockaddr_in *)v, n); - if (m == LWS_ITOSA_NOT_USABLE) { - lwsl_info("%s: netif %s: Not usable\n", - __func__, iface); - return m; - } - if (m == LWS_ITOSA_NOT_EXIST) { - lwsl_info("%s: netif %s: Does not exist\n", - __func__, iface); - return m; - } - serv_addr6.sin6_scope_id = lws_get_addr_scope(iface); - } - - serv_addr6.sin6_family = AF_INET6; - serv_addr6.sin6_port = htons(port); - } else -#endif - { - v = (struct sockaddr *)&serv_addr4; - n = sizeof(serv_addr4); - bzero((char *) &serv_addr4, sizeof(serv_addr4)); - serv_addr4.sin_addr.s_addr = INADDR_ANY; - serv_addr4.sin_family = AF_INET; - -#if !defined(LWS_WITH_ESP32) && !defined(LWS_PLAT_OPTEE) - if (iface) { - m = interface_to_sa(vhost, iface, - (struct sockaddr_in *)v, n); - if (m == LWS_ITOSA_NOT_USABLE) { - lwsl_info("%s: netif %s: Not usable\n", - __func__, iface); - return m; - } - if (m == LWS_ITOSA_NOT_EXIST) { - lwsl_info("%s: netif %s: Does not exist\n", - __func__, iface); - return m; - } - } -#endif - serv_addr4.sin_port = htons(port); - } /* ipv4 */ - - /* just checking for the interface extant */ - if (sockfd == LWS_SOCK_INVALID) - return LWS_ITOSA_USABLE; - - n = bind(sockfd, v, n); -#ifdef LWS_WITH_UNIX_SOCK - if (n < 0 && LWS_UNIX_SOCK_ENABLED(vhost)) { - lwsl_err("ERROR on binding fd %d to \"%s\" (%d %d)\n", - sockfd, iface, n, LWS_ERRNO); - return LWS_ITOSA_NOT_EXIST; - } else -#endif - if (n < 0) { - lwsl_err("ERROR on binding fd %d to port %d (%d %d)\n", - sockfd, port, n, LWS_ERRNO); - - /* if something already listening, tell caller to fail permanently */ - - if (LWS_ERRNO == LWS_EADDRINUSE) - return LWS_ITOSA_BUSY; - - /* otherwise ask caller to retry later */ - - return LWS_ITOSA_NOT_EXIST; - } - -#if defined(LWS_WITH_UNIX_SOCK) - if (LWS_UNIX_SOCK_ENABLED(vhost) && vhost->context->uid) - if (chown(serv_unix.sun_path, vhost->context->uid, - vhost->context->gid)) - lwsl_notice("%s: chown for unix skt %s failed\n", - __func__, serv_unix.sun_path); -#endif - -#ifndef LWS_PLAT_OPTEE - if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) - lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO)); - else -#endif -#if defined(LWS_WITH_IPV6) - port = (sin.ss_family == AF_INET6) ? - ntohs(((struct sockaddr_in6 *) &sin)->sin6_port) : - ntohs(((struct sockaddr_in *) &sin)->sin_port); -#else - { - struct sockaddr_in sain; - memcpy(&sain, &sin, sizeof(sain)); - port = ntohs(sain.sin_port); - } -#endif - - return port; -} - -LWS_VISIBLE LWS_EXTERN int -lws_get_vhost_listen_port(struct lws_vhost *vhost) -{ - return vhost->listen_port; -} - -#if defined(LWS_WITH_IPV6) -LWS_EXTERN unsigned long -lws_get_addr_scope(const char *ipaddr) -{ - unsigned long scope = 0; - -#ifndef WIN32 - struct ifaddrs *addrs, *addr; - char ip[NI_MAXHOST]; - unsigned int i; - - getifaddrs(&addrs); - for (addr = addrs; addr; addr = addr->ifa_next) { - if (!addr->ifa_addr || - addr->ifa_addr->sa_family != AF_INET6) - continue; - - getnameinfo(addr->ifa_addr, - sizeof(struct sockaddr_in6), - ip, sizeof(ip), - NULL, 0, NI_NUMERICHOST); - - i = 0; - while (ip[i]) - if (ip[i++] == '%') { - ip[i - 1] = '\0'; - break; - } - - if (!strcmp(ip, ipaddr)) { - scope = if_nametoindex(addr->ifa_name); - break; - } - } - freeifaddrs(addrs); -#else - PIP_ADAPTER_ADDRESSES adapter, addrs = NULL; - PIP_ADAPTER_UNICAST_ADDRESS addr; - ULONG size = 0; - DWORD ret; - struct sockaddr_in6 *sockaddr; - char ip[NI_MAXHOST]; - unsigned int i; - int found = 0; - - for (i = 0; i < 5; i++) - { - ret = GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, - NULL, addrs, &size); - if ((ret == NO_ERROR) || (ret == ERROR_NO_DATA)) { - break; - } else if (ret == ERROR_BUFFER_OVERFLOW) - { - if (addrs) - free(addrs); - addrs = (IP_ADAPTER_ADDRESSES *)malloc(size); - } else - { - if (addrs) - { - free(addrs); - addrs = NULL; - } - lwsl_err("Failed to get IPv6 address table (%d)", ret); - break; - } - } - - if ((ret == NO_ERROR) && (addrs)) { - adapter = addrs; - while (adapter && !found) { - addr = adapter->FirstUnicastAddress; - while (addr && !found) { - if (addr->Address.lpSockaddr->sa_family == - AF_INET6) { - sockaddr = (struct sockaddr_in6 *) - (addr->Address.lpSockaddr); - - lws_plat_inet_ntop(sockaddr->sin6_family, - &sockaddr->sin6_addr, - ip, sizeof(ip)); - - if (!strcmp(ip, ipaddr)) { - scope = sockaddr->sin6_scope_id; - found = 1; - break; - } - } - addr = addr->Next; - } - adapter = adapter->Next; - } - } - if (addrs) - free(addrs); -#endif - - return scope; -} -#endif - static const char *hex = "0123456789ABCDEF"; LWS_VISIBLE LWS_EXTERN const char * @@ -3474,64 +1088,6 @@ lws_mutex_refcount_unlock(struct lws_mutex_refcount *mr) #endif /* SMP */ -LWS_VISIBLE LWS_EXTERN int -lws_is_cgi(struct lws *wsi) { -#ifdef LWS_WITH_CGI - return !!wsi->http.cgi; -#else - return 0; -#endif -} - -const struct lws_protocol_vhost_options * -lws_pvo_search(const struct lws_protocol_vhost_options *pvo, const char *name) -{ - while (pvo) { - if (!strcmp(pvo->name, name)) - break; - - pvo = pvo->next; - } - - return pvo; -} - -int -lws_pvo_get_str(void *in, const char *name, const char **result) -{ - const struct lws_protocol_vhost_options *pv = - lws_pvo_search((const struct lws_protocol_vhost_options *)in, - name); - - if (!pv) - return 1; - - *result = (const char *)pv->value; - - return 0; -} - -void -lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs) -{ - const struct lws_vhost *vh = ctx->vhost_list; - - while (vh) { - - cs->rx += vh->conn_stats.rx; - cs->tx += vh->conn_stats.tx; - cs->h1_conn += vh->conn_stats.h1_conn; - cs->h1_trans += vh->conn_stats.h1_trans; - cs->h2_trans += vh->conn_stats.h2_trans; - cs->ws_upg += vh->conn_stats.ws_upg; - cs->h2_upg += vh->conn_stats.h2_upg; - cs->h2_alpn += vh->conn_stats.h2_alpn; - cs->h2_subs += vh->conn_stats.h2_subs; - cs->rejected += vh->conn_stats.rejected; - - vh = vh->vhost_next; - } -} const char * lws_cmdline_option(int argc, const char **argv, const char *val) @@ -3555,496 +1111,3 @@ lws_cmdline_option(int argc, const char **argv, const char *val) return NULL; } -#ifdef LWS_WITH_SERVER_STATUS - -LWS_EXTERN int -lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len) -{ -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - static const char * const prots[] = { - "http://", - "https://", - "file://", - "cgi://", - ">http://", - ">https://", - "callback://" - }; -#endif - char *orig = buf, *end = buf + len - 1, first = 1; - int n = 0; - - if (len < 100) - return 0; - - buf += lws_snprintf(buf, end - buf, - "{\n \"name\":\"%s\",\n" - " \"port\":\"%d\",\n" - " \"use_ssl\":\"%d\",\n" - " \"sts\":\"%d\",\n" - " \"rx\":\"%llu\",\n" - " \"tx\":\"%llu\",\n" - " \"h1_conn\":\"%lu\",\n" - " \"h1_trans\":\"%lu\",\n" - " \"h2_trans\":\"%lu\",\n" - " \"ws_upg\":\"%lu\",\n" - " \"rejected\":\"%lu\",\n" - " \"h2_upg\":\"%lu\",\n" - " \"h2_alpn\":\"%lu\",\n" - " \"h2_subs\":\"%lu\"" - , - vh->name, vh->listen_port, -#if defined(LWS_WITH_TLS) - vh->tls.use_ssl & LCCSCF_USE_SSL, -#else - 0, -#endif - !!(vh->options & LWS_SERVER_OPTION_STS), - vh->conn_stats.rx, vh->conn_stats.tx, - vh->conn_stats.h1_conn, - vh->conn_stats.h1_trans, - vh->conn_stats.h2_trans, - vh->conn_stats.ws_upg, - vh->conn_stats.rejected, - vh->conn_stats.h2_upg, - vh->conn_stats.h2_alpn, - vh->conn_stats.h2_subs - ); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - if (vh->http.mount_list) { - const struct lws_http_mount *m = vh->http.mount_list; - - buf += lws_snprintf(buf, end - buf, ",\n \"mounts\":["); - while (m) { - if (!first) - buf += lws_snprintf(buf, end - buf, ","); - buf += lws_snprintf(buf, end - buf, - "\n {\n \"mountpoint\":\"%s\",\n" - " \"origin\":\"%s%s\",\n" - " \"cache_max_age\":\"%d\",\n" - " \"cache_reuse\":\"%d\",\n" - " \"cache_revalidate\":\"%d\",\n" - " \"cache_intermediaries\":\"%d\"\n" - , - m->mountpoint, - prots[m->origin_protocol], - m->origin, - m->cache_max_age, - m->cache_reusable, - m->cache_revalidate, - m->cache_intermediaries); - if (m->def) - buf += lws_snprintf(buf, end - buf, - ",\n \"default\":\"%s\"", - m->def); - buf += lws_snprintf(buf, end - buf, "\n }"); - first = 0; - m = m->mount_next; - } - buf += lws_snprintf(buf, end - buf, "\n ]"); - } -#endif - if (vh->protocols) { - n = 0; - first = 1; - - buf += lws_snprintf(buf, end - buf, ",\n \"ws-protocols\":["); - while (n < vh->count_protocols) { - if (!first) - buf += lws_snprintf(buf, end - buf, ","); - buf += lws_snprintf(buf, end - buf, - "\n {\n \"%s\":{\n" - " \"status\":\"ok\"\n }\n }" - , - vh->protocols[n].name); - first = 0; - n++; - } - buf += lws_snprintf(buf, end - buf, "\n ]"); - } - - buf += lws_snprintf(buf, end - buf, "\n}"); - - return buf - orig; -} - - -LWS_EXTERN LWS_VISIBLE int -lws_json_dump_context(const struct lws_context *context, char *buf, int len, - int hide_vhosts) -{ - char *orig = buf, *end = buf + len - 1, first = 1; - const struct lws_vhost *vh = context->vhost_list; - const struct lws_context_per_thread *pt; - time_t t = time(NULL); - int n, listening = 0, cgi_count = 0; - struct lws_conn_stats cs; - double d = 0; -#ifdef LWS_WITH_CGI - struct lws_cgi * const *pcgi; -#endif - -#ifdef LWS_WITH_LIBUV - uv_uptime(&d); -#endif - - buf += lws_snprintf(buf, end - buf, "{ " - "\"version\":\"%s\",\n" - "\"uptime\":\"%ld\",\n", - lws_get_library_version(), - (long)d); - -#ifdef LWS_HAVE_GETLOADAVG - { - double d[3]; - int m; - - m = getloadavg(d, 3); - for (n = 0; n < m; n++) { - buf += lws_snprintf(buf, end - buf, - "\"l%d\":\"%.2f\",\n", - n + 1, d[n]); - } - } -#endif - - buf += lws_snprintf(buf, end - buf, "\"contexts\":[\n"); - - buf += lws_snprintf(buf, end - buf, "{ " - "\"context_uptime\":\"%ld\",\n" - "\"cgi_spawned\":\"%d\",\n" - "\"pt_fd_max\":\"%d\",\n" - "\"ah_pool_max\":\"%d\",\n" - "\"deprecated\":\"%d\",\n" - "\"wsi_alive\":\"%d\",\n", - (unsigned long)(t - context->time_up), - context->count_cgi_spawned, - context->fd_limit_per_thread, - context->max_http_header_pool, - context->deprecated, - context->count_wsi_allocated); - - buf += lws_snprintf(buf, end - buf, "\"pt\":[\n "); - for (n = 0; n < context->count_threads; n++) { - pt = &context->pt[n]; - if (n) - buf += lws_snprintf(buf, end - buf, ","); - buf += lws_snprintf(buf, end - buf, - "\n {\n" - " \"fds_count\":\"%d\",\n" - " \"ah_pool_inuse\":\"%d\",\n" - " \"ah_wait_list\":\"%d\"\n" - " }", - pt->fds_count, - pt->http.ah_count_in_use, - pt->http.ah_wait_list_length); - } - - buf += lws_snprintf(buf, end - buf, "]"); - - buf += lws_snprintf(buf, end - buf, ", \"vhosts\":[\n "); - - first = 1; - vh = context->vhost_list; - listening = 0; - cs = context->conn_stats; - lws_sum_stats(context, &cs); - while (vh) { - - if (!hide_vhosts) { - if (!first) - if(buf != end) - *buf++ = ','; - buf += lws_json_dump_vhost(vh, buf, end - buf); - first = 0; - } - if (vh->lserv_wsi) - listening++; - vh = vh->vhost_next; - } - - buf += lws_snprintf(buf, end - buf, - "],\n\"listen_wsi\":\"%d\",\n" - " \"rx\":\"%llu\",\n" - " \"tx\":\"%llu\",\n" - " \"h1_conn\":\"%lu\",\n" - " \"h1_trans\":\"%lu\",\n" - " \"h2_trans\":\"%lu\",\n" - " \"ws_upg\":\"%lu\",\n" - " \"rejected\":\"%lu\",\n" - " \"h2_alpn\":\"%lu\",\n" - " \"h2_subs\":\"%lu\",\n" - " \"h2_upg\":\"%lu\"", - listening, cs.rx, cs.tx, - cs.h1_conn, - cs.h1_trans, - cs.h2_trans, - cs.ws_upg, - cs.rejected, - cs.h2_alpn, - cs.h2_subs, - cs.h2_upg); - -#ifdef LWS_WITH_CGI - for (n = 0; n < context->count_threads; n++) { - pt = &context->pt[n]; - pcgi = &pt->http.cgi_list; - - while (*pcgi) { - pcgi = &(*pcgi)->cgi_list; - - cgi_count++; - } - } -#endif - buf += lws_snprintf(buf, end - buf, ",\n \"cgi_alive\":\"%d\"\n ", - cgi_count); - - buf += lws_snprintf(buf, end - buf, "}"); - - - buf += lws_snprintf(buf, end - buf, "]}\n "); - - return buf - orig; -} - -#endif - -#if defined(LWS_WITH_STATS) - -LWS_VISIBLE LWS_EXTERN uint64_t -lws_stats_get(struct lws_context *context, int index) -{ - if (index >= LWSSTATS_SIZE) - return 0; - - return context->lws_stats[index]; -} - -LWS_VISIBLE LWS_EXTERN void -lws_stats_log_dump(struct lws_context *context) -{ - struct lws_vhost *v = context->vhost_list; - int n; -#if defined(LWS_WITH_PEER_LIMITS) - int m; -#endif - - if (!context->updated) - return; - - context->updated = 0; - - lwsl_notice("\n"); - lwsl_notice("LWS internal statistics dump ----->\n"); - lwsl_notice("LWSSTATS_C_CONNECTIONS: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_CONNECTIONS)); - lwsl_notice("LWSSTATS_C_API_CLOSE: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_API_CLOSE)); - lwsl_notice("LWSSTATS_C_API_READ: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_API_READ)); - lwsl_notice("LWSSTATS_C_API_LWS_WRITE: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_API_LWS_WRITE)); - lwsl_notice("LWSSTATS_C_API_WRITE: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_API_WRITE)); - lwsl_notice("LWSSTATS_C_WRITE_PARTIALS: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_WRITE_PARTIALS)); - lwsl_notice("LWSSTATS_C_WRITEABLE_CB_REQ: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_WRITEABLE_CB_REQ)); - lwsl_notice("LWSSTATS_C_WRITEABLE_CB_EFF_REQ: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_WRITEABLE_CB_EFF_REQ)); - lwsl_notice("LWSSTATS_C_WRITEABLE_CB: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_WRITEABLE_CB)); - lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN)); - lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_FAILED: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_SSL_CONNECTIONS_FAILED)); - lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)); - lwsl_notice("LWSSTATS_C_SSL_CONNS_HAD_RX: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_SSL_CONNS_HAD_RX)); - lwsl_notice("LWSSTATS_C_PEER_LIMIT_AH_DENIED: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_PEER_LIMIT_AH_DENIED)); - lwsl_notice("LWSSTATS_C_PEER_LIMIT_WSI_DENIED: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_PEER_LIMIT_WSI_DENIED)); - - lwsl_notice("LWSSTATS_C_TIMEOUTS: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_TIMEOUTS)); - lwsl_notice("LWSSTATS_C_SERVICE_ENTRY: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_C_SERVICE_ENTRY)); - lwsl_notice("LWSSTATS_B_READ: %8llu\n", - (unsigned long long)lws_stats_get(context, LWSSTATS_B_READ)); - lwsl_notice("LWSSTATS_B_WRITE: %8llu\n", - (unsigned long long)lws_stats_get(context, LWSSTATS_B_WRITE)); - lwsl_notice("LWSSTATS_B_PARTIALS_ACCEPTED_PARTS: %8llu\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_B_PARTIALS_ACCEPTED_PARTS)); - lwsl_notice("LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY: %8llums\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY) / 1000); - if (lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)) - lwsl_notice(" Avg accept delay: %8llums\n", - (unsigned long long)(lws_stats_get(context, - LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY) / - lws_stats_get(context, - LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)) / 1000); - lwsl_notice("LWSSTATS_MS_SSL_RX_DELAY: %8llums\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_MS_SSL_RX_DELAY) / 1000); - if (lws_stats_get(context, LWSSTATS_C_SSL_CONNS_HAD_RX)) - lwsl_notice(" Avg accept-rx delay: %8llums\n", - (unsigned long long)(lws_stats_get(context, - LWSSTATS_MS_SSL_RX_DELAY) / - lws_stats_get(context, - LWSSTATS_C_SSL_CONNS_HAD_RX)) / 1000); - - lwsl_notice("LWSSTATS_MS_WRITABLE_DELAY: %8lluus\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_MS_WRITABLE_DELAY)); - lwsl_notice("LWSSTATS_MS_WORST_WRITABLE_DELAY: %8lluus\n", - (unsigned long long)lws_stats_get(context, - LWSSTATS_MS_WORST_WRITABLE_DELAY)); - if (lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB)) - lwsl_notice(" Avg writable delay: %8lluus\n", - (unsigned long long)(lws_stats_get(context, - LWSSTATS_MS_WRITABLE_DELAY) / - lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB))); - lwsl_notice("Simultaneous SSL restriction: %8d/%d\n", - context->simultaneous_ssl, - context->simultaneous_ssl_restriction); - - lwsl_notice("Live wsi: %8d\n", - context->count_wsi_allocated); - - context->updated = 1; - - while (v) { - if (v->lserv_wsi && - v->lserv_wsi->position_in_fds_table != LWS_NO_FDS_POS) { - - struct lws_context_per_thread *pt = - &context->pt[(int)v->lserv_wsi->tsi]; - struct lws_pollfd *pfd; - - pfd = &pt->fds[v->lserv_wsi->position_in_fds_table]; - - lwsl_notice(" Listen port %d actual POLLIN: %d\n", - v->listen_port, - (int)pfd->events & LWS_POLLIN); - } - - v = v->vhost_next; - } - - for (n = 0; n < context->count_threads; n++) { - struct lws_context_per_thread *pt = &context->pt[n]; - struct lws *wl; - int m = 0; - - lwsl_notice("PT %d\n", n + 1); - - lws_pt_lock(pt, __func__); - - lwsl_notice(" AH in use / max: %d / %d\n", - pt->http.ah_count_in_use, - context->max_http_header_pool); - - wl = pt->http.ah_wait_list; - while (wl) { - m++; - wl = wl->http.ah_wait_list; - } - - lwsl_notice(" AH wait list count / actual: %d / %d\n", - pt->http.ah_wait_list_length, m); - - lws_pt_unlock(pt); - } - -#if defined(LWS_WITH_PEER_LIMITS) - m = 0; - for (n = 0; n < (int)context->pl_hash_elements; n++) { - lws_start_foreach_llp(struct lws_peer **, peer, - context->pl_hash_table[n]) { - m++; - } lws_end_foreach_llp(peer, next); - } - - lwsl_notice(" Peers: total active %d\n", m); - if (m > 10) { - m = 10; - lwsl_notice(" (showing 10 peers only)\n"); - } - - if (m) { - for (n = 0; n < (int)context->pl_hash_elements; n++) { - char buf[72]; - - lws_start_foreach_llp(struct lws_peer **, peer, - context->pl_hash_table[n]) { - struct lws_peer *df = *peer; - - if (!lws_plat_inet_ntop(df->af, df->addr, buf, - sizeof(buf) - 1)) - strcpy(buf, "unknown"); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - lwsl_notice(" peer %s: count wsi: %d, count ah: %d\n", - buf, df->count_wsi, - df->http.count_ah); -#else - lwsl_notice(" peer %s: count wsi: %d\n", - buf, df->count_wsi); -#endif - - if (!--m) - break; - } lws_end_foreach_llp(peer, next); - } - } -#endif - - lwsl_notice("\n"); -} - -void -lws_stats_atomic_bump(struct lws_context * context, - struct lws_context_per_thread *pt, int index, uint64_t bump) -{ - lws_pt_stats_lock(pt); - context->lws_stats[index] += bump; - if (index != LWSSTATS_C_SERVICE_ENTRY) - context->updated = 1; - lws_pt_stats_unlock(pt); -} - -void -lws_stats_atomic_max(struct lws_context * context, - struct lws_context_per_thread *pt, int index, uint64_t val) -{ - lws_pt_stats_lock(pt); - if (val > context->lws_stats[index]) { - context->lws_stats[index] = val; - context->updated = 1; - } - lws_pt_stats_unlock(pt); -} - -#endif - diff --git a/lib/core/logs.c b/lib/core/logs.c new file mode 100644 index 0000000000..278c847361 --- /dev/null +++ b/lib/core/logs.c @@ -0,0 +1,273 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + +#ifdef LWS_HAVE_SYS_TYPES_H +#include +#endif + +int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE; +static void (*lwsl_emit)(int level, const char *line) +#ifndef LWS_PLAT_OPTEE + = lwsl_emit_stderr +#endif + ; +#ifndef LWS_PLAT_OPTEE +static const char * const log_level_names[] = { + "ERR", + "WARN", + "NOTICE", + "INFO", + "DEBUG", + "PARSER", + "HEADER", + "EXTENSION", + "CLIENT", + "LATENCY", + "USER", + "THREAD", + "?", + "?" +}; +#endif + +LWS_VISIBLE int +lwsl_timestamp(int level, char *p, int len) +{ +#ifndef LWS_PLAT_OPTEE +#ifndef _WIN32_WCE + time_t o_now = time(NULL); +#endif + unsigned long long now; + struct tm *ptm = NULL; +#ifndef WIN32 + struct tm tm; +#endif + int n; + +#ifndef _WIN32_WCE +#ifdef WIN32 + ptm = localtime(&o_now); +#else + if (localtime_r(&o_now, &tm)) + ptm = &tm; +#endif +#endif + p[0] = '\0'; + for (n = 0; n < LLL_COUNT; n++) { + if (level != (1 << n)) + continue; + now = lws_time_in_microseconds() / 100; + if (ptm) + n = lws_snprintf(p, len, + "[%04d/%02d/%02d %02d:%02d:%02d:%04d] %s: ", + ptm->tm_year + 1900, + ptm->tm_mon + 1, + ptm->tm_mday, + ptm->tm_hour, + ptm->tm_min, + ptm->tm_sec, + (int)(now % 10000), log_level_names[n]); + else + n = lws_snprintf(p, len, "[%llu:%04d] %s: ", + (unsigned long long) now / 10000, + (int)(now % 10000), log_level_names[n]); + return n; + } +#else + p[0] = '\0'; +#endif + + return 0; +} + +#ifndef LWS_PLAT_OPTEE +static const char * const colours[] = { + "[31;1m", /* LLL_ERR */ + "[36;1m", /* LLL_WARN */ + "[35;1m", /* LLL_NOTICE */ + "[32;1m", /* LLL_INFO */ + "[34;1m", /* LLL_DEBUG */ + "[33;1m", /* LLL_PARSER */ + "[33m", /* LLL_HEADER */ + "[33m", /* LLL_EXT */ + "[33m", /* LLL_CLIENT */ + "[33;1m", /* LLL_LATENCY */ + "[30;1m", /* LLL_USER */ + "[31m", /* LLL_THREAD */ +}; + +static char tty; + +LWS_VISIBLE void +lwsl_emit_stderr(int level, const char *line) +{ + char buf[50]; + int n, m = LWS_ARRAY_SIZE(colours) - 1; + + if (!tty) + tty = isatty(2) | 2; + lwsl_timestamp(level, buf, sizeof(buf)); + + if (tty == 3) { + n = 1 << (LWS_ARRAY_SIZE(colours) - 1); + while (n) { + if (level & n) + break; + m--; + n >>= 1; + } + fprintf(stderr, "%c%s%s%s%c[0m", 27, colours[m], buf, line, 27); + } else + fprintf(stderr, "%s%s", buf, line); +} + +LWS_VISIBLE void +lwsl_emit_stderr_notimestamp(int level, const char *line) +{ + int n, m = LWS_ARRAY_SIZE(colours) - 1; + + if (!tty) + tty = isatty(2) | 2; + + if (tty == 3) { + n = 1 << (LWS_ARRAY_SIZE(colours) - 1); + while (n) { + if (level & n) + break; + m--; + n >>= 1; + } + fprintf(stderr, "%c%s%s%c[0m", 27, colours[m], line, 27); + } else + fprintf(stderr, "%s", line); +} + +#endif + +LWS_VISIBLE void _lws_logv(int filter, const char *format, va_list vl) +{ + char buf[256]; + int n; + + if (!(log_level & filter)) + return; + + n = vsnprintf(buf, sizeof(buf) - 1, format, vl); + (void)n; + /* vnsprintf returns what it would have written, even if truncated */ + if (n > (int)sizeof(buf) - 1) { + n = sizeof(buf) - 5; + buf[n++] = '.'; + buf[n++] = '.'; + buf[n++] = '.'; + buf[n++] = '\n'; + buf[n] = '\0'; + } + if (n > 0) + buf[n] = '\0'; + + lwsl_emit(filter, buf); +} + +LWS_VISIBLE void _lws_log(int filter, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _lws_logv(filter, format, ap); + va_end(ap); +} + +LWS_VISIBLE void lws_set_log_level(int level, + void (*func)(int level, const char *line)) +{ + log_level = level; + if (func) + lwsl_emit = func; +} + +LWS_VISIBLE int lwsl_visible(int level) +{ + return log_level & level; +} + +LWS_VISIBLE void +lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len) +{ + unsigned char *buf = (unsigned char *)vbuf; + unsigned int n; + + if (!lwsl_visible(hexdump_level)) + return; + + if (!len) { + _lws_log(hexdump_level, "(hexdump: zero length)\n"); + return; + } + + if (!vbuf) { + _lws_log(hexdump_level, "(hexdump: trying to dump %d at NULL)\n", + (int)len); + return; + } + + _lws_log(hexdump_level, "\n"); + + for (n = 0; n < len;) { + unsigned int start = n, m; + char line[80], *p = line; + + p += snprintf(p, 10, "%04X: ", start); + + for (m = 0; m < 16 && n < len; m++) + p += snprintf(p, 5, "%02X ", buf[n++]); + while (m++ < 16) + p += snprintf(p, 5, " "); + + p += snprintf(p, 6, " "); + + for (m = 0; m < 16 && (start + m) < len; m++) { + if (buf[start + m] >= ' ' && buf[start + m] < 127) + *p++ = buf[start + m]; + else + *p++ = '.'; + } + while (m++ < 16) + *p++ = ' '; + + *p++ = '\n'; + *p = '\0'; + _lws_log(hexdump_level, "%s", line); + (void)line; + } + + _lws_log(hexdump_level, "\n"); +} + +LWS_VISIBLE void +lwsl_hexdump(const void *vbuf, size_t len) +{ +#if defined(_DEBUG) + lwsl_hexdump_level(LLL_DEBUG, vbuf, len); +#endif +} diff --git a/lib/core/private.h b/lib/core/private.h index 43aa14a5f0..78ff9477a3 100644 --- a/lib/core/private.h +++ b/lib/core/private.h @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010 - 2018 Andy Green + * Copyright (C) 2010 - 2019 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -99,6 +99,7 @@ #define strerror(x) "" #endif + /* * * ------ private platform defines ------ @@ -133,7 +134,6 @@ #include "libwebsockets.h" - #include "tls/private.h" #if defined(WIN32) || defined(_WIN32) @@ -154,22 +154,7 @@ extern "C" { #endif -/* - * All lws_tls...() functions must return this type, converting the - * native backend result and doing the extra work to determine which one - * as needed. - * - * Native TLS backend return codes are NOT ALLOWED outside the backend. - * - * Non-SSL mode also uses these types. - */ -enum lws_ssl_capable_status { - LWS_SSL_CAPABLE_ERROR = -1, /* it failed */ - LWS_SSL_CAPABLE_DONE = 0, /* it succeeded */ - LWS_SSL_CAPABLE_MORE_SERVICE_READ = -2, /* retry WANT_READ */ - LWS_SSL_CAPABLE_MORE_SERVICE_WRITE = -3, /* retry WANT_WRITE */ - LWS_SSL_CAPABLE_MORE_SERVICE = -4, /* general retry */ -}; + #if defined(__clang__) #define lws_memory_barrier() __sync_synchronize() @@ -179,103 +164,6 @@ enum lws_ssl_capable_status { #define lws_memory_barrier() #endif -/* - * - * ------ roles ------ - * - */ - -#include "roles/private.h" - -/* null-terminated array of pointers to roles lws built with */ -extern const struct lws_role_ops *available_roles[]; - -#define LWS_FOR_EVERY_AVAILABLE_ROLE_START(xx) { \ - const struct lws_role_ops **ppxx = available_roles; \ - while (*ppxx) { \ - const struct lws_role_ops *xx = *ppxx++; - -#define LWS_FOR_EVERY_AVAILABLE_ROLE_END }} - -/* - * - * ------ event_loop ops ------ - * - */ - -#include "event-libs/private.h" - -/* enums of socks version */ -enum socks_version { - SOCKS_VERSION_4 = 4, - SOCKS_VERSION_5 = 5 -}; - -/* enums of subnegotiation version */ -enum socks_subnegotiation_version { - SOCKS_SUBNEGOTIATION_VERSION_1 = 1, -}; - -/* enums of socks commands */ -enum socks_command { - SOCKS_COMMAND_CONNECT = 1, - SOCKS_COMMAND_BIND = 2, - SOCKS_COMMAND_UDP_ASSOCIATE = 3 -}; - -/* enums of socks address type */ -enum socks_atyp { - SOCKS_ATYP_IPV4 = 1, - SOCKS_ATYP_DOMAINNAME = 3, - SOCKS_ATYP_IPV6 = 4 -}; - -/* enums of socks authentication methods */ -enum socks_auth_method { - SOCKS_AUTH_NO_AUTH = 0, - SOCKS_AUTH_GSSAPI = 1, - SOCKS_AUTH_USERNAME_PASSWORD = 2 -}; - -/* enums of subnegotiation status */ -enum socks_subnegotiation_status { - SOCKS_SUBNEGOTIATION_STATUS_SUCCESS = 0, -}; - -/* enums of socks request reply */ -enum socks_request_reply { - SOCKS_REQUEST_REPLY_SUCCESS = 0, - SOCKS_REQUEST_REPLY_FAILURE_GENERAL = 1, - SOCKS_REQUEST_REPLY_CONNECTION_NOT_ALLOWED = 2, - SOCKS_REQUEST_REPLY_NETWORK_UNREACHABLE = 3, - SOCKS_REQUEST_REPLY_HOST_UNREACHABLE = 4, - SOCKS_REQUEST_REPLY_CONNECTION_REFUSED = 5, - SOCKS_REQUEST_REPLY_TTL_EXPIRED = 6, - SOCKS_REQUEST_REPLY_COMMAND_NOT_SUPPORTED = 7, - SOCKS_REQUEST_REPLY_ATYP_NOT_SUPPORTED = 8 -}; - -/* enums used to generate socks messages */ -enum socks_msg_type { - /* greeting */ - SOCKS_MSG_GREETING, - /* credential, user name and password */ - SOCKS_MSG_USERNAME_PASSWORD, - /* connect command */ - SOCKS_MSG_CONNECT -}; - -enum { - LWS_RXFLOW_ALLOW = (1 << 0), - LWS_RXFLOW_PENDING_CHANGE = (1 << 1), -}; - -enum lws_parser_return { - LPR_OK = 0, - LPR_FAIL = -1, - LPR_DO_FALLBACK = 2, - LPR_FORBIDDEN = -2 -}; struct lws_ring { void *buf; @@ -289,6 +177,11 @@ struct lws_ring { struct lws_protocols; struct lws; +#if defined(LWS_WITH_NETWORK) +#include "event-libs/private.h" +#endif + + struct lws_io_watcher { #ifdef LWS_WITH_LIBEV struct lws_io_watcher_libev ev; @@ -347,227 +240,11 @@ void lws_mutex_refcount_unlock(struct lws_mutex_refcount *mr); #endif -#define LWS_HRTIMER_NOWAIT (0x7fffffffffffffffll) - -/* - * so we can have n connections being serviced simultaneously, - * these things need to be isolated per-thread. - */ - -struct lws_context_per_thread { -#if LWS_MAX_SMP > 1 - pthread_mutex_t lock_stats; - struct lws_mutex_refcount mr; - pthread_t self; -#endif - - struct lws_context *context; - - /* - * usable by anything in the service code, but only if the scope - * does not last longer than the service action (since next service - * of any socket can likewise use it and overwrite) - */ - unsigned char *serv_buf; - - struct lws_dll_lws dll_head_timeout; - struct lws_dll_lws dll_head_hrtimer; - struct lws_dll_lws dll_head_buflist; /* guys with pending rxflow */ - -#if defined(LWS_WITH_TLS) - struct lws_pt_tls tls; -#endif - - struct lws_pollfd *fds; - volatile struct lws_foreign_thread_pollfd * volatile foreign_pfd_list; -#ifdef _WIN32 - WSAEVENT events; -#endif - lws_sockfd_type dummy_pipe_fds[2]; - struct lws *pipe_wsi; - - /* --- role based members --- */ - -#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_pt_role_ws ws; -#endif -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - struct lws_pt_role_http http; -#endif -#if defined(LWS_ROLE_DBUS) - struct lws_pt_role_dbus dbus; -#endif - - /* --- event library based members --- */ - -#if defined(LWS_WITH_LIBEV) - struct lws_pt_eventlibs_libev ev; -#endif -#if defined(LWS_WITH_LIBUV) - struct lws_pt_eventlibs_libuv uv; -#endif -#if defined(LWS_WITH_LIBEVENT) - struct lws_pt_eventlibs_libevent event; -#endif - -#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || \ - defined(LWS_WITH_LIBEVENT) - struct lws_signal_watcher w_sigint; -#endif - - /* --- */ - - unsigned long count_conns; - unsigned int fds_count; - - /* - * set to the Thread ID that's doing the service loop just before entry - * to poll indicates service thread likely idling in poll() - * volatile because other threads may check it as part of processing - * for pollfd event change. - */ - volatile int service_tid; - int service_tid_detected; - - volatile unsigned char inside_poll; - volatile unsigned char foreign_spinlock; - - unsigned char tid; - - unsigned char inside_service:1; - unsigned char event_loop_foreign:1; - unsigned char event_loop_destroy_processing_done:1; -}; - -struct lws_conn_stats { - unsigned long long rx, tx; - unsigned long h1_conn, h1_trans, h2_trans, ws_upg, h2_alpn, h2_subs, - h2_upg, rejected; -}; - -void -lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs); - -struct lws_timed_vh_protocol { - struct lws_timed_vh_protocol *next; - const struct lws_protocols *protocol; - struct lws_vhost *vhost; /* only used for pending processing */ - time_t time; - int reason; - int tsi_req; -}; - -/* - * virtual host -related context information - * vhostwide SSL context - * vhostwide proxy - * - * hierarchy: - * - * context -> vhost -> wsi - * - * incoming connection non-SSL vhost binding: - * - * listen socket -> wsi -> select vhost after first headers - * - * incoming connection SSL vhost binding: - * - * SSL SNI -> wsi -> bind after SSL negotiation - */ - - -struct lws_vhost { -#if !defined(LWS_WITHOUT_CLIENT) - char proxy_basic_auth_token[128]; -#endif -#if LWS_MAX_SMP > 1 - pthread_mutex_t lock; - char close_flow_vs_tsi[LWS_MAX_SMP]; +#if defined(LWS_WITH_NETWORK) +#include "core-net/private.h" #endif -#if defined(LWS_ROLE_H2) - struct lws_vhost_role_h2 h2; -#endif -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - struct lws_vhost_role_http http; -#endif -#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) - struct lws_vhost_role_ws ws; -#endif - -#if defined(LWS_WITH_SOCKS5) - char socks_proxy_address[128]; - char socks_user[96]; - char socks_password[96]; -#endif -#if defined(LWS_WITH_LIBEV) - struct lws_io_watcher w_accept; -#endif - struct lws_conn_stats conn_stats; - struct lws_context *context; - struct lws_vhost *vhost_next; - - struct lws *lserv_wsi; - const char *name; - const char *iface; - const char *listen_accept_role; - const char *listen_accept_protocol; - - void (*finalize)(struct lws_vhost *vh, void *arg); - void *finalize_arg; - -#if !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32) - int bind_iface; -#endif - const struct lws_protocols *protocols; - void **protocol_vh_privs; - const struct lws_protocol_vhost_options *pvo; - const struct lws_protocol_vhost_options *headers; - struct lws_dll_lws *same_vh_protocol_heads; - struct lws_vhost *no_listener_vhost_list; -#if !defined(LWS_NO_CLIENT) - struct lws_dll_lws dll_active_client_conns; -#endif - -#if defined(LWS_WITH_TLS) - struct lws_vhost_tls tls; -#endif - - struct lws_timed_vh_protocol *timed_vh_protocol_list; - void *user; - - int listen_port; - -#if defined(LWS_WITH_SOCKS5) - unsigned int socks_proxy_port; -#endif - unsigned int options; - int count_protocols; - int ka_time; - int ka_probes; - int ka_interval; - int keepalive_timeout; - int timeout_secs_ah_idle; - - int count_bound_wsi; - -#ifdef LWS_WITH_ACCESS_LOG - int log_fd; -#endif - - unsigned int created_vhost_protocols:1; - unsigned int being_destroyed:1; - - unsigned char default_protocol_index; - unsigned char raw_protocol_index; -}; - -void -lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi); -void -lws_vhost_unbind_wsi(struct lws *wsi); -void -__lws_vhost_destroy2(struct lws_vhost *vh); +#define LWS_HRTIMER_NOWAIT (0x7fffffffffffffffll) struct lws_deferred_free { @@ -576,35 +253,6 @@ struct lws_deferred_free void *payload; }; -typedef union { -#ifdef LWS_WITH_IPV6 - struct sockaddr_in6 sa6; -#endif - struct sockaddr_in sa4; -} sockaddr46; - - -#if defined(LWS_WITH_PEER_LIMITS) -struct lws_peer { - struct lws_peer *next; - struct lws_peer *peer_wait_list; - - time_t time_created; - time_t time_closed_all; - - uint8_t addr[32]; - uint32_t hash; - uint32_t count_wsi; - uint32_t total_wsi; - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - struct lws_peer_role_http http; -#endif - - uint8_t af; -}; -#endif - /* * the rest is managed per-context, that includes * @@ -630,21 +278,24 @@ struct lws_context { #if defined(LWS_WITH_ZIP_FOPS) struct lws_plat_file_ops fops_zip; #endif +#if defined(LWS_WITH_NETWORK) struct lws_context_per_thread pt[LWS_MAX_SMP]; struct lws_conn_stats conn_stats; -#if LWS_MAX_SMP > 1 - struct lws_mutex_refcount mr; -#endif + struct lws_vhost *vhost_list; + struct lws_vhost *no_listener_vhost_list; + struct lws_vhost *vhost_pending_destruction_list; + struct lws_plugin *plugin_list; #ifdef _WIN32 /* different implementation between unix and windows */ struct lws_fd_hashtable fd_hashtable[FD_HASHTABLE_MODULUS]; #else struct lws **lws_lookup; /* fd to wsi */ #endif - struct lws_vhost *vhost_list; - struct lws_vhost *no_listener_vhost_list; - struct lws_vhost *vhost_pending_destruction_list; - struct lws_plugin *plugin_list; +#endif +#if LWS_MAX_SMP > 1 + struct lws_mutex_refcount mr; +#endif + struct lws_deferred_free *deferred_free_list; #if defined(LWS_WITH_THREADPOOL) @@ -681,7 +332,7 @@ struct lws_context { struct lws_event_loop_ops *event_loop_ops; -#if defined(LWS_WITH_TLS) +#if defined(LWS_WITH_TLS) && defined(LWS_WITH_NETWORK) struct lws_context_tls tls; #endif @@ -752,89 +403,18 @@ lws_check_deferred_free(struct lws_context *context, int tsi, int force); #define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x] #define lws_get_vh_protocol(vh, x) vh->protocols[x] -LWS_EXTERN void -__lws_close_free_wsi_final(struct lws *wsi); -LWS_EXTERN void -lws_libuv_closehandle(struct lws *wsi); -LWS_EXTERN int -lws_libuv_check_watcher_active(struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN int -lws_plat_plugins_init(struct lws_context * context, const char * const *d); - -LWS_VISIBLE LWS_EXTERN int -lws_plat_plugins_destroy(struct lws_context * context); - -LWS_EXTERN void -lws_restart_ws_ping_pong_timer(struct lws *wsi); - -struct lws * -lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd); - int lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max); void lws_vhost_destroy1(struct lws_vhost *vh); -enum { - LWS_EV_READ = (1 << 0), - LWS_EV_WRITE = (1 << 1), - LWS_EV_START = (1 << 2), - LWS_EV_STOP = (1 << 3), - - LWS_EV_PREPARE_DELETION = (1u << 31), -}; - #if defined(LWS_WITH_ESP32) LWS_EXTERN int lws_find_string_in_file(const char *filename, const char *str, int stringlen); #endif -#ifdef LWS_WITH_IPV6 -#define LWS_IPV6_ENABLED(vh) \ - (!lws_check_opt(vh->context->options, LWS_SERVER_OPTION_DISABLE_IPV6) && \ - !lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_IPV6)) -#else -#define LWS_IPV6_ENABLED(context) (0) -#endif - -#ifdef LWS_WITH_UNIX_SOCK -#define LWS_UNIX_SOCK_ENABLED(vhost) \ - (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK) -#else -#define LWS_UNIX_SOCK_ENABLED(vhost) (0) -#endif - -enum uri_path_states { - URIPS_IDLE, - URIPS_SEEN_SLASH, - URIPS_SEEN_SLASH_DOT, - URIPS_SEEN_SLASH_DOT_DOT, -}; - -enum uri_esc_states { - URIES_IDLE, - URIES_SEEN_PERCENT, - URIES_SEEN_PERCENT_H1, -}; - - -#ifndef LWS_NO_CLIENT -struct client_info_stash { - char *address; - char *path; - char *host; - char *origin; - char *protocol; - char *method; - char *iface; - char *alpn; - void *opaque_user_data; /* not allocated or freed by lws */ -}; -#endif - signed char char_to_hex(const char c); @@ -848,231 +428,13 @@ struct lws_buflist { uint8_t buf[1]; /* true length of this is set by the oversize malloc */ }; -#define lws_wsi_is_udp(___wsi) (!!___wsi->udp) - -#define LWS_H2_FRAME_HEADER_LENGTH 9 - - -struct lws { - /* structs */ - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - struct _lws_http_mode_related http; -#endif -#if defined(LWS_ROLE_H2) - struct _lws_h2_related h2; -#endif -#if defined(LWS_ROLE_WS) - struct _lws_websocket_related *ws; /* allocated if we upgrade to ws */ -#endif -#if defined(LWS_ROLE_DBUS) - struct _lws_dbus_mode_related dbus; -#endif - - const struct lws_role_ops *role_ops; - lws_wsi_state_t wsistate; - lws_wsi_state_t wsistate_pre_close; - - /* lifetime members */ - -#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || \ - defined(LWS_WITH_LIBEVENT) - struct lws_io_watcher w_read; -#endif -#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBEVENT) - struct lws_io_watcher w_write; -#endif - - /* pointers */ - - struct lws_context *context; - struct lws_vhost *vhost; - struct lws *parent; /* points to parent, if any */ - struct lws *child_list; /* points to first child */ - struct lws *sibling_list; /* subsequent children at same level */ - - const struct lws_protocols *protocol; - struct lws_dll_lws same_vh_protocol; - - struct lws_dll_lws dll_timeout; - struct lws_dll_lws dll_hrtimer; - struct lws_dll_lws dll_buflist; /* guys with pending rxflow */ - -#if defined(LWS_WITH_THREADPOOL) - struct lws_threadpool_task *tp_task; -#endif - -#if defined(LWS_WITH_PEER_LIMITS) - struct lws_peer *peer; -#endif - - struct lws_udp *udp; -#ifndef LWS_NO_CLIENT - struct client_info_stash *stash; - char *client_hostname_copy; - struct lws_dll_lws dll_active_client_conns; - struct lws_dll_lws dll_client_transaction_queue_head; - struct lws_dll_lws dll_client_transaction_queue; -#endif - void *user_space; - void *opaque_parent_data; - void *opaque_user_data; - - struct lws_buflist *buflist; /* input-side buflist */ - struct lws_buflist *buflist_out; /* output-side buflist */ - -#if defined(LWS_WITH_TLS) - struct lws_lws_tls tls; -#endif - - lws_sock_file_fd_type desc; /* .filefd / .sockfd */ -#if defined(LWS_WITH_STATS) - uint64_t active_writable_req_us; -#if defined(LWS_WITH_TLS) - uint64_t accept_start_us; -#endif -#endif - - lws_usec_t pending_timer; /* hrtimer fires */ - time_t pending_timeout_set; /* second-resolution timeout start */ - -#ifdef LWS_LATENCY - unsigned long action_start; - unsigned long latency_start; -#endif - - /* ints */ -#define LWS_NO_FDS_POS (-1) - int position_in_fds_table; - -#ifndef LWS_NO_CLIENT - int chunk_remaining; -#endif - unsigned int cache_secs; - - unsigned int hdr_parsing_completed:1; - unsigned int http2_substream:1; - unsigned int upgraded_to_http2:1; - unsigned int h2_stream_carries_ws:1; - unsigned int seen_nonpseudoheader:1; - unsigned int listener:1; - unsigned int user_space_externally_allocated:1; - unsigned int socket_is_permanently_unusable:1; - unsigned int rxflow_change_to:2; - unsigned int conn_stat_done:1; - unsigned int cache_reuse:1; - unsigned int cache_revalidate:1; - unsigned int cache_intermediaries:1; - unsigned int favoured_pollin:1; - unsigned int sending_chunked:1; - unsigned int interpreting:1; - unsigned int already_did_cce:1; - unsigned int told_user_closed:1; - unsigned int told_event_loop_closed:1; - unsigned int waiting_to_send_close_frame:1; - unsigned int close_needs_ack:1; - unsigned int ipv6:1; - unsigned int parent_pending_cb_on_writable:1; - unsigned int cgi_stdout_zero_length:1; - unsigned int seen_zero_length_recv:1; - unsigned int rxflow_will_be_applied:1; - unsigned int event_pipe:1; - unsigned int handling_404:1; - unsigned int protocol_bind_balance:1; - unsigned int unix_skt:1; - unsigned int close_when_buffered_out_drained:1; - - unsigned int could_have_pending:1; /* detect back-to-back writes */ - unsigned int outer_will_close:1; - unsigned int shadow:1; /* we do not control fd lifecycle at all */ - -#ifdef LWS_WITH_ACCESS_LOG - unsigned int access_log_pending:1; -#endif -#ifndef LWS_NO_CLIENT - unsigned int do_ws:1; /* whether we are doing http or ws flow */ - unsigned int chunked:1; /* if the clientside connection is chunked */ - unsigned int client_rx_avail:1; - unsigned int client_http_body_pending:1; - unsigned int transaction_from_pipeline_queue:1; - unsigned int keepalive_active:1; - unsigned int keepalive_rejected:1; - unsigned int client_pipeline:1; - unsigned int client_h2_alpn:1; - unsigned int client_h2_substream:1; -#endif - -#ifdef _WIN32 - unsigned int sock_send_blocking:1; -#endif - -#ifndef LWS_NO_CLIENT - unsigned short c_port; -#endif - unsigned short pending_timeout_limit; - - /* chars */ - - char lws_rx_parse_state; /* enum lws_rx_parse_state */ - char rx_frame_type; /* enum lws_write_protocol */ - char pending_timeout; /* enum pending_timeout */ - char tsi; /* thread service index we belong to */ - char protocol_interpret_idx; - char redirects; - uint8_t rxflow_bitmap; -#ifdef LWS_WITH_CGI - char cgi_channel; /* which of stdin/out/err */ - char hdr_state; -#endif -#ifndef LWS_NO_CLIENT - char chunk_parser; /* enum lws_chunk_parser */ -#endif -#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT) - char reason_bf; /* internal writeable callback reason bitfield */ -#endif -#if defined(LWS_WITH_STATS) && defined(LWS_WITH_TLS) - char seen_rx; -#endif - uint8_t ws_over_h2_count; - /* volatile to make sure code is aware other thread can change */ - volatile char handling_pollout; - volatile char leave_pollout_active; -}; - -const struct lws_role_ops * -lws_role_by_name(const char *name); LWS_EXTERN char * lws_strdup(const char *s); -#define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap)) - -void -lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt); - LWS_EXTERN int log_level; -LWS_EXTERN int -lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, - const char *iface); -#if defined(LWS_WITH_IPV6) -LWS_EXTERN unsigned long -lws_get_addr_scope(const char *ipaddr); -#endif - -LWS_EXTERN void -lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller); -LWS_EXTERN void -__lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller); - -LWS_EXTERN void -__lws_free_wsi(struct lws *wsi); - -LWS_EXTERN int -__remove_wsi_socket_from_fds(struct lws *wsi); -LWS_EXTERN int -lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len); #ifndef LWS_LATENCY static LWS_INLINE void @@ -1094,144 +456,13 @@ lws_latency(struct lws_context *context, struct lws *wsi, const char *action, int ret, int completion); #endif -static LWS_INLINE int -lws_has_buffered_out(struct lws *wsi) { return !!wsi->buflist_out; } - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ws_client_rx_sm(struct lws *wsi, unsigned char c); - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_parse(struct lws *wsi, unsigned char *buf, int *len); - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_parse_urldecode(struct lws *wsi, uint8_t *_c); - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_http_action(struct lws *wsi); LWS_EXTERN int lws_b64_selftest(void); -LWS_EXTERN int -lws_service_flag_pending(struct lws_context *context, int tsi); - -LWS_EXTERN int -__lws_timed_callback_remove(struct lws_vhost *vh, struct lws_timed_vh_protocol *p); - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -__insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi); - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len); - -LWS_EXTERN void -lws_remove_from_timeout_list(struct lws *wsi); - -LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT -lws_client_connect_2(struct lws *wsi); - -LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT -lws_client_reset(struct lws **wsi, int ssl, const char *address, int port, - const char *path, const char *host); - -LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT -lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi); - -LWS_EXTERN char * LWS_WARN_UNUSED_RESULT -lws_generate_client_handshake(struct lws *wsi, char *pkt); - -LWS_EXTERN int -lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); - -LWS_EXTERN struct lws * -lws_http_client_connect_via_info2(struct lws *wsi); - - - -LWS_EXTERN void -lws_client_stash_destroy(struct lws *wsi); - -/* - * EXTENSIONS - */ - -#if defined(LWS_WITHOUT_EXTENSIONS) -#define lws_any_extension_handled(_a, _b, _c, _d) (0) -#define lws_ext_cb_active(_a, _b, _c, _d) (0) -#define lws_ext_cb_all_exts(_a, _b, _c, _d, _e) (0) -#define lws_issue_raw_ext_access lws_issue_raw -#define lws_context_init_extensions(_a, _b) -#endif - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_client_interpret_server_handshake(struct lws *wsi); -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c); - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len); - -LWS_EXTERN void -lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state, - const struct lws_role_ops *ops); - -int -lws_http_to_fallback(struct lws *wsi, unsigned char *buf, size_t len); -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -user_callback_handle_rxflow(lws_callback_function, struct lws *wsi, - enum lws_callback_reasons reason, void *user, - void *in, size_t len); - -LWS_EXTERN int -lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd, - int unix_skt); -LWS_EXTERN int -lws_plat_check_connection_error(struct lws *wsi); - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_header_table_attach(struct lws *wsi, int autoservice); - -LWS_EXTERN int -lws_header_table_detach(struct lws *wsi, int autoservice); -LWS_EXTERN int -__lws_header_table_detach(struct lws *wsi, int autoservice); - -LWS_EXTERN void -lws_header_table_reset(struct lws *wsi, int autoservice); - -void -__lws_header_table_reset(struct lws *wsi, int autoservice); - -LWS_EXTERN char * LWS_WARN_UNUSED_RESULT -lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h); - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s); - -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ensure_user_space(struct lws *wsi); - -LWS_EXTERN int -lws_change_pollfd(struct lws *wsi, int _and, int _or); - -#ifndef LWS_NO_SERVER - int _lws_vhost_init_server(const struct lws_context_creation_info *info, - struct lws_vhost *vhost); - LWS_EXTERN struct lws_vhost * - lws_select_vhost(struct lws_context *context, int port, const char *servername); - LWS_EXTERN int LWS_WARN_UNUSED_RESULT - lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len); - LWS_EXTERN void - lws_server_get_canonical_hostname(struct lws_context *context, - const struct lws_context_creation_info *info); -#else - #define _lws_vhost_init_server(_a, _b) (0) - #define lws_parse_ws(_a, _b, _c) (0) - #define lws_server_get_canonical_hostname(_a, _b) -#endif #ifndef LWS_NO_DAEMONIZE LWS_EXTERN int get_daemonize_pid(); @@ -1264,37 +495,8 @@ LWS_EXTERN void lwsl_emit_stderr(int level, const char *line); #endif -#if LWS_MAX_SMP > 1 - -static LWS_INLINE void -lws_pt_mutex_init(struct lws_context_per_thread *pt) -{ - lws_mutex_refcount_init(&pt->mr); - pthread_mutex_init(&pt->lock_stats, NULL); -} - -static LWS_INLINE void -lws_pt_mutex_destroy(struct lws_context_per_thread *pt) -{ - pthread_mutex_destroy(&pt->lock_stats); - lws_mutex_refcount_destroy(&pt->mr); -} - -#define lws_pt_lock(pt, reason) lws_mutex_refcount_lock(&pt->mr, reason) -#define lws_pt_unlock(pt) lws_mutex_refcount_unlock(&pt->mr) - -static LWS_INLINE void -lws_pt_stats_lock(struct lws_context_per_thread *pt) -{ - pthread_mutex_lock(&pt->lock_stats); -} - -static LWS_INLINE void -lws_pt_stats_unlock(struct lws_context_per_thread *pt) -{ - pthread_mutex_unlock(&pt->lock_stats); -} +#if LWS_MAX_SMP > 1 #define lws_context_lock(c, reason) lws_mutex_refcount_lock(&c->mr, reason) #define lws_context_unlock(c) lws_mutex_refcount_unlock(&c->mr) @@ -1339,52 +541,6 @@ lws_tls_check_cert_lifetime(struct lws_vhost *vhost); int lws_jws_selftest(void); int lws_jwe_selftest(void); -#ifndef LWS_NO_CLIENT -LWS_EXTERN int lws_client_socket_service(struct lws *wsi, - struct lws_pollfd *pollfd, - struct lws *wsi_conn); -LWS_EXTERN struct lws * -lws_client_wsi_effective(struct lws *wsi); -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_http_transaction_completed_client(struct lws *wsi); -#if !defined(LWS_WITH_TLS) - #define lws_context_init_client_ssl(_a, _b) (0) -#endif -LWS_EXTERN void -lws_decode_ssl_error(void); -#else -#define lws_context_init_client_ssl(_a, _b) (0) -#endif - -LWS_EXTERN int -__lws_rx_flow_control(struct lws *wsi); - -LWS_EXTERN int -_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa); - -#ifndef LWS_NO_SERVER -LWS_EXTERN int -lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len); -#else -#define lws_server_socket_service(_b, _c) (0) -#define lws_handshake_server(_a, _b, _c) (0) -#endif - -#ifdef LWS_WITH_ACCESS_LOG -LWS_EXTERN int -lws_access_log(struct lws *wsi); -LWS_EXTERN void -lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int len, int meth); -#else -#define lws_access_log(_a) -#endif - -LWS_EXTERN int -lws_cgi_kill_terminated(struct lws_context_per_thread *pt); - -LWS_EXTERN void -lws_cgi_remove_and_kill(struct lws *wsi); - int lws_protocol_init(struct lws_context *context); @@ -1417,12 +573,6 @@ void lws_free(void *p); #define lws_free_set_NULL(P) do { lws_realloc(P, 0, "free"); (P) = NULL; } while(0) #endif -int -lws_plat_pipe_create(struct lws *wsi); -int -lws_plat_pipe_signal(struct lws *wsi); -void -lws_plat_pipe_close(struct lws *wsi); int lws_create_event_pipes(struct lws_context *context); @@ -1434,47 +584,19 @@ lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path, const char **vpath); /* lws_plat_ */ -LWS_EXTERN void -lws_plat_delete_socket_from_fds(struct lws_context *context, - struct lws *wsi, int m); -LWS_EXTERN void -lws_plat_insert_socket_into_fds(struct lws_context *context, - struct lws *wsi); -LWS_EXTERN void -lws_plat_service_periodic(struct lws_context *context); -LWS_EXTERN int -lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi, - struct lws_pollfd *pfd); -LWS_EXTERN void -lws_add_wsi_to_draining_ext_list(struct lws *wsi); -LWS_EXTERN void -lws_remove_wsi_from_draining_ext_list(struct lws *wsi); LWS_EXTERN int lws_plat_context_early_init(void); LWS_EXTERN void lws_plat_context_early_destroy(struct lws_context *context); LWS_EXTERN void lws_plat_context_late_destroy(struct lws_context *context); -LWS_EXTERN int -lws_poll_listen_fd(struct lws_pollfd *fd); -LWS_EXTERN int -lws_plat_service(struct lws_context *context, int timeout_ms); -LWS_EXTERN LWS_VISIBLE int -_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi); - -LWS_EXTERN int -lws_pthread_self_to_tsi(struct lws_context *context); LWS_EXTERN int lws_plat_init(struct lws_context *context, const struct lws_context_creation_info *info); LWS_EXTERN void lws_plat_drop_app_privileges(const struct lws_context_creation_info *info); -LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT -lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt); -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_plat_inet_pton(int af, const char *src, void *dst); LWS_EXTERN int lws_check_byte_utf8(unsigned char state, unsigned char c); @@ -1483,111 +605,9 @@ lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len); LWS_EXTERN int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf, lws_filepos_t *amount); - -LWS_EXTERN void -lws_same_vh_protocol_remove(struct lws *wsi); -LWS_EXTERN void -__lws_same_vh_protocol_remove(struct lws *wsi); -LWS_EXTERN void -lws_same_vh_protocol_insert(struct lws *wsi, int n); - -LWS_EXTERN int -lws_broadcast(struct lws_context *context, int reason, void *in, size_t len); - -#if defined(LWS_WITH_STATS) - void - lws_stats_atomic_bump(struct lws_context * context, - struct lws_context_per_thread *pt, int index, uint64_t bump); - void - lws_stats_atomic_max(struct lws_context * context, - struct lws_context_per_thread *pt, int index, uint64_t val); -#else - static LWS_INLINE uint64_t lws_stats_atomic_bump(struct lws_context * context, - struct lws_context_per_thread *pt, int index, uint64_t bump) { - (void)context; (void)pt; (void)index; (void)bump; return 0; } - static LWS_INLINE uint64_t lws_stats_atomic_max(struct lws_context * context, - struct lws_context_per_thread *pt, int index, uint64_t val) { - (void)context; (void)pt; (void)index; (void)val; return 0; } -#endif - -/* socks */ -void socks_generate_msg(struct lws *wsi, enum socks_msg_type type, - ssize_t *msg_len); - -#if defined(LWS_WITH_PEER_LIMITS) -void -lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer); -int -lws_peer_confirm_ah_attach_ok(struct lws_context *context, - struct lws_peer *peer); -void -lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer); -void -lws_peer_cull_peer_wait_list(struct lws_context *context); -struct lws_peer * -lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd); -void -lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer, - struct lws *wsi); -void -lws_peer_dump_from_wsi(struct lws *wsi); -#endif - -#ifdef LWS_WITH_HUBBUB -hubbub_error -html_parser_cb(const hubbub_token *token, void *pw); -#endif - -int -lws_threadpool_tsi_context(struct lws_context *context, int tsi); - -void -__lws_remove_from_timeout_list(struct lws *wsi); - -lws_usec_t -__lws_hrtimer_service(struct lws_context_per_thread *pt); - -void -__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs); -int -__lws_change_pollfd(struct lws *wsi, int _and, int _or); - - -int -lws_callback_as_writeable(struct lws *wsi); -int -lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi, - struct lws_tokens *ebuf); -int -lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used, - int buffered); - - -char * -lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1); -int -lws_client_ws_upgrade(struct lws *wsi, const char **cce); -int -lws_create_client_ws_object(const struct lws_client_connect_info *i, - struct lws *wsi); -int -lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len); -int -lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn); -int -lws_tls_server_conn_alpn(struct lws *wsi); - -int -lws_ws_client_rx_sm_block(struct lws *wsi, unsigned char **buf, size_t len); -void -lws_destroy_event_pipe(struct lws *wsi); void lws_context_destroy2(struct lws_context *context); -int -lws_role_call_client_bind(struct lws *wsi, - const struct lws_client_connect_info *i); -void -lws_remove_child_from_any_parent(struct lws *wsi); + #ifdef __cplusplus }; diff --git a/lib/core/vfs.c b/lib/core/vfs.c new file mode 100644 index 0000000000..0f37d906e8 --- /dev/null +++ b/lib/core/vfs.c @@ -0,0 +1,134 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + + +LWS_VISIBLE LWS_EXTERN void +lws_set_fops(struct lws_context *context, const struct lws_plat_file_ops *fops) +{ + context->fops = fops; +} + +LWS_VISIBLE LWS_EXTERN lws_filepos_t +lws_vfs_tell(lws_fop_fd_t fop_fd) +{ + return fop_fd->pos; +} + +LWS_VISIBLE LWS_EXTERN lws_filepos_t +lws_vfs_get_length(lws_fop_fd_t fop_fd) +{ + return fop_fd->len; +} + +LWS_VISIBLE LWS_EXTERN uint32_t +lws_vfs_get_mod_time(lws_fop_fd_t fop_fd) +{ + return fop_fd->mod_time; +} + +LWS_VISIBLE lws_fileofs_t +lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + lws_fileofs_t ofs; + + ofs = fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, offset - fop_fd->pos); + + return ofs; +} + + +LWS_VISIBLE lws_fileofs_t +lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + return fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, fop_fd->len + + fop_fd->pos + offset); +} + + +const struct lws_plat_file_ops * +lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path, + const char **vpath) +{ + const struct lws_plat_file_ops *pf; + const char *p = vfs_path; + int n; + + *vpath = NULL; + + /* no non-platform fops, just use that */ + + if (!fops->next) + return fops; + + /* + * scan the vfs path looking for indications we are to be + * handled by a specific fops + */ + + while (p && *p) { + if (*p != '/') { + p++; + continue; + } + /* the first one is always platform fops, so skip */ + pf = fops->next; + while (pf) { + n = 0; + while (n < (int)LWS_ARRAY_SIZE(pf->fi) && pf->fi[n].sig) { + if (p >= vfs_path + pf->fi[n].len) + if (!strncmp(p - (pf->fi[n].len - 1), + pf->fi[n].sig, + pf->fi[n].len - 1)) { + *vpath = p + 1; + return pf; + } + + n++; + } + pf = pf->next; + } + p++; + } + + return fops; +} + +LWS_VISIBLE LWS_EXTERN lws_fop_fd_t LWS_WARN_UNUSED_RESULT +lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path, + lws_fop_flags_t *flags) +{ + const char *vpath = ""; + const struct lws_plat_file_ops *selected; + + selected = lws_vfs_select_fops(fops, vfs_path, &vpath); + + return selected->LWS_FOP_OPEN(fops, vfs_path, vpath, flags); +} + + +LWS_VISIBLE struct lws_plat_file_ops * +lws_get_fops(struct lws_context *context) +{ + return (struct lws_plat_file_ops *)context->fops; +} + diff --git a/lib/plat/optee/lws-plat-optee.c b/lib/plat/optee/lws-plat-optee.c index 6c9b97cd4a..b477f8dc52 100644 --- a/lib/plat/optee/lws-plat-optee.c +++ b/lib/plat/optee/lws-plat-optee.c @@ -1,31 +1,10 @@ #include "core/private.h" -/* - * included from libwebsockets.c for OPTEE builds - */ - int lws_plat_apply_FD_CLOEXEC(int n) { return 0; } -int -lws_plat_pipe_create(struct lws *wsi) -{ - return 1; -} - -int -lws_plat_pipe_signal(struct lws *wsi) -{ - return 1; -} - -void -lws_plat_pipe_close(struct lws *wsi) -{ -} - void TEE_GenerateRandom(void *randomBuffer, uint32_t randomBufferLen); uint64_t @@ -42,58 +21,6 @@ lws_get_random(struct lws_context *context, void *buf, int len) return len; } #endif -LWS_VISIBLE int -lws_send_pipe_choked(struct lws *wsi) -{ - struct lws *wsi_eff; - -#if defined(LWS_WITH_HTTP2) - wsi_eff = lws_get_network_wsi(wsi); -#else - wsi_eff = wsi; -#endif - - /* the fact we checked implies we avoided back-to-back writes */ - wsi_eff->could_have_pending = 0; - - /* treat the fact we got a truncated send pending as if we're choked */ - if (lws_has_buffered_out(wsi_eff) -#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) - || wsi->http.comp_ctx.buflist_comp || - wsi->http.comp_ctx.may_have_more -#endif - ) - return 1; - -#if 0 - struct lws_pollfd fds; - - /* treat the fact we got a truncated send pending as if we're choked */ - if (lws_has_buffered_out(wsi)) - return 1; - - fds.fd = wsi->desc.sockfd; - fds.events = POLLOUT; - fds.revents = 0; - - if (poll(&fds, 1, 0) != 1) - return 1; - - if ((fds.revents & POLLOUT) == 0) - return 1; -#endif - /* okay to send another packet without blocking */ - - return 0; -} - -int -lws_poll_listen_fd(struct lws_pollfd *fd) -{ -// return poll(fd, 1, 0); - - return 0; -} #if 0 void lwsl_emit_syslog(int level, const char *line) @@ -102,114 +29,6 @@ void lwsl_emit_syslog(int level, const char *line) } #endif -LWS_EXTERN int -_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) -{ - struct lws_context_per_thread *pt; - int n = -1, m, c; - //char buf; - - /* stay dead once we are dead */ - - if (!context || !context->vhost_list) - return 1; - - pt = &context->pt[tsi]; - - if (timeout_ms < 0) - goto faked_service; - - if (!pt->service_tid_detected) { - struct lws _lws; - - memset(&_lws, 0, sizeof(_lws)); - _lws.context = context; - - pt->service_tid = context->vhost_list->protocols[0].callback( - &_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); - pt->service_tid_detected = 1; - } - - /* - * is there anybody with pending stuff that needs service forcing? - */ - if (!lws_service_adjust_timeout(context, 1, tsi)) { - lwsl_notice("%s: doing forced service\n", __func__); - /* -1 timeout means just do forced service */ - _lws_plat_service_tsi(context, -1, pt->tid); - /* still somebody left who wants forced service? */ - if (!lws_service_adjust_timeout(context, 1, pt->tid)) - /* yes... come back again quickly */ - timeout_ms = 0; - } - - n = poll(pt->fds, pt->fds_count, timeout_ms); - - m = 0; - - if (pt->context->tls_ops && - pt->context->tls_ops->fake_POLLIN_for_buffered) - m = pt->context->tls_ops->fake_POLLIN_for_buffered(pt); - - if (/*!pt->ws.rx_draining_ext_list && */!m && !n) { /* nothing to do */ - lws_service_fd_tsi(context, NULL, tsi); - return 0; - } - -faked_service: - m = lws_service_flag_pending(context, tsi); - if (m) - c = -1; /* unknown limit */ - else - if (n < 0) { - if (LWS_ERRNO != LWS_EINTR) - return -1; - return 0; - } else - c = n; - - /* any socket with events to service? */ - for (n = 0; n < (int)pt->fds_count && c; n++) { - if (!pt->fds[n].revents) - continue; - - c--; -#if 0 - if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) { - if (read(pt->fds[n].fd, &buf, 1) != 1) - lwsl_err("Cannot read from dummy pipe."); - continue; - } -#endif - m = lws_service_fd_tsi(context, &pt->fds[n], tsi); - if (m < 0) - return -1; - /* if something closed, retry this slot */ - if (m) - n--; - } - - return 0; -} - -int -lws_plat_check_connection_error(struct lws *wsi) -{ - return 0; -} - -int -lws_plat_service(struct lws_context *context, int timeout_ms) -{ - return _lws_plat_service_tsi(context, timeout_ms, 0); -} - -int -lws_plat_set_socket_options(struct lws_vhost *vhost, int fd, int unix_skt) -{ - return 0; -} - void lws_plat_drop_app_privileges(const struct lws_context_creation_info *info) { @@ -229,60 +48,10 @@ lws_plat_context_early_destroy(struct lws_context *context) void lws_plat_context_late_destroy(struct lws_context *context) { +#if defined(LWS_WITH_NETWORK) if (context->lws_lookup) lws_free(context->lws_lookup); -} - -/* cast a struct sockaddr_in6 * into addr for ipv6 */ - -int -lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, - size_t addrlen) -{ - return -1; -} - -void -lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - - pt->fds[pt->fds_count++].revents = 0; -} - -void -lws_plat_delete_socket_from_fds(struct lws_context *context, - struct lws *wsi, int m) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - - pt->fds_count--; -} - -void -lws_plat_service_periodic(struct lws_context *context) -{ -} - -int -lws_plat_change_pollfd(struct lws_context *context, - struct lws *wsi, struct lws_pollfd *pfd) -{ - return 0; -} - -const char * -lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) -{ - //return inet_ntop(af, src, dst, cnt); - return "lws_plat_inet_ntop"; -} - -int -lws_plat_inet_pton(int af, const char *src, void *dst) -{ - //return inet_pton(af, src, dst); - return 1; +#endif } lws_fop_fd_t @@ -325,6 +94,7 @@ int lws_plat_init(struct lws_context *context, const struct lws_context_creation_info *info) { +#if defined(LWS_WITH_NETWORK) /* master context has the global fd lookup array */ context->lws_lookup = lws_zalloc(sizeof(struct lws *) * context->max_fds, "lws_lookup"); @@ -336,7 +106,7 @@ lws_plat_init(struct lws_context *context, lwsl_notice(" mem: platform fd map: %5lu bytes\n", (long)sizeof(struct lws *) * context->max_fds); - +#endif #ifdef LWS_WITH_PLUGINS if (info->plugin_dirs) lws_plat_plugins_init(context, info->plugin_dirs); @@ -345,13 +115,6 @@ lws_plat_init(struct lws_context *context, return 0; } -int -lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf, - int len) -{ - return 1; -} - int lws_plat_write_file(const char *filename, void *buf, int len) { diff --git a/lib/plat/optee/network.c b/lib/plat/optee/network.c new file mode 100644 index 0000000000..a3052932af --- /dev/null +++ b/lib/plat/optee/network.c @@ -0,0 +1,227 @@ +#include "core/private.h" + + +int +lws_plat_pipe_create(struct lws *wsi) +{ + return 1; +} + +int +lws_plat_pipe_signal(struct lws *wsi) +{ + return 1; +} + +void +lws_plat_pipe_close(struct lws *wsi) +{ +} + +LWS_VISIBLE int +lws_send_pipe_choked(struct lws *wsi) +{ + struct lws *wsi_eff; + +#if defined(LWS_WITH_HTTP2) + wsi_eff = lws_get_network_wsi(wsi); +#else + wsi_eff = wsi; +#endif + + /* the fact we checked implies we avoided back-to-back writes */ + wsi_eff->could_have_pending = 0; + + /* treat the fact we got a truncated send pending as if we're choked */ + if (lws_has_buffered_out(wsi_eff) +#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) + || wsi->http.comp_ctx.buflist_comp || + wsi->http.comp_ctx.may_have_more +#endif + ) + return 1; + + /* okay to send another packet without blocking */ + + return 0; +} + +int +lws_poll_listen_fd(struct lws_pollfd *fd) +{ +// return poll(fd, 1, 0); + + return 0; +} + + +LWS_EXTERN int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt; + int n = -1, m, c; + //char buf; + + /* stay dead once we are dead */ + + if (!context || !context->vhost_list) + return 1; + + pt = &context->pt[tsi]; + + if (timeout_ms < 0) + goto faked_service; + + if (!pt->service_tid_detected) { + struct lws _lws; + + memset(&_lws, 0, sizeof(_lws)); + _lws.context = context; + + pt->service_tid = context->vhost_list->protocols[0].callback( + &_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + pt->service_tid_detected = 1; + } + + /* + * is there anybody with pending stuff that needs service forcing? + */ + if (!lws_service_adjust_timeout(context, 1, tsi)) { + lwsl_notice("%s: doing forced service\n", __func__); + /* -1 timeout means just do forced service */ + _lws_plat_service_tsi(context, -1, pt->tid); + /* still somebody left who wants forced service? */ + if (!lws_service_adjust_timeout(context, 1, pt->tid)) + /* yes... come back again quickly */ + timeout_ms = 0; + } + + n = poll(pt->fds, pt->fds_count, timeout_ms); + + m = 0; + + if (pt->context->tls_ops && + pt->context->tls_ops->fake_POLLIN_for_buffered) + m = pt->context->tls_ops->fake_POLLIN_for_buffered(pt); + + if (/*!pt->ws.rx_draining_ext_list && */!m && !n) { /* nothing to do */ + lws_service_fd_tsi(context, NULL, tsi); + return 0; + } + +faked_service: + m = lws_service_flag_pending(context, tsi); + if (m) + c = -1; /* unknown limit */ + else + if (n < 0) { + if (LWS_ERRNO != LWS_EINTR) + return -1; + return 0; + } else + c = n; + + /* any socket with events to service? */ + for (n = 0; n < (int)pt->fds_count && c; n++) { + if (!pt->fds[n].revents) + continue; + + c--; +#if 0 + if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) { + if (read(pt->fds[n].fd, &buf, 1) != 1) + lwsl_err("Cannot read from dummy pipe."); + continue; + } +#endif + m = lws_service_fd_tsi(context, &pt->fds[n], tsi); + if (m < 0) + return -1; + /* if something closed, retry this slot */ + if (m) + n--; + } + + return 0; +} + +int +lws_plat_check_connection_error(struct lws *wsi) +{ + return 0; +} + +int +lws_plat_service(struct lws_context *context, int timeout_ms) +{ + return _lws_plat_service_tsi(context, timeout_ms, 0); +} + +int +lws_plat_set_socket_options(struct lws_vhost *vhost, int fd, int unix_skt) +{ + return 0; +} + + +int +lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf, + int len) +{ + return 1; +} + + +/* cast a struct sockaddr_in6 * into addr for ipv6 */ + +int +lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, + size_t addrlen) +{ + return -1; +} + +void +lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + pt->fds[pt->fds_count++].revents = 0; +} + +void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + pt->fds_count--; +} + +void +lws_plat_service_periodic(struct lws_context *context) +{ +} + +int +lws_plat_change_pollfd(struct lws_context *context, + struct lws *wsi, struct lws_pollfd *pfd) +{ + return 0; +} + +const char * +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) +{ + //return inet_ntop(af, src, dst, cnt); + return "lws_plat_inet_ntop"; +} + +int +lws_plat_inet_pton(int af, const char *src, void *dst) +{ + //return inet_pton(af, src, dst); + return 1; +} + + diff --git a/lib/plat/unix/unix-init.c b/lib/plat/unix/unix-init.c index fa9a30e8d2..d209b885eb 100644 --- a/lib/plat/unix/unix-init.c +++ b/lib/plat/unix/unix-init.c @@ -35,7 +35,7 @@ lws_plat_init(struct lws_context *context, const struct lws_context_creation_info *info) { int fd; - +#if defined(LWS_WITH_NETWORK) /* master context has the global fd lookup array */ context->lws_lookup = lws_zalloc(sizeof(struct lws *) * context->max_fds, "lws_lookup"); @@ -47,6 +47,7 @@ lws_plat_init(struct lws_context *context, lwsl_info(" mem: platform fd map: %5lu bytes\n", (unsigned long)(sizeof(struct lws *) * context->max_fds)); +#endif fd = lws_open(SYSTEM_RANDOM_FILEPATH, O_RDONLY); context->fd_random = fd; @@ -86,10 +87,10 @@ lws_plat_context_late_destroy(struct lws_context *context) if (context->plugin_list) lws_plat_plugins_destroy(context); #endif - +#if defined(LWS_WITH_NETWORK) if (context->lws_lookup) lws_free(context->lws_lookup); - +#endif if (!context->fd_random) lwsl_err("ZERO RANDOM FD\n"); if (context->fd_random != LWS_INVALID_FILE) diff --git a/lib/plat/unix/unix-service.c b/lib/plat/unix/unix-service.c index e61ef59959..b5fb5d7b82 100644 --- a/lib/plat/unix/unix-service.c +++ b/lib/plat/unix/unix-service.c @@ -135,9 +135,11 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) m |= !!pt->ws.rx_draining_ext_list; #endif +#if defined(LWS_WITH_TLS) if (pt->context->tls_ops && pt->context->tls_ops->fake_POLLIN_for_buffered) m |= pt->context->tls_ops->fake_POLLIN_for_buffered(pt); +#endif if (!m && !n) { /* nothing to do */ lws_service_fd_tsi(context, NULL, tsi); diff --git a/lib/plat/windows/windows-service.c b/lib/plat/windows/windows-service.c index 2a1d467dbe..aef7f6b52d 100644 --- a/lib/plat/windows/windows-service.c +++ b/lib/plat/windows/windows-service.c @@ -142,9 +142,11 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) if (ev == WSA_WAIT_EVENT_0) { unsigned int eIdx; +#if defined(LWS_WITH_TLS) if (pt->context->tls_ops && pt->context->tls_ops->fake_POLLIN_for_buffered) pt->context->tls_ops->fake_POLLIN_for_buffered(pt); +#endif for (eIdx = 0; eIdx < pt->fds_count; ++eIdx) { unsigned int err; diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c index 4015f4e121..c31dd6b86d 100644 --- a/lib/roles/http/client/client.c +++ b/lib/roles/http/client/client.c @@ -665,21 +665,6 @@ lws_http_client_http_response(struct lws *_wsi) return resp; } #endif -#if defined(LWS_PLAT_OPTEE) -char * -strrchr(const char *s, int c) -{ - char *hit = NULL; - - while (*s) - if (*(s++) == (char)c) - hit = (char *)s - 1; - - return hit; -} - -#define atoll atoi -#endif #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) int diff --git a/lib/tls/mbedtls/ssl.c b/lib/tls/mbedtls/ssl.c index b4558ba3ec..c4cd74aa59 100644 --- a/lib/tls/mbedtls/ssl.c +++ b/lib/tls/mbedtls/ssl.c @@ -1,7 +1,7 @@ /* * libwebsockets - mbedTLS-specific lws apis * - * Copyright (C) 2010-2018 Andy Green + * Copyright (C) 2010 - 2019 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,22 +22,6 @@ #include "core/private.h" #include "tls/mbedtls/private.h" -void -lws_tls_err_describe(void) -{ -} - -int -lws_context_init_ssl_library(const struct lws_context_creation_info *info) -{ - lwsl_info(" Compiled with MbedTLS support\n"); - - if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) - lwsl_info(" SSL disabled: no " - "LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); - - return 0; -} LWS_VISIBLE void lws_ssl_destroy(struct lws_vhost *vhost) diff --git a/lib/tls/mbedtls/tls.c b/lib/tls/mbedtls/tls.c new file mode 100644 index 0000000000..e894d20e93 --- /dev/null +++ b/lib/tls/mbedtls/tls.c @@ -0,0 +1,40 @@ +/* + * libwebsockets - mbedTLS-specific lws apis + * + * Copyright (C) 2010 - 2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" +#include "tls/mbedtls/private.h" + +void +lws_tls_err_describe(void) +{ +} + +int +lws_context_init_ssl_library(const struct lws_context_creation_info *info) +{ + lwsl_info(" Compiled with MbedTLS support\n"); + + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) + lwsl_info(" SSL disabled: no " + "LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); + + return 0; +} diff --git a/lib/tls/mbedtls/x509.c b/lib/tls/mbedtls/x509.c index 0c56862ffe..aec1969442 100644 --- a/lib/tls/mbedtls/x509.c +++ b/lib/tls/mbedtls/x509.c @@ -160,6 +160,7 @@ lws_tls_mbedtls_cert_info(mbedtls_x509_crt *x509, enum lws_tls_cert_info type, return 0; } +#if defined(LWS_WITH_NETWORK) int lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type, union lws_tls_cert_info_results *buf, size_t len) @@ -194,6 +195,7 @@ lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type, return -1; } +#endif int lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type, diff --git a/lib/tls/openssl/ssl.c b/lib/tls/openssl/ssl.c index 6643b5768b..462d9484b8 100644 --- a/lib/tls/openssl/ssl.c +++ b/lib/tls/openssl/ssl.c @@ -60,60 +60,6 @@ int lws_ssl_get_error(struct lws *wsi, int n) return m; } -char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) { - switch (status) { - case SSL_ERROR_NONE: - return lws_strncpy(buf, "SSL_ERROR_NONE", len); - case SSL_ERROR_ZERO_RETURN: - return lws_strncpy(buf, "SSL_ERROR_ZERO_RETURN", len); - case SSL_ERROR_WANT_READ: - return lws_strncpy(buf, "SSL_ERROR_WANT_READ", len); - case SSL_ERROR_WANT_WRITE: - return lws_strncpy(buf, "SSL_ERROR_WANT_WRITE", len); - case SSL_ERROR_WANT_CONNECT: - return lws_strncpy(buf, "SSL_ERROR_WANT_CONNECT", len); - case SSL_ERROR_WANT_ACCEPT: - return lws_strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len); - case SSL_ERROR_WANT_X509_LOOKUP: - return lws_strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len); - case SSL_ERROR_SYSCALL: - switch (ret) { - case 0: - lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: EOF"); - return buf; - case -1: -#ifndef LWS_PLAT_OPTEE - lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %s", - strerror(errno)); -#else - lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %d", errno); -#endif - return buf; - default: - return strncpy(buf, "SSL_ERROR_SYSCALL", len); - } - case SSL_ERROR_SSL: - return "SSL_ERROR_SSL"; - default: - return "SSL_ERROR_UNKNOWN"; - } -} - -void -lws_tls_err_describe(void) -{ - char buf[128]; - unsigned long l; - - do { - l = ERR_get_error(); - if (!l) - break; - ERR_error_string_n(l, buf, sizeof(buf)); - lwsl_info(" openssl error: %s\n", buf); - } while (l); - lwsl_info("\n"); -} static int lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag, @@ -144,49 +90,6 @@ lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, lws_context_init_ssl_pem_passwd_cb); } -int -lws_context_init_ssl_library(const struct lws_context_creation_info *info) -{ -#ifdef USE_WOLFSSL -#ifdef USE_OLD_CYASSL - lwsl_info(" Compiled with CyaSSL support\n"); -#else - lwsl_info(" Compiled with wolfSSL support\n"); -#endif -#else -#if defined(LWS_WITH_BORINGSSL) - lwsl_info(" Compiled with BoringSSL support\n"); -#else - lwsl_info(" Compiled with OpenSSL support\n"); -#endif -#endif - if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { - lwsl_info(" SSL disabled: no " - "LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); - return 0; - } - - /* basic openssl init */ - - lwsl_info("Doing SSL library init\n"); - -#if OPENSSL_VERSION_NUMBER < 0x10100000L - SSL_library_init(); - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); -#else - OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); -#endif - - openssl_websocket_private_data_index = - SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL); - - openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0, - NULL, NULL, NULL, NULL); - - return 0; -} - LWS_VISIBLE void lws_ssl_destroy(struct lws_vhost *vhost) { diff --git a/lib/tls/openssl/tls.c b/lib/tls/openssl/tls.c new file mode 100644 index 0000000000..f0e9048cd1 --- /dev/null +++ b/lib/tls/openssl/tls.c @@ -0,0 +1,126 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2018 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" +#include "tls/openssl/private.h" + +extern int openssl_websocket_private_data_index, +openssl_SSL_CTX_private_data_index; + +char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) { + switch (status) { + case SSL_ERROR_NONE: + return lws_strncpy(buf, "SSL_ERROR_NONE", len); + case SSL_ERROR_ZERO_RETURN: + return lws_strncpy(buf, "SSL_ERROR_ZERO_RETURN", len); + case SSL_ERROR_WANT_READ: + return lws_strncpy(buf, "SSL_ERROR_WANT_READ", len); + case SSL_ERROR_WANT_WRITE: + return lws_strncpy(buf, "SSL_ERROR_WANT_WRITE", len); + case SSL_ERROR_WANT_CONNECT: + return lws_strncpy(buf, "SSL_ERROR_WANT_CONNECT", len); + case SSL_ERROR_WANT_ACCEPT: + return lws_strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len); + case SSL_ERROR_WANT_X509_LOOKUP: + return lws_strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len); + case SSL_ERROR_SYSCALL: + switch (ret) { + case 0: + lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: EOF"); + return buf; + case -1: +#ifndef LWS_PLAT_OPTEE + lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %s", + strerror(errno)); +#else + lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %d", errno); +#endif + return buf; + default: + return strncpy(buf, "SSL_ERROR_SYSCALL", len); + } + case SSL_ERROR_SSL: + return "SSL_ERROR_SSL"; + default: + return "SSL_ERROR_UNKNOWN"; + } +} + +void +lws_tls_err_describe(void) +{ + char buf[128]; + unsigned long l; + + do { + l = ERR_get_error(); + if (!l) + break; + ERR_error_string_n(l, buf, sizeof(buf)); + lwsl_info(" openssl error: %s\n", buf); + } while (l); + lwsl_info("\n"); +} + + +int +lws_context_init_ssl_library(const struct lws_context_creation_info *info) +{ +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + lwsl_info(" Compiled with CyaSSL support\n"); +#else + lwsl_info(" Compiled with wolfSSL support\n"); +#endif +#else +#if defined(LWS_WITH_BORINGSSL) + lwsl_info(" Compiled with BoringSSL support\n"); +#else + lwsl_info(" Compiled with OpenSSL support\n"); +#endif +#endif + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { + lwsl_info(" SSL disabled: no " + "LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); + return 0; + } + + /* basic openssl init */ + + lwsl_info("Doing SSL library init\n"); + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_library_init(); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); +#else + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); +#endif +#if defined(LWS_WITH_NETWORK) + openssl_websocket_private_data_index = + SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL); + + openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0, + NULL, NULL, NULL, NULL); +#endif + + return 0; +} diff --git a/lib/tls/openssl/x509.c b/lib/tls/openssl/x509.c index 269506772d..33406f7940 100644 --- a/lib/tls/openssl/x509.c +++ b/lib/tls/openssl/x509.c @@ -1,7 +1,7 @@ /* - * libwebsockets - mbedTLS-specific lws apis + * libwebsockets - OpenSSL-specific lws apis * - * Copyright (C) 2010-2019 Andy Green + * Copyright (C) 2010 - 2019 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -167,6 +167,14 @@ lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type, return 0; } +int +lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type, + union lws_tls_cert_info_results *buf, size_t len) +{ + return lws_tls_openssl_cert_info(x509->cert, type, buf, len); +} + +#if defined(LWS_WITH_NETWORK) int lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type, union lws_tls_cert_info_results *buf, size_t len) @@ -182,12 +190,7 @@ lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type, #endif } -int -lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type, - union lws_tls_cert_info_results *buf, size_t len) -{ - return lws_tls_openssl_cert_info(x509->cert, type, buf, len); -} + int lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type, @@ -219,6 +222,7 @@ lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type, return rc; } +#endif int lws_x509_create(struct lws_x509_cert **x509) diff --git a/lib/tls/private-network.h b/lib/tls/private-network.h new file mode 100644 index 0000000000..2627d47289 --- /dev/null +++ b/lib/tls/private-network.h @@ -0,0 +1,189 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * This is included from core/private.h if LWS_WITH_TLS + */ + +struct lws_context_per_thread; +struct lws_tls_ops { + int (*fake_POLLIN_for_buffered)(struct lws_context_per_thread *pt); + int (*periodic_housekeeping)(struct lws_context *context, time_t now); +}; + +struct lws_context_tls { + char alpn_discovered[32]; + const char *alpn_default; + time_t last_cert_check_s; +}; + +struct lws_pt_tls { + struct lws_dll_lws pending_tls_head; +}; + +struct lws_tls_ss_pieces; + +struct alpn_ctx { + uint8_t data[23]; + uint8_t len; +}; + +struct lws_vhost_tls { + lws_tls_ctx *ssl_ctx; + lws_tls_ctx *ssl_client_ctx; + const char *alpn; + struct lws_tls_ss_pieces *ss; /* for acme tls certs */ + char *alloc_cert_path; + char *key_path; +#if defined(LWS_WITH_MBEDTLS) + lws_tls_x509 *x509_client_CA; +#endif + char ecdh_curve[16]; + struct alpn_ctx alpn_ctx; + + int use_ssl; + int allow_non_ssl_on_ssl_port; + int ssl_info_event_mask; + + unsigned int user_supplied_ssl_ctx:1; + unsigned int skipped_certs:1; +}; + +struct lws_lws_tls { + lws_tls_conn *ssl; + lws_tls_bio *client_bio; + struct lws_dll_lws pending_tls_list; + unsigned int use_ssl; + unsigned int redirect_to_https:1; +}; + + +LWS_EXTERN void +lws_context_init_alpn(struct lws_vhost *vhost); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_pending(struct lws *wsi); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd); +LWS_EXTERN int +lws_ssl_close(struct lws *wsi); +LWS_EXTERN void +lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost); +LWS_EXTERN void +lws_ssl_context_destroy(struct lws_context *context); +void +__lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi); +LWS_VISIBLE void +lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_bio_create(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_connect1(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_connect2(struct lws *wsi, char *errbuf, int len); +LWS_EXTERN int +lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt); +LWS_EXTERN int +lws_gate_accepts(struct lws_context *context, int on); +LWS_EXTERN void +lws_ssl_bind_passphrase(lws_tls_ctx *ssl_ctx, + const struct lws_context_creation_info *info); +LWS_EXTERN void +lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret); +LWS_EXTERN int +lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, + const char *cert, const char *private_key, + const char *mem_cert, size_t len_mem_cert, + const char *mem_privkey, size_t mem_privkey_len); +LWS_EXTERN enum lws_tls_extant +lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert, + const char *private_key); +#if !defined(LWS_NO_SERVER) + LWS_EXTERN int + lws_context_init_server_ssl(const struct lws_context_creation_info *info, + struct lws_vhost *vhost); + void + lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost); +#else + #define lws_context_init_server_ssl(_a, _b) (0) + #define lws_tls_acme_sni_cert_destroy(_a) +#endif + +LWS_EXTERN void +lws_ssl_destroy(struct lws_vhost *vhost); + +/* +* lws_tls_ abstract backend implementations +*/ + +LWS_EXTERN int +lws_tls_server_client_cert_verify_config(struct lws_vhost *vh); +LWS_EXTERN int +lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, + struct lws_vhost *vhost, struct lws *wsi); +LWS_EXTERN int +lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd); + +LWS_EXTERN enum lws_ssl_capable_status +lws_tls_server_accept(struct lws *wsi); + +LWS_EXTERN enum lws_ssl_capable_status +lws_tls_server_abort_connection(struct lws *wsi); + +LWS_EXTERN enum lws_ssl_capable_status +__lws_tls_shutdown(struct lws *wsi); + +LWS_EXTERN enum lws_ssl_capable_status +lws_tls_client_connect(struct lws *wsi); +LWS_EXTERN int +lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len); +LWS_EXTERN int +lws_tls_client_create_vhost_context(struct lws_vhost *vh, + const struct lws_context_creation_info *info, + const char *cipher_list, + const char *ca_filepath, + const void *ca_mem, + unsigned int ca_mem_len, + const char *cert_filepath, + const void *cert_mem, + unsigned int cert_mem_len, + const char *private_key_filepath); + +LWS_EXTERN lws_tls_ctx * +lws_tls_ctx_from_wsi(struct lws *wsi); +LWS_EXTERN int +lws_ssl_get_error(struct lws *wsi, int n); + +LWS_EXTERN int +lws_context_init_client_ssl(const struct lws_context_creation_info *info, + struct lws_vhost *vhost); + +LWS_EXTERN void +lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret); + +int +lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt); + + + + + diff --git a/lib/tls/private.h b/lib/tls/private.h index 1b374b89f9..bafd1c39b6 100644 --- a/lib/tls/private.h +++ b/lib/tls/private.h @@ -21,6 +21,10 @@ * This is included from core/private.h if LWS_WITH_TLS */ +#if !defined(__LWS_TLS_PRIVATE_H__) +#define __LWS_TLS_PRIVATE_H__ + + #if defined(LWS_WITH_TLS) #if defined(USE_WOLFSSL) @@ -102,12 +106,6 @@ enum lws_tls_extant { LWS_TLS_EXTANT_ALTERNATIVE }; -struct lws_context_per_thread; - -struct lws_tls_ops { - int (*fake_POLLIN_for_buffered)(struct lws_context_per_thread *pt); - int (*periodic_housekeeping)(struct lws_context *context, time_t now); -}; #if defined(LWS_WITH_TLS) @@ -116,190 +114,42 @@ typedef SSL_CTX lws_tls_ctx; typedef BIO lws_tls_bio; typedef X509 lws_tls_x509; +#if defined(LWS_WITH_NETWORK) +#include "tls/private-network.h" +#endif +LWS_EXTERN int +lws_context_init_ssl_library(const struct lws_context_creation_info *info); #define LWS_SSL_ENABLED(context) (context->tls.use_ssl) extern const struct lws_tls_ops tls_ops_openssl, tls_ops_mbedtls; -struct lws_context_tls { - char alpn_discovered[32]; - const char *alpn_default; - time_t last_cert_check_s; -}; - -struct lws_pt_tls { - struct lws_dll_lws pending_tls_head; -}; - -struct lws_tls_ss_pieces; - -struct alpn_ctx { - uint8_t data[23]; - uint8_t len; -}; - -struct lws_vhost_tls { - lws_tls_ctx *ssl_ctx; - lws_tls_ctx *ssl_client_ctx; - const char *alpn; - struct lws_tls_ss_pieces *ss; /* for acme tls certs */ - char *alloc_cert_path; - char *key_path; -#if defined(LWS_WITH_MBEDTLS) - lws_tls_x509 *x509_client_CA; -#endif - char ecdh_curve[16]; - struct alpn_ctx alpn_ctx; - - int use_ssl; - int allow_non_ssl_on_ssl_port; - int ssl_info_event_mask; - - unsigned int user_supplied_ssl_ctx:1; - unsigned int skipped_certs:1; -}; - -struct lws_lws_tls { - lws_tls_conn *ssl; - lws_tls_bio *client_bio; - struct lws_dll_lws pending_tls_list; - unsigned int use_ssl; - unsigned int redirect_to_https:1; -}; - struct lws_ec_valid_curves { int id; const char *jwa_name; /* list terminates with NULL jwa_name */ }; -LWS_EXTERN void -lws_context_init_alpn(struct lws_vhost *vhost); LWS_EXTERN enum lws_tls_extant lws_tls_use_any_upgrade_check_extant(const char *name); LWS_EXTERN int openssl_websocket_private_data_index; -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len); -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len); -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ssl_pending(struct lws *wsi); -LWS_EXTERN int -lws_context_init_ssl_library(const struct lws_context_creation_info *info); -LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd); -LWS_EXTERN int -lws_ssl_close(struct lws *wsi); -LWS_EXTERN void -lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost); -LWS_EXTERN void -lws_ssl_context_destroy(struct lws_context *context); -void -__lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi); -LWS_VISIBLE void -lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi); -LWS_EXTERN int -lws_ssl_client_bio_create(struct lws *wsi); -LWS_EXTERN int -lws_ssl_client_connect1(struct lws *wsi); -LWS_EXTERN int -lws_ssl_client_connect2(struct lws *wsi, char *errbuf, int len); + + LWS_EXTERN void lws_tls_err_describe(void); -LWS_EXTERN int -lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt); -LWS_EXTERN int -lws_gate_accepts(struct lws_context *context, int on); -LWS_EXTERN void -lws_ssl_bind_passphrase(lws_tls_ctx *ssl_ctx, - const struct lws_context_creation_info *info); -LWS_EXTERN void -lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret); + LWS_EXTERN int lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type, union lws_tls_cert_info_results *buf, size_t len); LWS_EXTERN int lws_tls_check_all_cert_lifetimes(struct lws_context *context); -LWS_EXTERN int -lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, - const char *cert, const char *private_key, - const char *mem_cert, size_t len_mem_cert, - const char *mem_privkey, size_t mem_privkey_len); -LWS_EXTERN enum lws_tls_extant -lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert, - const char *private_key); + LWS_EXTERN int lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename, const char *inbuf, lws_filepos_t inlen, uint8_t **buf, lws_filepos_t *amount); - -#if !defined(LWS_NO_SERVER) - LWS_EXTERN int - lws_context_init_server_ssl(const struct lws_context_creation_info *info, - struct lws_vhost *vhost); - void - lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost); -#else - #define lws_context_init_server_ssl(_a, _b) (0) - #define lws_tls_acme_sni_cert_destroy(_a) -#endif - -LWS_EXTERN void -lws_ssl_destroy(struct lws_vhost *vhost); LWS_EXTERN char * lws_ssl_get_error_string(int status, int ret, char *buf, size_t len); -/* - * lws_tls_ abstract backend implementations - */ - -LWS_EXTERN int -lws_tls_server_client_cert_verify_config(struct lws_vhost *vh); -LWS_EXTERN int -lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, - struct lws_vhost *vhost, struct lws *wsi); -LWS_EXTERN int -lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd); - -LWS_EXTERN enum lws_ssl_capable_status -lws_tls_server_accept(struct lws *wsi); - -LWS_EXTERN enum lws_ssl_capable_status -lws_tls_server_abort_connection(struct lws *wsi); - -LWS_EXTERN enum lws_ssl_capable_status -__lws_tls_shutdown(struct lws *wsi); - -LWS_EXTERN enum lws_ssl_capable_status -lws_tls_client_connect(struct lws *wsi); -LWS_EXTERN int -lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len); -LWS_EXTERN int -lws_tls_client_create_vhost_context(struct lws_vhost *vh, - const struct lws_context_creation_info *info, - const char *cipher_list, - const char *ca_filepath, - const void *ca_mem, - unsigned int ca_mem_len, - const char *cert_filepath, - const void *cert_mem, - unsigned int cert_mem_len, - const char *private_key_filepath); - -LWS_EXTERN lws_tls_ctx * -lws_tls_ctx_from_wsi(struct lws *wsi); -LWS_EXTERN int -lws_ssl_get_error(struct lws *wsi, int n); - -LWS_EXTERN int -lws_context_init_client_ssl(const struct lws_context_creation_info *info, - struct lws_vhost *vhost); - -LWS_EXTERN void -lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret); - -int -lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt); - int lws_gencrypto_bits_to_bytes(int bits); @@ -324,3 +174,4 @@ lws_genec_confirm_curve_allowed_by_tls_id(const char *allowed, int id, struct lws_jwk *jwk); #endif +#endif diff --git a/lib/tls/tls-network.c b/lib/tls/tls-network.c new file mode 100644 index 0000000000..410a55ea22 --- /dev/null +++ b/lib/tls/tls-network.c @@ -0,0 +1,255 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + +/* + * fakes POLLIN on all tls guys with buffered rx + * + * returns nonzero if any tls guys had POLLIN faked + */ + +int +lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt) +{ + int ret = 0; + + lws_start_foreach_dll_safe(struct lws_dll_lws *, p, p1, + pt->tls.pending_tls_head.next) { + struct lws *wsi = lws_container_of(p, struct lws, + tls.pending_tls_list); + + pt->fds[wsi->position_in_fds_table].revents |= + pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; + ret |= pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN; + + } lws_end_foreach_dll_safe(p, p1); + + return !!ret; +} + +void +__lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi) +{ + if (lws_dll_is_null(&wsi->tls.pending_tls_list)) + return; + + lws_dll_lws_remove(&wsi->tls.pending_tls_list); +} + +void +lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + lws_pt_lock(pt, __func__); + __lws_ssl_remove_wsi_from_buffered_list(wsi); + lws_pt_unlock(pt); +} + + +int +lws_tls_check_cert_lifetime(struct lws_vhost *v) +{ + time_t now = (time_t)lws_now_secs(), life = 0; + struct lws_acme_cert_aging_args caa; + union lws_tls_cert_info_results ir; + int n; + + if (v->tls.ssl_ctx && !v->tls.skipped_certs) { + + if (now < 1542933698) /* Nov 23 2018 00:42 UTC */ + /* our clock is wrong and we can't judge the certs */ + return -1; + + n = lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO, + &ir, 0); + if (n) + return 1; + + life = (ir.time - now) / (24 * 3600); + lwsl_notice(" vhost %s: cert expiry: %dd\n", v->name, + (int)life); + } else + lwsl_notice(" vhost %s: no cert\n", v->name); + + memset(&caa, 0, sizeof(caa)); + caa.vh = v; + lws_broadcast(v->context, LWS_CALLBACK_VHOST_CERT_AGING, (void *)&caa, + (size_t)(ssize_t)life); + + return 0; +} + +int +lws_tls_check_all_cert_lifetimes(struct lws_context *context) +{ + struct lws_vhost *v = context->vhost_list; + + while (v) { + if (lws_tls_check_cert_lifetime(v) < 0) + return -1; + v = v->vhost_next; + } + + return 0; +} + + +/* + * LWS_TLS_EXTANT_NO : skip adding the cert + * LWS_TLS_EXTANT_YES : use the cert and private key paths normally + * LWS_TLS_EXTANT_ALTERNATIVE: normal paths not usable, try alternate if poss + */ +enum lws_tls_extant +lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert, + const char *private_key) +{ + int n, m; + + /* + * The user code can choose to either pass the cert and + * key filepaths using the info members like this, or it can + * leave them NULL; force the vhost SSL_CTX init using the info + * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and + * set up the cert himself using the user callback + * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which + * happened just above and has the vhost SSL_CTX * in the user + * parameter. + */ + + if (!cert || !private_key) + return LWS_TLS_EXTANT_NO; + + n = lws_tls_use_any_upgrade_check_extant(cert); + if (n == LWS_TLS_EXTANT_ALTERNATIVE) + return LWS_TLS_EXTANT_ALTERNATIVE; + m = lws_tls_use_any_upgrade_check_extant(private_key); + if (m == LWS_TLS_EXTANT_ALTERNATIVE) + return LWS_TLS_EXTANT_ALTERNATIVE; + + if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) && + (vhost->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) { + lwsl_notice("Ignoring missing %s or %s\n", cert, private_key); + vhost->tls.skipped_certs = 1; + + return LWS_TLS_EXTANT_NO; + } + + /* + * the cert + key exist + */ + + return LWS_TLS_EXTANT_YES; +} + +#if !defined(LWS_NO_SERVER) +/* + * update the cert for every vhost using the given path + */ + +LWS_VISIBLE int +lws_tls_cert_updated(struct lws_context *context, const char *certpath, + const char *keypath, + const char *mem_cert, size_t len_mem_cert, + const char *mem_privkey, size_t len_mem_privkey) +{ + struct lws wsi; + + wsi.context = context; + + lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) { + wsi.vhost = v; /* not a real bound wsi */ + if (v->tls.alloc_cert_path && v->tls.key_path && + !strcmp(v->tls.alloc_cert_path, certpath) && + !strcmp(v->tls.key_path, keypath)) { + lws_tls_server_certs_load(v, &wsi, certpath, keypath, + mem_cert, len_mem_cert, + mem_privkey, len_mem_privkey); + + if (v->tls.skipped_certs) + lwsl_notice("%s: vhost %s: cert unset\n", + __func__, v->name); + } + } lws_end_foreach_ll(v, vhost_next); + + return 0; +} +#endif + +int +lws_gate_accepts(struct lws_context *context, int on) +{ + struct lws_vhost *v = context->vhost_list; + + lwsl_notice("%s: on = %d\n", __func__, on); + +#if defined(LWS_WITH_STATS) + context->updated = 1; +#endif + + while (v) { + if (v->tls.use_ssl && v->lserv_wsi && + lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on, + (LWS_POLLIN) * on)) + lwsl_notice("Unable to set accept POLLIN %d\n", on); + + v = v->vhost_next; + } + + return 0; +} + +/* comma-separated alpn list, like "h2,http/1.1" to openssl alpn format */ + +int +lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len) +{ + uint8_t *oos = os, *plen = NULL; + + while (*comma && len > 1) { + if (!plen && *comma == ' ') { + comma++; + continue; + } + if (!plen) { + plen = os++; + len--; + } + + if (*comma == ',') { + *plen = lws_ptr_diff(os, plen + 1); + plen = NULL; + comma++; + } else { + *os++ = *comma++; + len--; + } + } + + if (plen) + *plen = lws_ptr_diff(os, plen + 1); + + return lws_ptr_diff(os, oos); +} + + + diff --git a/lib/tls/tls.c b/lib/tls/tls.c index a32951689f..4121252e84 100644 --- a/lib/tls/tls.c +++ b/lib/tls/tls.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010-2017 Andy Green + * Copyright (C) 2010 - 2019 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,50 +20,7 @@ */ #include "core/private.h" - -/* - * fakes POLLIN on all tls guys with buffered rx - * - * returns nonzero if any tls guys had POLLIN faked - */ - -int -lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt) -{ - int ret = 0; - - lws_start_foreach_dll_safe(struct lws_dll_lws *, p, p1, - pt->tls.pending_tls_head.next) { - struct lws *wsi = lws_container_of(p, struct lws, - tls.pending_tls_list); - - pt->fds[wsi->position_in_fds_table].revents |= - pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; - ret |= pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN; - - } lws_end_foreach_dll_safe(p, p1); - - return !!ret; -} - -void -__lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi) -{ - if (lws_dll_is_null(&wsi->tls.pending_tls_list)) - return; - - lws_dll_lws_remove(&wsi->tls.pending_tls_list); -} - -void -lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi) -{ - struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - - lws_pt_lock(pt, __func__); - __lws_ssl_remove_wsi_from_buffered_list(wsi); - lws_pt_unlock(pt); -} +#include "tls/private.h" #if defined(LWS_WITH_ESP32) int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf, @@ -216,52 +173,6 @@ lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename, return 4; } -int -lws_tls_check_cert_lifetime(struct lws_vhost *v) -{ - time_t now = (time_t)lws_now_secs(), life = 0; - struct lws_acme_cert_aging_args caa; - union lws_tls_cert_info_results ir; - int n; - - if (v->tls.ssl_ctx && !v->tls.skipped_certs) { - - if (now < 1542933698) /* Nov 23 2018 00:42 UTC */ - /* our clock is wrong and we can't judge the certs */ - return -1; - - n = lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO, - &ir, 0); - if (n) - return 1; - - life = (ir.time - now) / (24 * 3600); - lwsl_notice(" vhost %s: cert expiry: %dd\n", v->name, - (int)life); - } else - lwsl_notice(" vhost %s: no cert\n", v->name); - - memset(&caa, 0, sizeof(caa)); - caa.vh = v; - lws_broadcast(v->context, LWS_CALLBACK_VHOST_CERT_AGING, (void *)&caa, - (size_t)(ssize_t)life); - - return 0; -} - -int -lws_tls_check_all_cert_lifetimes(struct lws_context *context) -{ - struct lws_vhost *v = context->vhost_list; - - while (v) { - if (lws_tls_check_cert_lifetime(v) < 0) - return -1; - v = v->vhost_next; - } - - return 0; -} #if !defined(LWS_WITH_ESP32) && !defined(LWS_PLAT_OPTEE) static int lws_tls_extant(const char *name) @@ -365,141 +276,3 @@ lws_tls_use_any_upgrade_check_extant(const char *name) #endif return LWS_TLS_EXTANT_YES; } - -/* - * LWS_TLS_EXTANT_NO : skip adding the cert - * LWS_TLS_EXTANT_YES : use the cert and private key paths normally - * LWS_TLS_EXTANT_ALTERNATIVE: normal paths not usable, try alternate if poss - */ -enum lws_tls_extant -lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert, - const char *private_key) -{ - int n, m; - - /* - * The user code can choose to either pass the cert and - * key filepaths using the info members like this, or it can - * leave them NULL; force the vhost SSL_CTX init using the info - * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and - * set up the cert himself using the user callback - * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which - * happened just above and has the vhost SSL_CTX * in the user - * parameter. - */ - - if (!cert || !private_key) - return LWS_TLS_EXTANT_NO; - - n = lws_tls_use_any_upgrade_check_extant(cert); - if (n == LWS_TLS_EXTANT_ALTERNATIVE) - return LWS_TLS_EXTANT_ALTERNATIVE; - m = lws_tls_use_any_upgrade_check_extant(private_key); - if (m == LWS_TLS_EXTANT_ALTERNATIVE) - return LWS_TLS_EXTANT_ALTERNATIVE; - - if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) && - (vhost->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) { - lwsl_notice("Ignoring missing %s or %s\n", cert, private_key); - vhost->tls.skipped_certs = 1; - - return LWS_TLS_EXTANT_NO; - } - - /* - * the cert + key exist - */ - - return LWS_TLS_EXTANT_YES; -} - -#if !defined(LWS_NO_SERVER) -/* - * update the cert for every vhost using the given path - */ - -LWS_VISIBLE int -lws_tls_cert_updated(struct lws_context *context, const char *certpath, - const char *keypath, - const char *mem_cert, size_t len_mem_cert, - const char *mem_privkey, size_t len_mem_privkey) -{ - struct lws wsi; - - wsi.context = context; - - lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) { - wsi.vhost = v; /* not a real bound wsi */ - if (v->tls.alloc_cert_path && v->tls.key_path && - !strcmp(v->tls.alloc_cert_path, certpath) && - !strcmp(v->tls.key_path, keypath)) { - lws_tls_server_certs_load(v, &wsi, certpath, keypath, - mem_cert, len_mem_cert, - mem_privkey, len_mem_privkey); - - if (v->tls.skipped_certs) - lwsl_notice("%s: vhost %s: cert unset\n", - __func__, v->name); - } - } lws_end_foreach_ll(v, vhost_next); - - return 0; -} -#endif - -int -lws_gate_accepts(struct lws_context *context, int on) -{ - struct lws_vhost *v = context->vhost_list; - - lwsl_notice("%s: on = %d\n", __func__, on); - -#if defined(LWS_WITH_STATS) - context->updated = 1; -#endif - - while (v) { - if (v->tls.use_ssl && v->lserv_wsi && - lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on, - (LWS_POLLIN) * on)) - lwsl_notice("Unable to set accept POLLIN %d\n", on); - - v = v->vhost_next; - } - - return 0; -} - -/* comma-separated alpn list, like "h2,http/1.1" to openssl alpn format */ - -int -lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len) -{ - uint8_t *oos = os, *plen = NULL; - - while (*comma && len > 1) { - if (!plen && *comma == ' ') { - comma++; - continue; - } - if (!plen) { - plen = os++; - len--; - } - - if (*comma == ',') { - *plen = lws_ptr_diff(os, plen + 1); - plen = NULL; - comma++; - } else { - *os++ = *comma++; - len--; - } - } - - if (plen) - *plen = lws_ptr_diff(os, plen + 1); - - return lws_ptr_diff(os, oos); -} -