1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

LWS_WITH_NETWORK: cmake option for no network code

This commit is contained in:
Andy Green 2019-01-13 06:58:21 +08:00
parent 9b5e45d383
commit 84a57540ab
40 changed files with 6557 additions and 6007 deletions

View file

@ -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"

View file

@ -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"
)

View file

@ -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

View file

@ -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

85
lib/core-net/client.c Normal file
View file

@ -0,0 +1,85 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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;
}

518
lib/core-net/close.c Normal file
View file

@ -0,0 +1,518 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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);
}

457
lib/core-net/network.c Normal file
View file

@ -0,0 +1,457 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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

1048
lib/core-net/private.h Normal file

File diff suppressed because it is too large Load diff

299
lib/core-net/server.c Normal file
View file

@ -0,0 +1,299 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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

View file

@ -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 @@ vh_timers_done:
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 @@ vh_timers_done:
if (context->tls_ops &&
context->tls_ops->periodic_housekeeping)
context->tls_ops->periodic_housekeeping(context, now);
#endif
return 0;
}

263
lib/core-net/stats.c Normal file
View file

@ -0,0 +1,263 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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

1241
lib/core-net/vhost.c Normal file

File diff suppressed because it is too large Load diff

251
lib/core-net/wsi-timeout.c Normal file
View file

@ -0,0 +1,251 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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;
}

883
lib/core-net/wsi.c Normal file
View file

@ -0,0 +1,883 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

273
lib/core/logs.c Normal file
View file

@ -0,0 +1,273 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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 <sys/types.h>
#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
}

File diff suppressed because it is too large Load diff

134
lib/core/vfs.c Normal file
View file

@ -0,0 +1,134 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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;
}

View file

@ -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)
{

227
lib/plat/optee/network.c Normal file
View file

@ -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;
}

View file

@ -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)

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - mbedTLS-specific lws apis
*
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* 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)

40
lib/tls/mbedtls/tls.c Normal file
View file

@ -0,0 +1,40 @@
/*
* libwebsockets - mbedTLS-specific lws apis
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* 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;
}

View file

@ -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,

View file

@ -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)
{

126
lib/tls/openssl/tls.c Normal file
View file

@ -0,0 +1,126 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
*
* 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;
}

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - mbedTLS-specific lws apis
* libwebsockets - OpenSSL-specific lws apis
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* 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)

189
lib/tls/private-network.h Normal file
View file

@ -0,0 +1,189 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* 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);

View file

@ -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

255
lib/tls/tls-network.c Normal file
View file

@ -0,0 +1,255 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* 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);
}

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* 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 @@ bail:
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);
}