Bump libwebsockets to version 3.0.0

This commit is contained in:
Fabio Alessandrelli 2018-06-07 13:33:24 +02:00
parent 9d23f1bf1a
commit e56a3c1dc4
98 changed files with 22365 additions and 17374 deletions

View File

@ -221,7 +221,7 @@ License: BSD-3-clause
Files: ./thirdparty/libwebsockets/ Files: ./thirdparty/libwebsockets/
Comment: libwebsockets Comment: libwebsockets
Copyright: 2010-2017, Andy Green Copyright: 2010-2017, Andy Green
License: LGPL-2.1+SLE (lws) License: LGPL-2.1+SLE (libwebsockets)
Files: ./thirdparty/mbedtls/ Files: ./thirdparty/mbedtls/
Comment: Mbed TLS Comment: Mbed TLS
@ -1061,7 +1061,7 @@ License: ISC
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
License: LGPL-2.1+SLE (lws) License: LGPL-2.1+SLE (libwebsockets)
Libwebsockets and included programs are provided under the terms of the GNU Libwebsockets and included programs are provided under the terms of the GNU
Library General Public License (LGPL) 2.1, with the following exceptions: Library General Public License (LGPL) 2.1, with the following exceptions:
. .
@ -1097,19 +1097,21 @@ License: LGPL-2.1+SLE (lws)
. .
Original liberal license retained Original liberal license retained
. .
- lib/sha-1.c - 3-clause BSD license retained, link to original - lib/misc/sha-1.c - 3-clause BSD license retained, link to original
- win32port/zlib - ZLIB license (see zlib.h) - win32port/zlib - ZLIB license (see zlib.h)
- lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls)
. .
Relicensed to libwebsocket license Relicensed to libwebsocket license
. .
- lib/base64-decode.c - relicensed to LGPL2.1+SLE, link to original - lib/misc/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
- lib/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE, - lib/misc/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE,
link to original Public Domain version link to original Public Domain version
. .
Public Domain (CC-zero) to simplify reuse Public Domain (CC-zero) to simplify reuse
. .
- test-server/*.c - test-apps/*.c
- test-server/*.h - test-apps/*.h
- minimal-examples/*
- lwsws/* - lwsws/*
. .
------ end of exceptions ------ end of exceptions

View File

@ -10,44 +10,54 @@ env_lws = env_modules.Clone()
thirdparty_dir = "#thirdparty/libwebsockets/" thirdparty_dir = "#thirdparty/libwebsockets/"
helper_dir = "win32helpers/" helper_dir = "win32helpers/"
thirdparty_sources = [ thirdparty_sources = [
"client/client.c",
"client/client-handshake.c",
"client/client-parser.c",
"client/ssl-client.c",
"ext/extension.c", "core/alloc.c",
"ext/extension-permessage-deflate.c", "core/context.c",
"core/libwebsockets.c",
"core/output.c",
"core/pollfd.c",
"core/service.c",
"server/fops-zip.c", "event-libs/poll/poll.c",
"server/lejp-conf.c",
"server/parsers.c",
"server/ranges.c",
"server/server.c",
"server/server-handshake.c",
"server/ssl-server.c",
"misc/base64-decode.c", "misc/base64-decode.c",
"misc/lejp.c", "misc/lejp.c",
"misc/sha-1.c", "misc/sha-1.c",
"alloc.c", "roles/h1/ops-h1.c",
"context.c", "roles/http/header.c",
"handshake.c", "roles/http/client/client.c",
"header.c", "roles/http/client/client-handshake.c",
"libwebsockets.c", "roles/http/server/fops-zip.c",
"output.c", "roles/http/server/lejp-conf.c",
"pollfd.c", "roles/http/server/parsers.c",
"service.c", "roles/http/server/server.c",
"ssl.c", "roles/listen/ops-listen.c",
"roles/pipe/ops-pipe.c",
"roles/raw/ops-raw.c",
"mbedtls_wrapper/library/ssl_cert.c", "roles/ws/client-ws.c",
"mbedtls_wrapper/library/ssl_pkey.c", "roles/ws/client-parser-ws.c",
"mbedtls_wrapper/library/ssl_stack.c", "roles/ws/ops-ws.c",
"mbedtls_wrapper/library/ssl_methods.c", "roles/ws/server-ws.c",
"mbedtls_wrapper/library/ssl_lib.c",
"mbedtls_wrapper/library/ssl_x509.c", "tls/tls.c",
"mbedtls_wrapper/platform/ssl_port.c", "tls/tls-client.c",
"mbedtls_wrapper/platform/ssl_pm.c", "tls/tls-server.c",
"tls/mbedtls/wrapper/library/ssl_cert.c",
"tls/mbedtls/wrapper/library/ssl_pkey.c",
"tls/mbedtls/wrapper/library/ssl_stack.c",
"tls/mbedtls/wrapper/library/ssl_methods.c",
"tls/mbedtls/wrapper/library/ssl_lib.c",
"tls/mbedtls/wrapper/library/ssl_x509.c",
"tls/mbedtls/wrapper/platform/ssl_port.c",
"tls/mbedtls/wrapper/platform/ssl_pm.c",
"tls/mbedtls/lws-genhash.c",
"tls/mbedtls/mbedtls-client.c",
"tls/mbedtls/lws-genrsa.c",
"tls/mbedtls/ssl.c",
"tls/mbedtls/mbedtls-server.c"
] ]
if env_lws["platform"] == "android": # Builtin getifaddrs if env_lws["platform"] == "android": # Builtin getifaddrs
@ -67,7 +77,7 @@ else:
env_lws.add_source_files(env.modules_sources, thirdparty_sources) env_lws.add_source_files(env.modules_sources, thirdparty_sources)
env_lws.Append(CPPPATH=[thirdparty_dir]) env_lws.Append(CPPPATH=[thirdparty_dir])
wrapper_includes = ["#thirdparty/libwebsockets/mbedtls_wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]] wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
env_lws.Prepend(CPPPATH=wrapper_includes) env_lws.Prepend(CPPPATH=wrapper_includes)
if env['builtin_mbedtls']: if env['builtin_mbedtls']:

View File

@ -32,6 +32,7 @@
#include "lws_client.h" #include "lws_client.h"
#include "core/io/ip.h" #include "core/io/ip.h"
#include "core/io/stream_peer_ssl.h" #include "core/io/stream_peer_ssl.h"
#include "tls/mbedtls/wrapper/include/openssl/ssl.h"
Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
@ -140,7 +141,7 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
destroy_context(); destroy_context();
return -1; // we should close the connection (would probably happen anyway) return -1; // we should close the connection (would probably happen anyway)
case LWS_CALLBACK_CLOSED: case LWS_CALLBACK_CLIENT_CLOSED:
peer_data->in_count = 0; peer_data->in_count = 0;
peer_data->out_count = 0; peer_data->out_count = 0;
peer_data->rbw.resize(0); peer_data->rbw.resize(0);

22
thirdparty/README.md vendored
View File

@ -237,19 +237,21 @@ changes are marked with `// -- GODOT --` comments.
## libwebsockets ## libwebsockets
- Upstream: https://github.com/warmcat/libwebsockets - Upstream: https://github.com/warmcat/libwebsockets
- Version: 2.4.2 - Version: 3.0.0
- License: LGPLv2.1 + static linking exception - License: LGPLv2.1 + static linking exception
File extracted from upstream source: File extracted from upstream source:
- Everything in `lib/` except `minilex.c`, `http2/`, `event-libs/`. - From `lib/` into `thirdparty/libwebsockets`:
- From `misc/` exclude `lws-genhash.c`, `lws-ring.c`, `romfs.{c,h}`, `smtp.c`. - Everything from `core`
- From `plat/` exclude `lws-plat-{esp*,optee}.c`. - From `event-libs` only the `poll` subfolder
- From `server/` exclude `access-log.c`, `cgi.c`, `daemonize.c`, `lws-spa.c`, - From `misc` only `base64-decode.c`, `getifaddrs.c`, `getifaddrs.h`, `lejp.c`, and `sha-1.c`
`peer-limits.c`, `rewrite.c` - From `plat` only `lws-plat-unix.c` and `lws-plat-win.c`
- Also copy `win32helpers/` from `win32port/` - From `roles` only `private.h`, `h1`, `http`, `listen`, `pipe`, `raw`, `ws`
- `mbedtls_wrapper/include/platform/ssl_port.h` has a small change to check for OSX and FreeBSD (missing `malloc.h`). - From `roles/http` exclude `minilex.c`
The bug is fixed in upstream master via `LWS_HAVE_MALLOC_H`, but not in the 2.4.1 branch (as the file structure has changed). - From `roles/http/server` exclude `access-log.c`, `lws-spa.c`, `ranges.c`, and `rewrite.c`
- You might need to apply the patch in `thirdparty/libwebsockets/mbedtls_verify.diff` (port of PR 1215) to future `2.4.x` releases if it does not get cherry picked. - From `roles/ws` exclude `ext` folder.
- From `tls` exclude `openssl` folder.
- Also copy `win32helpers/` from `win32port/` inside `thirdparty/libwebsockets`
Important: `lws_config.h` and `lws_config_private.h` contains custom Important: `lws_config.h` and `lws_config_private.h` contains custom
Godot build configurations, check them out when updating. Godot build configurations, check them out when updating.

View File

@ -33,19 +33,21 @@ to get original sources with the liberal terms.
Original liberal license retained Original liberal license retained
- lib/sha-1.c - 3-clause BSD license retained, link to original - lib/misc/sha-1.c - 3-clause BSD license retained, link to original
- win32port/zlib - ZLIB license (see zlib.h) - win32port/zlib - ZLIB license (see zlib.h)
- lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls)
Relicensed to libwebsocket license Relicensed to libwebsocket license
- lib/base64-decode.c - relicensed to LGPL2.1+SLE, link to original - lib/misc/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
- lib/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE, - lib/misc/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE,
link to original Public Domain version link to original Public Domain version
Public Domain (CC-zero) to simplify reuse Public Domain (CC-zero) to simplify reuse
- test-server/*.c - test-apps/*.c
- test-server/*.h - test-apps/*.h
- minimal-examples/*
- lwsws/* - lwsws/*
------ end of exceptions ------ end of exceptions
@ -107,7 +109,7 @@ modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be author's reputation will not be affected by problems that might be
introduced by others. introduced by others.
Finally, software patents pose a constant threat to the existence of Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a effectively restrict the users of a free program by obtaining a
@ -163,7 +165,7 @@ modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The "work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must former contains code derived from the library, whereas the latter must
be combined with the library in order to run. be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@ -210,7 +212,7 @@ Library.
You may charge a fee for the physical act of transferring a copy, You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a and you may at your option offer warranty protection in exchange for a
fee. fee.
2. You may modify your copy or copies of the Library or any portion 2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1 distribute such modifications or work under the terms of Section 1
@ -268,7 +270,7 @@ instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in that version instead if you wish.) Do not make any other change in
these notices. these notices.
Once this change is made in a given copy, it is irreversible for Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy. subsequent copies and derivative works made from that copy.
@ -319,7 +321,7 @@ Library will still fall under Section 6.)
distribute the object code for the work under the terms of Section 6. distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6, Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself. whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or 6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work work containing portions of the Library, and distribute that work
@ -381,7 +383,7 @@ restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you use both them and the Library together in an executable that you
distribute. distribute.
7. You may place library facilities that are a work based on the 7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined facilities not covered by this License, and distribute such a combined
@ -422,7 +424,7 @@ subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein. restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with You are not responsible for enforcing compliance by third parties with
this License. this License.
11. If, as a consequence of a court judgment or allegation of patent 11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues), infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or conditions are imposed on you (whether by court order, agreement or
@ -474,7 +476,7 @@ conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by license version number, you may choose any version ever published by
the Free Software Foundation. the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free 14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these, programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is write to the author to ask for permission. For software which is
@ -508,7 +510,7 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES. DAMAGES.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest If you develop a new library, and you want it to be of the greatest
@ -552,4 +554,3 @@ necessary. Here is a sample; alter the names:
Ty Coon, President of Vice Ty Coon, President of Vice
That's all there is to it! That's all there is to it!

File diff suppressed because it is too large Load Diff

View File

@ -1,625 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 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 "private-libwebsockets.h"
extern int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
extern void
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info);
extern int lws_ssl_get_error(struct lws *wsi, int n);
#if defined(USE_WOLFSSL)
#else
static int
OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
#if defined(LWS_WITH_MBEDTLS)
lwsl_notice("%s\n", __func__);
return 0;
#else
SSL *ssl;
int n;
struct lws *wsi;
/* keep old behaviour accepting self-signed server certs */
if (!preverify_ok) {
int err = X509_STORE_CTX_get_error(x509_ctx);
if (err != X509_V_OK) {
ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED) {
lwsl_notice("accepting self-signed certificate (verify_callback)\n");
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
return 1; // ok
} else if ((err == X509_V_ERR_CERT_NOT_YET_VALID ||
err == X509_V_ERR_CERT_HAS_EXPIRED) &&
wsi->use_ssl & LCCSCF_ALLOW_EXPIRED) {
if (err == X509_V_ERR_CERT_NOT_YET_VALID)
lwsl_notice("accepting not yet valid certificate (verify_callback)\n");
else if (err == X509_V_ERR_CERT_HAS_EXPIRED)
lwsl_notice("accepting expired certificate (verify_callback)\n");
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
return 1; // ok
}
}
}
ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
n = lws_get_context_protocol(wsi->context, 0).callback(wsi,
LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION,
x509_ctx, ssl, preverify_ok);
/* keep old behaviour if something wrong with server certs */
/* if ssl error is overruled in callback and cert is ok,
* X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); must be set and
* return value is 0 from callback */
if (!preverify_ok) {
int err = X509_STORE_CTX_get_error(x509_ctx);
if (err != X509_V_OK) { /* cert validation error was not handled in callback */
int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
const char* msg = X509_verify_cert_error_string(err);
lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;depth=%d)\n", msg, preverify_ok, err, depth);
return preverify_ok; // not ok
}
}
/* convert callback return code from 0 = OK to verify callback return value 1 = OK */
return !n;
#endif
}
#endif
int
lws_ssl_client_bio_create(struct lws *wsi)
{
char hostname[128], *p;
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
_WSI_TOKEN_CLIENT_HOST) <= 0) {
lwsl_err("%s: Unable to get hostname\n", __func__);
return -1;
}
/*
* remove any :port part on the hostname... necessary for network
* connection but typical certificates do not contain it
*/
p = hostname;
while (*p) {
if (*p == ':') {
*p = '\0';
break;
}
p++;
}
wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx);
if (!wsi->ssl) {
lwsl_err("SSL_new failed: %s\n",
ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
lws_ssl_elaborate_error();
return -1;
}
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
if (wsi->vhost->ssl_info_event_mask)
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
#endif
#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host
X509_VERIFY_PARAM *param;
(void)param;
if (!(wsi->use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
param = SSL_get0_param(wsi->ssl);
/* Enable automatic hostname checks */
X509_VERIFY_PARAM_set_hostflags(param,
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
X509_VERIFY_PARAM_set1_host(param, hostname, 0);
}
#endif
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
#ifndef USE_OLD_CYASSL
/* OpenSSL_client_verify_callback will be called @ SSL_connect() */
SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
#endif
#endif
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
#endif
/*
* use server name indication (SNI), if supported,
* when establishing connection
*/
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
#ifdef CYASSL_SNI_HOST_NAME
CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, hostname, strlen(hostname));
#endif
#else
#ifdef WOLFSSL_SNI_HOST_NAME
wolfSSL_UseSNI(wsi->ssl, WOLFSSL_SNI_HOST_NAME, hostname, strlen(hostname));
#endif
#endif
#else
#if defined(LWS_WITH_MBEDTLS)
SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
#else
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
SSL_set_tlsext_host_name(wsi->ssl, hostname);
#endif
#endif
#endif
#ifdef USE_WOLFSSL
/*
* wolfSSL/CyaSSL does certificate verification differently
* from OpenSSL.
* If we should ignore the certificate, we need to set
* this before SSL_new and SSL_connect is called.
* Otherwise the connect will simply fail with error code -155
*/
#ifdef USE_OLD_CYASSL
if (wsi->use_ssl == 2)
CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL);
#else
if (wsi->use_ssl == 2)
wolfSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL);
#endif
#endif /* USE_WOLFSSL */
#if !defined(LWS_WITH_MBEDTLS)
wsi->client_bio = BIO_new_socket(wsi->desc.sockfd, BIO_NOCLOSE);
SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
#else
SSL_set_fd(wsi->ssl, wsi->desc.sockfd);
#endif
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
CyaSSL_set_using_nonblock(wsi->ssl, 1);
#else
wolfSSL_set_using_nonblock(wsi->ssl, 1);
#endif
#else
#if !defined(LWS_WITH_MBEDTLS)
BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */
#endif
#endif
#if !defined(LWS_WITH_MBEDTLS)
SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index,
wsi);
#endif
return 0;
}
#if defined(LWS_WITH_MBEDTLS)
int ERR_get_error(void)
{
return 0;
}
#endif
int
lws_ssl_client_connect1(struct lws *wsi)
{
struct lws_context *context = wsi->context;
int n = 0;
lws_latency_pre(context, wsi);
n = SSL_connect(wsi->ssl);
lws_latency(context, wsi,
"SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0);
if (n < 0) {
n = lws_ssl_get_error(wsi, n);
if (n == SSL_ERROR_WANT_READ)
goto some_wait;
if (n == SSL_ERROR_WANT_WRITE) {
/*
* wants us to retry connect due to
* state of the underlying ssl layer...
* but since it may be stalled on
* blocked write, no incoming data may
* arrive to trigger the retry.
* Force (possibly many times if the SSL
* state persists in returning the
* condition code, but other sockets
* are getting serviced inbetweentimes)
* us to get called back when writable.
*/
lwsl_info("%s: WANT_WRITE... retrying\n", __func__);
lws_callback_on_writable(wsi);
some_wait:
wsi->mode = LWSCM_WSCL_WAITING_SSL;
return 0; /* no error */
}
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
lwsl_err("ssl hs1 error, X509_V_ERR = %d: errno %d: %s\n",
n, errno, ERR_error_string(n, sb));
lws_ssl_elaborate_error();
#if defined(LWS_WITH_MBEDTLS)
if (n == SSL_ERROR_SYSCALL)
return -1;
#endif
}
n = -1;
}
if (n <= 0) {
/*
* retry if new data comes until we
* run into the connection timeout or win
*/
unsigned long error = ERR_get_error();
if (error != SSL_ERROR_NONE) {
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
lwsl_err("SSL connect error %lu: %s\n",
error, ERR_error_string(error, sb));
return -1;
}
return 0;
}
return 1;
}
int
lws_ssl_client_connect2(struct lws *wsi)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
int n = 0;
if (wsi->mode == LWSCM_WSCL_WAITING_SSL) {
lws_latency_pre(context, wsi);
n = SSL_connect(wsi->ssl);
lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
lws_latency(context, wsi,
"SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0);
if (n < 0) {
n = lws_ssl_get_error(wsi, n);
if (n == SSL_ERROR_WANT_READ) {
lwsl_info("SSL_connect WANT_READ... retrying\n");
wsi->mode = LWSCM_WSCL_WAITING_SSL;
return 0; /* no error */
}
if (n == SSL_ERROR_WANT_WRITE) {
/*
* wants us to retry connect due to
* state of the underlying ssl layer...
* but since it may be stalled on
* blocked write, no incoming data may
* arrive to trigger the retry.
* Force (possibly many times if the SSL
* state persists in returning the
* condition code, but other sockets
* are getting serviced inbetweentimes)
* us to get called back when writable.
*/
lwsl_info("SSL_connect WANT_WRITE... retrying\n");
lws_callback_on_writable(wsi);
wsi->mode = LWSCM_WSCL_WAITING_SSL;
return 0; /* no error */
}
n = -1;
}
if (n <= 0) {
/*
* retry if new data comes until we
* run into the connection timeout or win
*/
unsigned long error = ERR_get_error();
if (error != SSL_ERROR_NONE) {
lwsl_err("SSL connect error %lu: %s\n",
error, ERR_error_string(error, sb));
return -1;
}
}
}
#if defined(LWS_WITH_MBEDTLS)
{
X509 *peer = SSL_get_peer_certificate(wsi->ssl);
if (!peer) {
lwsl_notice("peer did not provide cert\n");
return -1;
}
lwsl_notice("peer provided cert\n");
}
#endif
#ifndef USE_WOLFSSL
/*
* See comment above about wolfSSL certificate
* verification
*/
lws_latency_pre(context, wsi);
n = SSL_get_verify_result(wsi->ssl);
lws_latency(context, wsi,
"SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0);
lwsl_debug("get_verify says %d\n", n);
if (n != X509_V_OK) {
if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
(wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED)) {
lwsl_notice("accepting self-signed certificate\n");
} else if ((n == X509_V_ERR_CERT_NOT_YET_VALID ||
n == X509_V_ERR_CERT_HAS_EXPIRED) &&
(wsi->use_ssl & LCCSCF_ALLOW_EXPIRED)) {
lwsl_notice("accepting expired certificate\n");
} else if (n == X509_V_ERR_CERT_NOT_YET_VALID) {
lwsl_notice("Cert is from the future... "
"probably our clock... accepting...\n");
} else {
lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n",
n, ERR_error_string(n, sb));
lws_ssl_elaborate_error();
return -1;
}
}
#endif /* USE_WOLFSSL */
return 1;
}
int lws_context_init_client_ssl(struct lws_context_creation_info *info,
struct lws_vhost *vhost)
{
SSL_METHOD *method = NULL;
struct lws wsi;
unsigned long error;
const char *ca_filepath = info->ssl_ca_filepath;
#if !defined(LWS_WITH_MBEDTLS)
const char *cipher_list = info->ssl_cipher_list;
const char *private_key_filepath = info->ssl_private_key_filepath;
const char *cert_filepath = info->ssl_cert_filepath;
int n;
if (vhost->options & LWS_SERVER_OPTION_ONLY_RAW)
return 0;
/*
* for backwards-compatibility default to using ssl_... members, but
* if the newer client-specific ones are given, use those
*/
if (info->client_ssl_cipher_list)
cipher_list = info->client_ssl_cipher_list;
if (info->client_ssl_cert_filepath)
cert_filepath = info->client_ssl_cert_filepath;
if (info->client_ssl_private_key_filepath)
private_key_filepath = info->client_ssl_private_key_filepath;
#endif
if (info->client_ssl_ca_filepath)
ca_filepath = info->client_ssl_ca_filepath;
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
return 0;
if (vhost->ssl_client_ctx)
return 0;
if (info->provided_client_ssl_ctx) {
/* use the provided OpenSSL context if given one */
vhost->ssl_client_ctx = info->provided_client_ssl_ctx;
/* nothing for lib to delete */
vhost->user_supplied_ssl_ctx = 1;
return 0;
}
/* basic openssl init already happened in context init */
/* choose the most recent spin of the api */
#if defined(LWS_HAVE_TLS_CLIENT_METHOD)
method = (SSL_METHOD *)TLS_client_method();
#elif defined(LWS_HAVE_TLSV1_2_CLIENT_METHOD)
method = (SSL_METHOD *)TLSv1_2_client_method();
#else
method = (SSL_METHOD *)SSLv23_client_method();
#endif
if (!method) {
error = ERR_get_error();
lwsl_err("problem creating ssl method %lu: %s\n",
error, ERR_error_string(error,
(char *)vhost->context->pt[0].serv_buf));
return 1;
}
/* create context */
vhost->ssl_client_ctx = SSL_CTX_new(method);
if (!vhost->ssl_client_ctx) {
error = ERR_get_error();
lwsl_err("problem creating ssl context %lu: %s\n",
error, ERR_error_string(error,
(char *)vhost->context->pt[0].serv_buf));
return 1;
}
lwsl_notice("created client ssl context for %s\n", vhost->name);
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_NO_COMPRESSION);
#endif
#if defined(LWS_WITH_MBEDTLS)
if (ca_filepath) {
lws_filepos_t len;
uint8_t *buf;
/*
* prototype this here, the shim does not export it in the
* header, and we need to use the shim unchanged for ESP32 case
*/
X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len);
if (alloc_file(vhost->context, ca_filepath, &buf, &len)) {
lwsl_err("Load CA cert file %s failed\n", ca_filepath);
return 1;
}
vhost->x509_client_CA = d2i_X509(NULL, buf, len);
free(buf);
if (!vhost->x509_client_CA) {
lwsl_err("client CA: x509 parse failed\n");
return 1;
}
SSL_CTX_add_client_CA(vhost->ssl_client_ctx,
vhost->x509_client_CA);
lwsl_notice("client loaded CA for verification %s\n", ca_filepath);
}
#else
SSL_CTX_set_options(vhost->ssl_client_ctx,
SSL_OP_CIPHER_SERVER_PREFERENCE);
if (cipher_list)
SSL_CTX_set_cipher_list(vhost->ssl_client_ctx, cipher_list);
#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS))
/* loads OS default CA certs */
SSL_CTX_set_default_verify_paths(vhost->ssl_client_ctx);
#endif
/* openssl init for cert verification (for client sockets) */
if (!ca_filepath) {
if (!SSL_CTX_load_verify_locations(
vhost->ssl_client_ctx, NULL, LWS_OPENSSL_CLIENT_CERTS))
lwsl_err("Unable to load SSL Client certs from %s "
"(set by LWS_OPENSSL_CLIENT_CERTS) -- "
"client ssl isn't going to work\n",
LWS_OPENSSL_CLIENT_CERTS);
} else
if (!SSL_CTX_load_verify_locations(
vhost->ssl_client_ctx, ca_filepath, NULL)) {
lwsl_err(
"Unable to load SSL Client certs "
"file from %s -- client ssl isn't "
"going to work\n", info->client_ssl_ca_filepath);
lws_ssl_elaborate_error();
}
else
lwsl_info("loaded ssl_ca_filepath\n");
/*
* callback allowing user code to load extra verification certs
* helping the client to verify server identity
*/
/* support for client-side certificate authentication */
if (cert_filepath) {
lwsl_notice("%s: doing cert filepath\n", __func__);
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_client_ctx,
cert_filepath);
if (n < 1) {
lwsl_err("problem %d getting cert '%s'\n", n,
cert_filepath);
lws_ssl_elaborate_error();
return 1;
}
lwsl_notice("Loaded client cert %s\n", cert_filepath);
}
if (private_key_filepath) {
lwsl_notice("%s: doing private key filepath\n", __func__);
lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info);
/* set the private key from KeyFile */
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_client_ctx,
private_key_filepath, SSL_FILETYPE_PEM) != 1) {
lwsl_err("use_PrivateKey_file '%s'\n",
private_key_filepath);
lws_ssl_elaborate_error();
return 1;
}
lwsl_notice("Loaded client cert private key %s\n",
private_key_filepath);
/* verify private key */
if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) {
lwsl_err("Private SSL key doesn't match cert\n");
return 1;
}
}
#endif
/*
* give him a fake wsi with context set, so he can use
* lws_get_context() in the callback
*/
memset(&wsi, 0, sizeof(wsi));
wsi.vhost = vhost;
wsi.context = vhost->context;
vhost->protocols[0].callback(&wsi,
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
vhost->ssl_client_ctx, NULL, 0);
return 0;
}

View File

@ -1,4 +1,4 @@
#include "private-libwebsockets.h" #include "core/private.h"
#if defined(LWS_PLAT_OPTEE) #if defined(LWS_PLAT_OPTEE)
@ -51,10 +51,12 @@ void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason))
static void *_realloc(void *ptr, size_t size, const char *reason) static void *_realloc(void *ptr, size_t size, const char *reason)
{ {
if (size) { if (size) {
#if defined(LWS_PLAT_ESP32) #if defined(LWS_WITH_ESP32)
lwsl_notice("%s: size %lu: %s\n", __func__, (unsigned long)size, reason); lwsl_notice("%s: size %lu: %s (free heap %d)\n", __func__,
(unsigned long)size, reason, (unsigned int)esp_get_free_heap_size() - (int)size);
#else #else
lwsl_debug("%s: size %lu: %s\n", __func__, (unsigned long)size, reason); lwsl_debug("%s: size %lu: %s\n", __func__,
(unsigned long)size, reason);
#endif #endif
#if defined(LWS_PLAT_OPTEE) #if defined(LWS_PLAT_OPTEE)
return (void *)TEE_Realloc(ptr, size); return (void *)TEE_Realloc(ptr, size);

308
thirdparty/libwebsockets/core/output.c vendored Normal file
View File

@ -0,0 +1,308 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 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"
/*
* notice this returns number of bytes consumed, or -1
*/
int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
size_t real_len = len;
unsigned int n;
// lwsl_hexdump_err(buf, len);
/*
* Detect if we got called twice without going through the
* event loop to handle pending. This would be caused by either
* back-to-back writes in one WRITABLE (illegal) or calling lws_write()
* from outside the WRITABLE callback (illegal).
*/
if (wsi->could_have_pending) {
lwsl_hexdump_level(LLL_ERR, buf, len);
lwsl_err("** %p: vh: %s, prot: %s, role %s: "
"Illegal back-to-back write of %lu detected...\n",
wsi, wsi->vhost->name, wsi->protocol->name,
wsi->role_ops->name,
(unsigned long)len);
// assert(0);
return -1;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1);
if (!len)
return 0;
/* just ignore sends after we cleared the truncation buffer */
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE && !wsi->trunc_len)
return (int)len;
if (wsi->trunc_len && (buf < wsi->trunc_alloc ||
buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) {
lwsl_hexdump_level(LLL_ERR, buf, len);
lwsl_err("** %p: vh: %s, prot: %s, Sending new %lu, pending truncated ...\n"
" It's illegal to do an lws_write outside of\n"
" the writable callback: fix your code\n",
wsi, wsi->vhost->name, wsi->protocol->name,
(unsigned long)len);
assert(0);
return -1;
}
if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd))
lwsl_warn("** error invalid sock but expected to send\n");
/* limit sending */
if (wsi->protocol->tx_packet_size)
n = (int)wsi->protocol->tx_packet_size;
else {
n = (int)wsi->protocol->rx_buffer_size;
if (!n)
n = context->pt_serv_buf_size;
}
n += LWS_PRE + 4;
if (n > len)
n = (int)len;
/* nope, send it on the socket directly */
lws_latency_pre(context, wsi);
n = lws_ssl_capable_write(wsi, buf, n);
lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
/* something got written, it can have been truncated now */
wsi->could_have_pending = 1;
switch (n) {
case LWS_SSL_CAPABLE_ERROR:
/* we're going to close, let close know sends aren't possible */
wsi->socket_is_permanently_unusable = 1;
return -1;
case LWS_SSL_CAPABLE_MORE_SERVICE:
/*
* nothing got sent, not fatal. Retry the whole thing later,
* ie, implying treat it was a truncated send so it gets
* retried
*/
n = 0;
break;
}
/*
* we were already handling a truncated send?
*/
if (wsi->trunc_len) {
lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len);
wsi->trunc_offset += n;
wsi->trunc_len -= n;
if (!wsi->trunc_len) {
lwsl_info("** %p partial send completed\n", wsi);
/* done with it, but don't free it */
n = (int)real_len;
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
lwsl_info("** %p signalling to close now\n", wsi);
return -1; /* retry closing now */
}
}
/* always callback on writeable */
lws_callback_on_writable(wsi);
return n;
}
if ((unsigned int)n == real_len)
/* what we just sent went out cleanly */
return n;
/*
* Newly truncated send. Buffer the remainder (it will get
* first priority next time the socket is writable).
*/
lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n,
(unsigned long)real_len);
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1);
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n);
/*
* - if we still have a suitable malloc lying around, use it
* - or, if too small, reallocate it
* - or, if no buffer, create it
*/
if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) {
lws_free(wsi->trunc_alloc);
wsi->trunc_alloc_len = (unsigned int)(real_len - n);
wsi->trunc_alloc = lws_malloc(real_len - n,
"truncated send alloc");
if (!wsi->trunc_alloc) {
lwsl_err("truncated send: unable to malloc %lu\n",
(unsigned long)(real_len - n));
return -1;
}
}
wsi->trunc_offset = 0;
wsi->trunc_len = (unsigned int)(real_len - n);
memcpy(wsi->trunc_alloc, buf + n, real_len - n);
#if !defined(LWS_WITH_ESP32)
if (lws_wsi_is_udp(wsi)) {
/* stash original destination for fulfilling UDP partials */
wsi->udp->sa_pending = wsi->udp->sa;
wsi->udp->salen_pending = wsi->udp->salen;
}
#endif
/* since something buffered, force it to get another chance to send */
lws_callback_on_writable(wsi);
return (int)real_len;
}
LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
enum lws_write_protocol wp)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
if (wsi->parent_carries_io) {
struct lws_write_passthru pas;
pas.buf = buf;
pas.len = len;
pas.wp = wp;
pas.wsi = wsi;
if (wsi->parent->protocol->callback(wsi->parent,
LWS_CALLBACK_CHILD_WRITE_VIA_PARENT,
wsi->parent->user_space,
(void *)&pas, 0))
return 1;
return (int)len;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1);
if ((int)len < 0) {
lwsl_err("%s: suspicious len int %d, ulong %lu\n", __func__,
(int)len, (unsigned long)len);
return -1;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_WRITE, len);
#ifdef LWS_WITH_ACCESS_LOG
wsi->http.access_log.sent += len;
#endif
if (wsi->vhost)
wsi->vhost->conn_stats.tx += len;
assert(wsi->role_ops);
if (!wsi->role_ops->write_role_protocol)
return lws_issue_raw(wsi, buf, len);
return wsi->role_ops->write_role_protocol(wsi, buf, len, &wp);
}
LWS_VISIBLE int
lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n = 0;
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
if (lws_wsi_is_udp(wsi)) {
#if !defined(LWS_WITH_ESP32)
wsi->udp->salen = sizeof(wsi->udp->sa);
n = recvfrom(wsi->desc.sockfd, (char *)buf, len, 0,
&wsi->udp->sa, &wsi->udp->salen);
#endif
} else
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
if (n >= 0) {
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
return n;
}
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK ||
LWS_ERRNO == LWS_EINTR)
return LWS_SSL_CAPABLE_MORE_SERVICE;
lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO);
return LWS_SSL_CAPABLE_ERROR;
}
LWS_VISIBLE int
lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
int n = 0;
if (lws_wsi_is_udp(wsi)) {
#if !defined(LWS_WITH_ESP32)
if (wsi->trunc_len)
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa_pending, wsi->udp->salen_pending);
else
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa, wsi->udp->salen);
#endif
} else
n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
// lwsl_info("%s: sent len %d result %d", __func__, len, n);
if (n >= 0)
return n;
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK ||
LWS_ERRNO == LWS_EINTR) {
if (LWS_ERRNO == LWS_EWOULDBLOCK) {
lws_set_blocking_send(wsi);
}
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n",
len, wsi->desc.sockfd, n, LWS_ERRNO);
return LWS_SSL_CAPABLE_ERROR;
}
LWS_VISIBLE int
lws_ssl_pending_no_ssl(struct lws *wsi)
{
(void)wsi;
#if defined(LWS_WITH_ESP32)
return 100;
#else
return 0;
#endif
}

View File

@ -19,21 +19,31 @@
* MA 02110-1301 USA * MA 02110-1301 USA
*/ */
#include "private-libwebsockets.h" #include "core/private.h"
int int
_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa) _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
{ {
#if !defined(LWS_WITH_LIBUV) && !defined(LWS_WITH_LIBEV) && !defined(LWS_WITH_LIBEVENT)
volatile struct lws_context_per_thread *vpt;
#endif
struct lws_context_per_thread *pt; struct lws_context_per_thread *pt;
struct lws_context *context; struct lws_context *context;
int ret = 0, pa_events = 1; int ret = 0, pa_events = 1;
struct lws_pollfd *pfd; struct lws_pollfd *pfd;
int sampled_tid, tid; int sampled_tid, tid;
if (!wsi || wsi->position_in_fds_table < 0) if (!wsi)
return 0; return 0;
if (wsi->handling_pollout && !_and && _or == LWS_POLLOUT) { assert(wsi->position_in_fds_table == LWS_NO_FDS_POS ||
wsi->position_in_fds_table >= 0);
if (wsi->position_in_fds_table == LWS_NO_FDS_POS)
return 0;
if (((volatile struct lws *)wsi)->handling_pollout &&
!_and && _or == LWS_POLLOUT) {
/* /*
* Happening alongside service thread handling POLLOUT. * Happening alongside service thread handling POLLOUT.
* The danger is when he is finished, he will disable POLLOUT, * The danger is when he is finished, he will disable POLLOUT,
@ -42,7 +52,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
* Instead of changing the fds, inform the service thread * Instead of changing the fds, inform the service thread
* what happened, and ask it to leave POLLOUT active on exit * what happened, and ask it to leave POLLOUT active on exit
*/ */
wsi->leave_pollout_active = 1; ((volatile struct lws *)wsi)->leave_pollout_active = 1;
/* /*
* by definition service thread is not in poll wait, so no need * by definition service thread is not in poll wait, so no need
* to cancel service * to cancel service
@ -55,42 +65,106 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
context = wsi->context; context = wsi->context;
pt = &context->pt[(int)wsi->tsi]; pt = &context->pt[(int)wsi->tsi];
assert(wsi->position_in_fds_table >= 0 &&
wsi->position_in_fds_table < pt->fds_count); assert(wsi->position_in_fds_table < (int)pt->fds_count);
#if !defined(LWS_WITH_LIBUV) && \
!defined(LWS_WITH_LIBEV) && \
!defined(LWS_WITH_LIBEVENT)
/*
* This only applies when we use the default poll() event loop.
*
* BSD can revert pa->events at any time, when the kernel decides to
* exit from poll(). We can't protect against it using locking.
*
* Therefore we must check first if the service thread is in poll()
* wait; if so, we know we must be being called from a foreign thread,
* and we must keep a strictly ordered list of changes we made instead
* of trying to apply them, since when poll() exits, which may happen
* at any time it would revert our changes.
*
* The plat code will apply them when it leaves the poll() wait
* before doing anything else.
*/
vpt = (volatile struct lws_context_per_thread *)pt;
vpt->foreign_spinlock = 1;
lws_memory_barrier();
if (vpt->inside_poll) {
struct lws_foreign_thread_pollfd *ftp, **ftp1;
/*
* We are certainly a foreign thread trying to change events
* while the service thread is in the poll() wait.
*
* Create a list of changes to be applied after poll() exit,
* instead of trying to apply them now.
*/
ftp = lws_malloc(sizeof(*ftp), "ftp");
if (!ftp) {
vpt->foreign_spinlock = 0;
lws_memory_barrier();
ret = -1;
goto bail;
}
ftp->_and = _and;
ftp->_or = _or;
ftp->fd_index = wsi->position_in_fds_table;
ftp->next = NULL;
/* place at END of list to maintain order */
ftp1 = (struct lws_foreign_thread_pollfd **)
&vpt->foreign_pfd_list;
while (*ftp1)
ftp1 = &((*ftp1)->next);
*ftp1 = ftp;
vpt->foreign_spinlock = 0;
lws_memory_barrier();
lws_cancel_service_pt(wsi);
return 0;
}
vpt->foreign_spinlock = 0;
lws_memory_barrier();
#endif
pfd = &pt->fds[wsi->position_in_fds_table]; pfd = &pt->fds[wsi->position_in_fds_table];
pa->fd = wsi->desc.sockfd; pa->fd = wsi->desc.sockfd;
lwsl_debug("%s: wsi %p: fd %d events %d -> %d\n", __func__, wsi, pa->fd, pfd->events, (pfd->events & ~_and) | _or);
pa->prev_events = pfd->events; pa->prev_events = pfd->events;
pa->events = pfd->events = (pfd->events & ~_and) | _or; pa->events = pfd->events = (pfd->events & ~_and) | _or;
if (wsi->http2_substream) if (wsi->http2_substream)
return 0; return 0;
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD, if (wsi->vhost &&
wsi->user_space, (void *)pa, 0)) { wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_CHANGE_MODE_POLL_FD,
wsi->user_space, (void *)pa, 0)) {
ret = -1; ret = -1;
goto bail; goto bail;
} }
if (_and & LWS_POLLIN) { if (context->event_loop_ops->io) {
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ); if (_and & LWS_POLLIN)
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ); context->event_loop_ops->io(wsi,
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ); LWS_EV_STOP | LWS_EV_READ);
}
if (_or & LWS_POLLIN) { if (_or & LWS_POLLIN)
lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ); context->event_loop_ops->io(wsi,
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ); LWS_EV_START | LWS_EV_READ);
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
} if (_and & LWS_POLLOUT)
if (_and & LWS_POLLOUT) { context->event_loop_ops->io(wsi,
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); LWS_EV_STOP | LWS_EV_WRITE);
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); if (_or & LWS_POLLOUT)
} context->event_loop_ops->io(wsi,
if (_or & LWS_POLLOUT) { LWS_EV_START | LWS_EV_WRITE);
lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE);
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE);
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_WRITE);
} }
/* /*
@ -100,20 +174,16 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
* ... and the service thread is waiting ... * ... and the service thread is waiting ...
* then cancel it to force a restart with our changed events * then cancel it to force a restart with our changed events
*/ */
#if LWS_POSIX
pa_events = pa->prev_events != pa->events; pa_events = pa->prev_events != pa->events;
#endif
if (pa_events) { if (pa_events) {
if (lws_plat_change_pollfd(context, wsi, pfd)) { if (lws_plat_change_pollfd(context, wsi, pfd)) {
lwsl_info("%s failed\n", __func__); lwsl_info("%s failed\n", __func__);
ret = -1; ret = -1;
goto bail; goto bail;
} }
sampled_tid = context->service_tid; sampled_tid = context->service_tid;
if (sampled_tid) { if (sampled_tid && wsi->vhost) {
tid = wsi->vhost->protocols[0].callback(wsi, tid = wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
if (tid == -1) { if (tid == -1) {
@ -124,34 +194,39 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
lws_cancel_service_pt(wsi); lws_cancel_service_pt(wsi);
} }
} }
bail: bail:
return ret; return ret;
} }
#ifndef LWS_NO_SERVER #ifndef LWS_NO_SERVER
/*
* Enable or disable listen sockets on this pt globally...
* it's modulated according to the pt having space for a new accept.
*/
static void static void
lws_accept_modulation(struct lws_context_per_thread *pt, int allow) lws_accept_modulation(struct lws_context *context,
struct lws_context_per_thread *pt, int allow)
{ {
// multithread listen seems broken
#if 0
struct lws_vhost *vh = context->vhost_list; struct lws_vhost *vh = context->vhost_list;
struct lws_pollargs pa1; struct lws_pollargs pa1;
while (vh) { while (vh) {
if (allow) if (vh->lserv_wsi) {
_lws_change_pollfd(pt->wsi_listening, if (allow)
_lws_change_pollfd(vh->lserv_wsi,
0, LWS_POLLIN, &pa1); 0, LWS_POLLIN, &pa1);
else else
_lws_change_pollfd(pt->wsi_listening, _lws_change_pollfd(vh->lserv_wsi,
LWS_POLLIN, 0, &pa1); LWS_POLLIN, 0, &pa1);
}
vh = vh->vhost_next; vh = vh->vhost_next;
} }
#endif
} }
#endif #endif
int int
insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi) __insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
{ {
struct lws_pollargs pa = { wsi->desc.sockfd, LWS_POLLIN, 0 }; struct lws_pollargs pa = { wsi->desc.sockfd, LWS_POLLIN, 0 };
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
@ -167,52 +242,46 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
return 1; return 1;
} }
#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266) #if !defined(_WIN32)
if (wsi->desc.sockfd >= context->max_fds) { if (wsi->desc.sockfd - lws_plat_socket_offset() >= context->max_fds) {
lwsl_err("Socket fd %d is too high (%d)\n", lwsl_err("Socket fd %d is too high (%d) offset %d\n",
wsi->desc.sockfd, context->max_fds); wsi->desc.sockfd, context->max_fds, lws_plat_socket_offset());
return 1; return 1;
} }
#endif #endif
assert(wsi); assert(wsi);
assert(wsi->vhost); assert(wsi->event_pipe || wsi->vhost);
assert(lws_socket_is_valid(wsi->desc.sockfd)); assert(lws_socket_is_valid(wsi->desc.sockfd));
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *) &pa, 1)) wsi->user_space, (void *) &pa, 1))
return -1; return -1;
lws_pt_lock(pt);
pt->count_conns++; pt->count_conns++;
insert_wsi(context, wsi); insert_wsi(context, wsi);
#if defined(LWS_WITH_ESP8266) wsi->position_in_fds_table = pt->fds_count;
if (wsi->position_in_fds_table == -1)
#endif
wsi->position_in_fds_table = pt->fds_count;
pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd; pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd;
#if LWS_POSIX
pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN; pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN;
#else
pt->fds[wsi->position_in_fds_table].events = 0;
#endif
pa.events = pt->fds[pt->fds_count].events; pa.events = pt->fds[pt->fds_count].events;
lws_plat_insert_socket_into_fds(context, wsi); lws_plat_insert_socket_into_fds(context, wsi);
/* external POLL support via protocol 0 */ /* external POLL support via protocol 0 */
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD, if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
wsi->user_space, (void *) &pa, 0)) wsi->user_space, (void *) &pa, 0))
ret = -1; ret = -1;
#ifndef LWS_NO_SERVER #ifndef LWS_NO_SERVER
/* if no more room, defeat accepts on this thread */ /* if no more room, defeat accepts on this thread */
if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1) if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1)
lws_accept_modulation(pt, 0); lws_accept_modulation(context, pt, 0);
#endif #endif
lws_pt_unlock(pt);
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *)&pa, 1)) wsi->user_space, (void *)&pa, 1))
ret = -1; ret = -1;
@ -220,15 +289,13 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
} }
int int
remove_wsi_socket_from_fds(struct lws *wsi) __remove_wsi_socket_from_fds(struct lws *wsi)
{ {
struct lws_context *context = wsi->context; struct lws_context *context = wsi->context;
struct lws_pollargs pa = { wsi->desc.sockfd, 0, 0 }; struct lws_pollargs pa = { wsi->desc.sockfd, 0, 0 };
#if !defined(LWS_WITH_ESP8266)
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws *end_wsi; struct lws *end_wsi;
int v; int v;
#endif
int m, ret = 0; int m, ret = 0;
if (wsi->parent_carries_io) { if (wsi->parent_carries_io) {
@ -236,15 +303,16 @@ remove_wsi_socket_from_fds(struct lws *wsi)
return 0; return 0;
} }
#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266) #if !defined(_WIN32)
if (wsi->desc.sockfd > context->max_fds) { if (wsi->desc.sockfd - lws_plat_socket_offset() > context->max_fds) {
lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd, lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd,
context->max_fds); context->max_fds);
return 1; return 1;
} }
#endif #endif
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *)&pa, 1)) wsi->user_space, (void *)&pa, 1))
return -1; return -1;
@ -253,56 +321,89 @@ remove_wsi_socket_from_fds(struct lws *wsi)
/* the guy who is to be deleted's slot index in pt->fds */ /* the guy who is to be deleted's slot index in pt->fds */
m = wsi->position_in_fds_table; m = wsi->position_in_fds_table;
#if !defined(LWS_WITH_ESP8266) /* these are the only valid possibilities for position_in_fds_table */
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | assert(m == LWS_NO_FDS_POS || (m >= 0 &&
LWS_EV_PREPARE_DELETION); (unsigned int)m < pt->fds_count));
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE |
LWS_EV_PREPARE_DELETION);
lws_pt_lock(pt); if (context->event_loop_ops->io)
context->event_loop_ops->io(wsi,
LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE |
LWS_EV_PREPARE_DELETION);
lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n", lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n",
__func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table, __func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table,
pt->fds_count, pt->fds[pt->fds_count].fd); pt->fds_count, pt->fds[pt->fds_count].fd);
/* have the last guy take up the now vacant slot */ if (m != LWS_NO_FDS_POS) {
pt->fds[m] = pt->fds[pt->fds_count - 1];
#endif
/* this decrements pt->fds_count */
lws_plat_delete_socket_from_fds(context, wsi, m);
#if !defined(LWS_WITH_ESP8266)
v = (int) pt->fds[m].fd;
/* end guy's "position in fds table" is now the deletion guy's old one */
end_wsi = wsi_from_fd(context, v);
if (!end_wsi) {
lwsl_err("no wsi found for sock fd %d at pos %d, pt->fds_count=%d\n",
(int)pt->fds[m].fd, m, pt->fds_count);
assert(0);
} else
end_wsi->position_in_fds_table = m;
/* deletion guy's lws_lookup entry needs nuking */ /* have the last guy take up the now vacant slot */
delete_from_fd(context, wsi->desc.sockfd); pt->fds[m] = pt->fds[pt->fds_count - 1];
/* removed wsi has no position any more */ /* this decrements pt->fds_count */
wsi->position_in_fds_table = -1; lws_plat_delete_socket_from_fds(context, wsi, m);
v = (int) pt->fds[m].fd;
/* end guy's "position in fds table" is now the deletion guy's old one */
end_wsi = wsi_from_fd(context, v);
if (!end_wsi) {
lwsl_err("no wsi for fd %d at pos %d, pt->fds_count=%d\n",
(int)pt->fds[m].fd, m, pt->fds_count);
assert(0);
} else
end_wsi->position_in_fds_table = m;
/* deletion guy's lws_lookup entry needs nuking */
delete_from_fd(context, wsi->desc.sockfd);
/* removed wsi has no position any more */
wsi->position_in_fds_table = LWS_NO_FDS_POS;
}
/* remove also from external POLL support via protocol 0 */ /* remove also from external POLL support via protocol 0 */
if (lws_socket_is_valid(wsi->desc.sockfd)) if (lws_socket_is_valid(wsi->desc.sockfd) && wsi->vhost &&
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD, wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
wsi->user_space, (void *) &pa, 0)) wsi->user_space, (void *) &pa, 0))
ret = -1; ret = -1;
#ifndef LWS_NO_SERVER
if (!context->being_destroyed)
/* if this made some room, accept connects on this thread */
if ((unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
lws_accept_modulation(pt, 1);
#endif
lws_pt_unlock(pt);
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, #ifndef LWS_NO_SERVER
if (!context->being_destroyed &&
/* if this made some room, accept connects on this thread */
(unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
lws_accept_modulation(context, pt, 1);
#endif
if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 1)) wsi->user_space, (void *) &pa, 1))
ret = -1; ret = -1;
#endif
return ret;
}
int
__lws_change_pollfd(struct lws *wsi, int _and, int _or)
{
struct lws_context *context;
struct lws_pollargs pa;
int ret = 0;
if (!wsi || (!wsi->protocol && !wsi->event_pipe) ||
wsi->position_in_fds_table == LWS_NO_FDS_POS)
return 0;
context = lws_get_context(wsi);
if (!context)
return 1;
if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *) &pa, 0))
return -1;
ret = _lws_change_pollfd(wsi, _and, _or, &pa);
if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 0))
ret = -1;
return ret; return ret;
} }
@ -310,29 +411,13 @@ int
lws_change_pollfd(struct lws *wsi, int _and, int _or) lws_change_pollfd(struct lws *wsi, int _and, int _or)
{ {
struct lws_context_per_thread *pt; struct lws_context_per_thread *pt;
struct lws_context *context;
struct lws_pollargs pa;
int ret = 0; int ret = 0;
if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0) pt = &wsi->context->pt[(int)wsi->tsi];
return 1;
context = lws_get_context(wsi); lws_pt_lock(pt, __func__);
if (!context) ret = __lws_change_pollfd(wsi, _and, _or);
return 1;
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *) &pa, 0))
return -1;
pt = &context->pt[(int)wsi->tsi];
lws_pt_lock(pt);
ret = _lws_change_pollfd(wsi, _and, _or, &pa);
lws_pt_unlock(pt); lws_pt_unlock(pt);
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 0))
ret = -1;
return ret; return ret;
} }
@ -341,13 +426,9 @@ LWS_VISIBLE int
lws_callback_on_writable(struct lws *wsi) lws_callback_on_writable(struct lws *wsi)
{ {
struct lws_context_per_thread *pt; struct lws_context_per_thread *pt;
#ifdef LWS_WITH_HTTP2
struct lws *network_wsi, *wsi2;
int already;
#endif
int n; int n;
if (wsi->state == LWSS_SHUTDOWN) if (lwsi_state(wsi) == LRS_SHUTDOWN)
return 0; return 0;
if (wsi->socket_is_permanently_unusable) if (wsi->socket_is_permanently_unusable)
@ -375,72 +456,31 @@ lws_callback_on_writable(struct lws *wsi)
#if defined(LWS_WITH_STATS) #if defined(LWS_WITH_STATS)
if (!wsi->active_writable_req_us) { if (!wsi->active_writable_req_us) {
wsi->active_writable_req_us = time_in_microseconds(); wsi->active_writable_req_us = time_in_microseconds();
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1); lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1);
} }
#endif #endif
#ifdef LWS_WITH_HTTP2
lwsl_info("%s: %p\n", __func__, wsi);
if (wsi->mode != LWSCM_HTTP2_SERVING) if (wsi->role_ops->callback_on_writable) {
goto network_sock; if (wsi->role_ops->callback_on_writable(wsi))
return 1;
if (wsi->u.h2.requested_POLLOUT) { wsi = lws_get_network_wsi(wsi);
lwsl_info("already pending writable\n");
return 1;
} }
/* is this for DATA or for control messages? */ if (wsi->position_in_fds_table == LWS_NO_FDS_POS) {
if (wsi->upgraded_to_http2 && !wsi->u.h2.h2n->pps && lwsl_debug("%s: failed to find socket %d\n", __func__,
!lws_h2_tx_cr_get(wsi)) { wsi->desc.sockfd);
/*
* other side is not able to cope with us sending DATA
* anything so no matter if we have POLLOUT on our side if it's
* DATA we want to send.
*
* Delay waiting for our POLLOUT until peer indicates he has
* space for more using tx window command in http2 layer
*/
lwsl_notice("%s: %p: skint (%d)\n", __func__, wsi, wsi->u.h2.tx_cr);
wsi->u.h2.skint = 1;
return 0;
}
wsi->u.h2.skint = 0;
network_wsi = lws_get_network_wsi(wsi);
already = network_wsi->u.h2.requested_POLLOUT;
/* mark everybody above him as requesting pollout */
wsi2 = wsi;
while (wsi2) {
wsi2->u.h2.requested_POLLOUT = 1;
lwsl_info("mark %p pending writable\n", wsi2);
wsi2 = wsi2->u.h2.parent_wsi;
}
/* for network action, act only on the network wsi */
wsi = network_wsi;
if (already)
return 1;
network_sock:
#endif
if (lws_ext_cb_active(wsi, LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0))
return 1;
if (wsi->position_in_fds_table < 0) {
lwsl_debug("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd);
return -1; return -1;
} }
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) if (__lws_change_pollfd(wsi, 0, LWS_POLLOUT))
return -1; return -1;
return 1; return 1;
} }
/* /*
* stitch protocol choice into the vh protocol linked list * stitch protocol choice into the vh protocol linked list
* We always insert ourselves at the start of the list * We always insert ourselves at the start of the list
@ -458,6 +498,8 @@ lws_same_vh_protocol_insert(struct lws *wsi, int n)
lwsl_notice("Attempted to attach wsi twice to same vh prot\n"); lwsl_notice("Attempted to attach wsi twice to same vh prot\n");
} }
lws_vhost_lock(wsi->vhost);
wsi->same_vh_protocol_prev = &wsi->vhost->same_vh_protocol_list[n]; wsi->same_vh_protocol_prev = &wsi->vhost->same_vh_protocol_list[n];
/* old first guy is our next */ /* old first guy is our next */
wsi->same_vh_protocol_next = wsi->vhost->same_vh_protocol_list[n]; wsi->same_vh_protocol_next = wsi->vhost->same_vh_protocol_list[n];
@ -468,6 +510,10 @@ lws_same_vh_protocol_insert(struct lws *wsi, int n)
/* old first guy points back to us now */ /* old first guy points back to us now */
wsi->same_vh_protocol_next->same_vh_protocol_prev = wsi->same_vh_protocol_next->same_vh_protocol_prev =
&wsi->same_vh_protocol_next; &wsi->same_vh_protocol_next;
wsi->on_same_vh_list = 1;
lws_vhost_unlock(wsi->vhost);
} }
void void
@ -482,6 +528,11 @@ lws_same_vh_protocol_remove(struct lws *wsi)
*/ */
lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi); lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi);
if (!wsi->vhost || !wsi->on_same_vh_list)
return;
lws_vhost_lock(wsi->vhost);
if (wsi->same_vh_protocol_prev) { if (wsi->same_vh_protocol_prev) {
assert (*(wsi->same_vh_protocol_prev) == wsi); assert (*(wsi->same_vh_protocol_prev) == wsi);
lwsl_info("have prev %p, setting him to our next %p\n", lwsl_info("have prev %p, setting him to our next %p\n",
@ -493,13 +544,15 @@ lws_same_vh_protocol_remove(struct lws *wsi)
} }
/* our next should point back to our prev */ /* our next should point back to our prev */
if (wsi->same_vh_protocol_next) { if (wsi->same_vh_protocol_next)
wsi->same_vh_protocol_next->same_vh_protocol_prev = wsi->same_vh_protocol_next->same_vh_protocol_prev =
wsi->same_vh_protocol_prev; wsi->same_vh_protocol_prev;
}
wsi->same_vh_protocol_prev = NULL; wsi->same_vh_protocol_prev = NULL;
wsi->same_vh_protocol_next = NULL; wsi->same_vh_protocol_next = NULL;
wsi->on_same_vh_list = 0;
lws_vhost_unlock(wsi->vhost);
} }
@ -523,7 +576,8 @@ lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
assert(wsi->protocol == protocol); assert(wsi->protocol == protocol);
assert(*wsi->same_vh_protocol_prev == wsi); assert(*wsi->same_vh_protocol_prev == wsi);
if (wsi->same_vh_protocol_next) if (wsi->same_vh_protocol_next)
assert(wsi->same_vh_protocol_next->same_vh_protocol_prev == assert(wsi->same_vh_protocol_next->
same_vh_protocol_prev ==
&wsi->same_vh_protocol_next); &wsi->same_vh_protocol_next);
lws_callback_on_writable(wsi); lws_callback_on_writable(wsi);

1751
thirdparty/libwebsockets/core/private.h vendored Normal file

File diff suppressed because it is too large Load Diff

987
thirdparty/libwebsockets/core/service.c vendored Normal file
View File

@ -0,0 +1,987 @@
/*
* 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"
int
lws_callback_as_writeable(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int n, m;
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB, 1);
#if defined(LWS_WITH_STATS)
if (wsi->active_writable_req_us) {
uint64_t ul = time_in_microseconds() -
wsi->active_writable_req_us;
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_MS_WRITABLE_DELAY, ul);
lws_stats_atomic_max(wsi->context, pt,
LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
wsi->active_writable_req_us = 0;
}
#endif
n = wsi->role_ops->writeable_cb[lwsi_role_server(wsi)];
m = user_callback_handle_rxflow(wsi->protocol->callback,
wsi, (enum lws_callback_reasons) n,
wsi->user_space, NULL, 0);
return m;
}
LWS_VISIBLE int
lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
{
volatile struct lws *vwsi = (volatile struct lws *)wsi;
int n;
//lwsl_notice("%s: %p\n", __func__, wsi);
vwsi->leave_pollout_active = 0;
vwsi->handling_pollout = 1;
/*
* if another thread wants POLLOUT on us, from here on while
* handling_pollout is set, he will only set leave_pollout_active.
* If we are going to disable POLLOUT, we will check that first.
*/
wsi->could_have_pending = 0; /* clear back-to-back write detection */
/*
* user callback is lowest priority to get these notifications
* actually, since other pending things cannot be disordered
*
* Priority 1: pending truncated sends are incomplete ws fragments
* If anything else sent first the protocol would be
* corrupted.
*/
if (wsi->trunc_len) {
//lwsl_notice("%s: completing partial\n", __func__);
if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
wsi->trunc_len) < 0) {
lwsl_info("%s signalling to close\n", __func__);
goto bail_die;
}
/* leave POLLOUT active either way */
goto bail_ok;
} else
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
wsi->socket_is_permanently_unusable = 1;
goto bail_die; /* retry closing now */
}
#ifdef LWS_WITH_CGI
/*
* A cgi master's wire protocol remains h1 or h2. He is just getting
* his data from his child cgis.
*/
if (wsi->http.cgi) {
/* also one shot */
if (pollfd)
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_info("failed at set pollfd\n");
return 1;
}
goto user_service_go_again;
}
#endif
/* if we got here, we should have wire protocol ops set on the wsi */
assert(wsi->role_ops);
if (!wsi->role_ops->handle_POLLOUT)
goto bail_ok;
switch ((wsi->role_ops->handle_POLLOUT)(wsi)) {
case LWS_HP_RET_BAIL_OK:
goto bail_ok;
case LWS_HP_RET_BAIL_DIE:
goto bail_die;
case LWS_HP_RET_USER_SERVICE:
break;
default:
assert(0);
}
/* one shot */
if (wsi->parent_carries_io) {
vwsi->handling_pollout = 0;
vwsi->leave_pollout_active = 0;
return lws_callback_as_writeable(wsi);
}
if (pollfd) {
int eff = vwsi->leave_pollout_active;
if (!eff) {
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_info("failed at set pollfd\n");
goto bail_die;
}
}
vwsi->handling_pollout = 0;
/* cannot get leave_pollout_active set after the above */
if (!eff && wsi->leave_pollout_active) {
/*
* got set inbetween sampling eff and clearing
* handling_pollout, force POLLOUT on
*/
lwsl_debug("leave_pollout_active\n");
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
lwsl_info("failed at set pollfd\n");
goto bail_die;
}
}
vwsi->leave_pollout_active = 0;
}
if (lwsi_role_client(wsi) &&
!wsi->hdr_parsing_completed &&
lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS &&
lwsi_state(wsi) != LRS_ISSUE_HTTP_BODY
)
goto bail_ok;
#ifdef LWS_WITH_CGI
user_service_go_again:
#endif
if (wsi->role_ops->perform_user_POLLOUT) {
if (wsi->role_ops->perform_user_POLLOUT(wsi) == -1)
goto bail_die;
else
goto bail_ok;
}
lwsl_debug("%s: %p: non mux: wsistate 0x%x, ops %s\n", __func__, wsi,
wsi->wsistate, wsi->role_ops->name);
vwsi = (volatile struct lws *)wsi;
vwsi->leave_pollout_active = 0;
n = lws_callback_as_writeable(wsi);
vwsi->handling_pollout = 0;
if (vwsi->leave_pollout_active)
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
return n;
/*
* since these don't disable the POLLOUT, they are always doing the
* right thing for leave_pollout_active whether it was set or not.
*/
bail_ok:
vwsi->handling_pollout = 0;
vwsi->leave_pollout_active = 0;
return 0;
bail_die:
vwsi->handling_pollout = 0;
vwsi->leave_pollout_active = 0;
return -1;
}
static int
__lws_service_timeout_check(struct lws *wsi, time_t sec)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int n = 0;
(void)n;
/*
* if we went beyond the allowed time, kill the
* connection
*/
if (wsi->dll_timeout.prev &&
lws_compare_time_t(wsi->context, sec, wsi->pending_timeout_set) >
wsi->pending_timeout_limit) {
if (wsi->desc.sockfd != LWS_SOCK_INVALID &&
wsi->position_in_fds_table >= 0)
n = pt->fds[wsi->position_in_fds_table].events;
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_TIMEOUTS, 1);
/* no need to log normal idle keepalive timeout */
if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE)
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
lwsl_info("wsi %p: TIMEDOUT WAITING on %d "
"(did hdr %d, ah %p, wl %d, pfd "
"events %d) %llu vs %llu\n",
(void *)wsi, wsi->pending_timeout,
wsi->hdr_parsing_completed, wsi->http.ah,
pt->http.ah_wait_list_length, n,
(unsigned long long)sec,
(unsigned long long)wsi->pending_timeout_limit);
#if defined(LWS_WITH_CGI)
if (wsi->http.cgi)
lwsl_notice("CGI timeout: %s\n", wsi->http.cgi->summary);
#endif
#else
lwsl_info("wsi %p: TIMEDOUT WAITING on %d ", (void *)wsi,
wsi->pending_timeout);
#endif
/*
* Since he failed a timeout, he already had a chance to do
* something and was unable to... that includes situations like
* half closed connections. So process this "failed timeout"
* close as a violent death and don't try to do protocol
* cleanup like flush partials.
*/
wsi->socket_is_permanently_unusable = 1;
if (lwsi_state(wsi) == LRS_WAITING_SSL && wsi->protocol)
wsi->protocol->callback(wsi,
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
wsi->user_space,
(void *)"Timed out waiting SSL", 21);
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "timeout");
return 1;
}
return 0;
}
int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
uint8_t *buffered;
size_t blen;
int ret = 0, m;
/* his RX is flowcontrolled, don't send remaining now */
blen = lws_buflist_next_segment_len(&wsi->buflist, &buffered);
if (blen) {
if (buf >= buffered && buf + len <= buffered + blen) {
/* rxflow while we were spilling prev rxflow */
lwsl_info("%s: staying in rxflow buf\n", __func__);
return 1;
}
ret = 1;
}
/* a new rxflow, buffer it and warn caller */
m = lws_buflist_append_segment(&wsi->buflist, buf + n, len - n);
if (m < 0)
return -1;
if (m) {
lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
}
return ret;
}
/* this is used by the platform service code to stop us waiting for network
* activity in poll() when we have something that already needs service
*/
LWS_VISIBLE LWS_EXTERN int
lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
/* Figure out if we really want to wait in poll()
* We only need to wait if really nothing already to do and we have
* to wait for something from network
*/
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
/* 1) if we know we are draining rx ext, do not wait in poll */
if (pt->ws.rx_draining_ext_list)
return 0;
#endif
/* 2) if we know we have non-network pending data, do not wait in poll */
if (pt->context->tls_ops &&
pt->context->tls_ops->fake_POLLIN_for_buffered)
if (pt->context->tls_ops->fake_POLLIN_for_buffered(pt))
return 0;
/* 3) If there is any wsi with rxflow buffered and in a state to process
* it, we should not wait in poll
*/
lws_start_foreach_dll(struct lws_dll_lws *, d, pt->dll_head_buflist.next) {
struct lws *wsi = lws_container_of(d, struct lws, dll_buflist);
if (lwsi_state(wsi) != LRS_DEFERRING_ACTION)
return 0;
} lws_end_foreach_dll(d);
return timeout_ms;
}
/*
* POLLIN said there is something... we must read it, and either use it; or
* if other material already in the buflist append it and return the buflist
* head material.
*/
int
lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_tokens *ebuf)
{
int n, prior = (int)lws_buflist_next_segment_len(&wsi->buflist, NULL);
ebuf->token = (char *)pt->serv_buf;
ebuf->len = lws_ssl_capable_read(wsi, pt->serv_buf,
wsi->context->pt_serv_buf_size);
if (ebuf->len == LWS_SSL_CAPABLE_MORE_SERVICE && prior)
goto get_from_buflist;
if (ebuf->len <= 0)
return 0;
/* nothing in buflist already? Then just use what we read */
if (!prior)
return 0;
/* stash what we read */
n = lws_buflist_append_segment(&wsi->buflist, (uint8_t *)ebuf->token,
ebuf->len);
if (n < 0)
return -1;
if (n) {
lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
}
/* get the first buflist guy in line */
get_from_buflist:
ebuf->len = (int)lws_buflist_next_segment_len(&wsi->buflist,
(uint8_t **)&ebuf->token);
return 1; /* came from buflist */
}
int
lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used,
int buffered)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int m;
/* it's in the buflist; we didn't use any */
if (!used && buffered)
return 0;
if (used && buffered) {
m = lws_buflist_use_segment(&wsi->buflist, used);
lwsl_info("%s: draining rxflow: used %d, next %d\n",
__func__, used, m);
if (m)
return 0;
lwsl_info("%s: removed %p from dll_buflist\n", __func__, wsi);
lws_dll_lws_remove(&wsi->dll_buflist);
return 0;
}
/* any remainder goes on the buflist */
if (used != ebuf->len) {
m = lws_buflist_append_segment(&wsi->buflist,
(uint8_t *)ebuf->token + used,
ebuf->len - used);
if (m < 0)
return 1; /* OOM */
if (m) {
lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
}
}
return 0;
}
void
lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt)
{
struct lws_pollfd pfd;
if (!pt->dll_head_buflist.next)
return;
/*
* service all guys with pending rxflow that reached a state they can
* accept the pending data
*/
lws_pt_lock(pt, __func__);
lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
pt->dll_head_buflist.next) {
struct lws *wsi = lws_container_of(d, struct lws, dll_buflist);
pfd.events = LWS_POLLIN;
pfd.revents = LWS_POLLIN;
pfd.fd = -1;
lwsl_debug("%s: rxflow processing: %p 0x%x\n", __func__, wsi,
wsi->wsistate);
if (!lws_is_flowcontrolled(wsi) &&
lwsi_state(wsi) != LRS_DEFERRING_ACTION &&
(wsi->role_ops->handle_POLLIN)(pt, wsi, &pfd) ==
LWS_HPI_RET_PLEASE_CLOSE_ME)
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"close_and_handled");
} lws_end_foreach_dll_safe(d, d1);
lws_pt_unlock(pt);
}
/*
* guys that need POLLIN service again without waiting for network action
* can force POLLIN here if not flowcontrolled, so they will get service.
*
* Return nonzero if anybody got their POLLIN faked
*/
int
lws_service_flag_pending(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
#if defined(LWS_WITH_TLS)
struct lws *wsi, *wsi_next;
#endif
int forced = 0;
lws_pt_lock(pt, __func__);
/*
* 1) If there is any wsi with a buflist and in a state to process
* it, we should not wait in poll
*/
lws_start_foreach_dll(struct lws_dll_lws *, d, pt->dll_head_buflist.next) {
struct lws *wsi = lws_container_of(d, struct lws, dll_buflist);
if (lwsi_state(wsi) != LRS_DEFERRING_ACTION) {
forced = 1;
break;
}
} lws_end_foreach_dll(d);
#if defined(LWS_ROLE_WS)
forced |= role_ops_ws.service_flag_pending(context, tsi);
#endif
#if defined(LWS_WITH_TLS)
/*
* 2) For all guys with buffered SSL read data already saved up, if they
* are not flowcontrolled, fake their POLLIN status so they'll get
* service to use up the buffered incoming data, even though their
* network socket may have nothing
*/
wsi = pt->tls.pending_read_list;
while (wsi) {
wsi_next = wsi->tls.pending_read_list_next;
pt->fds[wsi->position_in_fds_table].revents |=
pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) {
forced = 1;
/*
* he's going to get serviced now, take him off the
* list of guys with buffered SSL. If he still has some
* at the end of the service, he'll get put back on the
* list then.
*/
__lws_ssl_remove_wsi_from_buffered_list(wsi);
}
wsi = wsi_next;
}
#endif
lws_pt_unlock(pt);
return forced;
}
static int
lws_service_periodic_checks(struct lws_context *context,
struct lws_pollfd *pollfd, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
lws_sockfd_type our_fd = 0, tmp_fd;
struct lws *wsi;
int timed_out = 0;
time_t now;
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
struct allocated_headers *ah;
int m;
#endif
if (!context->protocol_init_done)
if (lws_protocol_init(context))
return -1;
time(&now);
/*
* handle case that system time was uninitialized when lws started
* at boot, and got initialized a little later
*/
if (context->time_up < 1464083026 && now > 1464083026)
context->time_up = now;
if (context->last_timeout_check_s &&
now - context->last_timeout_check_s > 100) {
/*
* There has been a discontiguity. Any stored time that is
* less than context->time_discontiguity should have context->
* time_fixup added to it.
*
* Some platforms with no RTC will experience this as a normal
* event when ntp sets their clock, but we can have started
* long before that with a 0-based unix time.
*/
context->time_discontiguity = now;
context->time_fixup = now - context->last_timeout_check_s;
lwsl_notice("time discontiguity: at old time %llus, "
"new time %llus: +%llus\n",
(unsigned long long)context->last_timeout_check_s,
(unsigned long long)context->time_discontiguity,
(unsigned long long)context->time_fixup);
context->last_timeout_check_s = now - 1;
}
if (!lws_compare_time_t(context, context->last_timeout_check_s, now))
return 0;
context->last_timeout_check_s = now;
#if defined(LWS_WITH_STATS)
if (!tsi && now - context->last_dump > 10) {
lws_stats_log_dump(context);
context->last_dump = now;
}
#endif
lws_plat_service_periodic(context);
lws_check_deferred_free(context, 0);
#if defined(LWS_WITH_PEER_LIMITS)
lws_peer_cull_peer_wait_list(context);
#endif
/* retire unused deprecated context */
#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_ESP32)
#if !defined(_WIN32)
if (context->deprecated && !context->count_wsi_allocated) {
lwsl_notice("%s: ending deprecated context\n", __func__);
kill(getpid(), SIGINT);
return 0;
}
#endif
#endif
/* global timeout check once per second */
if (pollfd)
our_fd = pollfd->fd;
/*
* Phase 1: check every wsi on the timeout check list
*/
lws_pt_lock(pt, __func__);
lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
context->pt[tsi].dll_head_timeout.next) {
wsi = lws_container_of(d, struct lws, dll_timeout);
tmp_fd = wsi->desc.sockfd;
if (__lws_service_timeout_check(wsi, now)) {
/* he did time out... */
if (tmp_fd == our_fd)
/* it was the guy we came to service! */
timed_out = 1;
/* he's gone, no need to mark as handled */
}
} lws_end_foreach_dll_safe(d, d1);
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
/*
* Phase 2: double-check active ah timeouts independent of wsi
* timeout status
*/
ah = pt->http.ah_list;
while (ah) {
int len;
char buf[256];
const unsigned char *c;
if (!ah->in_use || !ah->wsi || !ah->assigned ||
(ah->wsi->vhost &&
lws_compare_time_t(context, now, ah->assigned) <
ah->wsi->vhost->timeout_secs_ah_idle + 360)) {
ah = ah->next;
continue;
}
/*
* a single ah session somehow got held for
* an unreasonable amount of time.
*
* Dump info on the connection...
*/
wsi = ah->wsi;
buf[0] = '\0';
#if !defined(LWS_PLAT_OPTEE)
lws_get_peer_simple(wsi, buf, sizeof(buf));
#else
buf[0] = '\0';
#endif
lwsl_notice("ah excessive hold: wsi %p\n"
" peer address: %s\n"
" ah pos %u\n",
wsi, buf, ah->pos);
buf[0] = '\0';
m = 0;
do {
c = lws_token_to_string(m);
if (!c)
break;
if (!(*c))
break;
len = lws_hdr_total_length(wsi, m);
if (!len || len > (int)sizeof(buf) - 1) {
m++;
continue;
}
if (lws_hdr_copy(wsi, buf,
sizeof buf, m) > 0) {
buf[sizeof(buf) - 1] = '\0';
lwsl_notice(" %s = %s\n",
(const char *)c, buf);
}
m++;
} while (1);
/* explicitly detach the ah */
lws_header_table_detach(wsi, 0);
/* ... and then drop the connection */
m = 0;
if (wsi->desc.sockfd == our_fd) {
m = timed_out;
/* it was the guy we came to service! */
timed_out = 1;
}
if (!m) /* if he didn't already timeout */
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"excessive ah");
ah = pt->http.ah_list;
}
#endif
lws_pt_unlock(pt);
#if 0
{
char s[300], *p = s;
for (n = 0; n < context->count_threads; n++)
p += sprintf(p, " %7lu (%5d), ",
context->pt[n].count_conns,
context->pt[n].fds_count);
lwsl_notice("load: %s\n", s);
}
#endif
/*
* Phase 3: vhost / protocol timer callbacks
*/
wsi = NULL;
lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) {
struct lws_timed_vh_protocol *nx;
if (v->timed_vh_protocol_list) {
lws_start_foreach_ll(struct lws_timed_vh_protocol *,
q, v->timed_vh_protocol_list) {
if (now >= q->time) {
if (!wsi)
wsi = lws_zalloc(sizeof(*wsi), "cbwsi");
wsi->context = context;
wsi->vhost = v;
wsi->protocol = q->protocol;
lwsl_debug("timed cb: vh %s, protocol %s, reason %d\n", v->name, q->protocol->name, q->reason);
q->protocol->callback(wsi, q->reason, NULL, NULL, 0);
nx = q->next;
lws_timed_callback_remove(v, q);
q = nx;
continue; /* we pointed ourselves to the next from the now-deleted guy */
}
} lws_end_foreach_ll(q, next);
}
} lws_end_foreach_ll(v, vhost_next);
if (wsi)
lws_free(wsi);
/*
* Phase 4: check for unconfigured vhosts due to required
* interface missing before
*/
lws_context_lock(context);
lws_start_foreach_llp(struct lws_vhost **, pv,
context->no_listener_vhost_list) {
struct lws_vhost *v = *pv;
lwsl_debug("deferred iface: checking if on vh %s\n", (*pv)->name);
if (_lws_vhost_init_server(NULL, *pv) == 0) {
/* became happy */
lwsl_notice("vh %s: became connected\n", v->name);
*pv = v->no_listener_vhost_list;
v->no_listener_vhost_list = NULL;
break;
}
} lws_end_foreach_llp(pv, no_listener_vhost_list);
lws_context_unlock(context);
/*
* Phase 5: role periodic checks
*/
#if defined(LWS_ROLE_WS)
role_ops_ws.periodic_checks(context, tsi, now);
#endif
#if defined(LWS_ROLE_CGI)
role_ops_cgi.periodic_checks(context, tsi, now);
#endif
/*
* Phase 6: check the remaining cert lifetime daily
*/
if (context->tls_ops &&
context->tls_ops->periodic_housekeeping)
context->tls_ops->periodic_housekeeping(context, now);
return timed_out;
}
LWS_VISIBLE int
lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws *wsi;
if (!context || context->being_destroyed1)
return -1;
/* the socket we came to service timed out, nothing to do */
if (lws_service_periodic_checks(context, pollfd, tsi) || !pollfd)
return 0;
/* no, here to service a socket descriptor */
wsi = wsi_from_fd(context, pollfd->fd);
if (!wsi)
/* not lws connection ... leave revents alone and return */
return 0;
/*
* so that caller can tell we handled, past here we need to
* zero down pollfd->revents after handling
*/
/* handle session socket closed */
if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
(pollfd->revents & LWS_POLLHUP)) {
wsi->socket_is_permanently_unusable = 1;
lwsl_debug("Session Socket %p (fd=%d) dead\n",
(void *)wsi, pollfd->fd);
goto close_and_handled;
}
#ifdef _WIN32
if (pollfd->revents & LWS_POLLOUT)
wsi->sock_send_blocking = FALSE;
#endif
if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
(pollfd->revents & LWS_POLLHUP)) {
lwsl_debug("pollhup\n");
wsi->socket_is_permanently_unusable = 1;
goto close_and_handled;
}
#if defined(LWS_WITH_TLS)
if (lwsi_state(wsi) == LRS_SHUTDOWN &&
lws_is_ssl(wsi) && wsi->tls.ssl) {
switch (__lws_tls_shutdown(wsi)) {
case LWS_SSL_CAPABLE_DONE:
case LWS_SSL_CAPABLE_ERROR:
goto close_and_handled;
case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
case LWS_SSL_CAPABLE_MORE_SERVICE:
goto handled;
}
}
#endif
wsi->could_have_pending = 0; /* clear back-to-back write detection */
/* okay, what we came here to do... */
/* if we got here, we should have wire protocol ops set on the wsi */
assert(wsi->role_ops);
// lwsl_notice("%s: %s: wsistate 0x%x\n", __func__, wsi->role_ops->name,
// wsi->wsistate);
switch ((wsi->role_ops->handle_POLLIN)(pt, wsi, pollfd)) {
case LWS_HPI_RET_WSI_ALREADY_DIED:
return 1;
case LWS_HPI_RET_HANDLED:
break;
case LWS_HPI_RET_PLEASE_CLOSE_ME:
close_and_handled:
lwsl_debug("%p: Close and handled\n", wsi);
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"close_and_handled");
#if defined(_DEBUG) && defined(LWS_WITH_LIBUV)
/*
* confirm close has no problem being called again while
* it waits for libuv service to complete the first async
* close
*/
if (context->event_loop_ops == &event_loop_ops_uv)
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"close_and_handled uv repeat test");
#endif
/*
* pollfd may point to something else after the close
* due to pollfd swapping scheme on delete on some platforms
* we can't clear revents now because it'd be the wrong guy's
* revents
*/
return 1;
default:
assert(0);
}
#if defined(LWS_WITH_TLS)
handled:
#endif
pollfd->revents = 0;
lws_pt_lock(pt, __func__);
__lws_hrtimer_service(pt);
lws_pt_unlock(pt);
return 0;
}
LWS_VISIBLE int
lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd)
{
return lws_service_fd_tsi(context, pollfd, 0);
}
LWS_VISIBLE int
lws_service(struct lws_context *context, int timeout_ms)
{
struct lws_context_per_thread *pt = &context->pt[0];
int n;
if (!context)
return 1;
pt->inside_service = 1;
if (context->event_loop_ops->run_pt) {
/* we are configured for an event loop */
context->event_loop_ops->run_pt(context, 0);
pt->inside_service = 0;
return 1;
}
n = lws_plat_service(context, timeout_ms);
pt->inside_service = 0;
return n;
}
LWS_VISIBLE int
lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
int n;
pt->inside_service = 1;
if (context->event_loop_ops->run_pt) {
/* we are configured for an event loop */
context->event_loop_ops->run_pt(context, tsi);
pt->inside_service = 0;
return 1;
}
n = _lws_plat_service_tsi(context, timeout_ms, tsi);
pt->inside_service = 0;
return n;
}

View File

@ -0,0 +1,43 @@
/*
* 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
*
* This is included from core/private.h if LWS_ROLE_WS
*/
#include <core/private.h>
struct lws_event_loop_ops event_loop_ops_poll = {
/* name */ "poll",
/* init_context */ NULL,
/* destroy_context1 */ NULL,
/* destroy_context2 */ NULL,
/* init_vhost_listen_wsi */ NULL,
/* init_pt */ NULL,
/* wsi_logical_close */ NULL,
/* check_client_connect_ok */ NULL,
/* close_handle_manually */ NULL,
/* accept */ NULL,
/* io */ NULL,
/* run */ NULL,
/* destroy_pt */ NULL,
/* destroy wsi */ NULL,
/* periodic_events_available */ 1,
};

View File

@ -0,0 +1,23 @@
/*
* 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
*
*/
extern struct lws_event_loop_ops event_loop_ops_poll;

View File

@ -0,0 +1,74 @@
/*
* 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
*
* This is included from core/private.h
*/
struct lws_event_loop_ops {
const char *name;
/* event loop-specific context init during context creation */
int (*init_context)(struct lws_context *context,
const struct lws_context_creation_info *info);
/* called during lws_destroy_context */
int (*destroy_context1)(struct lws_context *context);
/* called during lws_destroy_context2 */
int (*destroy_context2)(struct lws_context *context);
/* init vhost listening wsi */
int (*init_vhost_listen_wsi)(struct lws *wsi);
/* init the event loop for a pt */
int (*init_pt)(struct lws_context *context, void *_loop, int tsi);
/* called at end of first phase of close_free_wsi() */
int (*wsi_logical_close)(struct lws *wsi);
/* return nonzero if client connect not allowed */
int (*check_client_connect_ok)(struct lws *wsi);
/* close handle manually */
void (*close_handle_manually)(struct lws *wsi);
/* event loop accept processing */
void (*accept)(struct lws *wsi);
/* control wsi active events */
void (*io)(struct lws *wsi, int flags);
/* run the event loop for a pt */
void (*run_pt)(struct lws_context *context, int tsi);
/* called before pt is destroyed */
void (*destroy_pt)(struct lws_context *context, int tsi);
/* called just before wsi is freed */
void (*destroy_wsi)(struct lws *wsi);
unsigned int periodic_events_available:1;
};
/* bring in event libs private declarations */
#if defined(LWS_WITH_POLL)
#include "event-libs/poll/private.h"
#endif
#if defined(LWS_WITH_LIBUV)
#include "event-libs/libuv/private.h"
#endif
#if defined(LWS_WITH_LIBEVENT)
#include "event-libs/libevent/private.h"
#endif
#if defined(LWS_WITH_LIBEV)
#include "event-libs/libev/private.h"
#endif

View File

@ -1,473 +0,0 @@
/*
* ./lib/extension-permessage-deflate.c
*
* Copyright (C) 2016 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 "private-libwebsockets.h"
#include "extension-permessage-deflate.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define LWS_ZLIB_MEMLEVEL 8
const struct lws_ext_options lws_ext_pm_deflate_options[] = {
/* public RFC7692 settings */
{ "server_no_context_takeover", EXTARG_NONE },
{ "client_no_context_takeover", EXTARG_NONE },
{ "server_max_window_bits", EXTARG_OPT_DEC },
{ "client_max_window_bits", EXTARG_OPT_DEC },
/* ones only user code can set */
{ "rx_buf_size", EXTARG_DEC },
{ "tx_buf_size", EXTARG_DEC },
{ "compression_level", EXTARG_DEC },
{ "mem_level", EXTARG_DEC },
{ NULL, 0 }, /* sentinel */
};
static void
lws_extension_pmdeflate_restrict_args(struct lws *wsi,
struct lws_ext_pm_deflate_priv *priv)
{
int n, extra;
/* cap the RX buf at the nearest power of 2 to protocol rx buf */
n = wsi->context->pt_serv_buf_size;
if (wsi->protocol->rx_buffer_size)
n = wsi->protocol->rx_buffer_size;
extra = 7;
while (n >= 1 << (extra + 1))
extra++;
if (extra < priv->args[PMD_RX_BUF_PWR2]) {
priv->args[PMD_RX_BUF_PWR2] = extra;
lwsl_info(" Capping pmd rx to %d\n", 1 << extra);
}
}
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)
{
struct lws_ext_pm_deflate_priv *priv =
(struct lws_ext_pm_deflate_priv *)user;
struct lws_tokens *eff_buf = (struct lws_tokens *)in;
static unsigned char trail[] = { 0, 0, 0xff, 0xff };
int n, ret = 0, was_fin = 0, extra;
struct lws_ext_option_arg *oa;
switch (reason) {
case LWS_EXT_CB_NAMED_OPTION_SET:
oa = in;
if (!oa->option_name)
break;
for (n = 0; n < ARRAY_SIZE(lws_ext_pm_deflate_options); n++)
if (!strcmp(lws_ext_pm_deflate_options[n].name, oa->option_name))
break;
if (n == ARRAY_SIZE(lws_ext_pm_deflate_options))
break;
oa->option_index = n;
/* fallthru */
case LWS_EXT_CB_OPTION_SET:
oa = in;
lwsl_notice("%s: option set: idx %d, %s, len %d\n", __func__,
oa->option_index, oa->start, oa->len);
if (oa->start)
priv->args[oa->option_index] = atoi(oa->start);
else
priv->args[oa->option_index] = 1;
if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8)
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9;
lws_extension_pmdeflate_restrict_args(wsi, priv);
break;
case LWS_EXT_CB_OPTION_CONFIRM:
if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 ||
priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 ||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 ||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15)
return -1;
break;
case LWS_EXT_CB_CLIENT_CONSTRUCT:
case LWS_EXT_CB_CONSTRUCT:
n = context->pt_serv_buf_size;
if (wsi->protocol->rx_buffer_size)
n = wsi->protocol->rx_buffer_size;
if (n < 128) {
lwsl_info(" permessage-deflate requires the protocol (%s) to have an RX buffer >= 128\n",
wsi->protocol->name);
return -1;
}
/* fill in **user */
priv = lws_zalloc(sizeof(*priv), "pmd priv");
*((void **)user) = priv;
lwsl_ext("%s: LWS_EXT_CB_*CONSTRUCT\n", __func__);
memset(priv, 0, sizeof(*priv));
/* fill in pointer to options list */
if (in)
*((const struct lws_ext_options **)in) =
lws_ext_pm_deflate_options;
/* fallthru */
case LWS_EXT_CB_OPTION_DEFAULT:
/* set the public, RFC7692 defaults... */
priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0,
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0;
priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15;
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15;
/* ...and the ones the user code can override */
priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */
priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */
priv->args[PMD_COMP_LEVEL] = 1;
priv->args[PMD_MEM_LEVEL] = 8;
lws_extension_pmdeflate_restrict_args(wsi, priv);
break;
case LWS_EXT_CB_DESTROY:
lwsl_ext("%s: LWS_EXT_CB_DESTROY\n", __func__);
lws_free(priv->buf_rx_inflated);
lws_free(priv->buf_tx_deflated);
if (priv->rx_init)
(void)inflateEnd(&priv->rx);
if (priv->tx_init)
(void)deflateEnd(&priv->tx);
lws_free(priv);
return ret;
case LWS_EXT_CB_PAYLOAD_RX:
lwsl_ext(" %s: LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d\n",
__func__, eff_buf->token_len, priv->rx.avail_in);
if (!(wsi->u.ws.rsv_first_msg & 0x40))
return 0;
#if 0
for (n = 0; n < eff_buf->token_len; n++) {
printf("%02X ", (unsigned char)eff_buf->token[n]);
if ((n & 15) == 15)
printf("\n");
}
printf("\n");
#endif
if (!priv->rx_init)
if (inflateInit2(&priv->rx, -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
lwsl_err("%s: iniflateInit failed\n", __func__);
return -1;
}
priv->rx_init = 1;
if (!priv->buf_rx_inflated)
priv->buf_rx_inflated = lws_malloc(LWS_PRE + 7 + 5 +
(1 << priv->args[PMD_RX_BUF_PWR2]), "pmd rx inflate buf");
if (!priv->buf_rx_inflated) {
lwsl_err("%s: OOM\n", __func__);
return -1;
}
/*
* We have to leave the input stream alone if we didn't
* finish with it yet. The input stream is held in the wsi
* rx buffer by the caller, so this assumption is safe while
* we block new rx while draining the existing rx
*/
if (!priv->rx.avail_in && eff_buf->token && eff_buf->token_len) {
priv->rx.next_in = (unsigned char *)eff_buf->token;
priv->rx.avail_in = eff_buf->token_len;
}
priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
eff_buf->token = (char *)priv->rx.next_out;
priv->rx.avail_out = 1 << priv->args[PMD_RX_BUF_PWR2];
if (priv->rx_held_valid) {
lwsl_ext("-- RX piling on held byte --\n");
*(priv->rx.next_out++) = priv->rx_held;
priv->rx.avail_out--;
priv->rx_held_valid = 0;
}
/* if...
*
* - he has no remaining input content for this message, and
* - and this is the final fragment, and
* - we used everything that could be drained on the input side
*
* ...then put back the 00 00 FF FF the sender stripped as our
* input to zlib
*/
if (!priv->rx.avail_in && wsi->u.ws.final &&
!wsi->u.ws.rx_packet_length) {
lwsl_ext("RX APPEND_TRAILER-DO\n");
was_fin = 1;
priv->rx.next_in = trail;
priv->rx.avail_in = sizeof(trail);
}
n = inflate(&priv->rx, Z_NO_FLUSH);
lwsl_ext("inflate ret %d, avi %d, avo %d, wsifinal %d\n", n,
priv->rx.avail_in, priv->rx.avail_out, wsi->u.ws.final);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
lwsl_info("zlib error inflate %d: %s\n",
n, priv->rx.msg);
return -1;
}
/*
* If we did not already send in the 00 00 FF FF, and he's
* out of input, he did not EXACTLY fill the output buffer
* (which is ambiguous and we will force it to go around
* again by withholding a byte), and he's otherwise working on
* being a FIN fragment, then do the FIN message processing
* of faking up the 00 00 FF FF that the sender stripped.
*/
if (!priv->rx.avail_in && wsi->u.ws.final &&
!wsi->u.ws.rx_packet_length && !was_fin &&
priv->rx.avail_out /* ambiguous as to if it is the end */
) {
lwsl_ext("RX APPEND_TRAILER-DO\n");
was_fin = 1;
priv->rx.next_in = trail;
priv->rx.avail_in = sizeof(trail);
n = inflate(&priv->rx, Z_SYNC_FLUSH);
lwsl_ext("RX trailer inf returned %d, avi %d, avo %d\n", n,
priv->rx.avail_in, priv->rx.avail_out);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
lwsl_info("zlib error inflate %d: %s\n",
n, priv->rx.msg);
return -1;
}
}
/*
* we must announce in our returncode now if there is more
* output to be expected from inflate, so we can decide to
* set the FIN bit on this bufferload or not. However zlib
* is ambiguous when we exactly filled the inflate buffer. It
* does not give us a clue as to whether we should understand
* that to mean he ended on a buffer boundary, or if there is
* more in the pipeline.
*
* So to work around that safely, if it used all output space
* exactly, we ALWAYS say there is more coming and we withhold
* the last byte of the buffer to guarantee that is true.
*
* That still leaves us at least one byte to finish with a FIN
* on, even if actually nothing more is coming from the next
* inflate action itself.
*/
if (!priv->rx.avail_out) { /* he used all available out buf */
lwsl_ext("-- rx grabbing held --\n");
/* snip the last byte and hold it for next time */
priv->rx_held = *(--priv->rx.next_out);
priv->rx_held_valid = 1;
}
eff_buf->token_len = (char *)priv->rx.next_out - eff_buf->token;
priv->count_rx_between_fin += eff_buf->token_len;
lwsl_ext(" %s: RX leaving with new effbuff len %d, "
"ret %d, rx.avail_in=%d, TOTAL RX since FIN %lu\n",
__func__, eff_buf->token_len, priv->rx_held_valid,
priv->rx.avail_in,
(unsigned long)priv->count_rx_between_fin);
if (was_fin) {
priv->count_rx_between_fin = 0;
if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
(void)inflateEnd(&priv->rx);
priv->rx_init = 0;
}
}
#if 0
for (n = 0; n < eff_buf->token_len; n++)
putchar(eff_buf->token[n]);
puts("\n");
#endif
return priv->rx_held_valid;
case LWS_EXT_CB_PAYLOAD_TX:
if (!priv->tx_init) {
n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
Z_DEFLATED,
-priv->args[PMD_SERVER_MAX_WINDOW_BITS +
(wsi->vhost->listen_port <= 0)],
priv->args[PMD_MEM_LEVEL],
Z_DEFAULT_STRATEGY);
if (n != Z_OK) {
lwsl_ext("inflateInit2 failed %d\n", n);
return 1;
}
}
priv->tx_init = 1;
if (!priv->buf_tx_deflated)
priv->buf_tx_deflated = lws_malloc(LWS_PRE + 7 + 5 +
(1 << priv->args[PMD_TX_BUF_PWR2]), "pmd tx deflate buf");
if (!priv->buf_tx_deflated) {
lwsl_err("%s: OOM\n", __func__);
return -1;
}
if (eff_buf->token) {
lwsl_ext("%s: TX: eff_buf length %d\n", __func__,
eff_buf->token_len);
priv->tx.next_in = (unsigned char *)eff_buf->token;
priv->tx.avail_in = eff_buf->token_len;
}
#if 0
for (n = 0; n < eff_buf->token_len; n++) {
printf("%02X ", (unsigned char)eff_buf->token[n]);
if ((n & 15) == 15)
printf("\n");
}
printf("\n");
#endif
priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
eff_buf->token = (char *)priv->tx.next_out;
priv->tx.avail_out = 1 << priv->args[PMD_TX_BUF_PWR2];
n = deflate(&priv->tx, Z_SYNC_FLUSH);
if (n == Z_STREAM_ERROR) {
lwsl_ext("%s: Z_STREAM_ERROR\n", __func__);
return -1;
}
if (priv->tx_held_valid) {
priv->tx_held_valid = 0;
if (priv->tx.avail_out == 1 << priv->args[PMD_TX_BUF_PWR2])
/*
* we can get a situation he took something in
* but did not generate anything out, at the end
* of a message (eg, next thing he sends is 80
* 00, a zero length FIN, like Authobahn can
* send).
* If we have come back as a FIN, we must not
* place the pending trailer 00 00 FF FF, just
* the 1 byte of live data
*/
*(--eff_buf->token) = priv->tx_held[0];
else {
/* he generated data, prepend whole pending */
eff_buf->token -= 5;
for (n = 0; n < 5; n++)
eff_buf->token[n] = priv->tx_held[n];
}
}
priv->compressed_out = 1;
eff_buf->token_len = (int)(priv->tx.next_out -
(unsigned char *)eff_buf->token);
/*
* we must announce in our returncode now if there is more
* output to be expected from inflate, so we can decide to
* set the FIN bit on this bufferload or not. However zlib
* is ambiguous when we exactly filled the inflate buffer. It
* does not give us a clue as to whether we should understand
* that to mean he ended on a buffer boundary, or if there is
* more in the pipeline.
*
* Worse, the guy providing the stuff we are sending may not
* know until after that this was, actually, the last chunk,
* that can happen even if we did not fill the output buf, ie
* he may send after this a zero-length FIN fragment.
*
* This is super difficult because we must snip the last 4
* bytes in the case this is the last compressed output of the
* message. The only way to deal with it is defer sending the
* last 5 bytes of each frame until the next one, when we will
* be in a position to understand if that has a FIN or not.
*/
extra = !!(len & LWS_WRITE_NO_FIN) || !priv->tx.avail_out;
if (eff_buf->token_len >= 4 + extra) {
lwsl_ext("tx held %d\n", 4 + extra);
priv->tx_held_valid = extra;
for (n = 3 + extra; n >= 0; n--)
priv->tx_held[n] = *(--priv->tx.next_out);
eff_buf->token_len -= 4 + extra;
}
lwsl_ext(" TX rewritten with new effbuff len %d, ret %d\n",
eff_buf->token_len, !priv->tx.avail_out);
return !priv->tx.avail_out; /* 1 == have more tx pending */
case LWS_EXT_CB_PACKET_TX_PRESEND:
if (!priv->compressed_out)
break;
priv->compressed_out = 0;
if ((*(eff_buf->token) & 0x80) &&
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
lwsl_debug("PMD_CLIENT_NO_CONTEXT_TAKEOVER\n");
(void)deflateEnd(&priv->tx);
priv->tx_init = 0;
}
n = *(eff_buf->token) & 15;
/* set RSV1, but not on CONTINUATION */
if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
*eff_buf->token |= 0x40;
#if 0
for (n = 0; n < eff_buf->token_len; n++) {
printf("%02X ", (unsigned char)eff_buf->token[n]);
if ((n & 15) == 15)
puts("\n");
}
puts("\n");
#endif
lwsl_ext("%s: tx opcode 0x%02X\n", __func__,
(unsigned char)*eff_buf->token);
break;
default:
break;
}
return 0;
}

View File

@ -1,41 +0,0 @@
#include <zlib.h>
#define DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER 1
#define DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT Z_DEFAULT_COMPRESSION
enum arg_indexes {
PMD_SERVER_NO_CONTEXT_TAKEOVER,
PMD_CLIENT_NO_CONTEXT_TAKEOVER,
PMD_SERVER_MAX_WINDOW_BITS,
PMD_CLIENT_MAX_WINDOW_BITS,
PMD_RX_BUF_PWR2,
PMD_TX_BUF_PWR2,
PMD_COMP_LEVEL,
PMD_MEM_LEVEL,
PMD_ARG_COUNT
};
struct lws_ext_pm_deflate_priv {
z_stream rx;
z_stream tx;
unsigned char *buf_rx_inflated; /* RX inflated output buffer */
unsigned char *buf_tx_deflated; /* TX deflated output buffer */
size_t count_rx_between_fin;
unsigned char args[PMD_ARG_COUNT];
unsigned char tx_held[5];
unsigned char rx_held;
unsigned char tx_init:1;
unsigned char rx_init:1;
unsigned char compressed_out:1;
unsigned char rx_held_valid:1;
unsigned char tx_held_valid:1;
unsigned char rx_append_trailer:1;
unsigned char pending_tx_trailer:1;
};

View File

@ -1,344 +0,0 @@
#include "private-libwebsockets.h"
#include "extension-permessage-deflate.h"
LWS_VISIBLE void
lws_context_init_extensions(struct lws_context_creation_info *info,
struct lws_context *context)
{
lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
}
enum lws_ext_option_parser_states {
LEAPS_SEEK_NAME,
LEAPS_EAT_NAME,
LEAPS_SEEK_VAL,
LEAPS_EAT_DEC,
LEAPS_SEEK_ARG_TERM
};
LWS_VISIBLE int
lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
void *ext_user, const struct lws_ext_options *opts,
const char *in, int len)
{
enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME;
unsigned int match_map = 0, n, m, w = 0, count_options = 0,
pending_close_quote = 0;
struct lws_ext_option_arg oa;
oa.option_name = NULL;
while (opts[count_options].name)
count_options++;
while (len) {
lwsl_ext("'%c' %d", *in, leap);
switch (leap) {
case LEAPS_SEEK_NAME:
if (*in == ' ')
break;
if (*in == ',') {
len = 1;
break;
}
match_map = (1 << count_options) - 1;
leap = LEAPS_EAT_NAME;
w = 0;
/* fallthru */
case LEAPS_EAT_NAME:
oa.start = NULL;
oa.len = 0;
m = match_map;
n = 0;
pending_close_quote = 0;
while (m) {
if (m & 1) {
lwsl_ext(" m=%d, n=%d, w=%d\n", m, n, w);
if (*in == opts[n].name[w]) {
if (!opts[n].name[w + 1]) {
oa.option_index = n;
lwsl_ext("hit %d\n", oa.option_index);
leap = LEAPS_SEEK_VAL;
if (len == 1)
goto set_arg;
break;
}
} else {
match_map &= ~(1 << n);
if (!match_map) {
lwsl_ext("empty match map\n");
return -1;
}
}
}
m >>= 1;
n++;
}
w++;
break;
case LEAPS_SEEK_VAL:
if (*in == ' ')
break;
if (*in == ',') {
len = 1;
break;
}
if (*in == ';' || len == 1) { /* ie,nonoptional */
if (opts[oa.option_index].type == EXTARG_DEC)
return -1;
leap = LEAPS_SEEK_NAME;
goto set_arg;
}
if (*in == '=') {
w = 0;
pending_close_quote = 0;
if (opts[oa.option_index].type == EXTARG_NONE)
return -1;
leap = LEAPS_EAT_DEC;
break;
}
return -1;
case LEAPS_EAT_DEC:
if (*in >= '0' && *in <= '9') {
if (!w)
oa.start = in;
w++;
if (len != 1)
break;
}
if (!w && *in =='"') {
pending_close_quote = 1;
break;
}
if (!w)
return -1;
if (pending_close_quote && *in != '"' && len != 1)
return -1;
leap = LEAPS_SEEK_ARG_TERM;
if (oa.start)
oa.len = in - oa.start;
if (len == 1)
oa.len++;
set_arg:
ext->callback(lws_get_context(wsi),
ext, wsi, LWS_EXT_CB_OPTION_SET,
ext_user, (char *)&oa, 0);
if (len == 1)
break;
if (pending_close_quote && *in == '"')
break;
/* fallthru */
case LEAPS_SEEK_ARG_TERM:
if (*in == ' ')
break;
if (*in == ';') {
leap = LEAPS_SEEK_NAME;
break;
}
if (*in == ',') {
len = 1;
break;
}
return -1;
}
len--;
in++;
}
return 0;
}
/* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */
int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len)
{
int n, m, handled = 0;
for (n = 0; n < wsi->count_act_ext; n++) {
m = wsi->active_extensions[n]->callback(lws_get_context(wsi),
wsi->active_extensions[n], wsi, reason,
wsi->act_ext_user[n], arg, len);
if (m < 0) {
lwsl_ext("Ext '%s' failed to handle callback %d!\n",
wsi->active_extensions[n]->name, reason);
return -1;
}
/* valgrind... */
if (reason == LWS_EXT_CB_DESTROY)
wsi->act_ext_user[n] = NULL;
if (m > handled)
handled = m;
}
return handled;
}
int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
int reason, void *arg, int len)
{
int n = 0, m, handled = 0;
const struct lws_extension *ext;
if (!wsi || !wsi->vhost)
return 0;
ext = wsi->vhost->extensions;
while (ext && ext->callback && !handled) {
m = ext->callback(context, ext, wsi, reason,
(void *)(lws_intptr_t)n, arg, len);
if (m < 0) {
lwsl_ext("Ext '%s' failed to handle callback %d!\n",
wsi->active_extensions[n]->name, reason);
return -1;
}
if (m)
handled = 1;
ext++;
n++;
}
return 0;
}
int
lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
{
struct lws_tokens eff_buf;
int ret, m, n = 0;
eff_buf.token = (char *)buf;
eff_buf.token_len = len;
/*
* while we have original buf to spill ourselves, or extensions report
* more in their pipeline
*/
ret = 1;
while (ret == 1) {
/* default to nobody has more to spill */
ret = 0;
/* show every extension the new incoming data */
m = lws_ext_cb_active(wsi,
LWS_EXT_CB_PACKET_TX_PRESEND, &eff_buf, 0);
if (m < 0)
return -1;
if (m) /* handled */
ret = 1;
if ((char *)buf != eff_buf.token)
/*
* extension recreated it:
* need to buffer this if not all sent
*/
wsi->u.ws.clean_buffer = 0;
/* assuming they left us something to send, send it */
if (eff_buf.token_len) {
n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
eff_buf.token_len);
if (n < 0) {
lwsl_info("closing from ext access\n");
return -1;
}
/* always either sent it all or privately buffered */
if (wsi->u.ws.clean_buffer)
len = n;
}
lwsl_parser("written %d bytes to client\n", n);
/* no extension has more to spill? Then we can go */
if (!ret)
break;
/* we used up what we had */
eff_buf.token = NULL;
eff_buf.token_len = 0;
/*
* Did that leave the pipe choked?
* Or we had to hold on to some of it?
*/
if (!lws_send_pipe_choked(wsi) && !wsi->trunc_len)
/* no we could add more, lets's do that */
continue;
lwsl_debug("choked\n");
/*
* Yes, he's choked. Don't spill the rest now get a callback
* when he is ready to send and take care of it there
*/
lws_callback_on_writable(wsi);
wsi->extension_data_pending = 1;
ret = 0;
}
return len;
}
int
lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
void *v, size_t len)
{
struct lws_context *context = wsi->context;
int n, handled = 0;
/* maybe an extension will take care of it for us */
for (n = 0; n < wsi->count_act_ext && !handled; n++) {
if (!wsi->active_extensions[n]->callback)
continue;
handled |= wsi->active_extensions[n]->callback(context,
wsi->active_extensions[n], wsi,
r, wsi->act_ext_user[n], v, len);
}
return handled;
}
int
lws_set_extension_option(struct lws *wsi, const char *ext_name,
const char *opt_name, const char *opt_val)
{
struct lws_ext_option_arg oa;
int idx = 0;
/* first identify if the ext is active on this wsi */
while (idx < wsi->count_act_ext &&
strcmp(wsi->active_extensions[idx]->name, ext_name))
idx++;
if (idx == wsi->count_act_ext)
return -1; /* request ext not active on this wsi */
oa.option_name = opt_name;
oa.option_index = 0;
oa.start = opt_val;
oa.len = 0;
return wsi->active_extensions[idx]->callback(
wsi->context, wsi->active_extensions[idx], wsi,
LWS_EXT_CB_NAMED_OPTION_SET, wsi->act_ext_user[idx], &oa, 0);
}

View File

@ -1,280 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 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 "private-libwebsockets.h"
/*
* -04 of the protocol (actually the 80th version) has a radically different
* handshake. The 04 spec gives the following idea
*
* The handshake from the client looks as follows:
*
* GET /chat HTTP/1.1
* Host: server.example.com
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
* Sec-WebSocket-Origin: http://example.com
* Sec-WebSocket-Protocol: chat, superchat
* Sec-WebSocket-Version: 4
*
* The handshake from the server looks as follows:
*
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
* Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
* Sec-WebSocket-Protocol: chat
*/
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
/*
* We have to take care about parsing because the headers may be split
* into multiple fragments. They may contain unknown headers with arbitrary
* argument lengths. So, we parse using a single-character at a time state
* machine that is completely independent of packet size.
*
* Returns <0 for error or length of chars consumed from buf (up to len)
*/
LWS_VISIBLE int
lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
{
unsigned char *last_char, *oldbuf = buf;
lws_filepos_t body_chunk_len;
size_t n;
switch (wsi->state) {
#ifdef LWS_WITH_HTTP2
case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
case LWSS_HTTP2_ESTABLISHED:
n = 0;
//lwsl_debug("%s: starting new block of %d\n", __func__, (int)len);
/*
* wsi here is always the network connection wsi, not a stream
* wsi.
*/
while (n < len) {
/*
* we were accepting input but now we stopped doing so
*/
if (lws_is_flowcontrolled(wsi)) {
lws_rxflow_cache(wsi, buf, n, len);
return 1;
}
/* account for what we're using in rxflow buffer */
if (wsi->rxflow_buffer) {
wsi->rxflow_pos++;
assert(wsi->rxflow_pos <= wsi->rxflow_len);
}
if (lws_h2_parser(wsi, buf[n++])) {
lwsl_debug("%s: http2_parser bailed\n", __func__);
goto bail;
}
}
lwsl_debug("%s: used up block of %d\n", __func__, (int)len);
break;
#endif
case LWSS_HTTP_ISSUING_FILE:
return 0;
case LWSS_CLIENT_HTTP_ESTABLISHED:
break;
case LWSS_HTTP:
wsi->hdr_parsing_completed = 0;
/* fallthru */
case LWSS_HTTP_HEADERS:
if (!wsi->u.hdr.ah) {
lwsl_err("%s: LWSS_HTTP_HEADERS: NULL ah\n", __func__);
assert(0);
}
lwsl_parser("issuing %d bytes to parser\n", (int)len);
lwsl_hexdump(buf, (size_t)len);
if (lws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
last_char = buf;
if (lws_handshake_server(wsi, &buf, (size_t)len))
/* Handshake indicates this session is done. */
goto bail;
/* we might have transitioned to RAW */
if (wsi->mode == LWSCM_RAW)
/* we gave the read buffer to RAW handler already */
goto read_ok;
/*
* It's possible that we've exhausted our data already, or
* rx flow control has stopped us dealing with this early,
* but lws_handshake_server doesn't update len for us.
* Figure out how much was read, so that we can proceed
* appropriately:
*/
len -= (buf - last_char);
lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
if (!wsi->hdr_parsing_completed)
/* More header content on the way */
goto read_ok;
switch (wsi->state) {
case LWSS_HTTP:
case LWSS_HTTP_HEADERS:
goto read_ok;
case LWSS_HTTP_ISSUING_FILE:
goto read_ok;
case LWSS_HTTP_BODY:
wsi->u.http.rx_content_remain =
wsi->u.http.rx_content_length;
if (wsi->u.http.rx_content_remain)
goto http_postbody;
/* there is no POST content */
goto postbody_completion;
default:
break;
}
break;
case LWSS_HTTP_BODY:
http_postbody:
//lwsl_notice("http post body\n");
while (len && wsi->u.http.rx_content_remain) {
/* Copy as much as possible, up to the limit of:
* what we have in the read buffer (len)
* remaining portion of the POST body (content_remain)
*/
body_chunk_len = min(wsi->u.http.rx_content_remain, len);
wsi->u.http.rx_content_remain -= body_chunk_len;
len -= body_chunk_len;
#ifdef LWS_WITH_CGI
if (wsi->cgi) {
struct lws_cgi_args args;
args.ch = LWS_STDIN;
args.stdwsi = &wsi->cgi->stdwsi[0];
args.data = buf;
args.len = body_chunk_len;
/* returns how much used */
n = user_callback_handle_rxflow(
wsi->protocol->callback,
wsi, LWS_CALLBACK_CGI_STDIN_DATA,
wsi->user_space,
(void *)&args, 0);
if ((int)n < 0)
goto bail;
} else {
#endif
n = wsi->protocol->callback(wsi,
LWS_CALLBACK_HTTP_BODY, wsi->user_space,
buf, (size_t)body_chunk_len);
if (n)
goto bail;
n = (size_t)body_chunk_len;
#ifdef LWS_WITH_CGI
}
#endif
buf += n;
if (wsi->u.http.rx_content_remain) {
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
wsi->context->timeout_secs);
break;
}
/* he sent all the content in time */
postbody_completion:
#ifdef LWS_WITH_CGI
/*
* If we're running a cgi, we can't let him off the
* hook just because he sent his POST data
*/
if (wsi->cgi)
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI,
wsi->context->timeout_secs);
else
#endif
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
#ifdef LWS_WITH_CGI
if (!wsi->cgi)
#endif
{
lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
n = wsi->protocol->callback(wsi,
LWS_CALLBACK_HTTP_BODY_COMPLETION,
wsi->user_space, NULL, 0);
if (n)
goto bail;
if (wsi->http2_substream)
wsi->state = LWSS_HTTP2_ESTABLISHED;
}
break;
}
break;
case LWSS_ESTABLISHED:
case LWSS_AWAITING_CLOSE_ACK:
case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
case LWSS_SHUTDOWN:
if (lws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
switch (wsi->mode) {
case LWSCM_WS_SERVING:
if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
lwsl_info("interpret_incoming_packet has bailed\n");
goto bail;
}
break;
}
break;
default:
lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state);
break;
}
read_ok:
/* Nothing more to do for now */
lwsl_info("%s: read_ok, used %ld\n", __func__, (long)(buf - oldbuf));
return buf - oldbuf;
bail:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
return -1;
}

View File

@ -1,805 +0,0 @@
/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */,
0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */,
0x6F /* 'o' */, 0x51, 0x00 /* (to 0x0057 state 10) */,
0x68 /* 'h' */, 0x5D, 0x00 /* (to 0x0066 state 18) */,
0x63 /* 'c' */, 0x69, 0x00 /* (to 0x0075 state 23) */,
0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x0099 state 34) */,
0x73 /* 's' */, 0xA0, 0x00 /* (to 0x00B2 state 48) */,
0x0D /* '.' */, 0xD9, 0x00 /* (to 0x00EE state 68) */,
0x61 /* 'a' */, 0x31, 0x01 /* (to 0x0149 state 129) */,
0x69 /* 'i' */, 0x70, 0x01 /* (to 0x018B state 163) */,
0x64 /* 'd' */, 0x19, 0x02 /* (to 0x0237 state 265) */,
0x72 /* 'r' */, 0x22, 0x02 /* (to 0x0243 state 270) */,
0x3A /* ':' */, 0x53, 0x02 /* (to 0x0277 state 299) */,
0x65 /* 'e' */, 0xDF, 0x02 /* (to 0x0306 state 409) */,
0x66 /* 'f' */, 0xFB, 0x02 /* (to 0x0325 state 425) */,
0x6C /* 'l' */, 0x1D, 0x03 /* (to 0x034A state 458) */,
0x6D /* 'm' */, 0x40, 0x03 /* (to 0x0370 state 484) */,
0x74 /* 't' */, 0xAF, 0x03 /* (to 0x03E2 state 578) */,
0x76 /* 'v' */, 0xD0, 0x03 /* (to 0x0406 state 606) */,
0x77 /* 'w' */, 0xDD, 0x03 /* (to 0x0416 state 614) */,
0x78 /* 'x' */, 0x04, 0x04 /* (to 0x0440 state 650) */,
0x08, /* fail */
/* pos 0040: 1 */ 0xE5 /* 'e' -> */,
/* pos 0041: 2 */ 0xF4 /* 't' -> */,
/* pos 0042: 3 */ 0xA0 /* ' ' -> */,
/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */,
/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */,
0x72 /* 'r' */, 0x95, 0x01 /* (to 0x01DD state 211) */,
0x61 /* 'a' */, 0xDD, 0x03 /* (to 0x0428 state 631) */,
0x75 /* 'u' */, 0xDF, 0x03 /* (to 0x042D state 635) */,
0x08, /* fail */
/* pos 0052: 6 */ 0xF3 /* 's' -> */,
/* pos 0053: 7 */ 0xF4 /* 't' -> */,
/* pos 0054: 8 */ 0xA0 /* ' ' -> */,
/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */,
/* pos 0057: 10 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x005E state 11) */,
0x72 /* 'r' */, 0x51, 0x00 /* (to 0x00AB state 42) */,
0x08, /* fail */
/* pos 005e: 11 */ 0xF4 /* 't' -> */,
/* pos 005f: 12 */ 0xE9 /* 'i' -> */,
/* pos 0060: 13 */ 0xEF /* 'o' -> */,
/* pos 0061: 14 */ 0xEE /* 'n' -> */,
/* pos 0062: 15 */ 0xF3 /* 's' -> */,
/* pos 0063: 16 */ 0xA0 /* ' ' -> */,
/* pos 0064: 17 */ 0x00, 0x02 /* - terminal marker 2 - */,
/* pos 0066: 18 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x0070 state 19) */,
0x74 /* 't' */, 0xBF, 0x00 /* (to 0x0128 state 110) */,
0x65 /* 'e' */, 0xF8, 0x03 /* (to 0x0464 state 676) */,
0x08, /* fail */
/* pos 0070: 19 */ 0xF3 /* 's' -> */,
/* pos 0071: 20 */ 0xF4 /* 't' -> */,
/* pos 0072: 21 */ 0xBA /* ':' -> */,
/* pos 0073: 22 */ 0x00, 0x03 /* - terminal marker 3 - */,
/* pos 0075: 23 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x007C state 24) */,
0x61 /* 'a' */, 0x72, 0x01 /* (to 0x01EA state 217) */,
0x08, /* fail */
/* pos 007c: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0083 state 25) */,
0x6F /* 'o' */, 0x87, 0x01 /* (to 0x0206 state 243) */,
0x08, /* fail */
/* pos 0083: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x008A state 26) */,
0x74 /* 't' */, 0x86, 0x01 /* (to 0x020C state 248) */,
0x08, /* fail */
/* pos 008a: 26 */ 0xE5 /* 'e' -> */,
/* pos 008b: 27 */ 0xE3 /* 'c' -> */,
/* pos 008c: 28 */ 0xF4 /* 't' -> */,
/* pos 008d: 29 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0094 state 30) */,
0x20 /* ' ' */, 0xD2, 0x03 /* (to 0x0462 state 675) */,
0x08, /* fail */
/* pos 0094: 30 */ 0xEF /* 'o' -> */,
/* pos 0095: 31 */ 0xEE /* 'n' -> */,
/* pos 0096: 32 */ 0xBA /* ':' -> */,
/* pos 0097: 33 */ 0x00, 0x04 /* - terminal marker 4 - */,
/* pos 0099: 34 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x00A3 state 35) */,
0x73 /* 's' */, 0x5F, 0x03 /* (to 0x03FB state 596) */,
0x72 /* 'r' */, 0x97, 0x03 /* (to 0x0436 state 642) */,
0x08, /* fail */
/* pos 00a3: 35 */ 0xE7 /* 'g' -> */,
/* pos 00a4: 36 */ 0xF2 /* 'r' -> */,
/* pos 00a5: 37 */ 0xE1 /* 'a' -> */,
/* pos 00a6: 38 */ 0xE4 /* 'd' -> */,
/* pos 00a7: 39 */ 0xE5 /* 'e' -> */,
/* pos 00a8: 40 */ 0xBA /* ':' -> */,
/* pos 00a9: 41 */ 0x00, 0x05 /* - terminal marker 5 - */,
/* pos 00ab: 42 */ 0xE9 /* 'i' -> */,
/* pos 00ac: 43 */ 0xE7 /* 'g' -> */,
/* pos 00ad: 44 */ 0xE9 /* 'i' -> */,
/* pos 00ae: 45 */ 0xEE /* 'n' -> */,
/* pos 00af: 46 */ 0xBA /* ':' -> */,
/* pos 00b0: 47 */ 0x00, 0x06 /* - terminal marker 6 - */,
/* pos 00b2: 48 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x00B9 state 49) */,
0x74 /* 't' */, 0x13, 0x03 /* (to 0x03C8 state 553) */,
0x08, /* fail */
/* pos 00b9: 49 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x00C3 state 50) */,
0x72 /* 'r' */, 0xFC, 0x02 /* (to 0x03B8 state 539) */,
0x74 /* 't' */, 0xFF, 0x02 /* (to 0x03BE state 544) */,
0x08, /* fail */
/* pos 00c3: 50 */ 0xAD /* '-' -> */,
/* pos 00c4: 51 */ 0xF7 /* 'w' -> */,
/* pos 00c5: 52 */ 0xE5 /* 'e' -> */,
/* pos 00c6: 53 */ 0xE2 /* 'b' -> */,
/* pos 00c7: 54 */ 0xF3 /* 's' -> */,
/* pos 00c8: 55 */ 0xEF /* 'o' -> */,
/* pos 00c9: 56 */ 0xE3 /* 'c' -> */,
/* pos 00ca: 57 */ 0xEB /* 'k' -> */,
/* pos 00cb: 58 */ 0xE5 /* 'e' -> */,
/* pos 00cc: 59 */ 0xF4 /* 't' -> */,
/* pos 00cd: 60 */ 0xAD /* '-' -> */,
/* pos 00ce: 61 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x00E7 state 62) */,
0x65 /* 'e' */, 0x20, 0x00 /* (to 0x00F1 state 70) */,
0x6B /* 'k' */, 0x29, 0x00 /* (to 0x00FD state 81) */,
0x70 /* 'p' */, 0x38, 0x00 /* (to 0x010F state 88) */,
0x61 /* 'a' */, 0x3F, 0x00 /* (to 0x0119 state 97) */,
0x6E /* 'n' */, 0x44, 0x00 /* (to 0x0121 state 104) */,
0x76 /* 'v' */, 0x86, 0x01 /* (to 0x0266 state 284) */,
0x6F /* 'o' */, 0x8C, 0x01 /* (to 0x026F state 292) */,
0x08, /* fail */
/* pos 00e7: 62 */ 0xF2 /* 'r' -> */,
/* pos 00e8: 63 */ 0xE1 /* 'a' -> */,
/* pos 00e9: 64 */ 0xE6 /* 'f' -> */,
/* pos 00ea: 65 */ 0xF4 /* 't' -> */,
/* pos 00eb: 66 */ 0xBA /* ':' -> */,
/* pos 00ec: 67 */ 0x00, 0x07 /* - terminal marker 7 - */,
/* pos 00ee: 68 */ 0x8A /* '.' -> */,
/* pos 00ef: 69 */ 0x00, 0x08 /* - terminal marker 8 - */,
/* pos 00f1: 70 */ 0xF8 /* 'x' -> */,
/* pos 00f2: 71 */ 0xF4 /* 't' -> */,
/* pos 00f3: 72 */ 0xE5 /* 'e' -> */,
/* pos 00f4: 73 */ 0xEE /* 'n' -> */,
/* pos 00f5: 74 */ 0xF3 /* 's' -> */,
/* pos 00f6: 75 */ 0xE9 /* 'i' -> */,
/* pos 00f7: 76 */ 0xEF /* 'o' -> */,
/* pos 00f8: 77 */ 0xEE /* 'n' -> */,
/* pos 00f9: 78 */ 0xF3 /* 's' -> */,
/* pos 00fa: 79 */ 0xBA /* ':' -> */,
/* pos 00fb: 80 */ 0x00, 0x09 /* - terminal marker 9 - */,
/* pos 00fd: 81 */ 0xE5 /* 'e' -> */,
/* pos 00fe: 82 */ 0xF9 /* 'y' -> */,
/* pos 00ff: 83 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0109 state 84) */,
0x32 /* '2' */, 0x0A, 0x00 /* (to 0x010C state 86) */,
0x3A /* ':' */, 0x5F, 0x01 /* (to 0x0264 state 283) */,
0x08, /* fail */
/* pos 0109: 84 */ 0xBA /* ':' -> */,
/* pos 010a: 85 */ 0x00, 0x0A /* - terminal marker 10 - */,
/* pos 010c: 86 */ 0xBA /* ':' -> */,
/* pos 010d: 87 */ 0x00, 0x0B /* - terminal marker 11 - */,
/* pos 010f: 88 */ 0xF2 /* 'r' -> */,
/* pos 0110: 89 */ 0xEF /* 'o' -> */,
/* pos 0111: 90 */ 0xF4 /* 't' -> */,
/* pos 0112: 91 */ 0xEF /* 'o' -> */,
/* pos 0113: 92 */ 0xE3 /* 'c' -> */,
/* pos 0114: 93 */ 0xEF /* 'o' -> */,
/* pos 0115: 94 */ 0xEC /* 'l' -> */,
/* pos 0116: 95 */ 0xBA /* ':' -> */,
/* pos 0117: 96 */ 0x00, 0x0C /* - terminal marker 12 - */,
/* pos 0119: 97 */ 0xE3 /* 'c' -> */,
/* pos 011a: 98 */ 0xE3 /* 'c' -> */,
/* pos 011b: 99 */ 0xE5 /* 'e' -> */,
/* pos 011c: 100 */ 0xF0 /* 'p' -> */,
/* pos 011d: 101 */ 0xF4 /* 't' -> */,
/* pos 011e: 102 */ 0xBA /* ':' -> */,
/* pos 011f: 103 */ 0x00, 0x0D /* - terminal marker 13 - */,
/* pos 0121: 104 */ 0xEF /* 'o' -> */,
/* pos 0122: 105 */ 0xEE /* 'n' -> */,
/* pos 0123: 106 */ 0xE3 /* 'c' -> */,
/* pos 0124: 107 */ 0xE5 /* 'e' -> */,
/* pos 0125: 108 */ 0xBA /* ':' -> */,
/* pos 0126: 109 */ 0x00, 0x0E /* - terminal marker 14 - */,
/* pos 0128: 110 */ 0xF4 /* 't' -> */,
/* pos 0129: 111 */ 0xF0 /* 'p' -> */,
/* pos 012a: 112 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x0131 state 113) */,
0x32 /* '2' */, 0x10, 0x00 /* (to 0x013D state 118) */,
0x08, /* fail */
/* pos 0131: 113 */ 0xB1 /* '1' -> */,
/* pos 0132: 114 */ 0xAE /* '.' -> */,
/* pos 0133: 115 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x013A state 116) */,
0x30 /* '0' */, 0x1B, 0x03 /* (to 0x0451 state 660) */,
0x08, /* fail */
/* pos 013a: 116 */ 0xA0 /* ' ' -> */,
/* pos 013b: 117 */ 0x00, 0x0F /* - terminal marker 15 - */,
/* pos 013d: 118 */ 0xAD /* '-' -> */,
/* pos 013e: 119 */ 0xF3 /* 's' -> */,
/* pos 013f: 120 */ 0xE5 /* 'e' -> */,
/* pos 0140: 121 */ 0xF4 /* 't' -> */,
/* pos 0141: 122 */ 0xF4 /* 't' -> */,
/* pos 0142: 123 */ 0xE9 /* 'i' -> */,
/* pos 0143: 124 */ 0xEE /* 'n' -> */,
/* pos 0144: 125 */ 0xE7 /* 'g' -> */,
/* pos 0145: 126 */ 0xF3 /* 's' -> */,
/* pos 0146: 127 */ 0xBA /* ':' -> */,
/* pos 0147: 128 */ 0x00, 0x10 /* - terminal marker 16 - */,
/* pos 0149: 129 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x0156 state 130) */,
0x75 /* 'u' */, 0xAC, 0x00 /* (to 0x01F8 state 230) */,
0x67 /* 'g' */, 0x7D, 0x01 /* (to 0x02CC state 358) */,
0x6C /* 'l' */, 0x7E, 0x01 /* (to 0x02D0 state 361) */,
0x08, /* fail */
/* pos 0156: 130 */ 0xE3 /* 'c' -> */,
/* pos 0157: 131 */ 0xE5 /* 'e' -> */,
/* pos 0158: 132 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x015F state 133) */,
0x73 /* 's' */, 0x0E, 0x00 /* (to 0x0169 state 136) */,
0x08, /* fail */
/* pos 015f: 133 */ 0xF4 /* 't' -> */,
/* pos 0160: 134 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0167 state 135) */,
0x2D /* '-' */, 0x59, 0x00 /* (to 0x01BC state 192) */,
0x08, /* fail */
/* pos 0167: 135 */ 0x00, 0x11 /* - terminal marker 17 - */,
/* pos 0169: 136 */ 0xF3 /* 's' -> */,
/* pos 016a: 137 */ 0xAD /* '-' -> */,
/* pos 016b: 138 */ 0xE3 /* 'c' -> */,
/* pos 016c: 139 */ 0xEF /* 'o' -> */,
/* pos 016d: 140 */ 0xEE /* 'n' -> */,
/* pos 016e: 141 */ 0xF4 /* 't' -> */,
/* pos 016f: 142 */ 0xF2 /* 'r' -> */,
/* pos 0170: 143 */ 0xEF /* 'o' -> */,
/* pos 0171: 144 */ 0xEC /* 'l' -> */,
/* pos 0172: 145 */ 0xAD /* '-' -> */,
/* pos 0173: 146 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x017A state 147) */,
0x61 /* 'a' */, 0x48, 0x01 /* (to 0x02BE state 345) */,
0x08, /* fail */
/* pos 017a: 147 */ 0xE5 /* 'e' -> */,
/* pos 017b: 148 */ 0xF1 /* 'q' -> */,
/* pos 017c: 149 */ 0xF5 /* 'u' -> */,
/* pos 017d: 150 */ 0xE5 /* 'e' -> */,
/* pos 017e: 151 */ 0xF3 /* 's' -> */,
/* pos 017f: 152 */ 0xF4 /* 't' -> */,
/* pos 0180: 153 */ 0xAD /* '-' -> */,
/* pos 0181: 154 */ 0xE8 /* 'h' -> */,
/* pos 0182: 155 */ 0xE5 /* 'e' -> */,
/* pos 0183: 156 */ 0xE1 /* 'a' -> */,
/* pos 0184: 157 */ 0xE4 /* 'd' -> */,
/* pos 0185: 158 */ 0xE5 /* 'e' -> */,
/* pos 0186: 159 */ 0xF2 /* 'r' -> */,
/* pos 0187: 160 */ 0xF3 /* 's' -> */,
/* pos 0188: 161 */ 0xBA /* ':' -> */,
/* pos 0189: 162 */ 0x00, 0x12 /* - terminal marker 18 - */,
/* pos 018b: 163 */ 0xE6 /* 'f' -> */,
/* pos 018c: 164 */ 0xAD /* '-' -> */,
/* pos 018d: 165 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x019A state 166) */,
0x6E /* 'n' */, 0x20, 0x00 /* (to 0x01B0 state 181) */,
0x72 /* 'r' */, 0x9E, 0x01 /* (to 0x0331 state 435) */,
0x75 /* 'u' */, 0xA2, 0x01 /* (to 0x0338 state 441) */,
0x08, /* fail */
/* pos 019a: 166 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x01A1 state 167) */,
0x61 /* 'a' */, 0x8E, 0x01 /* (to 0x032B state 430) */,
0x08, /* fail */
/* pos 01a1: 167 */ 0xE4 /* 'd' -> */,
/* pos 01a2: 168 */ 0xE9 /* 'i' -> */,
/* pos 01a3: 169 */ 0xE6 /* 'f' -> */,
/* pos 01a4: 170 */ 0xE9 /* 'i' -> */,
/* pos 01a5: 171 */ 0xE5 /* 'e' -> */,
/* pos 01a6: 172 */ 0xE4 /* 'd' -> */,
/* pos 01a7: 173 */ 0xAD /* '-' -> */,
/* pos 01a8: 174 */ 0xF3 /* 's' -> */,
/* pos 01a9: 175 */ 0xE9 /* 'i' -> */,
/* pos 01aa: 176 */ 0xEE /* 'n' -> */,
/* pos 01ab: 177 */ 0xE3 /* 'c' -> */,
/* pos 01ac: 178 */ 0xE5 /* 'e' -> */,
/* pos 01ad: 179 */ 0xBA /* ':' -> */,
/* pos 01ae: 180 */ 0x00, 0x13 /* - terminal marker 19 - */,
/* pos 01b0: 181 */ 0xEF /* 'o' -> */,
/* pos 01b1: 182 */ 0xEE /* 'n' -> */,
/* pos 01b2: 183 */ 0xE5 /* 'e' -> */,
/* pos 01b3: 184 */ 0xAD /* '-' -> */,
/* pos 01b4: 185 */ 0xED /* 'm' -> */,
/* pos 01b5: 186 */ 0xE1 /* 'a' -> */,
/* pos 01b6: 187 */ 0xF4 /* 't' -> */,
/* pos 01b7: 188 */ 0xE3 /* 'c' -> */,
/* pos 01b8: 189 */ 0xE8 /* 'h' -> */,
/* pos 01b9: 190 */ 0xBA /* ':' -> */,
/* pos 01ba: 191 */ 0x00, 0x14 /* - terminal marker 20 - */,
/* pos 01bc: 192 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x01C9 state 193) */,
0x6C /* 'l' */, 0x14, 0x00 /* (to 0x01D3 state 202) */,
0x63 /* 'c' */, 0xEB, 0x00 /* (to 0x02AD state 330) */,
0x72 /* 'r' */, 0xF1, 0x00 /* (to 0x02B6 state 338) */,
0x08, /* fail */
/* pos 01c9: 193 */ 0xEE /* 'n' -> */,
/* pos 01ca: 194 */ 0xE3 /* 'c' -> */,
/* pos 01cb: 195 */ 0xEF /* 'o' -> */,
/* pos 01cc: 196 */ 0xE4 /* 'd' -> */,
/* pos 01cd: 197 */ 0xE9 /* 'i' -> */,
/* pos 01ce: 198 */ 0xEE /* 'n' -> */,
/* pos 01cf: 199 */ 0xE7 /* 'g' -> */,
/* pos 01d0: 200 */ 0xBA /* ':' -> */,
/* pos 01d1: 201 */ 0x00, 0x15 /* - terminal marker 21 - */,
/* pos 01d3: 202 */ 0xE1 /* 'a' -> */,
/* pos 01d4: 203 */ 0xEE /* 'n' -> */,
/* pos 01d5: 204 */ 0xE7 /* 'g' -> */,
/* pos 01d6: 205 */ 0xF5 /* 'u' -> */,
/* pos 01d7: 206 */ 0xE1 /* 'a' -> */,
/* pos 01d8: 207 */ 0xE7 /* 'g' -> */,
/* pos 01d9: 208 */ 0xE5 /* 'e' -> */,
/* pos 01da: 209 */ 0xBA /* ':' -> */,
/* pos 01db: 210 */ 0x00, 0x16 /* - terminal marker 22 - */,
/* pos 01dd: 211 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01E4 state 212) */,
0x6F /* 'o' */, 0x9E, 0x01 /* (to 0x037E state 497) */,
0x08, /* fail */
/* pos 01e4: 212 */ 0xE7 /* 'g' -> */,
/* pos 01e5: 213 */ 0xED /* 'm' -> */,
/* pos 01e6: 214 */ 0xE1 /* 'a' -> */,
/* pos 01e7: 215 */ 0xBA /* ':' -> */,
/* pos 01e8: 216 */ 0x00, 0x17 /* - terminal marker 23 - */,
/* pos 01ea: 217 */ 0xE3 /* 'c' -> */,
/* pos 01eb: 218 */ 0xE8 /* 'h' -> */,
/* pos 01ec: 219 */ 0xE5 /* 'e' -> */,
/* pos 01ed: 220 */ 0xAD /* '-' -> */,
/* pos 01ee: 221 */ 0xE3 /* 'c' -> */,
/* pos 01ef: 222 */ 0xEF /* 'o' -> */,
/* pos 01f0: 223 */ 0xEE /* 'n' -> */,
/* pos 01f1: 224 */ 0xF4 /* 't' -> */,
/* pos 01f2: 225 */ 0xF2 /* 'r' -> */,
/* pos 01f3: 226 */ 0xEF /* 'o' -> */,
/* pos 01f4: 227 */ 0xEC /* 'l' -> */,
/* pos 01f5: 228 */ 0xBA /* ':' -> */,
/* pos 01f6: 229 */ 0x00, 0x18 /* - terminal marker 24 - */,
/* pos 01f8: 230 */ 0xF4 /* 't' -> */,
/* pos 01f9: 231 */ 0xE8 /* 'h' -> */,
/* pos 01fa: 232 */ 0xEF /* 'o' -> */,
/* pos 01fb: 233 */ 0xF2 /* 'r' -> */,
/* pos 01fc: 234 */ 0xE9 /* 'i' -> */,
/* pos 01fd: 235 */ 0xFA /* 'z' -> */,
/* pos 01fe: 236 */ 0xE1 /* 'a' -> */,
/* pos 01ff: 237 */ 0xF4 /* 't' -> */,
/* pos 0200: 238 */ 0xE9 /* 'i' -> */,
/* pos 0201: 239 */ 0xEF /* 'o' -> */,
/* pos 0202: 240 */ 0xEE /* 'n' -> */,
/* pos 0203: 241 */ 0xBA /* ':' -> */,
/* pos 0204: 242 */ 0x00, 0x19 /* - terminal marker 25 - */,
/* pos 0206: 243 */ 0xEB /* 'k' -> */,
/* pos 0207: 244 */ 0xE9 /* 'i' -> */,
/* pos 0208: 245 */ 0xE5 /* 'e' -> */,
/* pos 0209: 246 */ 0xBA /* ':' -> */,
/* pos 020a: 247 */ 0x00, 0x1A /* - terminal marker 26 - */,
/* pos 020c: 248 */ 0xE5 /* 'e' -> */,
/* pos 020d: 249 */ 0xEE /* 'n' -> */,
/* pos 020e: 250 */ 0xF4 /* 't' -> */,
/* pos 020f: 251 */ 0xAD /* '-' -> */,
/* pos 0210: 252 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0220 state 253) */,
0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0231 state 260) */,
0x64 /* 'd' */, 0xC0, 0x00 /* (to 0x02D6 state 366) */,
0x65 /* 'e' */, 0xCA, 0x00 /* (to 0x02E3 state 378) */,
0x72 /* 'r' */, 0xE3, 0x00 /* (to 0x02FF state 403) */,
0x08, /* fail */
/* pos 0220: 253 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x022A state 254) */,
0x61 /* 'a' */, 0xCA, 0x00 /* (to 0x02ED state 387) */,
0x6F /* 'o' */, 0xD0, 0x00 /* (to 0x02F6 state 395) */,
0x08, /* fail */
/* pos 022a: 254 */ 0xEE /* 'n' -> */,
/* pos 022b: 255 */ 0xE7 /* 'g' -> */,
/* pos 022c: 256 */ 0xF4 /* 't' -> */,
/* pos 022d: 257 */ 0xE8 /* 'h' -> */,
/* pos 022e: 258 */ 0xBA /* ':' -> */,
/* pos 022f: 259 */ 0x00, 0x1B /* - terminal marker 27 - */,
/* pos 0231: 260 */ 0xF9 /* 'y' -> */,
/* pos 0232: 261 */ 0xF0 /* 'p' -> */,
/* pos 0233: 262 */ 0xE5 /* 'e' -> */,
/* pos 0234: 263 */ 0xBA /* ':' -> */,
/* pos 0235: 264 */ 0x00, 0x1C /* - terminal marker 28 - */,
/* pos 0237: 265 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x023E state 266) */,
0x65 /* 'e' */, 0xF6, 0x01 /* (to 0x0430 state 637) */,
0x08, /* fail */
/* pos 023e: 266 */ 0xF4 /* 't' -> */,
/* pos 023f: 267 */ 0xE5 /* 'e' -> */,
/* pos 0240: 268 */ 0xBA /* ':' -> */,
/* pos 0241: 269 */ 0x00, 0x1D /* - terminal marker 29 - */,
/* pos 0243: 270 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x024A state 271) */,
0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0250 state 276) */,
0x08, /* fail */
/* pos 024a: 271 */ 0xEE /* 'n' -> */,
/* pos 024b: 272 */ 0xE7 /* 'g' -> */,
/* pos 024c: 273 */ 0xE5 /* 'e' -> */,
/* pos 024d: 274 */ 0xBA /* ':' -> */,
/* pos 024e: 275 */ 0x00, 0x1E /* - terminal marker 30 - */,
/* pos 0250: 276 */ 0x66 /* 'f' */, 0x07, 0x00 /* (to 0x0257 state 277) */,
0x74 /* 't' */, 0x5A, 0x01 /* (to 0x03AD state 529) */,
0x08, /* fail */
/* pos 0257: 277 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x025E state 278) */,
0x72 /* 'r' */, 0x4D, 0x01 /* (to 0x03A7 state 524) */,
0x08, /* fail */
/* pos 025e: 278 */ 0xF2 /* 'r' -> */,
/* pos 025f: 279 */ 0xE5 /* 'e' -> */,
/* pos 0260: 280 */ 0xF2 /* 'r' -> */,
/* pos 0261: 281 */ 0xBA /* ':' -> */,
/* pos 0262: 282 */ 0x00, 0x1F /* - terminal marker 31 - */,
/* pos 0264: 283 */ 0x00, 0x20 /* - terminal marker 32 - */,
/* pos 0266: 284 */ 0xE5 /* 'e' -> */,
/* pos 0267: 285 */ 0xF2 /* 'r' -> */,
/* pos 0268: 286 */ 0xF3 /* 's' -> */,
/* pos 0269: 287 */ 0xE9 /* 'i' -> */,
/* pos 026a: 288 */ 0xEF /* 'o' -> */,
/* pos 026b: 289 */ 0xEE /* 'n' -> */,
/* pos 026c: 290 */ 0xBA /* ':' -> */,
/* pos 026d: 291 */ 0x00, 0x21 /* - terminal marker 33 - */,
/* pos 026f: 292 */ 0xF2 /* 'r' -> */,
/* pos 0270: 293 */ 0xE9 /* 'i' -> */,
/* pos 0271: 294 */ 0xE7 /* 'g' -> */,
/* pos 0272: 295 */ 0xE9 /* 'i' -> */,
/* pos 0273: 296 */ 0xEE /* 'n' -> */,
/* pos 0274: 297 */ 0xBA /* ':' -> */,
/* pos 0275: 298 */ 0x00, 0x22 /* - terminal marker 34 - */,
/* pos 0277: 299 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0284 state 300) */,
0x6D /* 'm' */, 0x14, 0x00 /* (to 0x028E state 309) */,
0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0295 state 315) */,
0x73 /* 's' */, 0x1A, 0x00 /* (to 0x029A state 319) */,
0x08, /* fail */
/* pos 0284: 300 */ 0xF5 /* 'u' -> */,
/* pos 0285: 301 */ 0xF4 /* 't' -> */,
/* pos 0286: 302 */ 0xE8 /* 'h' -> */,
/* pos 0287: 303 */ 0xEF /* 'o' -> */,
/* pos 0288: 304 */ 0xF2 /* 'r' -> */,
/* pos 0289: 305 */ 0xE9 /* 'i' -> */,
/* pos 028a: 306 */ 0xF4 /* 't' -> */,
/* pos 028b: 307 */ 0xF9 /* 'y' -> */,
/* pos 028c: 308 */ 0x00, 0x23 /* - terminal marker 35 - */,
/* pos 028e: 309 */ 0xE5 /* 'e' -> */,
/* pos 028f: 310 */ 0xF4 /* 't' -> */,
/* pos 0290: 311 */ 0xE8 /* 'h' -> */,
/* pos 0291: 312 */ 0xEF /* 'o' -> */,
/* pos 0292: 313 */ 0xE4 /* 'd' -> */,
/* pos 0293: 314 */ 0x00, 0x24 /* - terminal marker 36 - */,
/* pos 0295: 315 */ 0xE1 /* 'a' -> */,
/* pos 0296: 316 */ 0xF4 /* 't' -> */,
/* pos 0297: 317 */ 0xE8 /* 'h' -> */,
/* pos 0298: 318 */ 0x00, 0x25 /* - terminal marker 37 - */,
/* pos 029a: 319 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x02A1 state 320) */,
0x74 /* 't' */, 0x0A, 0x00 /* (to 0x02A7 state 325) */,
0x08, /* fail */
/* pos 02a1: 320 */ 0xE8 /* 'h' -> */,
/* pos 02a2: 321 */ 0xE5 /* 'e' -> */,
/* pos 02a3: 322 */ 0xED /* 'm' -> */,
/* pos 02a4: 323 */ 0xE5 /* 'e' -> */,
/* pos 02a5: 324 */ 0x00, 0x26 /* - terminal marker 38 - */,
/* pos 02a7: 325 */ 0xE1 /* 'a' -> */,
/* pos 02a8: 326 */ 0xF4 /* 't' -> */,
/* pos 02a9: 327 */ 0xF5 /* 'u' -> */,
/* pos 02aa: 328 */ 0xF3 /* 's' -> */,
/* pos 02ab: 329 */ 0x00, 0x27 /* - terminal marker 39 - */,
/* pos 02ad: 330 */ 0xE8 /* 'h' -> */,
/* pos 02ae: 331 */ 0xE1 /* 'a' -> */,
/* pos 02af: 332 */ 0xF2 /* 'r' -> */,
/* pos 02b0: 333 */ 0xF3 /* 's' -> */,
/* pos 02b1: 334 */ 0xE5 /* 'e' -> */,
/* pos 02b2: 335 */ 0xF4 /* 't' -> */,
/* pos 02b3: 336 */ 0xBA /* ':' -> */,
/* pos 02b4: 337 */ 0x00, 0x28 /* - terminal marker 40 - */,
/* pos 02b6: 338 */ 0xE1 /* 'a' -> */,
/* pos 02b7: 339 */ 0xEE /* 'n' -> */,
/* pos 02b8: 340 */ 0xE7 /* 'g' -> */,
/* pos 02b9: 341 */ 0xE5 /* 'e' -> */,
/* pos 02ba: 342 */ 0xF3 /* 's' -> */,
/* pos 02bb: 343 */ 0xBA /* ':' -> */,
/* pos 02bc: 344 */ 0x00, 0x29 /* - terminal marker 41 - */,
/* pos 02be: 345 */ 0xEC /* 'l' -> */,
/* pos 02bf: 346 */ 0xEC /* 'l' -> */,
/* pos 02c0: 347 */ 0xEF /* 'o' -> */,
/* pos 02c1: 348 */ 0xF7 /* 'w' -> */,
/* pos 02c2: 349 */ 0xAD /* '-' -> */,
/* pos 02c3: 350 */ 0xEF /* 'o' -> */,
/* pos 02c4: 351 */ 0xF2 /* 'r' -> */,
/* pos 02c5: 352 */ 0xE9 /* 'i' -> */,
/* pos 02c6: 353 */ 0xE7 /* 'g' -> */,
/* pos 02c7: 354 */ 0xE9 /* 'i' -> */,
/* pos 02c8: 355 */ 0xEE /* 'n' -> */,
/* pos 02c9: 356 */ 0xBA /* ':' -> */,
/* pos 02ca: 357 */ 0x00, 0x2A /* - terminal marker 42 - */,
/* pos 02cc: 358 */ 0xE5 /* 'e' -> */,
/* pos 02cd: 359 */ 0xBA /* ':' -> */,
/* pos 02ce: 360 */ 0x00, 0x2B /* - terminal marker 43 - */,
/* pos 02d0: 361 */ 0xEC /* 'l' -> */,
/* pos 02d1: 362 */ 0xEF /* 'o' -> */,
/* pos 02d2: 363 */ 0xF7 /* 'w' -> */,
/* pos 02d3: 364 */ 0xBA /* ':' -> */,
/* pos 02d4: 365 */ 0x00, 0x2C /* - terminal marker 44 - */,
/* pos 02d6: 366 */ 0xE9 /* 'i' -> */,
/* pos 02d7: 367 */ 0xF3 /* 's' -> */,
/* pos 02d8: 368 */ 0xF0 /* 'p' -> */,
/* pos 02d9: 369 */ 0xEF /* 'o' -> */,
/* pos 02da: 370 */ 0xF3 /* 's' -> */,
/* pos 02db: 371 */ 0xE9 /* 'i' -> */,
/* pos 02dc: 372 */ 0xF4 /* 't' -> */,
/* pos 02dd: 373 */ 0xE9 /* 'i' -> */,
/* pos 02de: 374 */ 0xEF /* 'o' -> */,
/* pos 02df: 375 */ 0xEE /* 'n' -> */,
/* pos 02e0: 376 */ 0xBA /* ':' -> */,
/* pos 02e1: 377 */ 0x00, 0x2D /* - terminal marker 45 - */,
/* pos 02e3: 378 */ 0xEE /* 'n' -> */,
/* pos 02e4: 379 */ 0xE3 /* 'c' -> */,
/* pos 02e5: 380 */ 0xEF /* 'o' -> */,
/* pos 02e6: 381 */ 0xE4 /* 'd' -> */,
/* pos 02e7: 382 */ 0xE9 /* 'i' -> */,
/* pos 02e8: 383 */ 0xEE /* 'n' -> */,
/* pos 02e9: 384 */ 0xE7 /* 'g' -> */,
/* pos 02ea: 385 */ 0xBA /* ':' -> */,
/* pos 02eb: 386 */ 0x00, 0x2E /* - terminal marker 46 - */,
/* pos 02ed: 387 */ 0xEE /* 'n' -> */,
/* pos 02ee: 388 */ 0xE7 /* 'g' -> */,
/* pos 02ef: 389 */ 0xF5 /* 'u' -> */,
/* pos 02f0: 390 */ 0xE1 /* 'a' -> */,
/* pos 02f1: 391 */ 0xE7 /* 'g' -> */,
/* pos 02f2: 392 */ 0xE5 /* 'e' -> */,
/* pos 02f3: 393 */ 0xBA /* ':' -> */,
/* pos 02f4: 394 */ 0x00, 0x2F /* - terminal marker 47 - */,
/* pos 02f6: 395 */ 0xE3 /* 'c' -> */,
/* pos 02f7: 396 */ 0xE1 /* 'a' -> */,
/* pos 02f8: 397 */ 0xF4 /* 't' -> */,
/* pos 02f9: 398 */ 0xE9 /* 'i' -> */,
/* pos 02fa: 399 */ 0xEF /* 'o' -> */,
/* pos 02fb: 400 */ 0xEE /* 'n' -> */,
/* pos 02fc: 401 */ 0xBA /* ':' -> */,
/* pos 02fd: 402 */ 0x00, 0x30 /* - terminal marker 48 - */,
/* pos 02ff: 403 */ 0xE1 /* 'a' -> */,
/* pos 0300: 404 */ 0xEE /* 'n' -> */,
/* pos 0301: 405 */ 0xE7 /* 'g' -> */,
/* pos 0302: 406 */ 0xE5 /* 'e' -> */,
/* pos 0303: 407 */ 0xBA /* ':' -> */,
/* pos 0304: 408 */ 0x00, 0x31 /* - terminal marker 49 - */,
/* pos 0306: 409 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x030D state 410) */,
0x78 /* 'x' */, 0x09, 0x00 /* (to 0x0312 state 414) */,
0x08, /* fail */
/* pos 030d: 410 */ 0xE1 /* 'a' -> */,
/* pos 030e: 411 */ 0xE7 /* 'g' -> */,
/* pos 030f: 412 */ 0xBA /* ':' -> */,
/* pos 0310: 413 */ 0x00, 0x32 /* - terminal marker 50 - */,
/* pos 0312: 414 */ 0xF0 /* 'p' -> */,
/* pos 0313: 415 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x031A state 416) */,
0x69 /* 'i' */, 0x09, 0x00 /* (to 0x031F state 420) */,
0x08, /* fail */
/* pos 031a: 416 */ 0xE3 /* 'c' -> */,
/* pos 031b: 417 */ 0xF4 /* 't' -> */,
/* pos 031c: 418 */ 0xBA /* ':' -> */,
/* pos 031d: 419 */ 0x00, 0x33 /* - terminal marker 51 - */,
/* pos 031f: 420 */ 0xF2 /* 'r' -> */,
/* pos 0320: 421 */ 0xE5 /* 'e' -> */,
/* pos 0321: 422 */ 0xF3 /* 's' -> */,
/* pos 0322: 423 */ 0xBA /* ':' -> */,
/* pos 0323: 424 */ 0x00, 0x34 /* - terminal marker 52 - */,
/* pos 0325: 425 */ 0xF2 /* 'r' -> */,
/* pos 0326: 426 */ 0xEF /* 'o' -> */,
/* pos 0327: 427 */ 0xED /* 'm' -> */,
/* pos 0328: 428 */ 0xBA /* ':' -> */,
/* pos 0329: 429 */ 0x00, 0x35 /* - terminal marker 53 - */,
/* pos 032b: 430 */ 0xF4 /* 't' -> */,
/* pos 032c: 431 */ 0xE3 /* 'c' -> */,
/* pos 032d: 432 */ 0xE8 /* 'h' -> */,
/* pos 032e: 433 */ 0xBA /* ':' -> */,
/* pos 032f: 434 */ 0x00, 0x36 /* - terminal marker 54 - */,
/* pos 0331: 435 */ 0xE1 /* 'a' -> */,
/* pos 0332: 436 */ 0xEE /* 'n' -> */,
/* pos 0333: 437 */ 0xE7 /* 'g' -> */,
/* pos 0334: 438 */ 0xE5 /* 'e' -> */,
/* pos 0335: 439 */ 0xBA /* ':' -> */,
/* pos 0336: 440 */ 0x00, 0x37 /* - terminal marker 55 - */,
/* pos 0338: 441 */ 0xEE /* 'n' -> */,
/* pos 0339: 442 */ 0xED /* 'm' -> */,
/* pos 033a: 443 */ 0xEF /* 'o' -> */,
/* pos 033b: 444 */ 0xE4 /* 'd' -> */,
/* pos 033c: 445 */ 0xE9 /* 'i' -> */,
/* pos 033d: 446 */ 0xE6 /* 'f' -> */,
/* pos 033e: 447 */ 0xE9 /* 'i' -> */,
/* pos 033f: 448 */ 0xE5 /* 'e' -> */,
/* pos 0340: 449 */ 0xE4 /* 'd' -> */,
/* pos 0341: 450 */ 0xAD /* '-' -> */,
/* pos 0342: 451 */ 0xF3 /* 's' -> */,
/* pos 0343: 452 */ 0xE9 /* 'i' -> */,
/* pos 0344: 453 */ 0xEE /* 'n' -> */,
/* pos 0345: 454 */ 0xE3 /* 'c' -> */,
/* pos 0346: 455 */ 0xE5 /* 'e' -> */,
/* pos 0347: 456 */ 0xBA /* ':' -> */,
/* pos 0348: 457 */ 0x00, 0x38 /* - terminal marker 56 - */,
/* pos 034a: 458 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0354 state 459) */,
0x69 /* 'i' */, 0x15, 0x00 /* (to 0x0362 state 472) */,
0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0367 state 476) */,
0x08, /* fail */
/* pos 0354: 459 */ 0xF3 /* 's' -> */,
/* pos 0355: 460 */ 0xF4 /* 't' -> */,
/* pos 0356: 461 */ 0xAD /* '-' -> */,
/* pos 0357: 462 */ 0xED /* 'm' -> */,
/* pos 0358: 463 */ 0xEF /* 'o' -> */,
/* pos 0359: 464 */ 0xE4 /* 'd' -> */,
/* pos 035a: 465 */ 0xE9 /* 'i' -> */,
/* pos 035b: 466 */ 0xE6 /* 'f' -> */,
/* pos 035c: 467 */ 0xE9 /* 'i' -> */,
/* pos 035d: 468 */ 0xE5 /* 'e' -> */,
/* pos 035e: 469 */ 0xE4 /* 'd' -> */,
/* pos 035f: 470 */ 0xBA /* ':' -> */,
/* pos 0360: 471 */ 0x00, 0x39 /* - terminal marker 57 - */,
/* pos 0362: 472 */ 0xEE /* 'n' -> */,
/* pos 0363: 473 */ 0xEB /* 'k' -> */,
/* pos 0364: 474 */ 0xBA /* ':' -> */,
/* pos 0365: 475 */ 0x00, 0x3A /* - terminal marker 58 - */,
/* pos 0367: 476 */ 0xE3 /* 'c' -> */,
/* pos 0368: 477 */ 0xE1 /* 'a' -> */,
/* pos 0369: 478 */ 0xF4 /* 't' -> */,
/* pos 036a: 479 */ 0xE9 /* 'i' -> */,
/* pos 036b: 480 */ 0xEF /* 'o' -> */,
/* pos 036c: 481 */ 0xEE /* 'n' -> */,
/* pos 036d: 482 */ 0xBA /* ':' -> */,
/* pos 036e: 483 */ 0x00, 0x3B /* - terminal marker 59 - */,
/* pos 0370: 484 */ 0xE1 /* 'a' -> */,
/* pos 0371: 485 */ 0xF8 /* 'x' -> */,
/* pos 0372: 486 */ 0xAD /* '-' -> */,
/* pos 0373: 487 */ 0xE6 /* 'f' -> */,
/* pos 0374: 488 */ 0xEF /* 'o' -> */,
/* pos 0375: 489 */ 0xF2 /* 'r' -> */,
/* pos 0376: 490 */ 0xF7 /* 'w' -> */,
/* pos 0377: 491 */ 0xE1 /* 'a' -> */,
/* pos 0378: 492 */ 0xF2 /* 'r' -> */,
/* pos 0379: 493 */ 0xE4 /* 'd' -> */,
/* pos 037a: 494 */ 0xF3 /* 's' -> */,
/* pos 037b: 495 */ 0xBA /* ':' -> */,
/* pos 037c: 496 */ 0x00, 0x3C /* - terminal marker 60 - */,
/* pos 037e: 497 */ 0xF8 /* 'x' -> */,
/* pos 037f: 498 */ 0xF9 /* 'y' -> */,
/* pos 0380: 499 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0387 state 500) */,
0x20 /* ' ' */, 0xBB, 0x00 /* (to 0x043E state 649) */,
0x08, /* fail */
/* pos 0387: 500 */ 0xE1 /* 'a' -> */,
/* pos 0388: 501 */ 0xF5 /* 'u' -> */,
/* pos 0389: 502 */ 0xF4 /* 't' -> */,
/* pos 038a: 503 */ 0xE8 /* 'h' -> */,
/* pos 038b: 504 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0392 state 505) */,
0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x039C state 514) */,
0x08, /* fail */
/* pos 0392: 505 */ 0xEE /* 'n' -> */,
/* pos 0393: 506 */ 0xF4 /* 't' -> */,
/* pos 0394: 507 */ 0xE9 /* 'i' -> */,
/* pos 0395: 508 */ 0xE3 /* 'c' -> */,
/* pos 0396: 509 */ 0xE1 /* 'a' -> */,
/* pos 0397: 510 */ 0xF4 /* 't' -> */,
/* pos 0398: 511 */ 0xE5 /* 'e' -> */,
/* pos 0399: 512 */ 0xBA /* ':' -> */,
/* pos 039a: 513 */ 0x00, 0x3D /* - terminal marker 61 - */,
/* pos 039c: 514 */ 0xF2 /* 'r' -> */,
/* pos 039d: 515 */ 0xE9 /* 'i' -> */,
/* pos 039e: 516 */ 0xFA /* 'z' -> */,
/* pos 039f: 517 */ 0xE1 /* 'a' -> */,
/* pos 03a0: 518 */ 0xF4 /* 't' -> */,
/* pos 03a1: 519 */ 0xE9 /* 'i' -> */,
/* pos 03a2: 520 */ 0xEF /* 'o' -> */,
/* pos 03a3: 521 */ 0xEE /* 'n' -> */,
/* pos 03a4: 522 */ 0xBA /* ':' -> */,
/* pos 03a5: 523 */ 0x00, 0x3E /* - terminal marker 62 - */,
/* pos 03a7: 524 */ 0xE5 /* 'e' -> */,
/* pos 03a8: 525 */ 0xF3 /* 's' -> */,
/* pos 03a9: 526 */ 0xE8 /* 'h' -> */,
/* pos 03aa: 527 */ 0xBA /* ':' -> */,
/* pos 03ab: 528 */ 0x00, 0x3F /* - terminal marker 63 - */,
/* pos 03ad: 529 */ 0xF2 /* 'r' -> */,
/* pos 03ae: 530 */ 0xF9 /* 'y' -> */,
/* pos 03af: 531 */ 0xAD /* '-' -> */,
/* pos 03b0: 532 */ 0xE1 /* 'a' -> */,
/* pos 03b1: 533 */ 0xE6 /* 'f' -> */,
/* pos 03b2: 534 */ 0xF4 /* 't' -> */,
/* pos 03b3: 535 */ 0xE5 /* 'e' -> */,
/* pos 03b4: 536 */ 0xF2 /* 'r' -> */,
/* pos 03b5: 537 */ 0xBA /* ':' -> */,
/* pos 03b6: 538 */ 0x00, 0x40 /* - terminal marker 64 - */,
/* pos 03b8: 539 */ 0xF6 /* 'v' -> */,
/* pos 03b9: 540 */ 0xE5 /* 'e' -> */,
/* pos 03ba: 541 */ 0xF2 /* 'r' -> */,
/* pos 03bb: 542 */ 0xBA /* ':' -> */,
/* pos 03bc: 543 */ 0x00, 0x41 /* - terminal marker 65 - */,
/* pos 03be: 544 */ 0xAD /* '-' -> */,
/* pos 03bf: 545 */ 0xE3 /* 'c' -> */,
/* pos 03c0: 546 */ 0xEF /* 'o' -> */,
/* pos 03c1: 547 */ 0xEF /* 'o' -> */,
/* pos 03c2: 548 */ 0xEB /* 'k' -> */,
/* pos 03c3: 549 */ 0xE9 /* 'i' -> */,
/* pos 03c4: 550 */ 0xE5 /* 'e' -> */,
/* pos 03c5: 551 */ 0xBA /* ':' -> */,
/* pos 03c6: 552 */ 0x00, 0x42 /* - terminal marker 66 - */,
/* pos 03c8: 553 */ 0xF2 /* 'r' -> */,
/* pos 03c9: 554 */ 0xE9 /* 'i' -> */,
/* pos 03ca: 555 */ 0xE3 /* 'c' -> */,
/* pos 03cb: 556 */ 0xF4 /* 't' -> */,
/* pos 03cc: 557 */ 0xAD /* '-' -> */,
/* pos 03cd: 558 */ 0xF4 /* 't' -> */,
/* pos 03ce: 559 */ 0xF2 /* 'r' -> */,
/* pos 03cf: 560 */ 0xE1 /* 'a' -> */,
/* pos 03d0: 561 */ 0xEE /* 'n' -> */,
/* pos 03d1: 562 */ 0xF3 /* 's' -> */,
/* pos 03d2: 563 */ 0xF0 /* 'p' -> */,
/* pos 03d3: 564 */ 0xEF /* 'o' -> */,
/* pos 03d4: 565 */ 0xF2 /* 'r' -> */,
/* pos 03d5: 566 */ 0xF4 /* 't' -> */,
/* pos 03d6: 567 */ 0xAD /* '-' -> */,
/* pos 03d7: 568 */ 0xF3 /* 's' -> */,
/* pos 03d8: 569 */ 0xE5 /* 'e' -> */,
/* pos 03d9: 570 */ 0xE3 /* 'c' -> */,
/* pos 03da: 571 */ 0xF5 /* 'u' -> */,
/* pos 03db: 572 */ 0xF2 /* 'r' -> */,
/* pos 03dc: 573 */ 0xE9 /* 'i' -> */,
/* pos 03dd: 574 */ 0xF4 /* 't' -> */,
/* pos 03de: 575 */ 0xF9 /* 'y' -> */,
/* pos 03df: 576 */ 0xBA /* ':' -> */,
/* pos 03e0: 577 */ 0x00, 0x43 /* - terminal marker 67 - */,
/* pos 03e2: 578 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x03E9 state 579) */,
0x65 /* 'e' */, 0x84, 0x00 /* (to 0x0469 state 680) */,
0x08, /* fail */
/* pos 03e9: 579 */ 0xE1 /* 'a' -> */,
/* pos 03ea: 580 */ 0xEE /* 'n' -> */,
/* pos 03eb: 581 */ 0xF3 /* 's' -> */,
/* pos 03ec: 582 */ 0xE6 /* 'f' -> */,
/* pos 03ed: 583 */ 0xE5 /* 'e' -> */,
/* pos 03ee: 584 */ 0xF2 /* 'r' -> */,
/* pos 03ef: 585 */ 0xAD /* '-' -> */,
/* pos 03f0: 586 */ 0xE5 /* 'e' -> */,
/* pos 03f1: 587 */ 0xEE /* 'n' -> */,
/* pos 03f2: 588 */ 0xE3 /* 'c' -> */,
/* pos 03f3: 589 */ 0xEF /* 'o' -> */,
/* pos 03f4: 590 */ 0xE4 /* 'd' -> */,
/* pos 03f5: 591 */ 0xE9 /* 'i' -> */,
/* pos 03f6: 592 */ 0xEE /* 'n' -> */,
/* pos 03f7: 593 */ 0xE7 /* 'g' -> */,
/* pos 03f8: 594 */ 0xBA /* ':' -> */,
/* pos 03f9: 595 */ 0x00, 0x44 /* - terminal marker 68 - */,
/* pos 03fb: 596 */ 0xE5 /* 'e' -> */,
/* pos 03fc: 597 */ 0xF2 /* 'r' -> */,
/* pos 03fd: 598 */ 0xAD /* '-' -> */,
/* pos 03fe: 599 */ 0xE1 /* 'a' -> */,
/* pos 03ff: 600 */ 0xE7 /* 'g' -> */,
/* pos 0400: 601 */ 0xE5 /* 'e' -> */,
/* pos 0401: 602 */ 0xEE /* 'n' -> */,
/* pos 0402: 603 */ 0xF4 /* 't' -> */,
/* pos 0403: 604 */ 0xBA /* ':' -> */,
/* pos 0404: 605 */ 0x00, 0x45 /* - terminal marker 69 - */,
/* pos 0406: 606 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x040D state 607) */,
0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0412 state 611) */,
0x08, /* fail */
/* pos 040d: 607 */ 0xF2 /* 'r' -> */,
/* pos 040e: 608 */ 0xF9 /* 'y' -> */,
/* pos 040f: 609 */ 0xBA /* ':' -> */,
/* pos 0410: 610 */ 0x00, 0x46 /* - terminal marker 70 - */,
/* pos 0412: 611 */ 0xE1 /* 'a' -> */,
/* pos 0413: 612 */ 0xBA /* ':' -> */,
/* pos 0414: 613 */ 0x00, 0x47 /* - terminal marker 71 - */,
/* pos 0416: 614 */ 0xF7 /* 'w' -> */,
/* pos 0417: 615 */ 0xF7 /* 'w' -> */,
/* pos 0418: 616 */ 0xAD /* '-' -> */,
/* pos 0419: 617 */ 0xE1 /* 'a' -> */,
/* pos 041a: 618 */ 0xF5 /* 'u' -> */,
/* pos 041b: 619 */ 0xF4 /* 't' -> */,
/* pos 041c: 620 */ 0xE8 /* 'h' -> */,
/* pos 041d: 621 */ 0xE5 /* 'e' -> */,
/* pos 041e: 622 */ 0xEE /* 'n' -> */,
/* pos 041f: 623 */ 0xF4 /* 't' -> */,
/* pos 0420: 624 */ 0xE9 /* 'i' -> */,
/* pos 0421: 625 */ 0xE3 /* 'c' -> */,
/* pos 0422: 626 */ 0xE1 /* 'a' -> */,
/* pos 0423: 627 */ 0xF4 /* 't' -> */,
/* pos 0424: 628 */ 0xE5 /* 'e' -> */,
/* pos 0425: 629 */ 0xBA /* ':' -> */,
/* pos 0426: 630 */ 0x00, 0x48 /* - terminal marker 72 - */,
/* pos 0428: 631 */ 0xF4 /* 't' -> */,
/* pos 0429: 632 */ 0xE3 /* 'c' -> */,
/* pos 042a: 633 */ 0xE8 /* 'h' -> */,
/* pos 042b: 634 */ 0x00, 0x49 /* - terminal marker 73 - */,
/* pos 042d: 635 */ 0xF4 /* 't' -> */,
/* pos 042e: 636 */ 0x00, 0x4A /* - terminal marker 74 - */,
/* pos 0430: 637 */ 0xEC /* 'l' -> */,
/* pos 0431: 638 */ 0xE5 /* 'e' -> */,
/* pos 0432: 639 */ 0xF4 /* 't' -> */,
/* pos 0433: 640 */ 0xE5 /* 'e' -> */,
/* pos 0434: 641 */ 0x00, 0x4B /* - terminal marker 75 - */,
/* pos 0436: 642 */ 0xE9 /* 'i' -> */,
/* pos 0437: 643 */ 0xAD /* '-' -> */,
/* pos 0438: 644 */ 0xE1 /* 'a' -> */,
/* pos 0439: 645 */ 0xF2 /* 'r' -> */,
/* pos 043a: 646 */ 0xE7 /* 'g' -> */,
/* pos 043b: 647 */ 0xF3 /* 's' -> */,
/* pos 043c: 648 */ 0x00, 0x4C /* - terminal marker 76 - */,
/* pos 043e: 649 */ 0x00, 0x4D /* - terminal marker 77 - */,
/* pos 0440: 650 */ 0xAD /* '-' -> */,
/* pos 0441: 651 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0448 state 652) */,
0x66 /* 'f' */, 0x10, 0x00 /* (to 0x0454 state 662) */,
0x08, /* fail */
/* pos 0448: 652 */ 0xE5 /* 'e' -> */,
/* pos 0449: 653 */ 0xE1 /* 'a' -> */,
/* pos 044a: 654 */ 0xEC /* 'l' -> */,
/* pos 044b: 655 */ 0xAD /* '-' -> */,
/* pos 044c: 656 */ 0xE9 /* 'i' -> */,
/* pos 044d: 657 */ 0xF0 /* 'p' -> */,
/* pos 044e: 658 */ 0xBA /* ':' -> */,
/* pos 044f: 659 */ 0x00, 0x4E /* - terminal marker 78 - */,
/* pos 0451: 660 */ 0xA0 /* ' ' -> */,
/* pos 0452: 661 */ 0x00, 0x4F /* - terminal marker 79 - */,
/* pos 0454: 662 */ 0xEF /* 'o' -> */,
/* pos 0455: 663 */ 0xF2 /* 'r' -> */,
/* pos 0456: 664 */ 0xF7 /* 'w' -> */,
/* pos 0457: 665 */ 0xE1 /* 'a' -> */,
/* pos 0458: 666 */ 0xF2 /* 'r' -> */,
/* pos 0459: 667 */ 0xE4 /* 'd' -> */,
/* pos 045a: 668 */ 0xE5 /* 'e' -> */,
/* pos 045b: 669 */ 0xE4 /* 'd' -> */,
/* pos 045c: 670 */ 0xAD /* '-' -> */,
/* pos 045d: 671 */ 0xE6 /* 'f' -> */,
/* pos 045e: 672 */ 0xEF /* 'o' -> */,
/* pos 045f: 673 */ 0xF2 /* 'r' -> */,
/* pos 0460: 674 */ 0x00, 0x50 /* - terminal marker 80 - */,
/* pos 0462: 675 */ 0x00, 0x51 /* - terminal marker 81 - */,
/* pos 0464: 676 */ 0xE1 /* 'a' -> */,
/* pos 0465: 677 */ 0xE4 /* 'd' -> */,
/* pos 0466: 678 */ 0xA0 /* ' ' -> */,
/* pos 0467: 679 */ 0x00, 0x52 /* - terminal marker 82 - */,
/* pos 0469: 680 */ 0xBA /* ':' -> */,
/* pos 046a: 681 */ 0x00, 0x53 /* - terminal marker 83 - */,
/* total size 1132 bytes */

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,12 @@
#define LWS_INSTALL_DATADIR "/usr/local/share" #define LWS_INSTALL_DATADIR "/usr/local/share"
#define LWS_ROLE_H1
#define LWS_ROLE_WS
#define LWS_ROLE_RAW
/* #undef LWS_ROLE_H2 */
/* #undef LWS_ROLE_CGI */
/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL. /* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL.
* LWS_OPENSSL_SUPPORT needs to be set also for this to work. */ * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
/* #undef USE_WOLFSSL */ /* #undef USE_WOLFSSL */
@ -25,26 +31,26 @@
#define LWS_WITH_MBEDTLS #define LWS_WITH_MBEDTLS
/* #undef LWS_WITH_POLARSSL */ /* #undef LWS_WITH_POLARSSL */
/* #undef LWS_WITH_ESP8266 */
/* #undef LWS_WITH_ESP32 */ /* #undef LWS_WITH_ESP32 */
/* #undef LWS_WITH_PLUGINS */ /* #undef LWS_WITH_PLUGINS */
/* #undef LWS_WITH_NO_LOGS */ /* #undef LWS_WITH_NO_LOGS */
/* The Libwebsocket version */ /* The Libwebsocket version */
#define LWS_LIBRARY_VERSION "2.4.2" #define LWS_LIBRARY_VERSION "3.0.0"
#define LWS_LIBRARY_VERSION_MAJOR 2 #define LWS_LIBRARY_VERSION_MAJOR 3
#define LWS_LIBRARY_VERSION_MINOR 4 #define LWS_LIBRARY_VERSION_MINOR 0
#define LWS_LIBRARY_VERSION_PATCH 2 #define LWS_LIBRARY_VERSION_PATCH 0
/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */ /* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */
#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH #define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH
/* The current git commit hash that we're building from */ /* The current git commit hash that we're building from */
#define LWS_BUILD_HASH "8964ce9db75a98e463dfafd2e89f2bc8a95ec6ed" #define LWS_BUILD_HASH "v2.0.0-948-geaa935a8"
/* Build with OpenSSL support */ /* Build with OpenSSL support ... alias of LWS_WITH_TLS for compatibility*/
#define LWS_OPENSSL_SUPPORT #define LWS_OPENSSL_SUPPORT
#define LWS_WITH_TLS
/* The client should load and trust CA root certs it finds in the OS */ /* The client should load and trust CA root certs it finds in the OS */
/* #undef LWS_SSL_CLIENT_USE_OS_CA_CERTS */ /* #undef LWS_SSL_CLIENT_USE_OS_CA_CERTS */
@ -53,7 +59,13 @@
/* #undef LWS_OPENSSL_CLIENT_CERTS "../share" */ /* #undef LWS_OPENSSL_CLIENT_CERTS "../share" */
/* Turn off websocket extensions */ /* Turn off websocket extensions */
/* #undef LWS_NO_EXTENSIONS */ #define LWS_WITHOUT_EXTENSIONS
/* notice if client or server gone */
/* #undef LWS_WITHOUT_SERVER */
/* #undef LWS_WITHOUT_CLIENT */
#define LWS_WITH_POLL
/* Enable libev io loop */ /* Enable libev io loop */
/* #undef LWS_WITH_LIBEV */ /* #undef LWS_WITH_LIBEV */
@ -99,8 +111,11 @@
/* #undef LWS_HAVE_SSL_CTX_set1_param */ /* #undef LWS_HAVE_SSL_CTX_set1_param */
#define LWS_HAVE_X509_VERIFY_PARAM_set1_host #define LWS_HAVE_X509_VERIFY_PARAM_set1_host
/* #undef LWS_HAVE_RSA_SET0_KEY */ /* #undef LWS_HAVE_RSA_SET0_KEY */
/* #undef LWS_HAVE_X509_get_key_usage */
/* #undef LWS_HAVE_SSL_CTX_get0_certificate */
/* #undef LWS_HAVE_UV_VERSION_H */ /* #undef LWS_HAVE_UV_VERSION_H */
/* #undef LWS_HAVE_PTHREAD_H */
/* CGI apis */ /* CGI apis */
/* #undef LWS_WITH_CGI */ /* #undef LWS_WITH_CGI */
@ -112,7 +127,7 @@
/* #undef LWS_WITH_HTTP_PROXY */ /* #undef LWS_WITH_HTTP_PROXY */
/* HTTP Ranges support */ /* HTTP Ranges support */
#define LWS_WITH_RANGES /* #undef LWS_WITH_RANGES */
/* Http access log support */ /* Http access log support */
/* #undef LWS_WITH_ACCESS_LOG */ /* #undef LWS_WITH_ACCESS_LOG */
@ -134,7 +149,7 @@
/* #undef LWS_PLAT_OPTEE */ /* #undef LWS_PLAT_OPTEE */
/* ZIP FOPS */ /* ZIP FOPS */
#define LWS_WITH_ZIP_FOPS /* #undef LWS_WITH_ZIP_FOPS */
#define LWS_HAVE_STDINT_H #define LWS_HAVE_STDINT_H
/* #undef LWS_AVOID_SIGPIPE_IGN */ /* #undef LWS_AVOID_SIGPIPE_IGN */
@ -151,11 +166,26 @@
/* #undef LWS_HAVE__ATOI64 */ /* #undef LWS_HAVE__ATOI64 */
/* #undef LWS_HAVE__STAT32I64 */ /* #undef LWS_HAVE__STAT32I64 */
/* #undef LWS_WITH_JWS */
/* #undef LWS_WITH_ACME */
/* #undef LWS_WITH_SELFTESTS */
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
#define LWS_HAVE_MALLOC_H
#endif
#if !defined(IPHONE_ENABLED) && !defined(OSX_ENABLED)
#define LWS_HAVE_PIPE2
#endif
/* OpenSSL various APIs */ /* OpenSSL various APIs */
#define LWS_HAVE_TLS_CLIENT_METHOD #define LWS_HAVE_TLS_CLIENT_METHOD
/* #undef LWS_HAVE_TLSV1_2_CLIENT_METHOD */ /* #undef LWS_HAVE_TLSV1_2_CLIENT_METHOD */
/* #undef LWS_HAVE_SSL_SET_INFO_CALLBACK */ /* #undef LWS_HAVE_SSL_SET_INFO_CALLBACK */
/* #undef LWS_HAVE_SSL_EXTRA_CHAIN_CERTS */
/* #undef LWS_HAVE_SSL_get0_alpn_selected */
/* #undef LWS_HAVE_SSL_set_alpn_protos */
#define LWS_HAS_INTPTR_T #define LWS_HAS_INTPTR_T

View File

@ -100,6 +100,8 @@
/* Define to 1 if you have the <unistd.h> header file. */ /* Define to 1 if you have the <unistd.h> header file. */
#define LWS_HAVE_UNISTD_H #define LWS_HAVE_UNISTD_H
#define LWS_HAVE_TCP_USER_TIMEOUT
/* Define to 1 if you have the `vfork' function. */ /* Define to 1 if you have the `vfork' function. */
#define LWS_HAVE_VFORK #define LWS_HAVE_VFORK

View File

@ -1,74 +0,0 @@
diff --git a/thirdparty/libwebsockets/client/ssl-client.c b/thirdparty/libwebsockets/client/ssl-client.c
index 6626e0844..962c6e3cb 100644
--- a/thirdparty/libwebsockets/client/ssl-client.c
+++ b/thirdparty/libwebsockets/client/ssl-client.c
@@ -176,11 +176,7 @@ lws_ssl_client_bio_create(struct lws *wsi)
#endif
#else
#if defined(LWS_WITH_MBEDTLS)
- if (wsi->vhost->x509_client_CA)
- SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
- else
- SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback);
-
+ SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
#else
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
SSL_set_tlsext_host_name(wsi->ssl, hostname);
diff --git a/thirdparty/libwebsockets/mbedtls_wrapper/platform/ssl_pm.c b/thirdparty/libwebsockets/mbedtls_wrapper/platform/ssl_pm.c
index 63504919c..4e3d61109 100644
--- a/thirdparty/libwebsockets/mbedtls_wrapper/platform/ssl_pm.c
+++ b/thirdparty/libwebsockets/mbedtls_wrapper/platform/ssl_pm.c
@@ -218,7 +218,7 @@ static int ssl_pm_reload_crt(SSL *ssl)
struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm;
if (ssl->verify_mode == SSL_VERIFY_PEER)
- mode = MBEDTLS_SSL_VERIFY_REQUIRED;
+ mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE)
@@ -712,11 +712,39 @@ long ssl_pm_get_verify_result(const SSL *ssl)
struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl);
- if (ret) {
- SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return 0x%x", ret);
+
+ if (!ret)
+ return X509_V_OK;
+
+ if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED ||
+ (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED))
+ // Allows us to use LCCSCF_ALLOW_SELFSIGNED to skip verification
+ verify_result = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+
+ else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
+ verify_result = X509_V_ERR_HOSTNAME_MISMATCH;
+
+ else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) ||
+ (ret & MBEDTLS_X509_BADCRL_BAD_KEY))
+ verify_result = X509_V_ERR_CA_KEY_TOO_SMALL;
+
+ else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) ||
+ (ret & MBEDTLS_X509_BADCRL_BAD_MD))
+ verify_result = X509_V_ERR_CA_MD_TOO_WEAK;
+
+ else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) ||
+ (ret & MBEDTLS_X509_BADCRL_FUTURE))
+ verify_result = X509_V_ERR_CERT_NOT_YET_VALID;
+
+ else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) ||
+ (ret & MBEDTLS_X509_BADCRL_EXPIRED))
+ verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
+
+ else
verify_result = X509_V_ERR_UNSPECIFIED;
- } else
- verify_result = X509_V_OK;
+
+ SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL,
+ "mbedtls_ssl_get_verify_result() return 0x%x", ret);
return verify_result;
}

View File

@ -40,15 +40,18 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "private-libwebsockets.h" #include "core/private.h"
static const char encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" static const char encode_orig[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789+/"; "abcdefghijklmnopqrstuvwxyz0123456789+/";
static const char encode_url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789-_";
static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW" static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW"
"$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; "$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
LWS_VISIBLE int static int
lws_b64_encode_string(const char *in, int in_len, char *out, int out_size) _lws_b64_encode_string(const char *encode, const char *in, int in_len,
char *out, int out_size)
{ {
unsigned char triple[3]; unsigned char triple[3];
int i; int i;
@ -89,26 +92,47 @@ lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
return done; return done;
} }
LWS_VISIBLE int
lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
{
return _lws_b64_encode_string(encode_orig, in, in_len, out, out_size);
}
LWS_VISIBLE int
lws_b64_encode_string_url(const char *in, int in_len, char *out, int out_size)
{
return _lws_b64_encode_string(encode_url, in, in_len, out, out_size);
}
/* /*
* returns length of decoded string in out, or -1 if out was too small * returns length of decoded string in out, or -1 if out was too small
* according to out_size * according to out_size
*
* Only reads up to in_len chars, otherwise if in_len is -1 on entry reads until
* the first NUL in the input.
*/ */
LWS_VISIBLE int static int
lws_b64_decode_string(const char *in, char *out, int out_size) _lws_b64_decode_string(const char *in, int in_len, char *out, int out_size)
{ {
int len, i, c = 0, done = 0; int len, i, c = 0, done = 0;
unsigned char v, quad[4]; unsigned char v, quad[4];
while (*in) { while (in_len && *in) {
len = 0; len = 0;
for (i = 0; i < 4 && *in; i++) { for (i = 0; i < 4 && in_len && *in; i++) {
v = 0; v = 0;
c = 0; c = 0;
while (*in && !v) { while (in_len && *in && !v) {
c = v = *in++; c = v = *in++;
in_len--;
/* support the url base64 variant too */
if (v == '-')
c = v = '+';
if (v == '_')
c = v = '/';
v = (v < 43 || v > 122) ? 0 : decode[v - 43]; v = (v < 43 || v > 122) ? 0 : decode[v - 43];
if (v) if (v)
v = (v == '$') ? 0 : v - 61; v = (v == '$') ? 0 : v - 61;
@ -131,7 +155,7 @@ lws_b64_decode_string(const char *in, char *out, int out_size)
* bytes." (wikipedia) * bytes." (wikipedia)
*/ */
if (!*in && c == '=') if ((!in_len || !*in) && c == '=')
len--; len--;
if (len >= 2) if (len >= 2)
@ -152,6 +176,18 @@ lws_b64_decode_string(const char *in, char *out, int out_size)
return done; return done;
} }
LWS_VISIBLE int
lws_b64_decode_string(const char *in, char *out, int out_size)
{
return _lws_b64_decode_string(in, -1, out, out_size);
}
LWS_VISIBLE int
lws_b64_decode_string_len(const char *in, int in_len, char *out, int out_size)
{
return _lws_b64_decode_string(in, in_len, out, out_size);
}
#if 0 #if 0
int int
lws_b64_selftest(void) lws_b64_selftest(void)

View File

@ -43,7 +43,7 @@
#include <string.h> #include <string.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#include "private-libwebsockets.h" #include "core/private.h"
#ifdef LWS_HAVE_SYS_SOCKIO_H #ifdef LWS_HAVE_SYS_SOCKIO_H
#include <sys/sockio.h> #include <sys/sockio.h>

View File

@ -1,14 +1,26 @@
/* /*
* Lightweight Embedded JSON Parser * Lightweight Embedded JSON Parser
* *
* Copyright (C) 2013 Andy Green <andy@warmcat.com> * Copyright (C) 2013-2017 Andy Green <andy@warmcat.com>
* This code is licensed under LGPL 2.1 *
* http://www.gnu.org/licenses/lgpl-2.1.html * 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 <libwebsockets.h>
#include <string.h> #include <string.h>
#include "lejp.h"
#include <stdio.h> #include <stdio.h>
/** /**
@ -148,7 +160,8 @@ lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len)
n = ctx->wild[wildcard]; n = ctx->wild[wildcard];
while (--len && n < ctx->ppos && (n == ctx->wild[wildcard] || ctx->path[n] != '.')) while (--len && n < ctx->ppos &&
(n == ctx->wild[wildcard] || ctx->path[n] != '.'))
*dest++ = ctx->path[n++]; *dest++ = ctx->path[n++];
*dest = '\0'; *dest = '\0';
@ -186,7 +199,6 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
while (len--) { while (len--) {
c = *json++; c = *json++;
s = ctx->st[ctx->sp].s; s = ctx->st[ctx->sp].s;
/* skip whitespace unless we should care */ /* skip whitespace unless we should care */
@ -411,6 +423,26 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
} }
goto add_stack_level; goto add_stack_level;
case ']':
/* pop */
ctx->sp--;
if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) {
ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY;
goto reject;
}
/* drop the path [n] bit */
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
ctx->path[ctx->ppos] = '\0';
if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len)
/*
* we shrank the path to be
* smaller than the matching point
*/
ctx->path_match = 0;
goto array_end;
case 't': /* true */ case 't': /* true */
ctx->uni = 0; ctx->uni = 0;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
@ -582,8 +614,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
goto reject; goto reject;
} }
/* drop the path [n] bit */ /* drop the path [n] bit */
ctx->ppos = ctx->st[ctx->sp - 1].p; if (ctx->sp) {
ctx->ipos = ctx->st[ctx->sp - 1].i; ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
}
ctx->path[ctx->ppos] = '\0'; ctx->path[ctx->ppos] = '\0';
if (ctx->path_match && if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len) ctx->ppos <= ctx->path_match_len)
@ -609,8 +643,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
} }
/* pop */ /* pop */
ctx->sp--; ctx->sp--;
ctx->ppos = ctx->st[ctx->sp - 1].p; if (ctx->sp) {
ctx->ipos = ctx->st[ctx->sp - 1].i; ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
}
ctx->path[ctx->ppos] = '\0'; ctx->path[ctx->ppos] = '\0';
if (ctx->path_match && if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len) ctx->ppos <= ctx->path_match_len)
@ -631,6 +667,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
goto reject; goto reject;
case LEJP_MP_ARRAY_END: case LEJP_MP_ARRAY_END:
array_end:
ctx->path[ctx->ppos] = '\0'; ctx->path[ctx->ppos] = '\0';
if (c == ',') { if (c == ',') {
/* increment this stack level's index */ /* increment this stack level's index */

View File

@ -1,232 +0,0 @@
#include "libwebsockets.h"
struct lejp_ctx;
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0]))
#endif
#define LEJP_FLAG_WS_KEEP 64
#define LEJP_FLAG_WS_COMMENTLINE 32
enum lejp_states {
LEJP_IDLE = 0,
LEJP_MEMBERS = 1,
LEJP_M_P = 2,
LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3,
LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4,
LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5,
LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6,
LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7,
LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8,
LEJP_MP_DELIM = 9,
LEJP_MP_VALUE = 10,
LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11,
LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12,
LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13,
LEJP_MP_COMMA_OR_END = 14,
LEJP_MP_ARRAY_END = 15,
};
enum lejp_reasons {
LEJP_CONTINUE = -1,
LEJP_REJECT_IDLE_NO_BRACE = -2,
LEJP_REJECT_MEMBERS_NO_CLOSE = -3,
LEJP_REJECT_MP_NO_OPEN_QUOTE = -4,
LEJP_REJECT_MP_STRING_UNDERRUN = -5,
LEJP_REJECT_MP_ILLEGAL_CTRL = -6,
LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7,
LEJP_REJECT_ILLEGAL_HEX = -8,
LEJP_REJECT_MP_DELIM_MISSING_COLON = -9,
LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10,
LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11,
LEJP_REJECT_MP_VAL_NUM_FORMAT = -12,
LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13,
LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14,
LEJP_REJECT_MP_C_OR_E_UNDERF = -15,
LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16,
LEJP_REJECT_MP_ARRAY_END_MISSING = -17,
LEJP_REJECT_STACK_OVERFLOW = -18,
LEJP_REJECT_MP_DELIM_ISTACK = -19,
LEJP_REJECT_NUM_TOO_LONG = -20,
LEJP_REJECT_MP_C_OR_E_NEITHER = -21,
LEJP_REJECT_UNKNOWN = -22,
LEJP_REJECT_CALLBACK = -23
};
#define LEJP_FLAG_CB_IS_VALUE 64
enum lejp_callbacks {
LEJPCB_CONSTRUCTED = 0,
LEJPCB_DESTRUCTED = 1,
LEJPCB_START = 2,
LEJPCB_COMPLETE = 3,
LEJPCB_FAILED = 4,
LEJPCB_PAIR_NAME = 5,
LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6,
LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7,
LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8,
LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9,
LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10,
LEJPCB_VAL_STR_START = 11, /* notice handle separately */
LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12,
LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13,
LEJPCB_ARRAY_START = 14,
LEJPCB_ARRAY_END = 15,
LEJPCB_OBJECT_START = 16,
LEJPCB_OBJECT_END = 17
};
/**
* _lejp_callback() - User parser actions
* \param ctx: LEJP context
* \param reason: Callback reason
*
* Your user callback is associated with the context at construction time,
* and receives calls as the parsing progresses.
*
* All of the callbacks may be ignored and just return 0.
*
* The reasons it might get called, found in @reason, are:
*
* LEJPCB_CONSTRUCTED: The context was just constructed... you might want to
* perform one-time allocation for the life of the context.
*
* LEJPCB_DESTRUCTED: The context is being destructed... if you made any
* allocations at construction-time, you can free them now
*
* LEJPCB_START: Parsing is beginning at the first byte of input
*
* LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or
* positive return code from lejp_parse indicating the
* amount of unused bytes left in the input buffer
*
* LEJPCB_FAILED: Parsing failed. You'll get a negative error code
* returned from lejp_parse
*
* LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed,
* this callback occurs. You can find the new name at
* the end of ctx->path[]
*
* LEJPCB_VAL_TRUE: The "true" value appeared
*
* LEJPCB_VAL_FALSE: The "false" value appeared
*
* LEJPCB_VAL_NULL: The "null" value appeared
*
* LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf
*
* LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf
*
* LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet
*
* LEJPCB_VAL_STR_CHUNK: We parsed LEJP_STRING_CHUNK -1 bytes of string data in
* ctx->buf, which is as much as we can buffer, so we are
* spilling it. If all your strings are less than
* LEJP_STRING_CHUNK - 1 bytes, you will never see this
* callback.
*
* LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the
* string is in ctx->buf.
*
* LEJPCB_ARRAY_START: An array started
*
* LEJPCB_ARRAY_END: An array ended
*
* LEJPCB_OBJECT_START: An object started
*
* LEJPCB_OBJECT_END: An object ended
*/
LWS_EXTERN signed char _lejp_callback(struct lejp_ctx *ctx, char reason);
typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason);
#ifndef LEJP_MAX_DEPTH
#define LEJP_MAX_DEPTH 12
#endif
#ifndef LEJP_MAX_INDEX_DEPTH
#define LEJP_MAX_INDEX_DEPTH 5
#endif
#ifndef LEJP_MAX_PATH
#define LEJP_MAX_PATH 128
#endif
#ifndef LEJP_STRING_CHUNK
/* must be >= 30 to assemble floats */
#define LEJP_STRING_CHUNK 255
#endif
enum num_flags {
LEJP_SEEN_MINUS = (1 << 0),
LEJP_SEEN_POINT = (1 << 1),
LEJP_SEEN_POST_POINT = (1 << 2),
LEJP_SEEN_EXP = (1 << 3)
};
struct _lejp_stack {
char s; /* lejp_state stack*/
char p; /* path length */
char i; /* index array length */
char b; /* user bitfield */
};
struct lejp_ctx {
/* sorted by type for most compact alignment
*
* pointers
*/
signed char (*callback)(struct lejp_ctx *ctx, char reason);
void *user;
const char * const *paths;
/* arrays */
struct _lejp_stack st[LEJP_MAX_DEPTH];
unsigned short i[LEJP_MAX_INDEX_DEPTH]; /* index array */
unsigned short wild[LEJP_MAX_INDEX_DEPTH]; /* index array */
char path[LEJP_MAX_PATH];
char buf[LEJP_STRING_CHUNK];
/* int */
unsigned int line;
/* short */
unsigned short uni;
/* char */
unsigned char npos;
unsigned char dcount;
unsigned char f;
unsigned char sp; /* stack head */
unsigned char ipos; /* index stack depth */
unsigned char ppos;
unsigned char count_paths;
unsigned char path_match;
unsigned char path_match_len;
unsigned char wildcount;
};
LWS_VISIBLE LWS_EXTERN void
lejp_construct(struct lejp_ctx *ctx,
signed char (*callback)(struct lejp_ctx *ctx, char reason),
void *user, const char * const *paths, unsigned char paths_count);
LWS_VISIBLE LWS_EXTERN void
lejp_destruct(struct lejp_ctx *ctx);
LWS_VISIBLE LWS_EXTERN int
lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len);
LWS_VISIBLE LWS_EXTERN void
lejp_change_callback(struct lejp_ctx *ctx,
signed char (*callback)(struct lejp_ctx *ctx, char reason));
LWS_VISIBLE LWS_EXTERN int
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);

View File

@ -32,7 +32,7 @@
* implemented by Jun-ichiro itojun Itoh <itojun@itojun.org> * implemented by Jun-ichiro itojun Itoh <itojun@itojun.org>
*/ */
#include "private-libwebsockets.h" #include "core/private.h"
#ifdef LWS_HAVE_SYS_TYPES_H #ifdef LWS_HAVE_SYS_TYPES_H
#include <sys/types.h> #include <sys/types.h>

View File

@ -1,883 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 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 "private-libwebsockets.h"
static int
lws_0405_frame_mask_generate(struct lws *wsi)
{
#if 0
wsi->u.ws.mask[0] = 0;
wsi->u.ws.mask[1] = 0;
wsi->u.ws.mask[2] = 0;
wsi->u.ws.mask[3] = 0;
#else
int n;
/* fetch the per-frame nonce */
n = lws_get_random(lws_get_context(wsi), wsi->u.ws.mask, 4);
if (n != 4) {
lwsl_parser("Unable to read from random device %s %d\n",
SYSTEM_RANDOM_FILEPATH, n);
return 1;
}
#endif
/* start masking from first byte of masking key buffer */
wsi->u.ws.mask_idx = 0;
return 0;
}
/*
* notice this returns number of bytes consumed, or -1
*/
int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
size_t real_len = len;
unsigned int n;
int m;
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1);
if (!len)
return 0;
/* just ignore sends after we cleared the truncation buffer */
if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
!wsi->trunc_len)
return len;
if (wsi->trunc_len && (buf < wsi->trunc_alloc ||
buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) {
char dump[20];
strncpy(dump, (char *)buf, sizeof(dump) - 1);
dump[sizeof(dump) - 1] = '\0';
#if defined(LWS_WITH_ESP8266)
lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n",
wsi, (unsigned long)len, dump);
#else
lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n"
" It's illegal to do an lws_write outside of\n"
" the writable callback: fix your code\n",
wsi, (unsigned long)len, dump);
#endif
assert(0);
return -1;
}
m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_DO_SEND, &buf, len);
if (m < 0)
return -1;
if (m) /* handled */ {
n = m;
goto handle_truncated_send;
}
if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd))
lwsl_warn("** error invalid sock but expected to send\n");
/* limit sending */
if (wsi->protocol->tx_packet_size)
n = wsi->protocol->tx_packet_size;
else {
n = wsi->protocol->rx_buffer_size;
if (!n)
n = context->pt_serv_buf_size;
}
n += LWS_PRE + 4;
if (n > len)
n = len;
#if defined(LWS_WITH_ESP8266)
if (wsi->pending_send_completion) {
n = 0;
goto handle_truncated_send;
}
#endif
/* nope, send it on the socket directly */
lws_latency_pre(context, wsi);
n = lws_ssl_capable_write(wsi, buf, n);
lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
switch (n) {
case LWS_SSL_CAPABLE_ERROR:
/* we're going to close, let close know sends aren't possible */
wsi->socket_is_permanently_unusable = 1;
return -1;
case LWS_SSL_CAPABLE_MORE_SERVICE:
/* nothing got sent, not fatal, retry the whole thing later */
n = 0;
break;
}
handle_truncated_send:
/*
* we were already handling a truncated send?
*/
if (wsi->trunc_len) {
lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len);
wsi->trunc_offset += n;
wsi->trunc_len -= n;
if (!wsi->trunc_len) {
lwsl_info("***** %p partial send completed\n", wsi);
/* done with it, but don't free it */
n = real_len;
if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
lwsl_info("***** %p signalling to close now\n", wsi);
return -1; /* retry closing now */
}
}
/* always callback on writeable */
lws_callback_on_writable(wsi);
return n;
}
if ((unsigned int)n == real_len)
/* what we just sent went out cleanly */
return n;
/*
* Newly truncated send. Buffer the remainder (it will get
* first priority next time the socket is writable)
*/
lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n,
(unsigned long)real_len);
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1);
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n);
/*
* - if we still have a suitable malloc lying around, use it
* - or, if too small, reallocate it
* - or, if no buffer, create it
*/
if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) {
lws_free(wsi->trunc_alloc);
wsi->trunc_alloc_len = real_len - n;
wsi->trunc_alloc = lws_malloc(real_len - n, "truncated send alloc");
if (!wsi->trunc_alloc) {
lwsl_err("truncated send: unable to malloc %lu\n",
(unsigned long)(real_len - n));
return -1;
}
}
wsi->trunc_offset = 0;
wsi->trunc_len = real_len - n;
memcpy(wsi->trunc_alloc, buf + n, real_len - n);
/* since something buffered, force it to get another chance to send */
lws_callback_on_writable(wsi);
return real_len;
}
LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
enum lws_write_protocol wp)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int masked7 = (wsi->mode == LWSCM_WS_CLIENT);
unsigned char is_masked_bit = 0;
unsigned char *dropmask = NULL;
struct lws_tokens eff_buf;
size_t orig_len = len;
int pre = 0, n;
if (wsi->parent_carries_io) {
struct lws_write_passthru pas;
pas.buf = buf;
pas.len = len;
pas.wp = wp;
pas.wsi = wsi;
if (wsi->parent->protocol->callback(wsi->parent,
LWS_CALLBACK_CHILD_WRITE_VIA_PARENT,
wsi->parent->user_space,
(void *)&pas, 0))
return 1;
return len;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1);
if ((int)len < 0) {
lwsl_err("%s: suspicious len int %d, ulong %lu\n", __func__,
(int)len, (unsigned long)len);
return -1;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_WRITE, len);
#ifdef LWS_WITH_ACCESS_LOG
wsi->access_log.sent += len;
#endif
if (wsi->vhost)
wsi->vhost->conn_stats.tx += len;
if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) {
/* remove us from the list */
struct lws **w = &pt->tx_draining_ext_list;
wsi->u.ws.tx_draining_ext = 0;
/* remove us from context draining ext list */
while (*w) {
if (*w == wsi) {
*w = wsi->u.ws.tx_draining_ext_list;
break;
}
w = &((*w)->u.ws.tx_draining_ext_list);
}
wsi->u.ws.tx_draining_ext_list = NULL;
wp = (wsi->u.ws.tx_draining_stashed_wp & 0xc0) |
LWS_WRITE_CONTINUATION;
lwsl_ext("FORCED draining wp to 0x%02X\n", wp);
}
lws_restart_ws_ping_pong_timer(wsi);
if ((wp & 0x1f) == LWS_WRITE_HTTP ||
(wp & 0x1f) == LWS_WRITE_HTTP_FINAL ||
(wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION ||
(wp & 0x1f) == LWS_WRITE_HTTP_HEADERS)
goto send_raw;
/* if not in a state to send stuff, then just send nothing */
if (wsi->state != LWSS_ESTABLISHED &&
((wsi->state != LWSS_RETURNED_CLOSE_ALREADY &&
wsi->state != LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION &&
wsi->state != LWSS_AWAITING_CLOSE_ACK) ||
wp != LWS_WRITE_CLOSE)) {
lwsl_debug("binning\n");
return 0;
}
/* if we are continuing a frame that already had its header done */
if (wsi->u.ws.inside_frame) {
lwsl_debug("INSIDE FRAME\n");
goto do_more_inside_frame;
}
wsi->u.ws.clean_buffer = 1;
/*
* give a chance to the extensions to modify payload
* the extension may decide to produce unlimited payload erratically
* (eg, compression extension), so we require only that if he produces
* something, it will be a complete fragment of the length known at
* the time (just the fragment length known), and if he has
* more we will come back next time he is writeable and allow him to
* produce more fragments until he's drained.
*
* This allows what is sent each time it is writeable to be limited to
* a size that can be sent without partial sends or blocking, allows
* interleaving of control frames and other connection service.
*/
eff_buf.token = (char *)buf;
eff_buf.token_len = len;
switch ((int)wp) {
case LWS_WRITE_PING:
case LWS_WRITE_PONG:
case LWS_WRITE_CLOSE:
break;
default:
lwsl_debug("LWS_EXT_CB_PAYLOAD_TX\n");
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &eff_buf, wp);
if (n < 0)
return -1;
if (n && eff_buf.token_len) {
lwsl_debug("drain len %d\n", (int)eff_buf.token_len);
/* extension requires further draining */
wsi->u.ws.tx_draining_ext = 1;
wsi->u.ws.tx_draining_ext_list = pt->tx_draining_ext_list;
pt->tx_draining_ext_list = wsi;
/* we must come back to do more */
lws_callback_on_writable(wsi);
/*
* keep a copy of the write type for the overall
* action that has provoked generation of these
* fragments, so the last guy can use its FIN state.
*/
wsi->u.ws.tx_draining_stashed_wp = wp;
/* this is definitely not actually the last fragment
* because the extension asserted he has more coming
* So make sure this intermediate one doesn't go out
* with a FIN.
*/
wp |= LWS_WRITE_NO_FIN;
}
if (eff_buf.token_len && wsi->u.ws.stashed_write_pending) {
wsi->u.ws.stashed_write_pending = 0;
wp = (wp &0xc0) | (int)wsi->u.ws.stashed_write_type;
}
}
/*
* an extension did something we need to keep... for example, if
* compression extension, it has already updated its state according
* to this being issued
*/
if ((char *)buf != eff_buf.token) {
/*
* ext might eat it, but not have anything to issue yet.
* In that case we have to follow his lead, but stash and
* replace the write type that was lost here the first time.
*/
if (len && !eff_buf.token_len) {
if (!wsi->u.ws.stashed_write_pending)
wsi->u.ws.stashed_write_type = (char)wp & 0x3f;
wsi->u.ws.stashed_write_pending = 1;
return len;
}
/*
* extension recreated it:
* need to buffer this if not all sent
*/
wsi->u.ws.clean_buffer = 0;
}
buf = (unsigned char *)eff_buf.token;
len = eff_buf.token_len;
if (!buf) {
lwsl_err("null buf (%d)\n", (int)len);
return -1;
}
switch (wsi->ietf_spec_revision) {
case 13:
if (masked7) {
pre += 4;
dropmask = &buf[0 - pre];
is_masked_bit = 0x80;
}
switch (wp & 0xf) {
case LWS_WRITE_TEXT:
n = LWSWSOPC_TEXT_FRAME;
break;
case LWS_WRITE_BINARY:
n = LWSWSOPC_BINARY_FRAME;
break;
case LWS_WRITE_CONTINUATION:
n = LWSWSOPC_CONTINUATION;
break;
case LWS_WRITE_CLOSE:
n = LWSWSOPC_CLOSE;
break;
case LWS_WRITE_PING:
n = LWSWSOPC_PING;
break;
case LWS_WRITE_PONG:
n = LWSWSOPC_PONG;
break;
default:
lwsl_warn("lws_write: unknown write opc / wp\n");
return -1;
}
if (!(wp & LWS_WRITE_NO_FIN))
n |= 1 << 7;
if (len < 126) {
pre += 2;
buf[-pre] = n;
buf[-pre + 1] = (unsigned char)(len | is_masked_bit);
} else {
if (len < 65536) {
pre += 4;
buf[-pre] = n;
buf[-pre + 1] = 126 | is_masked_bit;
buf[-pre + 2] = (unsigned char)(len >> 8);
buf[-pre + 3] = (unsigned char)len;
} else {
pre += 10;
buf[-pre] = n;
buf[-pre + 1] = 127 | is_masked_bit;
#if defined __LP64__
buf[-pre + 2] = (len >> 56) & 0x7f;
buf[-pre + 3] = len >> 48;
buf[-pre + 4] = len >> 40;
buf[-pre + 5] = len >> 32;
#else
buf[-pre + 2] = 0;
buf[-pre + 3] = 0;
buf[-pre + 4] = 0;
buf[-pre + 5] = 0;
#endif
buf[-pre + 6] = (unsigned char)(len >> 24);
buf[-pre + 7] = (unsigned char)(len >> 16);
buf[-pre + 8] = (unsigned char)(len >> 8);
buf[-pre + 9] = (unsigned char)len;
}
}
break;
}
do_more_inside_frame:
/*
* Deal with masking if we are in client -> server direction and
* the wp demands it
*/
if (masked7) {
if (!wsi->u.ws.inside_frame)
if (lws_0405_frame_mask_generate(wsi)) {
lwsl_err("frame mask generation failed\n");
return -1;
}
/*
* in v7, just mask the payload
*/
if (dropmask) { /* never set if already inside frame */
for (n = 4; n < (int)len + 4; n++)
dropmask[n] = dropmask[n] ^ wsi->u.ws.mask[
(wsi->u.ws.mask_idx++) & 3];
/* copy the frame nonce into place */
memcpy(dropmask, wsi->u.ws.mask, 4);
}
}
send_raw:
switch ((int)(wp & 0x1f)) {
case LWS_WRITE_CLOSE:
/* lwsl_hexdump(&buf[-pre], len); */
case LWS_WRITE_HTTP:
case LWS_WRITE_HTTP_FINAL:
case LWS_WRITE_HTTP_HEADERS:
case LWS_WRITE_HTTP_HEADERS_CONTINUATION:
case LWS_WRITE_PONG:
case LWS_WRITE_PING:
#ifdef LWS_WITH_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING) {
unsigned char flags = 0;
n = LWS_H2_FRAME_TYPE_DATA;
if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS) {
n = LWS_H2_FRAME_TYPE_HEADERS;
if (!(wp & LWS_WRITE_NO_FIN))
flags = LWS_H2_FLAG_END_HEADERS;
if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) {
flags |= LWS_H2_FLAG_END_STREAM;
wsi->u.h2.send_END_STREAM = 1;
}
}
if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION) {
n = LWS_H2_FRAME_TYPE_CONTINUATION;
if (!(wp & LWS_WRITE_NO_FIN))
flags = LWS_H2_FLAG_END_HEADERS;
if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) {
flags |= LWS_H2_FLAG_END_STREAM;
wsi->u.h2.send_END_STREAM = 1;
}
}
if (((wp & 0x1f) == LWS_WRITE_HTTP ||
(wp & 0x1f) == LWS_WRITE_HTTP_FINAL) &&
wsi->u.http.tx_content_length) {
wsi->u.http.tx_content_remain -= len;
lwsl_info("%s: wsi %p: tx_content_remain = %llu\n", __func__, wsi,
(unsigned long long)wsi->u.http.tx_content_remain);
if (!wsi->u.http.tx_content_remain) {
lwsl_info("%s: selecting final write mode\n", __func__);
wp = LWS_WRITE_HTTP_FINAL;
}
}
if ((wp & 0x1f) == LWS_WRITE_HTTP_FINAL || (wp & LWS_WRITE_H2_STREAM_END)) {
//lws_get_network_wsi(wsi)->u.h2.END_STREAM) {
lwsl_info("%s: setting END_STREAM\n", __func__);
flags |= LWS_H2_FLAG_END_STREAM;
wsi->u.h2.send_END_STREAM = 1;
}
return lws_h2_frame_write(wsi, n, flags,
wsi->u.h2.my_sid, len, buf);
}
#endif
return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre);
default:
break;
}
/*
* give any active extensions a chance to munge the buffer
* before send. We pass in a pointer to an lws_tokens struct
* prepared with the default buffer and content length that's in
* there. Rather than rewrite the default buffer, extensions
* that expect to grow the buffer can adapt .token to
* point to their own per-connection buffer in the extension
* user allocation. By default with no extensions or no
* extension callback handling, just the normal input buffer is
* used then so it is efficient.
*
* callback returns 1 in case it wants to spill more buffers
*
* This takes care of holding the buffer if send is incomplete, ie,
* if wsi->u.ws.clean_buffer is 0 (meaning an extension meddled with
* the buffer). If wsi->u.ws.clean_buffer is 1, it will instead
* return to the user code how much OF THE USER BUFFER was consumed.
*/
n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre);
wsi->u.ws.inside_frame = 1;
if (n <= 0)
return n;
if (n == (int)len + pre) {
/* everything in the buffer was handled (or rebuffered...) */
wsi->u.ws.inside_frame = 0;
return orig_len;
}
/*
* it is how many bytes of user buffer got sent... may be < orig_len
* in which case callback when writable has already been arranged
* and user code can call lws_write() again with the rest
* later.
*/
return n - pre;
}
LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws_process_html_args args;
lws_filepos_t amount, poss;
unsigned char *p, *pstart;
#if defined(LWS_WITH_RANGES)
unsigned char finished = 0;
#endif
int n, m;
lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream);
while (!lws_send_pipe_choked(wsi)) {
if (wsi->trunc_len) {
if (lws_issue_raw(wsi, wsi->trunc_alloc +
wsi->trunc_offset,
wsi->trunc_len) < 0) {
lwsl_info("%s: closing\n", __func__);
goto file_had_it;
}
continue;
}
if (wsi->u.http.filepos == wsi->u.http.filelen)
goto all_sent;
n = 0;
pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
p = pstart;
#if defined(LWS_WITH_RANGES)
if (wsi->u.http.range.count_ranges && !wsi->u.http.range.inside) {
lwsl_notice("%s: doing range start %llu\n", __func__, wsi->u.http.range.start);
if ((long long)lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
wsi->u.http.range.start -
wsi->u.http.filepos) < 0)
goto file_had_it;
wsi->u.http.filepos = wsi->u.http.range.start;
if (wsi->u.http.range.count_ranges > 1) {
n = lws_snprintf((char *)p, context->pt_serv_buf_size - LWS_H2_FRAME_HEADER_LENGTH,
"_lws\x0d\x0a"
"Content-Type: %s\x0d\x0a"
"Content-Range: bytes %llu-%llu/%llu\x0d\x0a"
"\x0d\x0a",
wsi->u.http.multipart_content_type,
wsi->u.http.range.start,
wsi->u.http.range.end,
wsi->u.http.range.extent);
p += n;
}
wsi->u.http.range.budget = wsi->u.http.range.end -
wsi->u.http.range.start + 1;
wsi->u.http.range.inside = 1;
}
#endif
poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH;
if (poss > wsi->u.http.tx_content_remain)
poss = wsi->u.http.tx_content_remain;
/*
* if there is a hint about how much we will do well to send at one time,
* restrict ourselves to only trying to send that.
*/
if (wsi->protocol->tx_packet_size &&
poss > wsi->protocol->tx_packet_size)
poss = wsi->protocol->tx_packet_size;
#if defined(LWS_WITH_HTTP2)
m = lws_h2_tx_cr_get(wsi);
if (!m) {
lwsl_info("%s: came here with no tx credit", __func__);
return 0;
}
if (m < poss)
poss = m;
/*
* consumption of the actual payload amount sent will be handled
* when the http2 data frame is sent
*/
#endif
#if defined(LWS_WITH_RANGES)
if (wsi->u.http.range.count_ranges) {
if (wsi->u.http.range.count_ranges > 1)
poss -= 7; /* allow for final boundary */
if (poss > wsi->u.http.range.budget)
poss = wsi->u.http.range.budget;
}
#endif
if (wsi->sending_chunked) {
/* we need to drop the chunk size in here */
p += 10;
/* allow for the chunk to grow by 128 in translation */
poss -= 10 + 128;
}
if (lws_vfs_file_read(wsi->u.http.fop_fd, &amount, p, poss) < 0)
goto file_had_it; /* caller will close */
if (wsi->sending_chunked)
n = (int)amount;
else
n = (p - pstart) + (int)amount;
lwsl_debug("%s: sending %d\n", __func__, n);
if (n) {
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
context->timeout_secs);
if (wsi->sending_chunked) {
args.p = (char *)p;
args.len = n;
args.max_len = (unsigned int)poss + 128;
args.final = wsi->u.http.filepos + n ==
wsi->u.http.filelen;
if (user_callback_handle_rxflow(
wsi->vhost->protocols[(int)wsi->protocol_interpret_idx].callback, wsi,
LWS_CALLBACK_PROCESS_HTML,
wsi->user_space, &args, 0) < 0)
goto file_had_it;
n = args.len;
p = (unsigned char *)args.p;
} else
p = pstart;
#if defined(LWS_WITH_RANGES)
if (wsi->u.http.range.send_ctr + 1 ==
wsi->u.http.range.count_ranges && // last range
wsi->u.http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
wsi->u.http.range.budget - amount == 0) {// final part
n += lws_snprintf((char *)pstart + n, 6,
"_lws\x0d\x0a"); // append trailing boundary
lwsl_debug("added trailing boundary\n");
}
#endif
m = lws_write(wsi, p, n,
wsi->u.http.filepos == wsi->u.http.filelen ?
LWS_WRITE_HTTP_FINAL :
LWS_WRITE_HTTP
);
if (m < 0)
goto file_had_it;
wsi->u.http.filepos += amount;
#if defined(LWS_WITH_RANGES)
if (wsi->u.http.range.count_ranges >= 1) {
wsi->u.http.range.budget -= amount;
if (wsi->u.http.range.budget == 0) {
lwsl_notice("range budget exhausted\n");
wsi->u.http.range.inside = 0;
wsi->u.http.range.send_ctr++;
if (lws_ranges_next(&wsi->u.http.range) < 1) {
finished = 1;
goto all_sent;
}
}
}
#endif
if (m != n) {
/* adjust for what was not sent */
if (lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
m - n) ==
(unsigned long)-1)
goto file_had_it;
}
}
all_sent:
if ((!wsi->trunc_len && wsi->u.http.filepos >= wsi->u.http.filelen)
#if defined(LWS_WITH_RANGES)
|| finished)
#else
)
#endif
{
wsi->state = LWSS_HTTP;
/* we might be in keepalive, so close it off here */
lws_vfs_file_close(&wsi->u.http.fop_fd);
lwsl_debug("file completed\n");
if (wsi->protocol->callback &&
user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
wsi->user_space, NULL,
0) < 0) {
/*
* For http/1.x, the choices from
* transaction_completed are either
* 0 to use the connection for pipelined
* or nonzero to hang it up.
*
* However for http/2. while we are
* still interested in hanging up the
* nwsi if there was a network-level
* fatal error, simply completing the
* transaction is a matter of the stream
* state, not the root connection at the
* network level
*/
if (wsi->http2_substream)
return 1;
else
return -1;
}
return 1; /* >0 indicates completed */
}
}
lws_callback_on_writable(wsi);
return 0; /* indicates further processing must be done */
file_had_it:
lws_vfs_file_close(&wsi->u.http.fop_fd);
return -1;
}
#if LWS_POSIX
LWS_VISIBLE int
lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n;
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
if (n >= 0) {
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
lws_restart_ws_ping_pong_timer(wsi);
return n;
}
#if LWS_POSIX
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK ||
LWS_ERRNO == LWS_EINTR)
return LWS_SSL_CAPABLE_MORE_SERVICE;
#endif
lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO);
return LWS_SSL_CAPABLE_ERROR;
}
LWS_VISIBLE int
lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
int n = 0;
#if LWS_POSIX
n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
// lwsl_info("%s: sent len %d result %d", __func__, len, n);
if (n >= 0)
return n;
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK ||
LWS_ERRNO == LWS_EINTR) {
if (LWS_ERRNO == LWS_EWOULDBLOCK) {
lws_set_blocking_send(wsi);
}
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
#else
(void)n;
(void)wsi;
(void)buf;
(void)len;
// !!!
#endif
lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n",
len, wsi->desc.sockfd, n, LWS_ERRNO);
return LWS_SSL_CAPABLE_ERROR;
}
#endif
LWS_VISIBLE int
lws_ssl_pending_no_ssl(struct lws *wsi)
{
(void)wsi;
#if defined(LWS_WITH_ESP32)
return 100;
#else
return 0;
#endif
}

View File

@ -19,7 +19,8 @@
* MA 02110-1301 USA * MA 02110-1301 USA
*/ */
#include "private-libwebsockets.h" #define _GNU_SOURCE
#include "core/private.h"
#include <pwd.h> #include <pwd.h>
#include <grp.h> #include <grp.h>
@ -29,6 +30,56 @@
#endif #endif
#include <dirent.h> #include <dirent.h>
int
lws_plat_socket_offset(void)
{
return 0;
}
int
lws_plat_pipe_create(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
#if defined(LWS_HAVE_PIPE2)
return pipe2(pt->dummy_pipe_fds, O_NONBLOCK);
#else
return pipe(pt->dummy_pipe_fds);
#endif
}
int
lws_plat_pipe_signal(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char buf = 0;
int n;
n = write(pt->dummy_pipe_fds[1], &buf, 1);
return n != 1;
}
void
lws_plat_pipe_close(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
if (pt->dummy_pipe_fds[0] && pt->dummy_pipe_fds[0] != -1)
close(pt->dummy_pipe_fds[0]);
if (pt->dummy_pipe_fds[1] && pt->dummy_pipe_fds[1] != -1)
close(pt->dummy_pipe_fds[1]);
pt->dummy_pipe_fds[0] = pt->dummy_pipe_fds[1] = -1;
}
#ifdef __QNX__
# include "netinet/tcp_var.h"
# define TCP_KEEPINTVL TCPCTL_KEEPINTVL
# define TCP_KEEPIDLE TCPCTL_KEEPIDLE
# define TCP_KEEPCNT TCPCTL_KEEPCNT
#endif
unsigned long long time_in_microseconds(void) unsigned long long time_in_microseconds(void)
{ {
struct timeval tv; struct timeval tv;
@ -52,6 +103,10 @@ lws_send_pipe_choked(struct lws *wsi)
#if defined(LWS_WITH_HTTP2) #if defined(LWS_WITH_HTTP2)
wsi_eff = lws_get_network_wsi(wsi); wsi_eff = lws_get_network_wsi(wsi);
#endif #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 */ /* treat the fact we got a truncated send pending as if we're choked */
if (wsi_eff->trunc_len) if (wsi_eff->trunc_len)
return 1; return 1;
@ -77,29 +132,6 @@ lws_poll_listen_fd(struct lws_pollfd *fd)
return poll(fd, 1, 0); return poll(fd, 1, 0);
} }
LWS_VISIBLE void
lws_cancel_service_pt(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char buf = 0;
if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
lwsl_err("Cannot write to dummy pipe");
}
LWS_VISIBLE void
lws_cancel_service(struct lws_context *context)
{
struct lws_context_per_thread *pt = &context->pt[0];
char buf = 0, m = context->count_threads;
while (m--) {
if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
lwsl_err("Cannot write to dummy pipe");
pt++;
}
}
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
{ {
int syslog_level = LOG_DEBUG; int syslog_level = LOG_DEBUG;
@ -124,9 +156,10 @@ LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
LWS_VISIBLE LWS_EXTERN int LWS_VISIBLE LWS_EXTERN int
_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
{ {
volatile struct lws_foreign_thread_pollfd *ftp, *next;
volatile struct lws_context_per_thread *vpt;
struct lws_context_per_thread *pt; struct lws_context_per_thread *pt;
int n = -1, m, c; int n = -1, m, c;
char buf;
/* stay dead once we are dead */ /* stay dead once we are dead */
@ -134,15 +167,15 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
return 1; return 1;
pt = &context->pt[tsi]; pt = &context->pt[tsi];
vpt = (volatile struct lws_context_per_thread *)pt;
lws_stats_atomic_bump(context, pt, LWSSTATS_C_SERVICE_ENTRY, 1); lws_stats_atomic_bump(context, pt, LWSSTATS_C_SERVICE_ENTRY, 1);
if (timeout_ms < 0) if (timeout_ms < 0)
goto faked_service; goto faked_service;
lws_libev_run(context, tsi); if (context->event_loop_ops->run_pt)
lws_libuv_run(context, tsi); context->event_loop_ops->run_pt(context, tsi);
lws_libevent_run(context, tsi);
if (!context->service_tid_detected) { if (!context->service_tid_detected) {
struct lws _lws; struct lws _lws;
@ -169,15 +202,73 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
timeout_ms = 0; timeout_ms = 0;
} }
n = poll(pt->fds, pt->fds_count, timeout_ms); if (timeout_ms) {
lws_pt_lock(pt, __func__);
/* don't stay in poll wait longer than next hr timeout */
lws_usec_t t = __lws_hrtimer_service(pt);
if ((lws_usec_t)timeout_ms * 1000 > t)
timeout_ms = t / 1000;
lws_pt_unlock(pt);
}
#ifdef LWS_OPENSSL_SUPPORT vpt->inside_poll = 1;
if (!n && !pt->rx_draining_ext_list && lws_memory_barrier();
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) { n = poll(pt->fds, pt->fds_count, timeout_ms);
#else vpt->inside_poll = 0;
if (!pt->rx_draining_ext_list && !n) /* poll timeout */ { lws_memory_barrier();
/* Collision will be rare and brief. Just spin until it completes */
while (vpt->foreign_spinlock)
;
/*
* At this point we are not inside a foreign thread pollfd change,
* and we have marked ourselves as outside the poll() wait. So we
* are the only guys that can modify the lws_foreign_thread_pollfd
* list on the pt. Drain the list and apply the changes to the
* affected pollfds in the correct order.
*/
lws_pt_lock(pt, __func__);
ftp = vpt->foreign_pfd_list;
//lwsl_notice("cleared list %p\n", ftp);
while (ftp) {
struct lws *wsi;
struct lws_pollfd *pfd;
next = ftp->next;
pfd = &vpt->fds[ftp->fd_index];
if (lws_socket_is_valid(pfd->fd)) {
wsi = wsi_from_fd(context, pfd->fd);
if (wsi)
__lws_change_pollfd(wsi, ftp->_and, ftp->_or);
}
lws_free((void *)ftp);
ftp = next;
}
vpt->foreign_pfd_list = NULL;
lws_memory_barrier();
/* we have come out of a poll wait... check the hrtimer list */
__lws_hrtimer_service(pt);
lws_pt_unlock(pt);
m = 0;
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
m |= !!pt->ws.rx_draining_ext_list;
#endif #endif
if (pt->context->tls_ops &&
pt->context->tls_ops->fake_POLLIN_for_buffered)
m |= pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
if (!m && !n) { /* nothing to do */
lws_service_fd_tsi(context, NULL, tsi); lws_service_fd_tsi(context, NULL, tsi);
lws_service_do_ripe_rxflow(pt);
return 0; return 0;
} }
@ -194,18 +285,12 @@ faked_service:
c = n; c = n;
/* any socket with events to service? */ /* any socket with events to service? */
for (n = 0; n < pt->fds_count && c; n++) { for (n = 0; n < (int)pt->fds_count && c; n++) {
if (!pt->fds[n].revents) if (!pt->fds[n].revents)
continue; continue;
c--; c--;
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;
}
m = lws_service_fd_tsi(context, &pt->fds[n], tsi); m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
if (m < 0) if (m < 0)
return -1; return -1;
@ -214,6 +299,8 @@ faked_service:
n--; n--;
} }
lws_service_do_ripe_rxflow(pt);
return 0; return 0;
} }
@ -262,6 +349,14 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
*/ */
#else #else
/* set the keepalive conditions we want on it too */ /* set the keepalive conditions we want on it too */
#if defined(LWS_HAVE_TCP_USER_TIMEOUT)
optval = 1000 * (vhost->ka_time +
(vhost->ka_interval * vhost->ka_probes));
if (setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT,
(const void *)&optval, optlen) < 0)
return 1;
#endif
optval = vhost->ka_time; optval = vhost->ka_time;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
(const void *)&optval, optlen) < 0) (const void *)&optval, optlen) < 0)
@ -292,7 +387,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
/* Disable Nagle */ /* Disable Nagle */
optval = 1; optval = 1;
#if defined (__sun) #if defined (__sun) || defined(__QNX__)
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0) if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
return 1; return 1;
#elif !defined(__APPLE__) && \ #elif !defined(__APPLE__) && \
@ -317,7 +412,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) #if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
static void static void
_lws_plat_apply_caps(int mode, cap_value_t *cv, int count) _lws_plat_apply_caps(int mode, const cap_value_t *cv, int count)
{ {
cap_t caps; cap_t caps;
@ -334,7 +429,7 @@ _lws_plat_apply_caps(int mode, cap_value_t *cv, int count)
#endif #endif
LWS_VISIBLE void LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info) lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
{ {
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) #if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
int n; int n;
@ -449,8 +544,8 @@ lws_plat_plugins_init(struct lws_context * context, const char * const *d)
} }
plugin->list = context->plugin_list; plugin->list = context->plugin_list;
context->plugin_list = plugin; context->plugin_list = plugin;
strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1); lws_strncpy(plugin->name, namelist[i]->d_name,
plugin->name[sizeof(plugin->name) - 1] = '\0'; sizeof(plugin->name));
plugin->l = l; plugin->l = l;
plugin->caps = lcaps; plugin->caps = lcaps;
context->plugin_protocol_count += lcaps.count_protocols; context->plugin_protocol_count += lcaps.count_protocols;
@ -543,9 +638,6 @@ lws_plat_context_early_destroy(struct lws_context *context)
LWS_VISIBLE void LWS_VISIBLE void
lws_plat_context_late_destroy(struct lws_context *context) lws_plat_context_late_destroy(struct lws_context *context)
{ {
struct lws_context_per_thread *pt = &context->pt[0];
int m = context->count_threads;
#ifdef LWS_WITH_PLUGINS #ifdef LWS_WITH_PLUGINS
if (context->plugin_list) if (context->plugin_list)
lws_plat_plugins_destroy(context); lws_plat_plugins_destroy(context);
@ -554,13 +646,6 @@ lws_plat_context_late_destroy(struct lws_context *context)
if (context->lws_lookup) if (context->lws_lookup)
lws_free(context->lws_lookup); lws_free(context->lws_lookup);
while (m--) {
if (pt->dummy_pipe_fds[0])
close(pt->dummy_pipe_fds[0]);
if (pt->dummy_pipe_fds[1])
close(pt->dummy_pipe_fds[1]);
pt++;
}
if (!context->fd_random) if (!context->fd_random)
lwsl_err("ZERO RANDOM FD\n"); lwsl_err("ZERO RANDOM FD\n");
if (context->fd_random != LWS_INVALID_FILE) if (context->fd_random != LWS_INVALID_FILE)
@ -573,7 +658,7 @@ LWS_VISIBLE int
lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
size_t addrlen) size_t addrlen)
{ {
int rc = -1; int rc = LWS_ITOSA_NOT_EXIST;
struct ifaddrs *ifr; struct ifaddrs *ifr;
struct ifaddrs *ifc; struct ifaddrs *ifc;
@ -586,12 +671,19 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
if (!ifc->ifa_addr) if (!ifc->ifa_addr)
continue; continue;
lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname); lwsl_debug(" interface %s vs %s (fam %d) ipv6 %d\n", ifc->ifa_name, ifname, ifc->ifa_addr->sa_family, ipv6);
if (strcmp(ifc->ifa_name, ifname)) if (strcmp(ifc->ifa_name, ifname))
continue; continue;
switch (ifc->ifa_addr->sa_family) { switch (ifc->ifa_addr->sa_family) {
#if defined(AF_PACKET)
case AF_PACKET:
/* interface exists but is not usable */
rc = LWS_ITOSA_NOT_USABLE;
continue;
#endif
case AF_INET: case AF_INET:
#ifdef LWS_WITH_IPV6 #ifdef LWS_WITH_IPV6
if (ipv6) { if (ipv6) {
@ -619,20 +711,20 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
default: default:
continue; continue;
} }
rc = 0; rc = LWS_ITOSA_USABLE;
} }
freeifaddrs(ifr); freeifaddrs(ifr);
if (rc == -1) { if (rc) {
/* check if bind to IP address */ /* check if bind to IP address */
#ifdef LWS_WITH_IPV6 #ifdef LWS_WITH_IPV6
if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1)
rc = 0; rc = LWS_ITOSA_USABLE;
else else
#endif #endif
if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1) if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1)
rc = 0; rc = LWS_ITOSA_USABLE;
} }
return rc; return rc;
@ -643,9 +735,8 @@ lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
{ {
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ); if (context->event_loop_ops->io)
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ); context->event_loop_ops->io(wsi, LWS_EV_START | LWS_EV_READ);
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
pt->fds[pt->fds_count++].revents = 0; pt->fds[pt->fds_count++].revents = 0;
} }
@ -656,9 +747,9 @@ lws_plat_delete_socket_from_fds(struct lws_context *context,
{ {
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); if (context->event_loop_ops->io)
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); context->event_loop_ops->io(wsi,
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
pt->fds_count--; pt->fds_count--;
} }
@ -739,7 +830,8 @@ _lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
{ {
lws_fileofs_t r; lws_fileofs_t r;
if (offset > 0 && offset > fop_fd->len - fop_fd->pos) if (offset > 0 &&
offset > (lws_fileofs_t)fop_fd->len - (lws_fileofs_t)fop_fd->pos)
offset = fop_fd->len - fop_fd->pos; offset = fop_fd->len - fop_fd->pos;
if ((lws_fileofs_t)fop_fd->pos + offset < 0) if ((lws_fileofs_t)fop_fd->pos + offset < 0)
@ -793,13 +885,11 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
return 0; return 0;
} }
LWS_VISIBLE int LWS_VISIBLE int
lws_plat_init(struct lws_context *context, lws_plat_init(struct lws_context *context,
struct lws_context_creation_info *info) const struct lws_context_creation_info *info)
{ {
struct lws_context_per_thread *pt = &context->pt[0]; int fd;
int n = context->count_threads, fd;
/* master context has the global fd lookup array */ /* master context has the global fd lookup array */
context->lws_lookup = lws_zalloc(sizeof(struct lws *) * context->lws_lookup = lws_zalloc(sizeof(struct lws *) *
@ -821,26 +911,6 @@ lws_plat_init(struct lws_context *context,
return 1; return 1;
} }
if (!lws_libev_init_fd_table(context) &&
!lws_libuv_init_fd_table(context) &&
!lws_libevent_init_fd_table(context)) {
/* otherwise libev/uv/event handled it instead */
while (n--) {
if (pipe(pt->dummy_pipe_fds)) {
lwsl_err("Unable to create pipe\n");
return 1;
}
/* use the read end of pipe as first item */
pt->fds[0].fd = pt->dummy_pipe_fds[0];
pt->fds[0].events = LWS_POLLIN;
pt->fds[0].revents = 0;
pt->fds_count = 1;
pt++;
}
}
#ifdef LWS_WITH_PLUGINS #ifdef LWS_WITH_PLUGINS
if (info->plugin_dirs) if (info->plugin_dirs)
lws_plat_plugins_init(context, info->plugin_dirs); lws_plat_plugins_init(context, info->plugin_dirs);
@ -848,3 +918,52 @@ lws_plat_init(struct lws_context *context,
return 0; return 0;
} }
LWS_VISIBLE int
lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
int len)
{
int n;
n = write(fd, buf, len);
fsync(fd);
lseek(fd, 0, SEEK_SET);
return n != len;
}
LWS_VISIBLE int
lws_plat_write_file(const char *filename, void *buf, int len)
{
int m, fd;
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd == -1)
return 1;
m = write(fd, buf, len);
close(fd);
return m != len;
}
LWS_VISIBLE int
lws_plat_read_file(const char *filename, void *buf, int len)
{
int n, fd = open(filename, O_RDONLY);
if (fd == -1)
return -1;
n = read(fd, buf, len);
close(fd);
return n;
}
LWS_VISIBLE int
lws_plat_recommended_rsa_bits(void)
{
return 4096;
}

View File

@ -1,7 +1,34 @@
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS #ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS #define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif #endif
#include "private-libwebsockets.h" #include "core/private.h"
int
lws_plat_socket_offset(void)
{
return 0;
}
int
lws_plat_pipe_create(struct lws *wsi)
{
return 1;
}
int
lws_plat_pipe_signal(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
WSASetEvent(pt->events[0]); /* trigger the cancel event */
return 0;
}
void
lws_plat_pipe_close(struct lws *wsi)
{
}
unsigned long long unsigned long long
time_in_microseconds() time_in_microseconds()
@ -19,9 +46,10 @@ time_in_microseconds()
#endif #endif
/* /*
* As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a * As per Windows documentation for FILETIME, copy the resulting
* ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can * FILETIME structure to a ULARGE_INTEGER structure using memcpy
* prevent alignment faults on 64-bit Windows). * (using memcpy instead of direct assignment can prevent alignment
* faults on 64-bit Windows).
*/ */
memcpy(&datetime, &filetime, sizeof(datetime)); memcpy(&datetime, &filetime, sizeof(datetime));
@ -81,7 +109,7 @@ delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) { if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) {
while (n < context->fd_hashtable[h].length) { while (n < context->fd_hashtable[h].length) {
context->fd_hashtable[h].wsi[n] = context->fd_hashtable[h].wsi[n] =
context->fd_hashtable[h].wsi[n + 1]; context->fd_hashtable[h].wsi[n + 1];
n++; n++;
} }
context->fd_hashtable[h].length--; context->fd_hashtable[h].length--;
@ -94,8 +122,8 @@ delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
return 1; return 1;
} }
LWS_VISIBLE int lws_get_random(struct lws_context *context, LWS_VISIBLE int
void *buf, int len) lws_get_random(struct lws_context *context, void *buf, int len)
{ {
int n; int n;
char *p = (char *)buf; char *p = (char *)buf;
@ -106,16 +134,25 @@ LWS_VISIBLE int lws_get_random(struct lws_context *context,
return n; return n;
} }
LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi) LWS_VISIBLE int
{ lws_send_pipe_choked(struct lws *wsi)
{ struct lws *wsi_eff = wsi;
#if defined(LWS_WITH_HTTP2)
wsi_eff = lws_get_network_wsi(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 */ /* treat the fact we got a truncated send pending as if we're choked */
if (wsi->trunc_len) if (wsi_eff->trunc_len)
return 1; return 1;
return (int)wsi->sock_send_blocking; return (int)wsi_eff->sock_send_blocking;
} }
LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd) LWS_VISIBLE int
lws_poll_listen_fd(struct lws_pollfd *fd)
{ {
fd_set readfds; fd_set readfds;
struct timeval tv = { 0, 0 }; struct timeval tv = { 0, 0 };
@ -125,29 +162,11 @@ LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(fd->fd, &readfds); FD_SET(fd->fd, &readfds);
return select(fd->fd + 1, &readfds, NULL, NULL, &tv); return select(((int)fd->fd) + 1, &readfds, NULL, NULL, &tv);
} }
LWS_VISIBLE void LWS_VISIBLE void
lws_cancel_service(struct lws_context *context) lwsl_emit_syslog(int level, const char *line)
{
struct lws_context_per_thread *pt = &context->pt[0];
int n = context->count_threads;
while (n--) {
WSASetEvent(pt->events[0]);
pt++;
}
}
LWS_VISIBLE void
lws_cancel_service_pt(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
WSASetEvent(pt->events[0]);
}
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
{ {
lwsl_emit_stderr(level, line); lwsl_emit_stderr(level, line);
} }
@ -182,9 +201,8 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
context->service_tid_detected = 1; context->service_tid_detected = 1;
} }
if (timeout_ms < 0) if (timeout_ms < 0) {
{ if (lws_service_flag_pending(context, tsi)) {
if (lws_service_flag_pending(context, tsi)) {
/* any socket with events to service? */ /* any socket with events to service? */
for (n = 0; n < (int)pt->fds_count; n++) { for (n = 0; n < (int)pt->fds_count; n++) {
if (!pt->fds[n].revents) if (!pt->fds[n].revents)
@ -201,6 +219,9 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
return 0; return 0;
} }
if (context->event_loop_ops->run_pt)
context->event_loop_ops->run_pt(context, tsi);
for (i = 0; i < pt->fds_count; ++i) { for (i = 0; i < pt->fds_count; ++i) {
pfd = &pt->fds[i]; pfd = &pt->fds[i];
@ -220,6 +241,9 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
if (n) if (n)
i--; i--;
/*
* any wsi has truncated, force him signalled
*/
if (wsi->trunc_len) if (wsi->trunc_len)
WSASetEvent(pt->events[0]); WSASetEvent(pt->events[0]);
} }
@ -236,29 +260,44 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
timeout_ms = 0; timeout_ms = 0;
} }
ev = WSAWaitForMultipleEvents( 1, pt->events , FALSE, timeout_ms, FALSE); if (timeout_ms) {
lws_pt_lock(pt, __func__);
/* don't stay in poll wait longer than next hr timeout */
lws_usec_t t = __lws_hrtimer_service(pt);
if ((lws_usec_t)timeout_ms * 1000 > t)
timeout_ms = (int)(t / 1000);
lws_pt_unlock(pt);
}
ev = WSAWaitForMultipleEvents(1, pt->events, FALSE, timeout_ms, FALSE);
if (ev == WSA_WAIT_EVENT_0) { if (ev == WSA_WAIT_EVENT_0) {
unsigned int eIdx; unsigned int eIdx, err;
WSAResetEvent(pt->events[0]); WSAResetEvent(pt->events[0]);
if (pt->context->tls_ops &&
pt->context->tls_ops->fake_POLLIN_for_buffered)
pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
for (eIdx = 0; eIdx < pt->fds_count; ++eIdx) { for (eIdx = 0; eIdx < pt->fds_count; ++eIdx) {
if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, 0, &networkevents) == SOCKET_ERROR) { if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, 0,
lwsl_err("WSAEnumNetworkEvents() failed with error %d\n", LWS_ERRNO); &networkevents) == SOCKET_ERROR) {
lwsl_err("WSAEnumNetworkEvents() failed "
"with error %d\n", LWS_ERRNO);
return -1; return -1;
} }
pfd = &pt->fds[eIdx]; pfd = &pt->fds[eIdx];
pfd->revents = (short)networkevents.lNetworkEvents; pfd->revents = (short)networkevents.lNetworkEvents;
err = networkevents.iErrorCode[FD_CONNECT_BIT];
if ((networkevents.lNetworkEvents & FD_CONNECT) && if ((networkevents.lNetworkEvents & FD_CONNECT) &&
networkevents.iErrorCode[FD_CONNECT_BIT] && err && err != LWS_EALREADY &&
networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EALREADY && err != LWS_EINPROGRESS && err != LWS_EWOULDBLOCK &&
networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EINPROGRESS && err != WSAEINVAL) {
networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EWOULDBLOCK && lwsl_debug("Unable to connect errno=%d\n", err);
networkevents.iErrorCode[FD_CONNECT_BIT] != WSAEINVAL) {
lwsl_debug("Unable to connect errno=%d\n",
networkevents.iErrorCode[FD_CONNECT_BIT]);
pfd->revents |= LWS_POLLHUP; pfd->revents |= LWS_POLLHUP;
} }
@ -269,21 +308,19 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
} }
/* if something closed, retry this slot */ /* if something closed, retry this slot */
if (pfd->revents & LWS_POLLHUP) if (pfd->revents & LWS_POLLHUP)
--eIdx; --eIdx;
if( pfd->revents != 0 ) { if (pfd->revents)
lws_service_fd_tsi(context, pfd, tsi); lws_service_fd_tsi(context, pfd, tsi);
}
} }
} }
context->service_tid = 0; context->service_tid = 0;
if (ev == WSA_WAIT_TIMEOUT) { if (ev == WSA_WAIT_TIMEOUT)
lws_service_fd(context, NULL); lws_service_fd(context, NULL);
}
return 0;; return 0;
} }
LWS_VISIBLE int LWS_VISIBLE int
@ -309,7 +346,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
/* enable keepalive on this socket */ /* enable keepalive on this socket */
optval = 1; optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
(const char *)&optval, optlen) < 0) (const char *)&optval, optlen) < 0)
return 1; return 1;
alive.onoff = TRUE; alive.onoff = TRUE;
@ -317,7 +354,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
alive.keepaliveinterval = vhost->ka_interval; alive.keepaliveinterval = vhost->ka_interval;
if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
NULL, 0, &dwBytesRet, NULL, NULL)) NULL, 0, &dwBytesRet, NULL, NULL))
return 1; return 1;
} }
@ -343,7 +380,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
} }
LWS_VISIBLE void LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info) lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
{ {
} }
@ -406,7 +443,7 @@ lws_interface_to_sa(int ipv6,
if (ipv6) { if (ipv6) {
if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) { if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) {
return 0; return LWS_ITOSA_USABLE;
} }
} }
#endif #endif
@ -420,11 +457,11 @@ lws_interface_to_sa(int ipv6,
} }
if (address == INADDR_NONE) if (address == INADDR_NONE)
return -1; return LWS_ITOSA_NOT_EXIST;
addr->sin_addr.s_addr = (lws_intptr_t)address; addr->sin_addr.s_addr = (unsigned long)(lws_intptr_t)address;
return 0; return LWS_ITOSA_USABLE;
} }
LWS_VISIBLE void LWS_VISIBLE void
@ -542,7 +579,7 @@ LWS_VISIBLE int
lws_plat_inet_pton(int af, const char *src, void *dst) lws_plat_inet_pton(int af, const char *src, void *dst)
{ {
WCHAR *buffer; WCHAR *buffer;
DWORD bufferlen = strlen(src) + 1; DWORD bufferlen = (int)strlen(src) + 1;
BOOL ok = FALSE; BOOL ok = FALSE;
buffer = lws_malloc(bufferlen * 2, "inet_pton"); buffer = lws_malloc(bufferlen * 2, "inet_pton");
@ -692,7 +729,7 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
LWS_VISIBLE int LWS_VISIBLE int
lws_plat_init(struct lws_context *context, lws_plat_init(struct lws_context *context,
struct lws_context_creation_info *info) const struct lws_context_creation_info *info)
{ {
struct lws_context_per_thread *pt = &context->pt[0]; struct lws_context_per_thread *pt = &context->pt[0];
int i, n = context->count_threads; int i, n = context->count_threads;
@ -715,7 +752,7 @@ lws_plat_init(struct lws_context *context,
} }
pt->fds_count = 0; pt->fds_count = 0;
pt->events[0] = WSACreateEvent(); pt->events[0] = WSACreateEvent(); /* the cancel event */
pt++; pt++;
} }
@ -743,3 +780,50 @@ int fork(void)
exit(0); exit(0);
} }
LWS_VISIBLE int
lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
int len)
{
int n;
n = write(fd, buf, len);
lseek(fd, 0, SEEK_SET);
return n != len;
}
LWS_VISIBLE int
lws_plat_write_file(const char *filename, void *buf, int len)
{
int m, fd;
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd == -1)
return -1;
m = write(fd, buf, len);
close(fd);
return m != len;
}
LWS_VISIBLE int
lws_plat_read_file(const char *filename, void *buf, int len)
{
int n, fd = open(filename, O_RDONLY);
if (fd == -1)
return -1;
n = read(fd, buf, len);
close(fd);
return n;
}
LWS_VISIBLE int
lws_plat_recommended_rsa_bits(void)
{
return 4096;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,687 @@
/*
* 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>
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
/*
* We have to take care about parsing because the headers may be split
* into multiple fragments. They may contain unknown headers with arbitrary
* argument lengths. So, we parse using a single-character at a time state
* machine that is completely independent of packet size.
*
* Returns <0 for error or length of chars consumed from buf (up to len)
*/
int
lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
{
unsigned char *last_char, *oldbuf = buf;
lws_filepos_t body_chunk_len;
size_t n;
// lwsl_notice("%s: h1 path: wsi state 0x%x\n", __func__, lwsi_state(wsi));
switch (lwsi_state(wsi)) {
case LRS_ISSUING_FILE:
return 0;
case LRS_ESTABLISHED:
if (lwsi_role_ws(wsi))
goto ws_mode;
if (lwsi_role_client(wsi))
break;
wsi->hdr_parsing_completed = 0;
/* fallthru */
case LRS_HEADERS:
if (!wsi->http.ah) {
lwsl_err("%s: LRS_HEADERS: NULL ah\n", __func__);
assert(0);
}
lwsl_parser("issuing %d bytes to parser\n", (int)len);
#if defined(LWS_ROLE_WS) && !defined(LWS_NO_CLIENT)
if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
#endif
last_char = buf;
if (lws_handshake_server(wsi, &buf, (size_t)len))
/* Handshake indicates this session is done. */
goto bail;
/* we might have transitioned to RAW */
if (wsi->role_ops == &role_ops_raw_skt ||
wsi->role_ops == &role_ops_raw_file)
/* we gave the read buffer to RAW handler already */
goto read_ok;
/*
* It's possible that we've exhausted our data already, or
* rx flow control has stopped us dealing with this early,
* but lws_handshake_server doesn't update len for us.
* Figure out how much was read, so that we can proceed
* appropriately:
*/
len -= (buf - last_char);
// lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
if (!wsi->hdr_parsing_completed)
/* More header content on the way */
goto read_ok;
switch (lwsi_state(wsi)) {
case LRS_ESTABLISHED:
case LRS_HEADERS:
goto read_ok;
case LRS_ISSUING_FILE:
goto read_ok;
case LRS_BODY:
wsi->http.rx_content_remain =
wsi->http.rx_content_length;
if (wsi->http.rx_content_remain)
goto http_postbody;
/* there is no POST content */
goto postbody_completion;
default:
break;
}
break;
case LRS_BODY:
http_postbody:
lwsl_debug("%s: http post body: remain %d\n", __func__,
(int)wsi->http.rx_content_remain);
while (len && wsi->http.rx_content_remain) {
/* Copy as much as possible, up to the limit of:
* what we have in the read buffer (len)
* remaining portion of the POST body (content_remain)
*/
body_chunk_len = min(wsi->http.rx_content_remain, len);
wsi->http.rx_content_remain -= body_chunk_len;
len -= body_chunk_len;
#ifdef LWS_WITH_CGI
if (wsi->http.cgi) {
struct lws_cgi_args args;
args.ch = LWS_STDIN;
args.stdwsi = &wsi->http.cgi->stdwsi[0];
args.data = buf;
args.len = body_chunk_len;
/* returns how much used */
n = user_callback_handle_rxflow(
wsi->protocol->callback,
wsi, LWS_CALLBACK_CGI_STDIN_DATA,
wsi->user_space,
(void *)&args, 0);
if ((int)n < 0)
goto bail;
} else {
#endif
n = wsi->protocol->callback(wsi,
LWS_CALLBACK_HTTP_BODY, wsi->user_space,
buf, (size_t)body_chunk_len);
if (n)
goto bail;
n = (size_t)body_chunk_len;
#ifdef LWS_WITH_CGI
}
#endif
buf += n;
if (wsi->http.rx_content_remain) {
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
wsi->context->timeout_secs);
break;
}
/* he sent all the content in time */
postbody_completion:
#ifdef LWS_WITH_CGI
/*
* If we're running a cgi, we can't let him off the
* hook just because he sent his POST data
*/
if (wsi->http.cgi)
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI,
wsi->context->timeout_secs);
else
#endif
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
#ifdef LWS_WITH_CGI
if (!wsi->http.cgi)
#endif
{
lwsl_info("HTTP_BODY_COMPLETION: %p (%s)\n",
wsi, wsi->protocol->name);
n = wsi->protocol->callback(wsi,
LWS_CALLBACK_HTTP_BODY_COMPLETION,
wsi->user_space, NULL, 0);
if (n)
goto bail;
if (wsi->http2_substream)
lwsi_set_state(wsi, LRS_ESTABLISHED);
}
break;
}
break;
case LRS_AWAITING_CLOSE_ACK:
case LRS_WAITING_TO_SEND_CLOSE:
case LRS_SHUTDOWN:
ws_mode:
#if !defined(LWS_NO_CLIENT) && defined(LWS_ROLE_WS)
// lwsl_notice("%s: ws_mode\n", __func__);
if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
#endif
#if defined(LWS_ROLE_WS)
if (lwsi_role_ws(wsi) && lwsi_role_server(wsi) &&
/*
* for h2 we are on the swsi
*/
lws_parse_ws(wsi, &buf, (size_t)len) < 0) {
lwsl_info("%s: lws_parse_ws bailed\n", __func__);
goto bail;
}
#endif
// lwsl_notice("%s: ws_mode: buf moved on by %d\n", __func__,
// lws_ptr_diff(buf, oldbuf));
break;
case LRS_DEFERRING_ACTION:
lwsl_debug("%s: LRS_DEFERRING_ACTION\n", __func__);
break;
case LRS_SSL_ACK_PENDING:
break;
case LRS_DEAD_SOCKET:
lwsl_err("%s: Unhandled state LRS_DEAD_SOCKET\n", __func__);
goto bail;
// assert(0);
/* fallthru */
default:
lwsl_err("%s: Unhandled state %d\n", __func__, lwsi_state(wsi));
assert(0);
goto bail;
}
read_ok:
/* Nothing more to do for now */
// lwsl_info("%s: %p: read_ok, used %ld (len %d, state %d)\n", __func__,
// wsi, (long)(buf - oldbuf), (int)len, wsi->state);
return lws_ptr_diff(buf, oldbuf);
bail:
/*
* h2 / h2-ws calls us recursively in
*
* lws_read_h1()->
* lws_h2_parser()->
* lws_read_h1()
*
* pattern, having stripped the h2 framing in the middle.
*
* When taking down the whole connection, make sure that only the
* outer lws_read() does the wsi close.
*/
if (!wsi->outer_will_close)
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"lws_read_h1 bail");
return -1;
}
#if !defined(LWS_NO_SERVER)
static int
lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
struct lws_tokens ebuf;
int n, buffered;
if (lwsi_state(wsi) == LRS_DEFERRING_ACTION)
goto try_pollout;
/* any incoming data ready? */
if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
goto try_pollout;
/*
* If we previously just did POLLIN when IN and OUT were signaled
* (because POLLIN processing may have used up the POLLOUT), don't let
* that happen twice in a row... next time we see the situation favour
* POLLOUT
*/
if (wsi->favoured_pollin &&
(pollfd->revents & pollfd->events & LWS_POLLOUT)) {
// lwsl_notice("favouring pollout\n");
wsi->favoured_pollin = 0;
goto try_pollout;
}
/*
* We haven't processed that the tunnel is set up yet, so
* defer reading
*/
if (lwsi_state(wsi) == LRS_SSL_ACK_PENDING)
return LWS_HPI_RET_HANDLED;
/* these states imply we MUST have an ah attached */
if ((lwsi_state(wsi) == LRS_ESTABLISHED ||
lwsi_state(wsi) == LRS_ISSUING_FILE ||
lwsi_state(wsi) == LRS_HEADERS ||
lwsi_state(wsi) == LRS_BODY)) {
if (!wsi->http.ah && lws_header_table_attach(wsi, 0)) {
lwsl_info("%s: wsi %p: ah not available\n", __func__, wsi);
goto try_pollout;
}
/*
* We got here because there was specifically POLLIN...
* regardless of our buflist state, we need to get it,
* and either use it, or append to the buflist and use
* buflist head material.
*
* We will not notice a connection close until the buflist is
* exhausted and we tried to do a read of some kind.
*/
buffered = lws_buflist_aware_read(pt, wsi, &ebuf);
switch (ebuf.len) {
case 0:
lwsl_info("%s: read 0 len a\n", __func__);
wsi->seen_zero_length_recv = 1;
lws_change_pollfd(wsi, LWS_POLLIN, 0);
#if !defined(LWS_WITHOUT_EXTENSIONS)
/*
* autobahn requires us to win the race between close
* and draining the extensions
*/
if (wsi->ws &&
(wsi->ws->rx_draining_ext || wsi->ws->tx_draining_ext))
goto try_pollout;
#endif
/*
* normally, we respond to close with logically closing
* our side immediately
*/
goto fail;
case LWS_SSL_CAPABLE_ERROR:
goto fail;
case LWS_SSL_CAPABLE_MORE_SERVICE:
goto try_pollout;
}
/* just ignore incoming if waiting for close */
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
lwsl_notice("%s: just ignoring\n", __func__);
goto try_pollout;
}
if (lwsi_state(wsi) == LRS_ISSUING_FILE) {
// lwsl_notice("stashing: wsi %p: bd %d\n", wsi, buffered);
if (lws_buflist_aware_consume(wsi, &ebuf, 0, buffered))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
goto try_pollout;
}
/*
* Otherwise give it to whoever wants it according to the
* connection state
*/
#if defined(LWS_ROLE_H2)
if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
n = lws_read_h2(wsi, (uint8_t *)ebuf.token, ebuf.len);
else
#endif
n = lws_read_h1(wsi, (uint8_t *)ebuf.token, ebuf.len);
if (n < 0) /* we closed wsi */
return LWS_HPI_RET_WSI_ALREADY_DIED;
lwsl_debug("%s: consumed %d\n", __func__, n);
if (lws_buflist_aware_consume(wsi, &ebuf, n, buffered))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
/*
* during the parsing our role changed to something non-http,
* so the ah has no further meaning
*/
if (wsi->http.ah &&
!lwsi_role_h1(wsi) &&
!lwsi_role_h2(wsi) &&
!lwsi_role_cgi(wsi))
lws_header_table_detach(wsi, 0);
/*
* He may have used up the writability above, if we will defer
* POLLOUT processing in favour of POLLIN, note it
*/
if (pollfd->revents & LWS_POLLOUT)
wsi->favoured_pollin = 1;
return LWS_HPI_RET_HANDLED;
}
/*
* He may have used up the writability above, if we will defer POLLOUT
* processing in favour of POLLIN, note it
*/
if (pollfd->revents & LWS_POLLOUT)
wsi->favoured_pollin = 1;
try_pollout:
/* this handles POLLOUT for http serving fragments */
if (!(pollfd->revents & LWS_POLLOUT))
return LWS_HPI_RET_HANDLED;
/* one shot */
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_notice("%s a\n", __func__);
goto fail;
}
/* clear back-to-back write detection */
wsi->could_have_pending = 0;
if (lwsi_state(wsi) == LRS_DEFERRING_ACTION) {
lwsl_debug("%s: LRS_DEFERRING_ACTION now writable\n", __func__);
lwsi_set_state(wsi, LRS_ESTABLISHED);
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_info("failed at set pollfd\n");
goto fail;
}
}
if (!wsi->hdr_parsing_completed)
return LWS_HPI_RET_HANDLED;
if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_WRITEABLE_CB, 1);
#if defined(LWS_WITH_STATS)
if (wsi->active_writable_req_us) {
uint64_t ul = time_in_microseconds() -
wsi->active_writable_req_us;
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_MS_WRITABLE_DELAY, ul);
lws_stats_atomic_max(wsi->context, pt,
LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
wsi->active_writable_req_us = 0;
}
#endif
n = user_callback_handle_rxflow(wsi->protocol->callback, wsi,
LWS_CALLBACK_HTTP_WRITEABLE,
wsi->user_space, NULL, 0);
if (n < 0) {
lwsl_info("writeable_fail\n");
goto fail;
}
return LWS_HPI_RET_HANDLED;
}
/* >0 == completion, <0 == error
*
* We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
* it's done. That's the case even if we just completed the
* send, so wait for that.
*/
n = lws_serve_http_file_fragment(wsi);
if (n < 0)
goto fail;
return LWS_HPI_RET_HANDLED;
fail:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"server socket svc fail");
return LWS_HPI_RET_WSI_ALREADY_DIED;
}
#endif
static int
rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
// lwsl_notice("%s: %p: wsistate 0x%x %s, revents 0x%x\n", __func__, wsi,
// wsi->wsistate, wsi->role_ops->name, pollfd->revents);
#ifdef LWS_WITH_CGI
if (wsi->http.cgi && (pollfd->revents & LWS_POLLOUT)) {
if (lws_handle_POLLOUT_event(wsi, pollfd))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
return LWS_HPI_RET_HANDLED;
}
#endif
if (lws_is_flowcontrolled(wsi))
/* We cannot deal with any kind of new RX because we are
* RX-flowcontrolled.
*/
return LWS_HPI_RET_HANDLED;
#if !defined(LWS_NO_SERVER)
if (!lwsi_role_client(wsi)) {
int n;
lwsl_debug("%s: %p: wsistate 0x%x\n", __func__, wsi, wsi->wsistate);
n = lws_h1_server_socket_service(wsi, pollfd);
if (n != LWS_HPI_RET_HANDLED)
return n;
if (lwsi_state(wsi) != LRS_SSL_INIT)
if (lws_server_socket_service_ssl(wsi, LWS_SOCK_INVALID))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
return LWS_HPI_RET_HANDLED;
}
#endif
#ifndef LWS_NO_CLIENT
if ((pollfd->revents & LWS_POLLIN) &&
wsi->hdr_parsing_completed && !wsi->told_user_closed) {
/*
* In SSL mode we get POLLIN notification about
* encrypted data in.
*
* But that is not necessarily related to decrypted
* data out becoming available; in may need to perform
* other in or out before that happens.
*
* simply mark ourselves as having readable data
* and turn off our POLLIN
*/
wsi->client_rx_avail = 1;
lws_change_pollfd(wsi, LWS_POLLIN, 0);
//lwsl_notice("calling back %s\n", wsi->protocol->name);
/* let user code know, he'll usually ask for writeable
* callback and drain / re-enable it there
*/
if (user_callback_handle_rxflow(
wsi->protocol->callback,
wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
wsi->user_space, NULL, 0)) {
lwsl_info("RECEIVE_CLIENT_HTTP closed it\n");
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
return LWS_HPI_RET_HANDLED;
}
#endif
// if (lwsi_state(wsi) == LRS_ESTABLISHED)
// return LWS_HPI_RET_HANDLED;
#if !defined(LWS_NO_CLIENT)
if ((pollfd->revents & LWS_POLLOUT) &&
lws_handle_POLLOUT_event(wsi, pollfd)) {
lwsl_debug("POLLOUT event closed it\n");
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
if (lws_client_socket_service(wsi, pollfd, NULL))
return LWS_HPI_RET_WSI_ALREADY_DIED;
#endif
return LWS_HPI_RET_HANDLED;
}
int rops_handle_POLLOUT_h1(struct lws *wsi)
{
if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY)
return LWS_HP_RET_USER_SERVICE;
if (lwsi_role_client(wsi))
return LWS_HP_RET_USER_SERVICE;
return LWS_HP_RET_BAIL_OK;
}
static int
rops_write_role_protocol_h1(struct lws *wsi, unsigned char *buf, size_t len,
enum lws_write_protocol *wp)
{
#if 0
/* if not in a state to send stuff, then just send nothing */
if ((lwsi_state(wsi) != LRS_RETURNED_CLOSE &&
lwsi_state(wsi) != LRS_WAITING_TO_SEND_CLOSE &&
lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK)) {
//assert(0);
lwsl_debug("binning %d %d\n", lwsi_state(wsi), *wp);
return 0;
}
#endif
return lws_issue_raw(wsi, (unsigned char *)buf, len);
}
static int
rops_alpn_negotiated_h1(struct lws *wsi, const char *alpn)
{
lwsl_debug("%s: client %d\n", __func__, lwsi_role_client(wsi));
#if !defined(LWS_NO_CLIENT)
if (lwsi_role_client(wsi)) {
/*
* If alpn asserts it is http/1.1, server support for KA is
* mandatory.
*
* Knowing this lets us proceed with sending pipelined headers
* before we received the first response headers.
*/
wsi->keepalive_active = 1;
}
#endif
return 0;
}
static int
rops_destroy_role_h1(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
struct allocated_headers *ah;
/* we may not have an ah, but may be on the waiting list... */
lwsl_info("%s: ah det due to close\n", __func__);
__lws_header_table_detach(wsi, 0);
ah = pt->http.ah_list;
while (ah) {
if (ah->in_use && ah->wsi == wsi) {
lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi);
ah->in_use = 0;
ah->wsi = NULL;
pt->http.ah_count_in_use--;
break;
}
ah = ah->next;
}
return 0;
}
struct lws_role_ops role_ops_h1 = {
/* role name */ "h1",
/* alpn id */ "http/1.1",
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_h1,
/* handle_POLLOUT */ rops_handle_POLLOUT_h1,
/* perform_user_POLLOUT */ NULL,
/* callback_on_writable */ NULL,
/* tx_credit */ NULL,
/* write_role_protocol */ rops_write_role_protocol_h1,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ rops_alpn_negotiated_h1,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,
/* destroy_role */ rops_destroy_role_h1,
/* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE,
LWS_CALLBACK_HTTP_WRITEABLE },
/* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP,
LWS_CALLBACK_CLOSED_HTTP },
/* file_handle */ 0,
};

View File

@ -0,0 +1,27 @@
/*
* 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
*
* This is included from core/private.h if LWS_ROLE_H1
*
* Most of the h1 business is defined in the h1 / h2 common roles/http dir
*/
extern struct lws_role_ops role_ops_h1;
#define lwsi_role_h1(wsi) (wsi->role_ops == &role_ops_h1)

View File

@ -1,4 +1,4 @@
#include "private-libwebsockets.h" #include "core/private.h"
static int static int
lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result) lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
@ -20,7 +20,6 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
{ {
hints.ai_family = PF_UNSPEC; hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
} }
return getaddrinfo(ads, NULL, &hints, result); return getaddrinfo(ads, NULL, &hints, result);
@ -29,15 +28,20 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
struct lws * struct lws *
lws_client_connect_2(struct lws *wsi) lws_client_connect_2(struct lws *wsi)
{ {
sockaddr46 sa46; #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
struct addrinfo *result;
struct lws_context *context = wsi->context; struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
const char *adsin;
struct lws *wsi_piggyback = NULL;
struct lws_pollfd pfd; struct lws_pollfd pfd;
const char *cce = "", *iface;
int n, port;
ssize_t plen = 0; ssize_t plen = 0;
#endif
struct addrinfo *result;
const char *ads; const char *ads;
sockaddr46 sa46;
int n, port;
const char *cce = "", *iface;
const char *meth = NULL;
#ifdef LWS_WITH_IPV6 #ifdef LWS_WITH_IPV6
char ipv6only = lws_check_opt(wsi->vhost->options, char ipv6only = lws_check_opt(wsi->vhost->options,
LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY | LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY |
@ -48,24 +52,148 @@ lws_client_connect_2(struct lws *wsi)
#endif #endif
#endif #endif
lwsl_client("%s\n", __func__); lwsl_client("%s: %p\n", __func__, wsi);
if (!wsi->u.hdr.ah) { #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
if (!wsi->http.ah) {
cce = "ah was NULL at cc2"; cce = "ah was NULL at cc2";
lwsl_err("%s\n", cce); lwsl_err("%s\n", cce);
goto oom4; goto oom4;
} }
/* we can only piggyback GET or POST */
meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
if (meth && strcmp(meth, "GET") && strcmp(meth, "POST"))
goto create_new_conn;
/* we only pipeline connections that said it was okay */
if (!wsi->client_pipeline)
goto create_new_conn;
/*
* let's take a look first and see if there are any already-active
* client connections we can piggy-back on.
*/
adsin = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
lws_vhost_lock(wsi->vhost); /* ----------------------------------- { */
lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
wsi->vhost->dll_active_client_conns.next) {
struct lws *w = lws_container_of(d, struct lws,
dll_active_client_conns);
lwsl_debug("%s: check %s %s %d %d\n", __func__, adsin,
w->client_hostname_copy, wsi->c_port, w->c_port);
if (w != wsi && w->client_hostname_copy &&
!strcmp(adsin, w->client_hostname_copy) &&
#if defined(LWS_WITH_TLS)
(wsi->tls.use_ssl & LCCSCF_USE_SSL) ==
(w->tls.use_ssl & LCCSCF_USE_SSL) &&
#endif
wsi->c_port == w->c_port) {
/* someone else is already connected to the right guy */
/* do we know for a fact pipelining won't fly? */
if (w->keepalive_rejected) {
lwsl_info("defeating pipelining due to no "
"keepalive on server\n");
lws_vhost_unlock(wsi->vhost); /* } ---------- */
goto create_new_conn;
}
#if defined (LWS_WITH_HTTP2)
/*
* h2: in usable state already: just use it without
* going through the queue
*/
if (w->client_h2_alpn &&
(lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS ||
lwsi_state(w) == LRS_ESTABLISHED)) {
lwsl_info("%s: just join h2 directly\n",
__func__);
wsi->client_h2_alpn = 1;
lws_wsi_h2_adopt(w, wsi);
lws_vhost_unlock(wsi->vhost); /* } ---------- */
return wsi;
}
#endif
lwsl_info("applying %p to txn queue on %p (wsistate 0x%x)\n",
wsi, w, w->wsistate);
/*
* ...let's add ourselves to his transaction queue...
* we are adding ourselves at the HEAD
*/
lws_dll_lws_add_front(&wsi->dll_client_transaction_queue,
&w->dll_client_transaction_queue_head);
/*
* h1: pipeline our headers out on him,
* and wait for our turn at client transaction_complete
* to take over parsing the rx.
*/
wsi_piggyback = w;
lws_vhost_unlock(wsi->vhost); /* } ---------- */
goto send_hs;
}
} lws_end_foreach_dll_safe(d, d1);
lws_vhost_unlock(wsi->vhost); /* } ---------------------------------- */
create_new_conn:
#endif
/*
* clients who will create their own fresh connection keep a copy of
* the hostname they originally connected to, in case other connections
* want to use it too
*/
if (!wsi->client_hostname_copy)
wsi->client_hostname_copy =
strdup(lws_hdr_simple_ptr(wsi,
_WSI_TOKEN_CLIENT_PEER_ADDRESS));
/*
* If we made our own connection, and we're doing a method that can take
* a pipeline, we are an "active client connection".
*
* Add ourselves to the vhost list of those so that others can
* piggyback on our transaction queue
*/
if (meth && (!strcmp(meth, "GET") || !strcmp(meth, "POST")) &&
lws_dll_is_null(&wsi->dll_client_transaction_queue) &&
lws_dll_is_null(&wsi->dll_active_client_conns)) {
lws_vhost_lock(wsi->vhost);
lws_dll_lws_add_front(&wsi->dll_active_client_conns,
&wsi->vhost->dll_active_client_conns);
lws_vhost_unlock(wsi->vhost);
}
/* /*
* start off allowing ipv6 on connection if vhost allows it * start off allowing ipv6 on connection if vhost allows it
*/ */
wsi->ipv6 = LWS_IPV6_ENABLED(wsi->vhost); wsi->ipv6 = LWS_IPV6_ENABLED(wsi->vhost);
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
/* Decide what it is we need to connect to: /* Decide what it is we need to connect to:
* *
* Priority 1: connect to http proxy */ * Priority 1: connect to http proxy */
if (wsi->vhost->http_proxy_port) { if (wsi->vhost->http.http_proxy_port) {
plen = sprintf((char *)pt->serv_buf, plen = sprintf((char *)pt->serv_buf,
"CONNECT %s:%u HTTP/1.0\x0d\x0a" "CONNECT %s:%u HTTP/1.0\x0d\x0a"
"User-agent: libwebsockets\x0d\x0a", "User-agent: libwebsockets\x0d\x0a",
@ -78,8 +206,11 @@ lws_client_connect_2(struct lws *wsi)
wsi->vhost->proxy_basic_auth_token); wsi->vhost->proxy_basic_auth_token);
plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a"); plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a");
ads = wsi->vhost->http_proxy_address; ads = wsi->vhost->http.http_proxy_address;
port = wsi->vhost->http_proxy_port; port = wsi->vhost->http.http_proxy_port;
#else
if (0) {
#endif
#if defined(LWS_WITH_SOCKS5) #if defined(LWS_WITH_SOCKS5)
@ -104,12 +235,14 @@ lws_client_connect_2(struct lws *wsi)
* to whatever we decided to connect to * to whatever we decided to connect to
*/ */
lwsl_notice("%s: %p: address %s\n", __func__, wsi, ads); lwsl_info("%s: %p: address %s\n", __func__, wsi, ads);
n = lws_getaddrinfo46(wsi, ads, &result); n = lws_getaddrinfo46(wsi, ads, &result);
#ifdef LWS_WITH_IPV6 #ifdef LWS_WITH_IPV6
if (wsi->ipv6) { if (wsi->ipv6) {
struct sockaddr_in6 *sa6 =
((struct sockaddr_in6 *)result->ai_addr);
if (n) { if (n) {
/* lws_getaddrinfo46 failed, there is no usable result */ /* lws_getaddrinfo46 failed, there is no usable result */
@ -138,11 +271,10 @@ lws_client_connect_2(struct lws *wsi)
break; break;
case AF_INET6: case AF_INET6:
memcpy(&sa46.sa6.sin6_addr, memcpy(&sa46.sa6.sin6_addr, &sa6->sin6_addr,
&((struct sockaddr_in6 *)result->ai_addr)->sin6_addr,
sizeof(struct in6_addr)); sizeof(struct in6_addr));
sa46.sa6.sin6_scope_id = ((struct sockaddr_in6 *)result->ai_addr)->sin6_scope_id; sa46.sa6.sin6_scope_id = sa6->sin6_scope_id;
sa46.sa6.sin6_flowinfo = ((struct sockaddr_in6 *)result->ai_addr)->sin6_flowinfo; sa46.sa6.sin6_flowinfo = sa6->sin6_flowinfo;
break; break;
default: default:
lwsl_err("Unknown address family\n"); lwsl_err("Unknown address family\n");
@ -211,14 +343,11 @@ lws_client_connect_2(struct lws *wsi)
if (!lws_socket_is_valid(wsi->desc.sockfd)) { if (!lws_socket_is_valid(wsi->desc.sockfd)) {
#if defined(LWS_WITH_LIBUV) if (wsi->context->event_loop_ops->check_client_connect_ok &&
if (LWS_LIBUV_ENABLED(context)) wsi->context->event_loop_ops->check_client_connect_ok(wsi)) {
if (lws_libuv_check_watcher_active(wsi)) { cce = "waiting for event loop watcher to close";
lwsl_warn("Waiting for libuv watcher to close\n"); goto oom4;
cce = "waiting for libuv watcher to close"; }
goto oom4;
}
#endif
#ifdef LWS_WITH_IPV6 #ifdef LWS_WITH_IPV6
if (wsi->ipv6) if (wsi->ipv6)
@ -240,13 +369,12 @@ lws_client_connect_2(struct lws *wsi)
goto oom4; goto oom4;
} }
wsi->mode = LWSCM_WSCL_WAITING_CONNECT; lwsi_set_state(wsi, LRS_WAITING_CONNECT);
lws_libev_accept(wsi, wsi->desc); if (wsi->context->event_loop_ops->accept)
lws_libuv_accept(wsi, wsi->desc); wsi->context->event_loop_ops->accept(wsi);
lws_libevent_accept(wsi, wsi->desc);
if (insert_wsi_socket_into_fds(context, wsi)) { if (__insert_wsi_socket_into_fds(wsi->context, wsi)) {
compatible_close(wsi->desc.sockfd); compatible_close(wsi->desc.sockfd);
cce = "insert wsi failed"; cce = "insert wsi failed";
goto oom4; goto oom4;
@ -328,10 +456,11 @@ lws_client_connect_2(struct lws *wsi)
lwsl_client("connected\n"); lwsl_client("connected\n");
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
/* we are connected to server, or proxy */ /* we are connected to server, or proxy */
/* http proxy */ /* http proxy */
if (wsi->vhost->http_proxy_port) { if (wsi->vhost->http.http_proxy_port) {
/* /*
* OK from now on we talk via the proxy, so connect to that * OK from now on we talk via the proxy, so connect to that
@ -340,11 +469,11 @@ lws_client_connect_2(struct lws *wsi)
* leaving old string/frag there but unreferenced) * leaving old string/frag there but unreferenced)
*/ */
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
wsi->vhost->http_proxy_address)) wsi->vhost->http.http_proxy_address))
goto failed; goto failed;
wsi->c_port = wsi->vhost->http_proxy_port; wsi->c_port = wsi->vhost->http.http_proxy_port;
n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen, n = send(wsi->desc.sockfd, (char *)pt->serv_buf, (int)plen,
MSG_NOSIGNAL); MSG_NOSIGNAL);
if (n < 0) { if (n < 0) {
lwsl_debug("ERROR writing to proxy socket\n"); lwsl_debug("ERROR writing to proxy socket\n");
@ -355,10 +484,11 @@ lws_client_connect_2(struct lws *wsi)
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
AWAITING_TIMEOUT); AWAITING_TIMEOUT);
wsi->mode = LWSCM_WSCL_WAITING_PROXY_REPLY; lwsi_set_state(wsi, LRS_WAITING_PROXY_REPLY);
return wsi; return wsi;
} }
#endif
#if defined(LWS_WITH_SOCKS5) #if defined(LWS_WITH_SOCKS5)
/* socks proxy */ /* socks proxy */
else if (wsi->vhost->socks_proxy_port) { else if (wsi->vhost->socks_proxy_port) {
@ -373,72 +503,105 @@ lws_client_connect_2(struct lws *wsi)
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY, lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY,
AWAITING_TIMEOUT); AWAITING_TIMEOUT);
wsi->mode = LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY; lwsi_set_state(wsi, LRS_WAITING_SOCKS_GREETING_REPLY);
return wsi; return wsi;
} }
#endif #endif
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
send_hs:
/* if (wsi_piggyback &&
* provoke service to issue the handshake directly !lws_dll_is_null(&wsi->dll_client_transaction_queue)) {
* we need to do it this way because in the proxy case, this is the /*
* next state and executed only if and when we get a good proxy * We are pipelining on an already-established connection...
* response inside the state machine... but notice in SSL case this * we can skip tls establishment.
* may not have sent anything yet with 0 return, and won't until some */
* many retries from main loop. To stop that becoming endless,
* cover with a timeout.
*/
lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
AWAITING_TIMEOUT);
wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE; /*
pfd.fd = wsi->desc.sockfd; * we can't send our headers directly, because they have to
pfd.events = LWS_POLLIN; * be sent when the parent is writeable. The parent will check
pfd.revents = LWS_POLLIN; * for anybody on his client transaction queue that is in
* LRS_H1C_ISSUE_HANDSHAKE2, and let them write.
*
* If we are trying to do this too early, before the master
* connection has written his own headers, then it will just
* wait in the queue until it's possible to send them.
*/
lws_callback_on_writable(wsi_piggyback);
lwsl_info("%s: wsi %p: waiting to send headers (parent state %x)\n",
__func__, wsi, lwsi_state(wsi_piggyback));
} else {
lwsl_info("%s: wsi %p: client creating own connection\n",
__func__, wsi);
n = lws_service_fd(context, &pfd); /* we are making our own connection */
if (n < 0) { lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE);
cce = "first service failed";
goto failed; /*
* provoke service to issue the handshake directly.
*
* we need to do it this way because in the proxy case, this is
* the next state and executed only if and when we get a good
* proxy response inside the state machine... but notice in
* SSL case this may not have sent anything yet with 0 return,
* and won't until many retries from main loop. To stop that
* becoming endless, cover with a timeout.
*/
lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE,
AWAITING_TIMEOUT);
pfd.fd = wsi->desc.sockfd;
pfd.events = LWS_POLLIN;
pfd.revents = LWS_POLLIN;
n = lws_service_fd(context, &pfd);
if (n < 0) {
cce = "first service failed";
goto failed;
}
if (n) /* returns 1 on failure after closing wsi */
return NULL;
} }
if (n) /* returns 1 on failure after closing wsi */ #endif
return NULL;
return wsi; return wsi;
oom4: oom4:
/* we're closing, losing some rx is OK */ if (lwsi_role_client(wsi) && lwsi_state_est(wsi)) {
lws_header_table_force_to_detachable_state(wsi); wsi->protocol->callback(wsi,
if (wsi->mode == LWSCM_HTTP_CLIENT ||
wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED ||
wsi->mode == LWSCM_WSCL_WAITING_CONNECT) {
wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_CLIENT_CONNECTION_ERROR, LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
wsi->user_space, (void *)cce, strlen(cce)); wsi->user_space, (void *)cce, strlen(cce));
wsi->already_did_cce = 1; wsi->already_did_cce = 1;
} }
/* take care that we might be inserted in fds already */ /* take care that we might be inserted in fds already */
if (wsi->position_in_fds_table != -1) if (wsi->position_in_fds_table != LWS_NO_FDS_POS)
goto failed1; goto failed1;
lws_remove_from_timeout_list(wsi); lws_remove_from_timeout_list(wsi);
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
lws_header_table_detach(wsi, 0); lws_header_table_detach(wsi, 0);
#endif
lws_client_stash_destroy(wsi);
lws_free_set_NULL(wsi->client_hostname_copy);
lws_free(wsi); lws_free(wsi);
return NULL; return NULL;
failed: failed:
wsi->vhost->protocols[0].callback(wsi, wsi->protocol->callback(wsi,
LWS_CALLBACK_CLIENT_CONNECTION_ERROR, LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
wsi->user_space, (void *)cce, strlen(cce)); wsi->user_space, (void *)cce, strlen(cce));
wsi->already_did_cce = 1; wsi->already_did_cce = 1;
failed1: failed1:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2");
return NULL; return NULL;
} }
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
/** /**
* lws_client_reset() - retarget a connected wsi to start over with a new connection (ie, redirect) * lws_client_reset() - retarget a connected wsi to start over with a new connection (ie, redirect)
* this only works if still in HTTP, ie, not upgraded yet * this only works if still in HTTP, ie, not upgraded yet
@ -452,7 +615,8 @@ LWS_VISIBLE struct lws *
lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
const char *path, const char *host) const char *path, const char *host)
{ {
char origin[300] = "", protocol[300] = "", method[32] = "", iface[16] = "", *p; char origin[300] = "", protocol[300] = "", method[32] = "",
iface[16] = "", alpn[32] = "", *p;
struct lws *wsi = *pwsi; struct lws *wsi = *pwsi;
if (wsi->redirects == 3) { if (wsi->redirects == 3) {
@ -463,49 +627,42 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN); p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN);
if (p) if (p)
strncpy(origin, p, sizeof(origin) - 1); lws_strncpy(origin, p, sizeof(origin));
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
if (p) if (p)
strncpy(protocol, p, sizeof(protocol) - 1); lws_strncpy(protocol, p, sizeof(protocol));
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
if (p) if (p)
strncpy(method, p, sizeof(method) - 1); lws_strncpy(method, p, sizeof(method));
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
if (p) if (p)
strncpy(method, p, sizeof(iface) - 1); lws_strncpy(iface, p, sizeof(iface));
p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ALPN);
if (p)
lws_strncpy(alpn, p, sizeof(alpn));
lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d\n", lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d\n",
address, port, path, ssl); address, port, path, ssl);
/* close the connection by hand */ /* close the connection by hand */
#ifdef LWS_OPENSSL_SUPPORT #if defined(LWS_WITH_TLS)
lws_ssl_close(wsi); lws_ssl_close(wsi);
#endif #endif
#ifdef LWS_WITH_LIBUV if (wsi->context->event_loop_ops->close_handle_manually)
if (LWS_LIBUV_ENABLED(wsi->context)) { wsi->context->event_loop_ops->close_handle_manually(wsi);
lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi); else
/* compatible_close(wsi->desc.sockfd);
* libuv has to do his own close handle processing asynchronously
* but once it starts we can do everything else synchronously,
* including trash wsi->desc.sockfd since it took a copy.
*
* When it completes it will call compatible_close()
*/
lws_libuv_closehandle_manually(wsi);
} else
#else
compatible_close(wsi->desc.sockfd);
#endif
remove_wsi_socket_from_fds(wsi); __remove_wsi_socket_from_fds(wsi);
#ifdef LWS_OPENSSL_SUPPORT #if defined(LWS_WITH_TLS)
wsi->use_ssl = ssl; wsi->tls.use_ssl = ssl;
#else #else
if (ssl) { if (ssl) {
lwsl_err("%s: not configured for ssl\n", __func__); lwsl_err("%s: not configured for ssl\n", __func__);
@ -514,12 +671,12 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
#endif #endif
wsi->desc.sockfd = LWS_SOCK_INVALID; wsi->desc.sockfd = LWS_SOCK_INVALID;
wsi->state = LWSS_CLIENT_UNCONNECTED; lwsi_set_state(wsi, LRS_UNCONNECTED);
wsi->protocol = NULL; wsi->protocol = NULL;
wsi->pending_timeout = NO_PENDING_TIMEOUT; wsi->pending_timeout = NO_PENDING_TIMEOUT;
wsi->c_port = port; wsi->c_port = port;
wsi->hdr_parsing_completed = 0; wsi->hdr_parsing_completed = 0;
_lws_header_table_reset(wsi->u.hdr.ah); _lws_header_table_reset(wsi->http.ah);
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address)) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address))
return NULL; return NULL;
@ -544,6 +701,10 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE, if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE,
iface)) iface))
return NULL; return NULL;
if (alpn[0])
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ALPN,
alpn))
return NULL;
origin[0] = '/'; origin[0] = '/';
strncpy(&origin[1], path, sizeof(origin) - 2); strncpy(&origin[1], path, sizeof(origin) - 2);
@ -683,26 +844,68 @@ html_parser_cb(const hubbub_token *token, void *pw)
} }
#endif #endif
#endif
static char *
lws_strdup(const char *s)
{
char *d = lws_malloc(strlen(s) + 1, "strdup");
if (d)
strcpy(d, s);
return d;
}
void
lws_client_stash_destroy(struct lws *wsi)
{
if (!wsi || !wsi->stash)
return;
lws_free_set_NULL(wsi->stash->address);
lws_free_set_NULL(wsi->stash->path);
lws_free_set_NULL(wsi->stash->host);
lws_free_set_NULL(wsi->stash->origin);
lws_free_set_NULL(wsi->stash->protocol);
lws_free_set_NULL(wsi->stash->method);
lws_free_set_NULL(wsi->stash->iface);
lws_free_set_NULL(wsi->stash->alpn);
lws_free_set_NULL(wsi->stash);
}
LWS_VISIBLE struct lws * LWS_VISIBLE struct lws *
lws_client_connect_via_info(struct lws_client_connect_info *i) lws_client_connect_via_info(struct lws_client_connect_info *i)
{ {
struct lws *wsi; struct lws *wsi;
int v = SPEC_LATEST_SUPPORTED;
const struct lws_protocols *p; const struct lws_protocols *p;
const char *local = i->protocol;
if (i->context->requested_kill) if (i->context->requested_kill)
return NULL; return NULL;
if (!i->context->protocol_init_done) if (!i->context->protocol_init_done)
lws_protocol_init(i->context); lws_protocol_init(i->context);
/*
* If we have .local_protocol_name, use it to select the
* local protocol handler to bind to. Otherwise use .protocol if
* http[s].
*/
if (i->local_protocol_name)
local = i->local_protocol_name;
wsi = lws_zalloc(sizeof(struct lws), "client wsi"); wsi = lws_zalloc(sizeof(struct lws), "client wsi");
if (wsi == NULL) if (wsi == NULL)
goto bail; goto bail;
wsi->context = i->context; wsi->context = i->context;
#if defined(LWS_ROLE_H1)
/* assert the mode and union status (hdr) clearly */ /* assert the mode and union status (hdr) clearly */
lws_union_transition(wsi, LWSCM_HTTP_CLIENT); lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1);
#else
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_raw_skt);
#endif
wsi->desc.sockfd = LWS_SOCK_INVALID; wsi->desc.sockfd = LWS_SOCK_INVALID;
/* 1) fill up the wsi with stuff from the connect_info as far as it /* 1) fill up the wsi with stuff from the connect_info as far as it
@ -710,26 +913,52 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
* not even be able to get ahold of an ah at this point. * not even be able to get ahold of an ah at this point.
*/ */
/* -1 means just use latest supported */ if (!i->method) /* ie, ws */
if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one) #if defined(LWS_ROLE_WS)
v = i->ietf_version_or_minus_one; if (lws_create_client_ws_object(i, wsi))
return NULL;
#else
return NULL;
#endif
wsi->ietf_spec_revision = v;
wsi->user_space = NULL; wsi->user_space = NULL;
wsi->state = LWSS_CLIENT_UNCONNECTED;
wsi->pending_timeout = NO_PENDING_TIMEOUT; wsi->pending_timeout = NO_PENDING_TIMEOUT;
wsi->position_in_fds_table = -1; wsi->position_in_fds_table = LWS_NO_FDS_POS;
wsi->c_port = i->port; wsi->c_port = i->port;
wsi->vhost = i->vhost; wsi->vhost = i->vhost;
if (!wsi->vhost) if (!wsi->vhost)
wsi->vhost = i->context->vhost_list; wsi->vhost = i->context->vhost_list;
if (!wsi->vhost) {
lwsl_err("At least one vhost in the context is required\n");
goto bail;
}
wsi->protocol = &wsi->vhost->protocols[0]; wsi->protocol = &wsi->vhost->protocols[0];
wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE);
/* for http[s] connection, allow protocol selection by name */ /* reasonable place to start */
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
#if defined(LWS_ROLE_H1)
&role_ops_h1);
#else
&role_ops_raw_skt);
#endif
if (i->method && i->vhost && i->protocol) { /*
p = lws_vhost_name_to_protocol(i->vhost, i->protocol); * 1) for http[s] connection, allow protocol selection by name
* 2) for ws[s], if local_protocol_name given also use it for
* local protocol binding... this defeats the server
* protocol negotiation if so
*
* Otherwise leave at protocols[0]... the server will tell us
* which protocol we are associated with since we can give it a
* list.
*/
if (/*(i->method || i->local_protocol_name) && */local) {
lwsl_info("binding to %s\n", local);
p = lws_vhost_name_to_protocol(wsi->vhost, local);
if (p) if (p)
wsi->protocol = p; wsi->protocol = p;
} }
@ -745,10 +974,10 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
if (lws_ensure_user_space(wsi)) if (lws_ensure_user_space(wsi))
goto bail; goto bail;
#ifdef LWS_OPENSSL_SUPPORT #if defined(LWS_WITH_TLS)
wsi->use_ssl = i->ssl_connection; wsi->tls.use_ssl = i->ssl_connection;
#else #else
if (i->ssl_connection) { if (i->ssl_connection & LCCSCF_USE_SSL) {
lwsl_err("libwebsockets not configured for ssl\n"); lwsl_err("libwebsockets not configured for ssl\n");
goto bail; goto bail;
} }
@ -760,47 +989,63 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
* things pointed to have gone out of scope. * things pointed to have gone out of scope.
*/ */
wsi->u.hdr.stash = lws_malloc(sizeof(*wsi->u.hdr.stash), "client stash"); wsi->stash = lws_zalloc(sizeof(*wsi->stash), "client stash");
if (!wsi->u.hdr.stash) { if (!wsi->stash) {
lwsl_err("%s: OOM\n", __func__); lwsl_err("%s: OOM\n", __func__);
goto bail; goto bail1;
} }
wsi->u.hdr.stash->origin[0] = '\0'; wsi->stash->address = lws_strdup(i->address);
wsi->u.hdr.stash->protocol[0] = '\0'; wsi->stash->path = lws_strdup(i->path);
wsi->u.hdr.stash->method[0] = '\0'; wsi->stash->host = lws_strdup(i->host);
wsi->u.hdr.stash->iface[0] = '\0';
strncpy(wsi->u.hdr.stash->address, i->address, if (!wsi->stash->address || !wsi->stash->path || !wsi->stash->host)
sizeof(wsi->u.hdr.stash->address) - 1); goto bail1;
strncpy(wsi->u.hdr.stash->path, i->path,
sizeof(wsi->u.hdr.stash->path) - 1);
strncpy(wsi->u.hdr.stash->host, i->host,
sizeof(wsi->u.hdr.stash->host) - 1);
if (i->origin)
strncpy(wsi->u.hdr.stash->origin, i->origin,
sizeof(wsi->u.hdr.stash->origin) - 1);
if (i->protocol)
strncpy(wsi->u.hdr.stash->protocol, i->protocol,
sizeof(wsi->u.hdr.stash->protocol) - 1);
if (i->method)
strncpy(wsi->u.hdr.stash->method, i->method,
sizeof(wsi->u.hdr.stash->method) - 1);
if (i->iface)
strncpy(wsi->u.hdr.stash->iface, i->iface,
sizeof(wsi->u.hdr.stash->iface) - 1);
wsi->u.hdr.stash->address[sizeof(wsi->u.hdr.stash->address) - 1] = '\0'; if (i->origin) {
wsi->u.hdr.stash->path[sizeof(wsi->u.hdr.stash->path) - 1] = '\0'; wsi->stash->origin = lws_strdup(i->origin);
wsi->u.hdr.stash->host[sizeof(wsi->u.hdr.stash->host) - 1] = '\0'; if (!wsi->stash->origin)
wsi->u.hdr.stash->origin[sizeof(wsi->u.hdr.stash->origin) - 1] = '\0'; goto bail1;
wsi->u.hdr.stash->protocol[sizeof(wsi->u.hdr.stash->protocol) - 1] = '\0'; }
wsi->u.hdr.stash->method[sizeof(wsi->u.hdr.stash->method) - 1] = '\0'; if (i->protocol) {
wsi->u.hdr.stash->iface[sizeof(wsi->u.hdr.stash->iface) - 1] = '\0'; wsi->stash->protocol = lws_strdup(i->protocol);
if (!wsi->stash->protocol)
goto bail1;
}
if (i->method) {
wsi->stash->method = lws_strdup(i->method);
if (!wsi->stash->method)
goto bail1;
}
if (i->iface) {
wsi->stash->iface = lws_strdup(i->iface);
if (!wsi->stash->iface)
goto bail1;
}
/*
* For ws, default to http/1.1 only. If i->alpn is set, defer to
* whatever he has set in there (eg, "h2").
*
* The problem is he has to commit to h2 before he can find out if the
* server has the SETTINGS for ws-over-h2 enabled; if not then ws is
* not possible on that connection. So we only try it if he
* assertively said to use h2 alpn.
*/
if (!i->method && !i->alpn) {
wsi->stash->alpn = lws_strdup("http/1.1");
if (!wsi->stash->alpn)
goto bail1;
} else
if (i->alpn) {
wsi->stash->alpn = lws_strdup(i->alpn);
if (!wsi->stash->alpn)
goto bail1;
}
if (i->pwsi) if (i->pwsi)
*i->pwsi = wsi; *i->pwsi = wsi;
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
/* if we went on the waiting list, no probs just return the wsi /* if we went on the waiting list, no probs just return the wsi
* when we get the ah, now or later, he will call * when we get the ah, now or later, he will call
* lws_client_connect_via_info2() below. * lws_client_connect_via_info2() below.
@ -810,9 +1055,11 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
* if we failed here, the connection is already closed * if we failed here, the connection is already closed
* and freed. * and freed.
*/ */
goto bail1; goto bail2;
} }
#endif
if (i->parent_wsi) { if (i->parent_wsi) {
lwsl_info("%s: created child %p of parent %p\n", __func__, lwsl_info("%s: created child %p of parent %p\n", __func__,
wsi, i->parent_wsi); wsi, i->parent_wsi);
@ -822,17 +1069,21 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
} }
#ifdef LWS_WITH_HTTP_PROXY #ifdef LWS_WITH_HTTP_PROXY
if (i->uri_replace_to) if (i->uri_replace_to)
wsi->rw = lws_rewrite_create(wsi, html_parser_cb, wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb,
i->uri_replace_from, i->uri_replace_from,
i->uri_replace_to); i->uri_replace_to);
#endif #endif
return wsi; return wsi;
bail1:
lws_client_stash_destroy(wsi);
bail: bail:
lws_free(wsi); lws_free(wsi);
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
bail1: bail2:
#endif
if (i->pwsi) if (i->pwsi)
*i->pwsi = NULL; *i->pwsi = NULL;
@ -842,28 +1093,27 @@ bail1:
struct lws * struct lws *
lws_client_connect_via_info2(struct lws *wsi) lws_client_connect_via_info2(struct lws *wsi)
{ {
struct client_info_stash *stash = wsi->u.hdr.stash; struct client_info_stash *stash = wsi->stash;
if (!stash) if (!stash)
return wsi; return wsi;
/* /*
* we're not necessarily in a position to action these right away, * we're not necessarily in a position to action these right away,
* stash them... we only need during connect phase so u.hdr is fine * stash them... we only need during connect phase so into a temp
* allocated stash
*/ */
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
stash->address)) stash->address))
goto bail1; goto bail1;
/* these only need u.hdr lifetime as well */
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path)) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path))
goto bail1; goto bail1;
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host)) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host))
goto bail1; goto bail1;
if (stash->origin[0]) if (stash->origin)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN,
stash->origin)) stash->origin))
goto bail1; goto bail1;
@ -871,45 +1121,28 @@ lws_client_connect_via_info2(struct lws *wsi)
* this is a list of protocols we tell the server we're okay with * this is a list of protocols we tell the server we're okay with
* stash it for later when we compare server response with it * stash it for later when we compare server response with it
*/ */
if (stash->protocol[0]) if (stash->protocol)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
stash->protocol)) stash->protocol))
goto bail1; goto bail1;
if (stash->method[0]) if (stash->method)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD, if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD,
stash->method)) stash->method))
goto bail1; goto bail1;
if (stash->iface[0]) if (stash->iface)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE, if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE,
stash->iface)) stash->iface))
goto bail1; goto bail1;
if (stash->alpn)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ALPN,
stash->alpn))
goto bail1;
#if defined(LWS_WITH_SOCKS5) #if defined(LWS_WITH_SOCKS5)
if (!wsi->vhost->socks_proxy_port) if (!wsi->vhost->socks_proxy_port)
lws_free_set_NULL(wsi->u.hdr.stash); lws_client_stash_destroy(wsi);
#endif #endif
/*
* Check with each extension if it is able to route and proxy this
* connection for us. For example, an extension like x-google-mux
* can handle this and then we don't need an actual socket for this
* connection.
*/
if (lws_ext_cb_all_exts(wsi->context, wsi,
LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION,
(void *)stash->address,
wsi->c_port) > 0) {
lwsl_client("lws_client_connect: ext handling conn\n");
lws_set_timeout(wsi,
PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE,
AWAITING_TIMEOUT);
wsi->mode = LWSCM_WSCL_WAITING_EXTENSION_CONNECT;
return wsi;
}
lwsl_client("lws_client_connect: direct conn\n");
wsi->context->count_wsi_allocated++; wsi->context->count_wsi_allocated++;
return lws_client_connect_2(wsi); return lws_client_connect_2(wsi);
@ -917,7 +1150,7 @@ lws_client_connect_via_info2(struct lws *wsi)
bail1: bail1:
#if defined(LWS_WITH_SOCKS5) #if defined(LWS_WITH_SOCKS5)
if (!wsi->vhost->socks_proxy_port) if (!wsi->vhost->socks_proxy_port)
lws_free_set_NULL(wsi->u.hdr.stash); lws_free_set_NULL(wsi->stash);
#endif #endif
return NULL; return NULL;
@ -1003,14 +1236,14 @@ void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
/* length of the user name */ /* length of the user name */
pt->serv_buf[len++] = n; pt->serv_buf[len++] = n;
/* user name */ /* user name */
strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_user, lws_strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_user,
context->pt_serv_buf_size - len); context->pt_serv_buf_size - len + 1);
len += n; len += n;
/* length of the password */ /* length of the password */
pt->serv_buf[len++] = passwd_len; pt->serv_buf[len++] = passwd_len;
/* password */ /* password */
strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_password, lws_strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_password,
context->pt_serv_buf_size - len); context->pt_serv_buf_size - len + 1);
len += passwd_len; len += passwd_len;
break; break;
@ -1029,9 +1262,9 @@ void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
n = len++; n = len++;
/* the address we tell SOCKS proxy to connect to */ /* the address we tell SOCKS proxy to connect to */
strncpy((char *)&(pt->serv_buf[len]), wsi->u.hdr.stash->address, lws_strncpy((char *)&(pt->serv_buf[len]), wsi->stash->address,
context->pt_serv_buf_size - len); context->pt_serv_buf_size - len + 1);
len += strlen(wsi->u.hdr.stash->address); len += strlen(wsi->stash->address);
net_num = htons(wsi->c_port); net_num = htons(wsi->c_port);
/* the port we tell SOCKS proxy to connect to */ /* the port we tell SOCKS proxy to connect to */
@ -1039,7 +1272,7 @@ void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
pt->serv_buf[len++] = p[1]; pt->serv_buf[len++] = p[1];
/* the length of the address, excluding port */ /* the length of the address, excluding port */
pt->serv_buf[n] = strlen(wsi->u.hdr.stash->address); pt->serv_buf[n] = strlen(wsi->stash->address);
break; break;
default: default:

File diff suppressed because it is too large Load Diff

View File

@ -19,12 +19,12 @@
* MA 02110-1301 USA * MA 02110-1301 USA
*/ */
#include "private-libwebsockets.h" #include "core/private.h"
#include "lextable-strings.h" #include "lextable-strings.h"
const unsigned char *lws_token_to_string(enum lws_token_indexes token) const unsigned char *
lws_token_to_string(enum lws_token_indexes token)
{ {
if ((unsigned int)token >= ARRAY_SIZE(set)) if ((unsigned int)token >= ARRAY_SIZE(set))
return NULL; return NULL;
@ -38,7 +38,7 @@ lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
unsigned char **p, unsigned char *end) unsigned char **p, unsigned char *end)
{ {
#ifdef LWS_WITH_HTTP2 #ifdef LWS_WITH_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING) if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
return lws_add_http2_header_by_name(wsi, name, return lws_add_http2_header_by_name(wsi, name,
value, length, p, end); value, length, p, end);
#else #else
@ -66,7 +66,7 @@ int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
unsigned char *end) unsigned char *end)
{ {
#ifdef LWS_WITH_HTTP2 #ifdef LWS_WITH_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING) if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
return 0; return 0;
#else #else
(void)wsi; (void)wsi;
@ -79,6 +79,25 @@ int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
return 0; return 0;
} }
int
lws_finalize_write_http_header(struct lws *wsi, unsigned char *start,
unsigned char **pp, unsigned char *end)
{
unsigned char *p;
int len;
if (lws_finalize_http_header(wsi, pp, end))
return 1;
p = *pp;
len = lws_ptr_diff(p, start);
if (lws_write(wsi, start, len, LWS_WRITE_HTTP_HEADERS) != len)
return 1;
return 0;
}
int int
lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token, lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
const unsigned char *value, int length, const unsigned char *value, int length,
@ -86,13 +105,14 @@ lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
{ {
const unsigned char *name; const unsigned char *name;
#ifdef LWS_WITH_HTTP2 #ifdef LWS_WITH_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING) if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
return lws_add_http2_header_by_token(wsi, token, value, return lws_add_http2_header_by_token(wsi, token, value,
length, p, end); length, p, end);
#endif #endif
name = lws_token_to_string(token); name = lws_token_to_string(token);
if (!name) if (!name)
return 1; return 1;
return lws_add_http_header_by_name(wsi, name, value, length, p, end); return lws_add_http_header_by_name(wsi, name, value, length, p, end);
} }
@ -107,8 +127,31 @@ int lws_add_http_header_content_length(struct lws *wsi,
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)b, n, p, end)) (unsigned char *)b, n, p, end))
return 1; return 1;
wsi->u.http.tx_content_length = content_length; wsi->http.tx_content_length = content_length;
wsi->u.http.tx_content_remain = content_length; wsi->http.tx_content_remain = content_length;
lwsl_info("%s: wsi %p: tx_content_length/remain %llu\n", __func__,
wsi, (unsigned long long)content_length);
return 0;
}
int
lws_add_http_common_headers(struct lws *wsi, unsigned int code,
const char *content_type, lws_filepos_t content_len,
unsigned char **p, unsigned char *end)
{
if (lws_add_http_header_status(wsi, code, p, end))
return 1;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)content_type,
(int)strlen(content_type), p, end))
return 1;
if (content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN &&
lws_add_http_header_content_length(wsi, content_len, p, end))
return 1;
return 0; return 0;
} }
@ -157,11 +200,11 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
int n; int n;
#ifdef LWS_WITH_ACCESS_LOG #ifdef LWS_WITH_ACCESS_LOG
wsi->access_log.response = code; wsi->http.access_log.response = code;
#endif #endif
#ifdef LWS_WITH_HTTP2 #ifdef LWS_WITH_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING) if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
return lws_add_http2_header_status(wsi, code, p, end); return lws_add_http2_header_status(wsi, code, p, end);
#endif #endif
if (code >= 400 && code < (400 + ARRAY_SIZE(err400))) if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
@ -171,18 +214,16 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
if (code == 100) if (code == 100)
description = "Continue"; description = "Continue";
if (code == 200) if (code == 200)
description = "OK"; description = "OK";
if (code == 304) if (code == 304)
description = "Not Modified"; description = "Not Modified";
else else
if (code >= 300 && code < 400) if (code >= 300 && code < 400)
description = "Redirect"; description = "Redirect";
if (wsi->u.http.request_version < ARRAY_SIZE(hver)) if (wsi->http.request_version < ARRAY_SIZE(hver))
p1 = hver[wsi->u.http.request_version]; p1 = hver[wsi->http.request_version];
else else
p1 = hver[0]; p1 = hver[0];
@ -196,7 +237,7 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
if (lws_add_http_header_by_name(wsi, if (lws_add_http_header_by_name(wsi,
(const unsigned char *)headers->name, (const unsigned char *)headers->name,
(unsigned char *)headers->value, (unsigned char *)headers->value,
strlen(headers->value), p, end)) (int)strlen(headers->value), p, end))
return 1; return 1;
headers = headers->next; headers = headers->next;
@ -231,6 +272,26 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
int n = 0, m = 0, len; int n = 0, m = 0, len;
char slen[20]; char slen[20];
if (!wsi->vhost) {
lwsl_err("%s: wsi not bound to vhost\n", __func__);
return 1;
}
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
if (!wsi->handling_404 &&
wsi->vhost->http.error_document_404 &&
code == HTTP_STATUS_NOT_FOUND)
/* we should do a redirect, and do the 404 there */
if (lws_http_redirect(wsi, HTTP_STATUS_FOUND,
(uint8_t *)wsi->vhost->http.error_document_404,
(int)strlen(wsi->vhost->http.error_document_404),
&p, end) > 0)
return 0;
#endif
/* if the redirect failed, just do a simple status */
p = start;
if (!html_body) if (!html_body)
html_body = ""; html_body = "";
@ -242,12 +303,11 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
&p, end)) &p, end))
return 1; return 1;
len = 35 + strlen(html_body) + sprintf(slen, "%d", code); len = 35 + (int)strlen(html_body) + sprintf(slen, "%d", code);
n = sprintf(slen, "%d", len); n = sprintf(slen, "%d", len);
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)slen, n, (unsigned char *)slen, n, &p, end))
&p, end))
return 1; return 1;
if (lws_finalize_http_header(wsi, &p, end)) if (lws_finalize_http_header(wsi, &p, end))
@ -270,7 +330,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
* Solve it by writing the headers now... * Solve it by writing the headers now...
*/ */
m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS); m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
if (m != (int)(p - start)) if (m != lws_ptr_diff(p, start))
return 1; return 1;
/* /*
@ -281,15 +341,15 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
len = sprintf((char *)body, len = sprintf((char *)body,
"<html><body><h1>%u</h1>%s</body></html>", "<html><body><h1>%u</h1>%s</body></html>",
code, html_body); code, html_body);
wsi->u.http.tx_content_length = len; wsi->http.tx_content_length = len;
wsi->u.http.tx_content_remain = len; wsi->http.tx_content_remain = len;
wsi->u.h2.pending_status_body = lws_malloc(len + LWS_PRE + 1, wsi->h2.pending_status_body = lws_malloc(len + LWS_PRE + 1,
"pending status body"); "pending status body");
if (!wsi->u.h2.pending_status_body) if (!wsi->h2.pending_status_body)
return -1; return -1;
strcpy(wsi->u.h2.pending_status_body + LWS_PRE, strcpy(wsi->h2.pending_status_body + LWS_PRE,
(const char *)body); (const char *)body);
lws_callback_on_writable(wsi); lws_callback_on_writable(wsi);
@ -305,15 +365,12 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
"<html><body><h1>%u</h1>%s</body></html>", "<html><body><h1>%u</h1>%s</body></html>",
code, html_body); code, html_body);
n = (int)(p - start); n = lws_ptr_diff(p, start);
m = lws_write(wsi, start, n, LWS_WRITE_HTTP); m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
if (m != n) if (m != n)
return 1; return 1;
} }
lwsl_notice("%s: return\n", __func__);
return m != n; return m != n;
} }
@ -322,34 +379,29 @@ lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
unsigned char **p, unsigned char *end) unsigned char **p, unsigned char *end)
{ {
unsigned char *start = *p; unsigned char *start = *p;
int n;
if (lws_add_http_header_status(wsi, code, p, end)) if (lws_add_http_header_status(wsi, code, p, end))
return -1; return -1;
if (lws_add_http_header_by_token(wsi, if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION, loc, len,
WSI_TOKEN_HTTP_LOCATION, p, end))
loc, len, p, end))
return -1; return -1;
/* /*
* if we're going with http/1.1 and keepalive, we have to give fake * if we're going with http/1.1 and keepalive, we have to give fake
* content metadata so the client knows we completed the transaction and * content metadata so the client knows we completed the transaction and
* it can do the redirect... * it can do the redirect...
*/ */
if (lws_add_http_header_by_token(wsi, if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)"text/html", 9, p,
(unsigned char *)"text/html", 9, end))
p, end))
return -1; return -1;
if (lws_add_http_header_by_token(wsi, if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
WSI_TOKEN_HTTP_CONTENT_LENGTH, (unsigned char *)"0", 1, p, end))
(unsigned char *)"0", 1, p, end))
return -1; return -1;
if (lws_finalize_http_header(wsi, p, end)) if (lws_finalize_http_header(wsi, p, end))
return -1; return -1;
n = lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END); return lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS |
LWS_WRITE_H2_STREAM_END);
return n;
} }

View File

@ -98,6 +98,10 @@ STORE_IN_ROM static const char * const set[] = {
"connect ", "connect ",
"head ", "head ",
"te:", /* http/2 wants it to reject it */ "te:", /* http/2 wants it to reject it */
"replay-nonce:", /* ACME */
":protocol", /* defined in mcmanus-httpbis-h2-ws-02 */
"x-auth-token:",
"", /* not matchable */ "", /* not matchable */

View File

@ -0,0 +1,838 @@
/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */,
0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */,
0x6F /* 'o' */, 0x51, 0x00 /* (to 0x0057 state 10) */,
0x68 /* 'h' */, 0x5D, 0x00 /* (to 0x0066 state 18) */,
0x63 /* 'c' */, 0x69, 0x00 /* (to 0x0075 state 23) */,
0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x0099 state 34) */,
0x73 /* 's' */, 0xA0, 0x00 /* (to 0x00B2 state 48) */,
0x0D /* '.' */, 0xD9, 0x00 /* (to 0x00EE state 68) */,
0x61 /* 'a' */, 0x31, 0x01 /* (to 0x0149 state 129) */,
0x69 /* 'i' */, 0x70, 0x01 /* (to 0x018B state 163) */,
0x64 /* 'd' */, 0x19, 0x02 /* (to 0x0237 state 265) */,
0x72 /* 'r' */, 0x22, 0x02 /* (to 0x0243 state 270) */,
0x3A /* ':' */, 0x56, 0x02 /* (to 0x027A state 299) */,
0x65 /* 'e' */, 0xE8, 0x02 /* (to 0x030F state 409) */,
0x66 /* 'f' */, 0x04, 0x03 /* (to 0x032E state 425) */,
0x6C /* 'l' */, 0x26, 0x03 /* (to 0x0353 state 458) */,
0x6D /* 'm' */, 0x49, 0x03 /* (to 0x0379 state 484) */,
0x74 /* 't' */, 0xB8, 0x03 /* (to 0x03EB state 578) */,
0x76 /* 'v' */, 0xD9, 0x03 /* (to 0x040F state 606) */,
0x77 /* 'w' */, 0xE6, 0x03 /* (to 0x041F state 614) */,
0x78 /* 'x' */, 0x0D, 0x04 /* (to 0x0449 state 650) */,
0x08, /* fail */
/* pos 0040: 1 */ 0xE5 /* 'e' -> */,
/* pos 0041: 2 */ 0xF4 /* 't' -> */,
/* pos 0042: 3 */ 0xA0 /* ' ' -> */,
/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */,
/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */,
0x72 /* 'r' */, 0x95, 0x01 /* (to 0x01DD state 211) */,
0x61 /* 'a' */, 0xE6, 0x03 /* (to 0x0431 state 631) */,
0x75 /* 'u' */, 0xE8, 0x03 /* (to 0x0436 state 635) */,
0x08, /* fail */
/* pos 0052: 6 */ 0xF3 /* 's' -> */,
/* pos 0053: 7 */ 0xF4 /* 't' -> */,
/* pos 0054: 8 */ 0xA0 /* ' ' -> */,
/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */,
/* pos 0057: 10 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x005E state 11) */,
0x72 /* 'r' */, 0x51, 0x00 /* (to 0x00AB state 42) */,
0x08, /* fail */
/* pos 005e: 11 */ 0xF4 /* 't' -> */,
/* pos 005f: 12 */ 0xE9 /* 'i' -> */,
/* pos 0060: 13 */ 0xEF /* 'o' -> */,
/* pos 0061: 14 */ 0xEE /* 'n' -> */,
/* pos 0062: 15 */ 0xF3 /* 's' -> */,
/* pos 0063: 16 */ 0xA0 /* ' ' -> */,
/* pos 0064: 17 */ 0x00, 0x02 /* - terminal marker 2 - */,
/* pos 0066: 18 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x0070 state 19) */,
0x74 /* 't' */, 0xBF, 0x00 /* (to 0x0128 state 110) */,
0x65 /* 'e' */, 0x04, 0x04 /* (to 0x0470 state 676) */,
0x08, /* fail */
/* pos 0070: 19 */ 0xF3 /* 's' -> */,
/* pos 0071: 20 */ 0xF4 /* 't' -> */,
/* pos 0072: 21 */ 0xBA /* ':' -> */,
/* pos 0073: 22 */ 0x00, 0x03 /* - terminal marker 3 - */,
/* pos 0075: 23 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x007C state 24) */,
0x61 /* 'a' */, 0x72, 0x01 /* (to 0x01EA state 217) */,
0x08, /* fail */
/* pos 007c: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0083 state 25) */,
0x6F /* 'o' */, 0x87, 0x01 /* (to 0x0206 state 243) */,
0x08, /* fail */
/* pos 0083: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x008A state 26) */,
0x74 /* 't' */, 0x86, 0x01 /* (to 0x020C state 248) */,
0x08, /* fail */
/* pos 008a: 26 */ 0xE5 /* 'e' -> */,
/* pos 008b: 27 */ 0xE3 /* 'c' -> */,
/* pos 008c: 28 */ 0xF4 /* 't' -> */,
/* pos 008d: 29 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0094 state 30) */,
0x20 /* ' ' */, 0xDE, 0x03 /* (to 0x046E state 675) */,
0x08, /* fail */
/* pos 0094: 30 */ 0xEF /* 'o' -> */,
/* pos 0095: 31 */ 0xEE /* 'n' -> */,
/* pos 0096: 32 */ 0xBA /* ':' -> */,
/* pos 0097: 33 */ 0x00, 0x04 /* - terminal marker 4 - */,
/* pos 0099: 34 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x00A3 state 35) */,
0x73 /* 's' */, 0x68, 0x03 /* (to 0x0404 state 596) */,
0x72 /* 'r' */, 0xA0, 0x03 /* (to 0x043F state 642) */,
0x08, /* fail */
/* pos 00a3: 35 */ 0xE7 /* 'g' -> */,
/* pos 00a4: 36 */ 0xF2 /* 'r' -> */,
/* pos 00a5: 37 */ 0xE1 /* 'a' -> */,
/* pos 00a6: 38 */ 0xE4 /* 'd' -> */,
/* pos 00a7: 39 */ 0xE5 /* 'e' -> */,
/* pos 00a8: 40 */ 0xBA /* ':' -> */,
/* pos 00a9: 41 */ 0x00, 0x05 /* - terminal marker 5 - */,
/* pos 00ab: 42 */ 0xE9 /* 'i' -> */,
/* pos 00ac: 43 */ 0xE7 /* 'g' -> */,
/* pos 00ad: 44 */ 0xE9 /* 'i' -> */,
/* pos 00ae: 45 */ 0xEE /* 'n' -> */,
/* pos 00af: 46 */ 0xBA /* ':' -> */,
/* pos 00b0: 47 */ 0x00, 0x06 /* - terminal marker 6 - */,
/* pos 00b2: 48 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x00B9 state 49) */,
0x74 /* 't' */, 0x1C, 0x03 /* (to 0x03D1 state 553) */,
0x08, /* fail */
/* pos 00b9: 49 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x00C3 state 50) */,
0x72 /* 'r' */, 0x05, 0x03 /* (to 0x03C1 state 539) */,
0x74 /* 't' */, 0x08, 0x03 /* (to 0x03C7 state 544) */,
0x08, /* fail */
/* pos 00c3: 50 */ 0xAD /* '-' -> */,
/* pos 00c4: 51 */ 0xF7 /* 'w' -> */,
/* pos 00c5: 52 */ 0xE5 /* 'e' -> */,
/* pos 00c6: 53 */ 0xE2 /* 'b' -> */,
/* pos 00c7: 54 */ 0xF3 /* 's' -> */,
/* pos 00c8: 55 */ 0xEF /* 'o' -> */,
/* pos 00c9: 56 */ 0xE3 /* 'c' -> */,
/* pos 00ca: 57 */ 0xEB /* 'k' -> */,
/* pos 00cb: 58 */ 0xE5 /* 'e' -> */,
/* pos 00cc: 59 */ 0xF4 /* 't' -> */,
/* pos 00cd: 60 */ 0xAD /* '-' -> */,
/* pos 00ce: 61 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x00E7 state 62) */,
0x65 /* 'e' */, 0x20, 0x00 /* (to 0x00F1 state 70) */,
0x6B /* 'k' */, 0x29, 0x00 /* (to 0x00FD state 81) */,
0x70 /* 'p' */, 0x38, 0x00 /* (to 0x010F state 88) */,
0x61 /* 'a' */, 0x3F, 0x00 /* (to 0x0119 state 97) */,
0x6E /* 'n' */, 0x44, 0x00 /* (to 0x0121 state 104) */,
0x76 /* 'v' */, 0x89, 0x01 /* (to 0x0269 state 284) */,
0x6F /* 'o' */, 0x8F, 0x01 /* (to 0x0272 state 292) */,
0x08, /* fail */
/* pos 00e7: 62 */ 0xF2 /* 'r' -> */,
/* pos 00e8: 63 */ 0xE1 /* 'a' -> */,
/* pos 00e9: 64 */ 0xE6 /* 'f' -> */,
/* pos 00ea: 65 */ 0xF4 /* 't' -> */,
/* pos 00eb: 66 */ 0xBA /* ':' -> */,
/* pos 00ec: 67 */ 0x00, 0x07 /* - terminal marker 7 - */,
/* pos 00ee: 68 */ 0x8A /* '.' -> */,
/* pos 00ef: 69 */ 0x00, 0x08 /* - terminal marker 8 - */,
/* pos 00f1: 70 */ 0xF8 /* 'x' -> */,
/* pos 00f2: 71 */ 0xF4 /* 't' -> */,
/* pos 00f3: 72 */ 0xE5 /* 'e' -> */,
/* pos 00f4: 73 */ 0xEE /* 'n' -> */,
/* pos 00f5: 74 */ 0xF3 /* 's' -> */,
/* pos 00f6: 75 */ 0xE9 /* 'i' -> */,
/* pos 00f7: 76 */ 0xEF /* 'o' -> */,
/* pos 00f8: 77 */ 0xEE /* 'n' -> */,
/* pos 00f9: 78 */ 0xF3 /* 's' -> */,
/* pos 00fa: 79 */ 0xBA /* ':' -> */,
/* pos 00fb: 80 */ 0x00, 0x09 /* - terminal marker 9 - */,
/* pos 00fd: 81 */ 0xE5 /* 'e' -> */,
/* pos 00fe: 82 */ 0xF9 /* 'y' -> */,
/* pos 00ff: 83 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0109 state 84) */,
0x32 /* '2' */, 0x0A, 0x00 /* (to 0x010C state 86) */,
0x3A /* ':' */, 0x62, 0x01 /* (to 0x0267 state 283) */,
0x08, /* fail */
/* pos 0109: 84 */ 0xBA /* ':' -> */,
/* pos 010a: 85 */ 0x00, 0x0A /* - terminal marker 10 - */,
/* pos 010c: 86 */ 0xBA /* ':' -> */,
/* pos 010d: 87 */ 0x00, 0x0B /* - terminal marker 11 - */,
/* pos 010f: 88 */ 0xF2 /* 'r' -> */,
/* pos 0110: 89 */ 0xEF /* 'o' -> */,
/* pos 0111: 90 */ 0xF4 /* 't' -> */,
/* pos 0112: 91 */ 0xEF /* 'o' -> */,
/* pos 0113: 92 */ 0xE3 /* 'c' -> */,
/* pos 0114: 93 */ 0xEF /* 'o' -> */,
/* pos 0115: 94 */ 0xEC /* 'l' -> */,
/* pos 0116: 95 */ 0xBA /* ':' -> */,
/* pos 0117: 96 */ 0x00, 0x0C /* - terminal marker 12 - */,
/* pos 0119: 97 */ 0xE3 /* 'c' -> */,
/* pos 011a: 98 */ 0xE3 /* 'c' -> */,
/* pos 011b: 99 */ 0xE5 /* 'e' -> */,
/* pos 011c: 100 */ 0xF0 /* 'p' -> */,
/* pos 011d: 101 */ 0xF4 /* 't' -> */,
/* pos 011e: 102 */ 0xBA /* ':' -> */,
/* pos 011f: 103 */ 0x00, 0x0D /* - terminal marker 13 - */,
/* pos 0121: 104 */ 0xEF /* 'o' -> */,
/* pos 0122: 105 */ 0xEE /* 'n' -> */,
/* pos 0123: 106 */ 0xE3 /* 'c' -> */,
/* pos 0124: 107 */ 0xE5 /* 'e' -> */,
/* pos 0125: 108 */ 0xBA /* ':' -> */,
/* pos 0126: 109 */ 0x00, 0x0E /* - terminal marker 14 - */,
/* pos 0128: 110 */ 0xF4 /* 't' -> */,
/* pos 0129: 111 */ 0xF0 /* 'p' -> */,
/* pos 012a: 112 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x0131 state 113) */,
0x32 /* '2' */, 0x10, 0x00 /* (to 0x013D state 118) */,
0x08, /* fail */
/* pos 0131: 113 */ 0xB1 /* '1' -> */,
/* pos 0132: 114 */ 0xAE /* '.' -> */,
/* pos 0133: 115 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x013A state 116) */,
0x30 /* '0' */, 0x27, 0x03 /* (to 0x045D state 660) */,
0x08, /* fail */
/* pos 013a: 116 */ 0xA0 /* ' ' -> */,
/* pos 013b: 117 */ 0x00, 0x0F /* - terminal marker 15 - */,
/* pos 013d: 118 */ 0xAD /* '-' -> */,
/* pos 013e: 119 */ 0xF3 /* 's' -> */,
/* pos 013f: 120 */ 0xE5 /* 'e' -> */,
/* pos 0140: 121 */ 0xF4 /* 't' -> */,
/* pos 0141: 122 */ 0xF4 /* 't' -> */,
/* pos 0142: 123 */ 0xE9 /* 'i' -> */,
/* pos 0143: 124 */ 0xEE /* 'n' -> */,
/* pos 0144: 125 */ 0xE7 /* 'g' -> */,
/* pos 0145: 126 */ 0xF3 /* 's' -> */,
/* pos 0146: 127 */ 0xBA /* ':' -> */,
/* pos 0147: 128 */ 0x00, 0x10 /* - terminal marker 16 - */,
/* pos 0149: 129 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x0156 state 130) */,
0x75 /* 'u' */, 0xAC, 0x00 /* (to 0x01F8 state 230) */,
0x67 /* 'g' */, 0x86, 0x01 /* (to 0x02D5 state 358) */,
0x6C /* 'l' */, 0x87, 0x01 /* (to 0x02D9 state 361) */,
0x08, /* fail */
/* pos 0156: 130 */ 0xE3 /* 'c' -> */,
/* pos 0157: 131 */ 0xE5 /* 'e' -> */,
/* pos 0158: 132 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x015F state 133) */,
0x73 /* 's' */, 0x0E, 0x00 /* (to 0x0169 state 136) */,
0x08, /* fail */
/* pos 015f: 133 */ 0xF4 /* 't' -> */,
/* pos 0160: 134 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0167 state 135) */,
0x2D /* '-' */, 0x59, 0x00 /* (to 0x01BC state 192) */,
0x08, /* fail */
/* pos 0167: 135 */ 0x00, 0x11 /* - terminal marker 17 - */,
/* pos 0169: 136 */ 0xF3 /* 's' -> */,
/* pos 016a: 137 */ 0xAD /* '-' -> */,
/* pos 016b: 138 */ 0xE3 /* 'c' -> */,
/* pos 016c: 139 */ 0xEF /* 'o' -> */,
/* pos 016d: 140 */ 0xEE /* 'n' -> */,
/* pos 016e: 141 */ 0xF4 /* 't' -> */,
/* pos 016f: 142 */ 0xF2 /* 'r' -> */,
/* pos 0170: 143 */ 0xEF /* 'o' -> */,
/* pos 0171: 144 */ 0xEC /* 'l' -> */,
/* pos 0172: 145 */ 0xAD /* '-' -> */,
/* pos 0173: 146 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x017A state 147) */,
0x61 /* 'a' */, 0x51, 0x01 /* (to 0x02C7 state 345) */,
0x08, /* fail */
/* pos 017a: 147 */ 0xE5 /* 'e' -> */,
/* pos 017b: 148 */ 0xF1 /* 'q' -> */,
/* pos 017c: 149 */ 0xF5 /* 'u' -> */,
/* pos 017d: 150 */ 0xE5 /* 'e' -> */,
/* pos 017e: 151 */ 0xF3 /* 's' -> */,
/* pos 017f: 152 */ 0xF4 /* 't' -> */,
/* pos 0180: 153 */ 0xAD /* '-' -> */,
/* pos 0181: 154 */ 0xE8 /* 'h' -> */,
/* pos 0182: 155 */ 0xE5 /* 'e' -> */,
/* pos 0183: 156 */ 0xE1 /* 'a' -> */,
/* pos 0184: 157 */ 0xE4 /* 'd' -> */,
/* pos 0185: 158 */ 0xE5 /* 'e' -> */,
/* pos 0186: 159 */ 0xF2 /* 'r' -> */,
/* pos 0187: 160 */ 0xF3 /* 's' -> */,
/* pos 0188: 161 */ 0xBA /* ':' -> */,
/* pos 0189: 162 */ 0x00, 0x12 /* - terminal marker 18 - */,
/* pos 018b: 163 */ 0xE6 /* 'f' -> */,
/* pos 018c: 164 */ 0xAD /* '-' -> */,
/* pos 018d: 165 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x019A state 166) */,
0x6E /* 'n' */, 0x20, 0x00 /* (to 0x01B0 state 181) */,
0x72 /* 'r' */, 0xA7, 0x01 /* (to 0x033A state 435) */,
0x75 /* 'u' */, 0xAB, 0x01 /* (to 0x0341 state 441) */,
0x08, /* fail */
/* pos 019a: 166 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x01A1 state 167) */,
0x61 /* 'a' */, 0x97, 0x01 /* (to 0x0334 state 430) */,
0x08, /* fail */
/* pos 01a1: 167 */ 0xE4 /* 'd' -> */,
/* pos 01a2: 168 */ 0xE9 /* 'i' -> */,
/* pos 01a3: 169 */ 0xE6 /* 'f' -> */,
/* pos 01a4: 170 */ 0xE9 /* 'i' -> */,
/* pos 01a5: 171 */ 0xE5 /* 'e' -> */,
/* pos 01a6: 172 */ 0xE4 /* 'd' -> */,
/* pos 01a7: 173 */ 0xAD /* '-' -> */,
/* pos 01a8: 174 */ 0xF3 /* 's' -> */,
/* pos 01a9: 175 */ 0xE9 /* 'i' -> */,
/* pos 01aa: 176 */ 0xEE /* 'n' -> */,
/* pos 01ab: 177 */ 0xE3 /* 'c' -> */,
/* pos 01ac: 178 */ 0xE5 /* 'e' -> */,
/* pos 01ad: 179 */ 0xBA /* ':' -> */,
/* pos 01ae: 180 */ 0x00, 0x13 /* - terminal marker 19 - */,
/* pos 01b0: 181 */ 0xEF /* 'o' -> */,
/* pos 01b1: 182 */ 0xEE /* 'n' -> */,
/* pos 01b2: 183 */ 0xE5 /* 'e' -> */,
/* pos 01b3: 184 */ 0xAD /* '-' -> */,
/* pos 01b4: 185 */ 0xED /* 'm' -> */,
/* pos 01b5: 186 */ 0xE1 /* 'a' -> */,
/* pos 01b6: 187 */ 0xF4 /* 't' -> */,
/* pos 01b7: 188 */ 0xE3 /* 'c' -> */,
/* pos 01b8: 189 */ 0xE8 /* 'h' -> */,
/* pos 01b9: 190 */ 0xBA /* ':' -> */,
/* pos 01ba: 191 */ 0x00, 0x14 /* - terminal marker 20 - */,
/* pos 01bc: 192 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x01C9 state 193) */,
0x6C /* 'l' */, 0x14, 0x00 /* (to 0x01D3 state 202) */,
0x63 /* 'c' */, 0xF4, 0x00 /* (to 0x02B6 state 330) */,
0x72 /* 'r' */, 0xFA, 0x00 /* (to 0x02BF state 338) */,
0x08, /* fail */
/* pos 01c9: 193 */ 0xEE /* 'n' -> */,
/* pos 01ca: 194 */ 0xE3 /* 'c' -> */,
/* pos 01cb: 195 */ 0xEF /* 'o' -> */,
/* pos 01cc: 196 */ 0xE4 /* 'd' -> */,
/* pos 01cd: 197 */ 0xE9 /* 'i' -> */,
/* pos 01ce: 198 */ 0xEE /* 'n' -> */,
/* pos 01cf: 199 */ 0xE7 /* 'g' -> */,
/* pos 01d0: 200 */ 0xBA /* ':' -> */,
/* pos 01d1: 201 */ 0x00, 0x15 /* - terminal marker 21 - */,
/* pos 01d3: 202 */ 0xE1 /* 'a' -> */,
/* pos 01d4: 203 */ 0xEE /* 'n' -> */,
/* pos 01d5: 204 */ 0xE7 /* 'g' -> */,
/* pos 01d6: 205 */ 0xF5 /* 'u' -> */,
/* pos 01d7: 206 */ 0xE1 /* 'a' -> */,
/* pos 01d8: 207 */ 0xE7 /* 'g' -> */,
/* pos 01d9: 208 */ 0xE5 /* 'e' -> */,
/* pos 01da: 209 */ 0xBA /* ':' -> */,
/* pos 01db: 210 */ 0x00, 0x16 /* - terminal marker 22 - */,
/* pos 01dd: 211 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01E4 state 212) */,
0x6F /* 'o' */, 0xA7, 0x01 /* (to 0x0387 state 497) */,
0x08, /* fail */
/* pos 01e4: 212 */ 0xE7 /* 'g' -> */,
/* pos 01e5: 213 */ 0xED /* 'm' -> */,
/* pos 01e6: 214 */ 0xE1 /* 'a' -> */,
/* pos 01e7: 215 */ 0xBA /* ':' -> */,
/* pos 01e8: 216 */ 0x00, 0x17 /* - terminal marker 23 - */,
/* pos 01ea: 217 */ 0xE3 /* 'c' -> */,
/* pos 01eb: 218 */ 0xE8 /* 'h' -> */,
/* pos 01ec: 219 */ 0xE5 /* 'e' -> */,
/* pos 01ed: 220 */ 0xAD /* '-' -> */,
/* pos 01ee: 221 */ 0xE3 /* 'c' -> */,
/* pos 01ef: 222 */ 0xEF /* 'o' -> */,
/* pos 01f0: 223 */ 0xEE /* 'n' -> */,
/* pos 01f1: 224 */ 0xF4 /* 't' -> */,
/* pos 01f2: 225 */ 0xF2 /* 'r' -> */,
/* pos 01f3: 226 */ 0xEF /* 'o' -> */,
/* pos 01f4: 227 */ 0xEC /* 'l' -> */,
/* pos 01f5: 228 */ 0xBA /* ':' -> */,
/* pos 01f6: 229 */ 0x00, 0x18 /* - terminal marker 24 - */,
/* pos 01f8: 230 */ 0xF4 /* 't' -> */,
/* pos 01f9: 231 */ 0xE8 /* 'h' -> */,
/* pos 01fa: 232 */ 0xEF /* 'o' -> */,
/* pos 01fb: 233 */ 0xF2 /* 'r' -> */,
/* pos 01fc: 234 */ 0xE9 /* 'i' -> */,
/* pos 01fd: 235 */ 0xFA /* 'z' -> */,
/* pos 01fe: 236 */ 0xE1 /* 'a' -> */,
/* pos 01ff: 237 */ 0xF4 /* 't' -> */,
/* pos 0200: 238 */ 0xE9 /* 'i' -> */,
/* pos 0201: 239 */ 0xEF /* 'o' -> */,
/* pos 0202: 240 */ 0xEE /* 'n' -> */,
/* pos 0203: 241 */ 0xBA /* ':' -> */,
/* pos 0204: 242 */ 0x00, 0x19 /* - terminal marker 25 - */,
/* pos 0206: 243 */ 0xEB /* 'k' -> */,
/* pos 0207: 244 */ 0xE9 /* 'i' -> */,
/* pos 0208: 245 */ 0xE5 /* 'e' -> */,
/* pos 0209: 246 */ 0xBA /* ':' -> */,
/* pos 020a: 247 */ 0x00, 0x1A /* - terminal marker 26 - */,
/* pos 020c: 248 */ 0xE5 /* 'e' -> */,
/* pos 020d: 249 */ 0xEE /* 'n' -> */,
/* pos 020e: 250 */ 0xF4 /* 't' -> */,
/* pos 020f: 251 */ 0xAD /* '-' -> */,
/* pos 0210: 252 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0220 state 253) */,
0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0231 state 260) */,
0x64 /* 'd' */, 0xC9, 0x00 /* (to 0x02DF state 366) */,
0x65 /* 'e' */, 0xD3, 0x00 /* (to 0x02EC state 378) */,
0x72 /* 'r' */, 0xEC, 0x00 /* (to 0x0308 state 403) */,
0x08, /* fail */
/* pos 0220: 253 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x022A state 254) */,
0x61 /* 'a' */, 0xD3, 0x00 /* (to 0x02F6 state 387) */,
0x6F /* 'o' */, 0xD9, 0x00 /* (to 0x02FF state 395) */,
0x08, /* fail */
/* pos 022a: 254 */ 0xEE /* 'n' -> */,
/* pos 022b: 255 */ 0xE7 /* 'g' -> */,
/* pos 022c: 256 */ 0xF4 /* 't' -> */,
/* pos 022d: 257 */ 0xE8 /* 'h' -> */,
/* pos 022e: 258 */ 0xBA /* ':' -> */,
/* pos 022f: 259 */ 0x00, 0x1B /* - terminal marker 27 - */,
/* pos 0231: 260 */ 0xF9 /* 'y' -> */,
/* pos 0232: 261 */ 0xF0 /* 'p' -> */,
/* pos 0233: 262 */ 0xE5 /* 'e' -> */,
/* pos 0234: 263 */ 0xBA /* ':' -> */,
/* pos 0235: 264 */ 0x00, 0x1C /* - terminal marker 28 - */,
/* pos 0237: 265 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x023E state 266) */,
0x65 /* 'e' */, 0xFF, 0x01 /* (to 0x0439 state 637) */,
0x08, /* fail */
/* pos 023e: 266 */ 0xF4 /* 't' -> */,
/* pos 023f: 267 */ 0xE5 /* 'e' -> */,
/* pos 0240: 268 */ 0xBA /* ':' -> */,
/* pos 0241: 269 */ 0x00, 0x1D /* - terminal marker 29 - */,
/* pos 0243: 270 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x024A state 271) */,
0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0250 state 276) */,
0x08, /* fail */
/* pos 024a: 271 */ 0xEE /* 'n' -> */,
/* pos 024b: 272 */ 0xE7 /* 'g' -> */,
/* pos 024c: 273 */ 0xE5 /* 'e' -> */,
/* pos 024d: 274 */ 0xBA /* ':' -> */,
/* pos 024e: 275 */ 0x00, 0x1E /* - terminal marker 30 - */,
/* pos 0250: 276 */ 0x66 /* 'f' */, 0x0A, 0x00 /* (to 0x025A state 277) */,
0x74 /* 't' */, 0x63, 0x01 /* (to 0x03B6 state 529) */,
0x70 /* 'p' */, 0x22, 0x02 /* (to 0x0478 state 682) */,
0x08, /* fail */
/* pos 025a: 277 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0261 state 278) */,
0x72 /* 'r' */, 0x53, 0x01 /* (to 0x03B0 state 524) */,
0x08, /* fail */
/* pos 0261: 278 */ 0xF2 /* 'r' -> */,
/* pos 0262: 279 */ 0xE5 /* 'e' -> */,
/* pos 0263: 280 */ 0xF2 /* 'r' -> */,
/* pos 0264: 281 */ 0xBA /* ':' -> */,
/* pos 0265: 282 */ 0x00, 0x1F /* - terminal marker 31 - */,
/* pos 0267: 283 */ 0x00, 0x20 /* - terminal marker 32 - */,
/* pos 0269: 284 */ 0xE5 /* 'e' -> */,
/* pos 026a: 285 */ 0xF2 /* 'r' -> */,
/* pos 026b: 286 */ 0xF3 /* 's' -> */,
/* pos 026c: 287 */ 0xE9 /* 'i' -> */,
/* pos 026d: 288 */ 0xEF /* 'o' -> */,
/* pos 026e: 289 */ 0xEE /* 'n' -> */,
/* pos 026f: 290 */ 0xBA /* ':' -> */,
/* pos 0270: 291 */ 0x00, 0x21 /* - terminal marker 33 - */,
/* pos 0272: 292 */ 0xF2 /* 'r' -> */,
/* pos 0273: 293 */ 0xE9 /* 'i' -> */,
/* pos 0274: 294 */ 0xE7 /* 'g' -> */,
/* pos 0275: 295 */ 0xE9 /* 'i' -> */,
/* pos 0276: 296 */ 0xEE /* 'n' -> */,
/* pos 0277: 297 */ 0xBA /* ':' -> */,
/* pos 0278: 298 */ 0x00, 0x22 /* - terminal marker 34 - */,
/* pos 027a: 299 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0287 state 300) */,
0x6D /* 'm' */, 0x14, 0x00 /* (to 0x0291 state 309) */,
0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0298 state 315) */,
0x73 /* 's' */, 0x20, 0x00 /* (to 0x02A3 state 319) */,
0x08, /* fail */
/* pos 0287: 300 */ 0xF5 /* 'u' -> */,
/* pos 0288: 301 */ 0xF4 /* 't' -> */,
/* pos 0289: 302 */ 0xE8 /* 'h' -> */,
/* pos 028a: 303 */ 0xEF /* 'o' -> */,
/* pos 028b: 304 */ 0xF2 /* 'r' -> */,
/* pos 028c: 305 */ 0xE9 /* 'i' -> */,
/* pos 028d: 306 */ 0xF4 /* 't' -> */,
/* pos 028e: 307 */ 0xF9 /* 'y' -> */,
/* pos 028f: 308 */ 0x00, 0x23 /* - terminal marker 35 - */,
/* pos 0291: 309 */ 0xE5 /* 'e' -> */,
/* pos 0292: 310 */ 0xF4 /* 't' -> */,
/* pos 0293: 311 */ 0xE8 /* 'h' -> */,
/* pos 0294: 312 */ 0xEF /* 'o' -> */,
/* pos 0295: 313 */ 0xE4 /* 'd' -> */,
/* pos 0296: 314 */ 0x00, 0x24 /* - terminal marker 36 - */,
/* pos 0298: 315 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x029F state 316) */,
0x72 /* 'r' */, 0xE9, 0x01 /* (to 0x0484 state 693) */,
0x08, /* fail */
/* pos 029f: 316 */ 0xF4 /* 't' -> */,
/* pos 02a0: 317 */ 0xE8 /* 'h' -> */,
/* pos 02a1: 318 */ 0x00, 0x25 /* - terminal marker 37 - */,
/* pos 02a3: 319 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x02AA state 320) */,
0x74 /* 't' */, 0x0A, 0x00 /* (to 0x02B0 state 325) */,
0x08, /* fail */
/* pos 02aa: 320 */ 0xE8 /* 'h' -> */,
/* pos 02ab: 321 */ 0xE5 /* 'e' -> */,
/* pos 02ac: 322 */ 0xED /* 'm' -> */,
/* pos 02ad: 323 */ 0xE5 /* 'e' -> */,
/* pos 02ae: 324 */ 0x00, 0x26 /* - terminal marker 38 - */,
/* pos 02b0: 325 */ 0xE1 /* 'a' -> */,
/* pos 02b1: 326 */ 0xF4 /* 't' -> */,
/* pos 02b2: 327 */ 0xF5 /* 'u' -> */,
/* pos 02b3: 328 */ 0xF3 /* 's' -> */,
/* pos 02b4: 329 */ 0x00, 0x27 /* - terminal marker 39 - */,
/* pos 02b6: 330 */ 0xE8 /* 'h' -> */,
/* pos 02b7: 331 */ 0xE1 /* 'a' -> */,
/* pos 02b8: 332 */ 0xF2 /* 'r' -> */,
/* pos 02b9: 333 */ 0xF3 /* 's' -> */,
/* pos 02ba: 334 */ 0xE5 /* 'e' -> */,
/* pos 02bb: 335 */ 0xF4 /* 't' -> */,
/* pos 02bc: 336 */ 0xBA /* ':' -> */,
/* pos 02bd: 337 */ 0x00, 0x28 /* - terminal marker 40 - */,
/* pos 02bf: 338 */ 0xE1 /* 'a' -> */,
/* pos 02c0: 339 */ 0xEE /* 'n' -> */,
/* pos 02c1: 340 */ 0xE7 /* 'g' -> */,
/* pos 02c2: 341 */ 0xE5 /* 'e' -> */,
/* pos 02c3: 342 */ 0xF3 /* 's' -> */,
/* pos 02c4: 343 */ 0xBA /* ':' -> */,
/* pos 02c5: 344 */ 0x00, 0x29 /* - terminal marker 41 - */,
/* pos 02c7: 345 */ 0xEC /* 'l' -> */,
/* pos 02c8: 346 */ 0xEC /* 'l' -> */,
/* pos 02c9: 347 */ 0xEF /* 'o' -> */,
/* pos 02ca: 348 */ 0xF7 /* 'w' -> */,
/* pos 02cb: 349 */ 0xAD /* '-' -> */,
/* pos 02cc: 350 */ 0xEF /* 'o' -> */,
/* pos 02cd: 351 */ 0xF2 /* 'r' -> */,
/* pos 02ce: 352 */ 0xE9 /* 'i' -> */,
/* pos 02cf: 353 */ 0xE7 /* 'g' -> */,
/* pos 02d0: 354 */ 0xE9 /* 'i' -> */,
/* pos 02d1: 355 */ 0xEE /* 'n' -> */,
/* pos 02d2: 356 */ 0xBA /* ':' -> */,
/* pos 02d3: 357 */ 0x00, 0x2A /* - terminal marker 42 - */,
/* pos 02d5: 358 */ 0xE5 /* 'e' -> */,
/* pos 02d6: 359 */ 0xBA /* ':' -> */,
/* pos 02d7: 360 */ 0x00, 0x2B /* - terminal marker 43 - */,
/* pos 02d9: 361 */ 0xEC /* 'l' -> */,
/* pos 02da: 362 */ 0xEF /* 'o' -> */,
/* pos 02db: 363 */ 0xF7 /* 'w' -> */,
/* pos 02dc: 364 */ 0xBA /* ':' -> */,
/* pos 02dd: 365 */ 0x00, 0x2C /* - terminal marker 44 - */,
/* pos 02df: 366 */ 0xE9 /* 'i' -> */,
/* pos 02e0: 367 */ 0xF3 /* 's' -> */,
/* pos 02e1: 368 */ 0xF0 /* 'p' -> */,
/* pos 02e2: 369 */ 0xEF /* 'o' -> */,
/* pos 02e3: 370 */ 0xF3 /* 's' -> */,
/* pos 02e4: 371 */ 0xE9 /* 'i' -> */,
/* pos 02e5: 372 */ 0xF4 /* 't' -> */,
/* pos 02e6: 373 */ 0xE9 /* 'i' -> */,
/* pos 02e7: 374 */ 0xEF /* 'o' -> */,
/* pos 02e8: 375 */ 0xEE /* 'n' -> */,
/* pos 02e9: 376 */ 0xBA /* ':' -> */,
/* pos 02ea: 377 */ 0x00, 0x2D /* - terminal marker 45 - */,
/* pos 02ec: 378 */ 0xEE /* 'n' -> */,
/* pos 02ed: 379 */ 0xE3 /* 'c' -> */,
/* pos 02ee: 380 */ 0xEF /* 'o' -> */,
/* pos 02ef: 381 */ 0xE4 /* 'd' -> */,
/* pos 02f0: 382 */ 0xE9 /* 'i' -> */,
/* pos 02f1: 383 */ 0xEE /* 'n' -> */,
/* pos 02f2: 384 */ 0xE7 /* 'g' -> */,
/* pos 02f3: 385 */ 0xBA /* ':' -> */,
/* pos 02f4: 386 */ 0x00, 0x2E /* - terminal marker 46 - */,
/* pos 02f6: 387 */ 0xEE /* 'n' -> */,
/* pos 02f7: 388 */ 0xE7 /* 'g' -> */,
/* pos 02f8: 389 */ 0xF5 /* 'u' -> */,
/* pos 02f9: 390 */ 0xE1 /* 'a' -> */,
/* pos 02fa: 391 */ 0xE7 /* 'g' -> */,
/* pos 02fb: 392 */ 0xE5 /* 'e' -> */,
/* pos 02fc: 393 */ 0xBA /* ':' -> */,
/* pos 02fd: 394 */ 0x00, 0x2F /* - terminal marker 47 - */,
/* pos 02ff: 395 */ 0xE3 /* 'c' -> */,
/* pos 0300: 396 */ 0xE1 /* 'a' -> */,
/* pos 0301: 397 */ 0xF4 /* 't' -> */,
/* pos 0302: 398 */ 0xE9 /* 'i' -> */,
/* pos 0303: 399 */ 0xEF /* 'o' -> */,
/* pos 0304: 400 */ 0xEE /* 'n' -> */,
/* pos 0305: 401 */ 0xBA /* ':' -> */,
/* pos 0306: 402 */ 0x00, 0x30 /* - terminal marker 48 - */,
/* pos 0308: 403 */ 0xE1 /* 'a' -> */,
/* pos 0309: 404 */ 0xEE /* 'n' -> */,
/* pos 030a: 405 */ 0xE7 /* 'g' -> */,
/* pos 030b: 406 */ 0xE5 /* 'e' -> */,
/* pos 030c: 407 */ 0xBA /* ':' -> */,
/* pos 030d: 408 */ 0x00, 0x31 /* - terminal marker 49 - */,
/* pos 030f: 409 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x0316 state 410) */,
0x78 /* 'x' */, 0x09, 0x00 /* (to 0x031B state 414) */,
0x08, /* fail */
/* pos 0316: 410 */ 0xE1 /* 'a' -> */,
/* pos 0317: 411 */ 0xE7 /* 'g' -> */,
/* pos 0318: 412 */ 0xBA /* ':' -> */,
/* pos 0319: 413 */ 0x00, 0x32 /* - terminal marker 50 - */,
/* pos 031b: 414 */ 0xF0 /* 'p' -> */,
/* pos 031c: 415 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0323 state 416) */,
0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0328 state 420) */,
0x08, /* fail */
/* pos 0323: 416 */ 0xE3 /* 'c' -> */,
/* pos 0324: 417 */ 0xF4 /* 't' -> */,
/* pos 0325: 418 */ 0xBA /* ':' -> */,
/* pos 0326: 419 */ 0x00, 0x33 /* - terminal marker 51 - */,
/* pos 0328: 420 */ 0xF2 /* 'r' -> */,
/* pos 0329: 421 */ 0xE5 /* 'e' -> */,
/* pos 032a: 422 */ 0xF3 /* 's' -> */,
/* pos 032b: 423 */ 0xBA /* ':' -> */,
/* pos 032c: 424 */ 0x00, 0x34 /* - terminal marker 52 - */,
/* pos 032e: 425 */ 0xF2 /* 'r' -> */,
/* pos 032f: 426 */ 0xEF /* 'o' -> */,
/* pos 0330: 427 */ 0xED /* 'm' -> */,
/* pos 0331: 428 */ 0xBA /* ':' -> */,
/* pos 0332: 429 */ 0x00, 0x35 /* - terminal marker 53 - */,
/* pos 0334: 430 */ 0xF4 /* 't' -> */,
/* pos 0335: 431 */ 0xE3 /* 'c' -> */,
/* pos 0336: 432 */ 0xE8 /* 'h' -> */,
/* pos 0337: 433 */ 0xBA /* ':' -> */,
/* pos 0338: 434 */ 0x00, 0x36 /* - terminal marker 54 - */,
/* pos 033a: 435 */ 0xE1 /* 'a' -> */,
/* pos 033b: 436 */ 0xEE /* 'n' -> */,
/* pos 033c: 437 */ 0xE7 /* 'g' -> */,
/* pos 033d: 438 */ 0xE5 /* 'e' -> */,
/* pos 033e: 439 */ 0xBA /* ':' -> */,
/* pos 033f: 440 */ 0x00, 0x37 /* - terminal marker 55 - */,
/* pos 0341: 441 */ 0xEE /* 'n' -> */,
/* pos 0342: 442 */ 0xED /* 'm' -> */,
/* pos 0343: 443 */ 0xEF /* 'o' -> */,
/* pos 0344: 444 */ 0xE4 /* 'd' -> */,
/* pos 0345: 445 */ 0xE9 /* 'i' -> */,
/* pos 0346: 446 */ 0xE6 /* 'f' -> */,
/* pos 0347: 447 */ 0xE9 /* 'i' -> */,
/* pos 0348: 448 */ 0xE5 /* 'e' -> */,
/* pos 0349: 449 */ 0xE4 /* 'd' -> */,
/* pos 034a: 450 */ 0xAD /* '-' -> */,
/* pos 034b: 451 */ 0xF3 /* 's' -> */,
/* pos 034c: 452 */ 0xE9 /* 'i' -> */,
/* pos 034d: 453 */ 0xEE /* 'n' -> */,
/* pos 034e: 454 */ 0xE3 /* 'c' -> */,
/* pos 034f: 455 */ 0xE5 /* 'e' -> */,
/* pos 0350: 456 */ 0xBA /* ':' -> */,
/* pos 0351: 457 */ 0x00, 0x38 /* - terminal marker 56 - */,
/* pos 0353: 458 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x035D state 459) */,
0x69 /* 'i' */, 0x15, 0x00 /* (to 0x036B state 472) */,
0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0370 state 476) */,
0x08, /* fail */
/* pos 035d: 459 */ 0xF3 /* 's' -> */,
/* pos 035e: 460 */ 0xF4 /* 't' -> */,
/* pos 035f: 461 */ 0xAD /* '-' -> */,
/* pos 0360: 462 */ 0xED /* 'm' -> */,
/* pos 0361: 463 */ 0xEF /* 'o' -> */,
/* pos 0362: 464 */ 0xE4 /* 'd' -> */,
/* pos 0363: 465 */ 0xE9 /* 'i' -> */,
/* pos 0364: 466 */ 0xE6 /* 'f' -> */,
/* pos 0365: 467 */ 0xE9 /* 'i' -> */,
/* pos 0366: 468 */ 0xE5 /* 'e' -> */,
/* pos 0367: 469 */ 0xE4 /* 'd' -> */,
/* pos 0368: 470 */ 0xBA /* ':' -> */,
/* pos 0369: 471 */ 0x00, 0x39 /* - terminal marker 57 - */,
/* pos 036b: 472 */ 0xEE /* 'n' -> */,
/* pos 036c: 473 */ 0xEB /* 'k' -> */,
/* pos 036d: 474 */ 0xBA /* ':' -> */,
/* pos 036e: 475 */ 0x00, 0x3A /* - terminal marker 58 - */,
/* pos 0370: 476 */ 0xE3 /* 'c' -> */,
/* pos 0371: 477 */ 0xE1 /* 'a' -> */,
/* pos 0372: 478 */ 0xF4 /* 't' -> */,
/* pos 0373: 479 */ 0xE9 /* 'i' -> */,
/* pos 0374: 480 */ 0xEF /* 'o' -> */,
/* pos 0375: 481 */ 0xEE /* 'n' -> */,
/* pos 0376: 482 */ 0xBA /* ':' -> */,
/* pos 0377: 483 */ 0x00, 0x3B /* - terminal marker 59 - */,
/* pos 0379: 484 */ 0xE1 /* 'a' -> */,
/* pos 037a: 485 */ 0xF8 /* 'x' -> */,
/* pos 037b: 486 */ 0xAD /* '-' -> */,
/* pos 037c: 487 */ 0xE6 /* 'f' -> */,
/* pos 037d: 488 */ 0xEF /* 'o' -> */,
/* pos 037e: 489 */ 0xF2 /* 'r' -> */,
/* pos 037f: 490 */ 0xF7 /* 'w' -> */,
/* pos 0380: 491 */ 0xE1 /* 'a' -> */,
/* pos 0381: 492 */ 0xF2 /* 'r' -> */,
/* pos 0382: 493 */ 0xE4 /* 'd' -> */,
/* pos 0383: 494 */ 0xF3 /* 's' -> */,
/* pos 0384: 495 */ 0xBA /* ':' -> */,
/* pos 0385: 496 */ 0x00, 0x3C /* - terminal marker 60 - */,
/* pos 0387: 497 */ 0xF8 /* 'x' -> */,
/* pos 0388: 498 */ 0xF9 /* 'y' -> */,
/* pos 0389: 499 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0390 state 500) */,
0x20 /* ' ' */, 0xBB, 0x00 /* (to 0x0447 state 649) */,
0x08, /* fail */
/* pos 0390: 500 */ 0xE1 /* 'a' -> */,
/* pos 0391: 501 */ 0xF5 /* 'u' -> */,
/* pos 0392: 502 */ 0xF4 /* 't' -> */,
/* pos 0393: 503 */ 0xE8 /* 'h' -> */,
/* pos 0394: 504 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x039B state 505) */,
0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x03A5 state 514) */,
0x08, /* fail */
/* pos 039b: 505 */ 0xEE /* 'n' -> */,
/* pos 039c: 506 */ 0xF4 /* 't' -> */,
/* pos 039d: 507 */ 0xE9 /* 'i' -> */,
/* pos 039e: 508 */ 0xE3 /* 'c' -> */,
/* pos 039f: 509 */ 0xE1 /* 'a' -> */,
/* pos 03a0: 510 */ 0xF4 /* 't' -> */,
/* pos 03a1: 511 */ 0xE5 /* 'e' -> */,
/* pos 03a2: 512 */ 0xBA /* ':' -> */,
/* pos 03a3: 513 */ 0x00, 0x3D /* - terminal marker 61 - */,
/* pos 03a5: 514 */ 0xF2 /* 'r' -> */,
/* pos 03a6: 515 */ 0xE9 /* 'i' -> */,
/* pos 03a7: 516 */ 0xFA /* 'z' -> */,
/* pos 03a8: 517 */ 0xE1 /* 'a' -> */,
/* pos 03a9: 518 */ 0xF4 /* 't' -> */,
/* pos 03aa: 519 */ 0xE9 /* 'i' -> */,
/* pos 03ab: 520 */ 0xEF /* 'o' -> */,
/* pos 03ac: 521 */ 0xEE /* 'n' -> */,
/* pos 03ad: 522 */ 0xBA /* ':' -> */,
/* pos 03ae: 523 */ 0x00, 0x3E /* - terminal marker 62 - */,
/* pos 03b0: 524 */ 0xE5 /* 'e' -> */,
/* pos 03b1: 525 */ 0xF3 /* 's' -> */,
/* pos 03b2: 526 */ 0xE8 /* 'h' -> */,
/* pos 03b3: 527 */ 0xBA /* ':' -> */,
/* pos 03b4: 528 */ 0x00, 0x3F /* - terminal marker 63 - */,
/* pos 03b6: 529 */ 0xF2 /* 'r' -> */,
/* pos 03b7: 530 */ 0xF9 /* 'y' -> */,
/* pos 03b8: 531 */ 0xAD /* '-' -> */,
/* pos 03b9: 532 */ 0xE1 /* 'a' -> */,
/* pos 03ba: 533 */ 0xE6 /* 'f' -> */,
/* pos 03bb: 534 */ 0xF4 /* 't' -> */,
/* pos 03bc: 535 */ 0xE5 /* 'e' -> */,
/* pos 03bd: 536 */ 0xF2 /* 'r' -> */,
/* pos 03be: 537 */ 0xBA /* ':' -> */,
/* pos 03bf: 538 */ 0x00, 0x40 /* - terminal marker 64 - */,
/* pos 03c1: 539 */ 0xF6 /* 'v' -> */,
/* pos 03c2: 540 */ 0xE5 /* 'e' -> */,
/* pos 03c3: 541 */ 0xF2 /* 'r' -> */,
/* pos 03c4: 542 */ 0xBA /* ':' -> */,
/* pos 03c5: 543 */ 0x00, 0x41 /* - terminal marker 65 - */,
/* pos 03c7: 544 */ 0xAD /* '-' -> */,
/* pos 03c8: 545 */ 0xE3 /* 'c' -> */,
/* pos 03c9: 546 */ 0xEF /* 'o' -> */,
/* pos 03ca: 547 */ 0xEF /* 'o' -> */,
/* pos 03cb: 548 */ 0xEB /* 'k' -> */,
/* pos 03cc: 549 */ 0xE9 /* 'i' -> */,
/* pos 03cd: 550 */ 0xE5 /* 'e' -> */,
/* pos 03ce: 551 */ 0xBA /* ':' -> */,
/* pos 03cf: 552 */ 0x00, 0x42 /* - terminal marker 66 - */,
/* pos 03d1: 553 */ 0xF2 /* 'r' -> */,
/* pos 03d2: 554 */ 0xE9 /* 'i' -> */,
/* pos 03d3: 555 */ 0xE3 /* 'c' -> */,
/* pos 03d4: 556 */ 0xF4 /* 't' -> */,
/* pos 03d5: 557 */ 0xAD /* '-' -> */,
/* pos 03d6: 558 */ 0xF4 /* 't' -> */,
/* pos 03d7: 559 */ 0xF2 /* 'r' -> */,
/* pos 03d8: 560 */ 0xE1 /* 'a' -> */,
/* pos 03d9: 561 */ 0xEE /* 'n' -> */,
/* pos 03da: 562 */ 0xF3 /* 's' -> */,
/* pos 03db: 563 */ 0xF0 /* 'p' -> */,
/* pos 03dc: 564 */ 0xEF /* 'o' -> */,
/* pos 03dd: 565 */ 0xF2 /* 'r' -> */,
/* pos 03de: 566 */ 0xF4 /* 't' -> */,
/* pos 03df: 567 */ 0xAD /* '-' -> */,
/* pos 03e0: 568 */ 0xF3 /* 's' -> */,
/* pos 03e1: 569 */ 0xE5 /* 'e' -> */,
/* pos 03e2: 570 */ 0xE3 /* 'c' -> */,
/* pos 03e3: 571 */ 0xF5 /* 'u' -> */,
/* pos 03e4: 572 */ 0xF2 /* 'r' -> */,
/* pos 03e5: 573 */ 0xE9 /* 'i' -> */,
/* pos 03e6: 574 */ 0xF4 /* 't' -> */,
/* pos 03e7: 575 */ 0xF9 /* 'y' -> */,
/* pos 03e8: 576 */ 0xBA /* ':' -> */,
/* pos 03e9: 577 */ 0x00, 0x43 /* - terminal marker 67 - */,
/* pos 03eb: 578 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x03F2 state 579) */,
0x65 /* 'e' */, 0x87, 0x00 /* (to 0x0475 state 680) */,
0x08, /* fail */
/* pos 03f2: 579 */ 0xE1 /* 'a' -> */,
/* pos 03f3: 580 */ 0xEE /* 'n' -> */,
/* pos 03f4: 581 */ 0xF3 /* 's' -> */,
/* pos 03f5: 582 */ 0xE6 /* 'f' -> */,
/* pos 03f6: 583 */ 0xE5 /* 'e' -> */,
/* pos 03f7: 584 */ 0xF2 /* 'r' -> */,
/* pos 03f8: 585 */ 0xAD /* '-' -> */,
/* pos 03f9: 586 */ 0xE5 /* 'e' -> */,
/* pos 03fa: 587 */ 0xEE /* 'n' -> */,
/* pos 03fb: 588 */ 0xE3 /* 'c' -> */,
/* pos 03fc: 589 */ 0xEF /* 'o' -> */,
/* pos 03fd: 590 */ 0xE4 /* 'd' -> */,
/* pos 03fe: 591 */ 0xE9 /* 'i' -> */,
/* pos 03ff: 592 */ 0xEE /* 'n' -> */,
/* pos 0400: 593 */ 0xE7 /* 'g' -> */,
/* pos 0401: 594 */ 0xBA /* ':' -> */,
/* pos 0402: 595 */ 0x00, 0x44 /* - terminal marker 68 - */,
/* pos 0404: 596 */ 0xE5 /* 'e' -> */,
/* pos 0405: 597 */ 0xF2 /* 'r' -> */,
/* pos 0406: 598 */ 0xAD /* '-' -> */,
/* pos 0407: 599 */ 0xE1 /* 'a' -> */,
/* pos 0408: 600 */ 0xE7 /* 'g' -> */,
/* pos 0409: 601 */ 0xE5 /* 'e' -> */,
/* pos 040a: 602 */ 0xEE /* 'n' -> */,
/* pos 040b: 603 */ 0xF4 /* 't' -> */,
/* pos 040c: 604 */ 0xBA /* ':' -> */,
/* pos 040d: 605 */ 0x00, 0x45 /* - terminal marker 69 - */,
/* pos 040f: 606 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0416 state 607) */,
0x69 /* 'i' */, 0x09, 0x00 /* (to 0x041B state 611) */,
0x08, /* fail */
/* pos 0416: 607 */ 0xF2 /* 'r' -> */,
/* pos 0417: 608 */ 0xF9 /* 'y' -> */,
/* pos 0418: 609 */ 0xBA /* ':' -> */,
/* pos 0419: 610 */ 0x00, 0x46 /* - terminal marker 70 - */,
/* pos 041b: 611 */ 0xE1 /* 'a' -> */,
/* pos 041c: 612 */ 0xBA /* ':' -> */,
/* pos 041d: 613 */ 0x00, 0x47 /* - terminal marker 71 - */,
/* pos 041f: 614 */ 0xF7 /* 'w' -> */,
/* pos 0420: 615 */ 0xF7 /* 'w' -> */,
/* pos 0421: 616 */ 0xAD /* '-' -> */,
/* pos 0422: 617 */ 0xE1 /* 'a' -> */,
/* pos 0423: 618 */ 0xF5 /* 'u' -> */,
/* pos 0424: 619 */ 0xF4 /* 't' -> */,
/* pos 0425: 620 */ 0xE8 /* 'h' -> */,
/* pos 0426: 621 */ 0xE5 /* 'e' -> */,
/* pos 0427: 622 */ 0xEE /* 'n' -> */,
/* pos 0428: 623 */ 0xF4 /* 't' -> */,
/* pos 0429: 624 */ 0xE9 /* 'i' -> */,
/* pos 042a: 625 */ 0xE3 /* 'c' -> */,
/* pos 042b: 626 */ 0xE1 /* 'a' -> */,
/* pos 042c: 627 */ 0xF4 /* 't' -> */,
/* pos 042d: 628 */ 0xE5 /* 'e' -> */,
/* pos 042e: 629 */ 0xBA /* ':' -> */,
/* pos 042f: 630 */ 0x00, 0x48 /* - terminal marker 72 - */,
/* pos 0431: 631 */ 0xF4 /* 't' -> */,
/* pos 0432: 632 */ 0xE3 /* 'c' -> */,
/* pos 0433: 633 */ 0xE8 /* 'h' -> */,
/* pos 0434: 634 */ 0x00, 0x49 /* - terminal marker 73 - */,
/* pos 0436: 635 */ 0xF4 /* 't' -> */,
/* pos 0437: 636 */ 0x00, 0x4A /* - terminal marker 74 - */,
/* pos 0439: 637 */ 0xEC /* 'l' -> */,
/* pos 043a: 638 */ 0xE5 /* 'e' -> */,
/* pos 043b: 639 */ 0xF4 /* 't' -> */,
/* pos 043c: 640 */ 0xE5 /* 'e' -> */,
/* pos 043d: 641 */ 0x00, 0x4B /* - terminal marker 75 - */,
/* pos 043f: 642 */ 0xE9 /* 'i' -> */,
/* pos 0440: 643 */ 0xAD /* '-' -> */,
/* pos 0441: 644 */ 0xE1 /* 'a' -> */,
/* pos 0442: 645 */ 0xF2 /* 'r' -> */,
/* pos 0443: 646 */ 0xE7 /* 'g' -> */,
/* pos 0444: 647 */ 0xF3 /* 's' -> */,
/* pos 0445: 648 */ 0x00, 0x4C /* - terminal marker 76 - */,
/* pos 0447: 649 */ 0x00, 0x4D /* - terminal marker 77 - */,
/* pos 0449: 650 */ 0xAD /* '-' -> */,
/* pos 044a: 651 */ 0x72 /* 'r' */, 0x0A, 0x00 /* (to 0x0454 state 652) */,
0x66 /* 'f' */, 0x13, 0x00 /* (to 0x0460 state 662) */,
0x61 /* 'a' */, 0x3C, 0x00 /* (to 0x048C state 700) */,
0x08, /* fail */
/* pos 0454: 652 */ 0xE5 /* 'e' -> */,
/* pos 0455: 653 */ 0xE1 /* 'a' -> */,
/* pos 0456: 654 */ 0xEC /* 'l' -> */,
/* pos 0457: 655 */ 0xAD /* '-' -> */,
/* pos 0458: 656 */ 0xE9 /* 'i' -> */,
/* pos 0459: 657 */ 0xF0 /* 'p' -> */,
/* pos 045a: 658 */ 0xBA /* ':' -> */,
/* pos 045b: 659 */ 0x00, 0x4E /* - terminal marker 78 - */,
/* pos 045d: 660 */ 0xA0 /* ' ' -> */,
/* pos 045e: 661 */ 0x00, 0x4F /* - terminal marker 79 - */,
/* pos 0460: 662 */ 0xEF /* 'o' -> */,
/* pos 0461: 663 */ 0xF2 /* 'r' -> */,
/* pos 0462: 664 */ 0xF7 /* 'w' -> */,
/* pos 0463: 665 */ 0xE1 /* 'a' -> */,
/* pos 0464: 666 */ 0xF2 /* 'r' -> */,
/* pos 0465: 667 */ 0xE4 /* 'd' -> */,
/* pos 0466: 668 */ 0xE5 /* 'e' -> */,
/* pos 0467: 669 */ 0xE4 /* 'd' -> */,
/* pos 0468: 670 */ 0xAD /* '-' -> */,
/* pos 0469: 671 */ 0xE6 /* 'f' -> */,
/* pos 046a: 672 */ 0xEF /* 'o' -> */,
/* pos 046b: 673 */ 0xF2 /* 'r' -> */,
/* pos 046c: 674 */ 0x00, 0x50 /* - terminal marker 80 - */,
/* pos 046e: 675 */ 0x00, 0x51 /* - terminal marker 81 - */,
/* pos 0470: 676 */ 0xE1 /* 'a' -> */,
/* pos 0471: 677 */ 0xE4 /* 'd' -> */,
/* pos 0472: 678 */ 0xA0 /* ' ' -> */,
/* pos 0473: 679 */ 0x00, 0x52 /* - terminal marker 82 - */,
/* pos 0475: 680 */ 0xBA /* ':' -> */,
/* pos 0476: 681 */ 0x00, 0x53 /* - terminal marker 83 - */,
/* pos 0478: 682 */ 0xEC /* 'l' -> */,
/* pos 0479: 683 */ 0xE1 /* 'a' -> */,
/* pos 047a: 684 */ 0xF9 /* 'y' -> */,
/* pos 047b: 685 */ 0xAD /* '-' -> */,
/* pos 047c: 686 */ 0xEE /* 'n' -> */,
/* pos 047d: 687 */ 0xEF /* 'o' -> */,
/* pos 047e: 688 */ 0xEE /* 'n' -> */,
/* pos 047f: 689 */ 0xE3 /* 'c' -> */,
/* pos 0480: 690 */ 0xE5 /* 'e' -> */,
/* pos 0481: 691 */ 0xBA /* ':' -> */,
/* pos 0482: 692 */ 0x00, 0x54 /* - terminal marker 84 - */,
/* pos 0484: 693 */ 0xEF /* 'o' -> */,
/* pos 0485: 694 */ 0xF4 /* 't' -> */,
/* pos 0486: 695 */ 0xEF /* 'o' -> */,
/* pos 0487: 696 */ 0xE3 /* 'c' -> */,
/* pos 0488: 697 */ 0xEF /* 'o' -> */,
/* pos 0489: 698 */ 0xEC /* 'l' -> */,
/* pos 048a: 699 */ 0x00, 0x55 /* - terminal marker 85 - */,
/* pos 048c: 700 */ 0xF5 /* 'u' -> */,
/* pos 048d: 701 */ 0xF4 /* 't' -> */,
/* pos 048e: 702 */ 0xE8 /* 'h' -> */,
/* pos 048f: 703 */ 0xAD /* '-' -> */,
/* pos 0490: 704 */ 0xF4 /* 't' -> */,
/* pos 0491: 705 */ 0xEF /* 'o' -> */,
/* pos 0492: 706 */ 0xEB /* 'k' -> */,
/* pos 0493: 707 */ 0xE5 /* 'e' -> */,
/* pos 0494: 708 */ 0xEE /* 'n' -> */,
/* pos 0495: 709 */ 0xBA /* ':' -> */,
/* pos 0496: 710 */ 0x00, 0x56 /* - terminal marker 86 - */,
/* total size 1176 bytes */

View File

@ -0,0 +1,257 @@
/*
* 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
*
* This is included from core/private.h if either H1 or H2 roles are
* enabled
*/
#if defined(LWS_WITH_HTTP_PROXY)
#include <hubbub/hubbub.h>
#include <hubbub/parser.h>
#endif
#define lwsi_role_http(wsi) (lwsi_role_h1(wsi) || lwsi_role_h2(wsi))
enum http_version {
HTTP_VERSION_1_0,
HTTP_VERSION_1_1,
HTTP_VERSION_2
};
enum http_connection_type {
HTTP_CONNECTION_CLOSE,
HTTP_CONNECTION_KEEP_ALIVE
};
/*
* This is totally opaque to code using the library. It's exported as a
* forward-reference pointer-only declaration; the user can use the pointer with
* other APIs to get information out of it.
*/
#if defined(LWS_WITH_ESP32)
typedef uint16_t ah_data_idx_t;
#else
typedef uint32_t ah_data_idx_t;
#endif
struct lws_fragments {
ah_data_idx_t offset;
uint16_t len;
uint8_t nfrag; /* which ah->frag[] continues this content, or 0 */
uint8_t flags; /* only http2 cares */
};
#if defined(LWS_WITH_RANGES)
enum range_states {
LWSRS_NO_ACTIVE_RANGE,
LWSRS_BYTES_EQ,
LWSRS_FIRST,
LWSRS_STARTING,
LWSRS_ENDING,
LWSRS_COMPLETED,
LWSRS_SYNTAX,
};
struct lws_range_parsing {
unsigned long long start, end, extent, agg, budget;
const char buf[128];
int pos;
enum range_states state;
char start_valid, end_valid, ctr, count_ranges, did_try, inside, send_ctr;
};
int
lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp,
unsigned long long extent);
int
lws_ranges_next(struct lws_range_parsing *rp);
void
lws_ranges_reset(struct lws_range_parsing *rp);
#endif
/*
* these are assigned from a pool held in the context.
* Both client and server mode uses them for http header analysis
*/
struct allocated_headers {
struct allocated_headers *next; /* linked list */
struct lws *wsi; /* owner */
char *data; /* prepared by context init to point to dedicated storage */
ah_data_idx_t data_length;
/*
* the randomly ordered fragments, indexed by frag_index and
* lws_fragments->nfrag for continuation.
*/
struct lws_fragments frags[WSI_TOKEN_COUNT];
time_t assigned;
/*
* for each recognized token, frag_index says which frag[] his data
* starts in (0 means the token did not appear)
* the actual header data gets dumped as it comes in, into data[]
*/
uint8_t frag_index[WSI_TOKEN_COUNT];
#ifndef LWS_NO_CLIENT
char initial_handshake_hash_base64[30];
#endif
uint32_t pos;
uint32_t http_response;
uint32_t current_token_limit;
int hdr_token_idx;
int16_t lextable_pos;
uint8_t in_use;
uint8_t nfrag;
char /*enum uri_path_states */ ups;
char /*enum uri_esc_states */ ues;
char esc_stash;
char post_literal_equal;
uint8_t /* enum lws_token_indexes */ parser_state;
};
#if defined(LWS_WITH_HTTP_PROXY)
struct lws_rewrite {
hubbub_parser *parser;
hubbub_parser_optparams params;
const char *from, *to;
int from_len, to_len;
unsigned char *p, *end;
struct lws *wsi;
};
static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len)
{
if ((int)s->len != len)
return 1;
return strncmp((const char *)s->ptr, p, len);
}
typedef hubbub_error (*hubbub_callback_t)(const hubbub_token *token, void *pw);
LWS_EXTERN struct lws_rewrite *
lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to);
LWS_EXTERN void
lws_rewrite_destroy(struct lws_rewrite *r);
LWS_EXTERN int
lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len);
#endif
struct lws_pt_role_http {
struct allocated_headers *ah_list;
struct lws *ah_wait_list;
#ifdef LWS_WITH_CGI
struct lws_cgi *cgi_list;
#endif
int ah_wait_list_length;
uint32_t ah_pool_length;
int ah_count_in_use;
};
struct lws_peer_role_http {
uint32_t count_ah;
uint32_t total_ah;
};
struct lws_vhost_role_http {
char http_proxy_address[128];
const struct lws_http_mount *mount_list;
const char *error_document_404;
unsigned int http_proxy_port;
};
#ifdef LWS_WITH_ACCESS_LOG
struct lws_access_log {
char *header_log;
char *user_agent;
char *referrer;
unsigned long sent;
int response;
};
#endif
struct _lws_http_mode_related {
struct lws *new_wsi_list;
#if defined(LWS_WITH_HTTP_PROXY)
struct lws_rewrite *rw;
#endif
struct allocated_headers *ah;
struct lws *ah_wait_list;
lws_filepos_t filepos;
lws_filepos_t filelen;
lws_fop_fd_t fop_fd;
#if defined(LWS_WITH_RANGES)
struct lws_range_parsing range;
char multipart_content_type[64];
#endif
#ifdef LWS_WITH_ACCESS_LOG
struct lws_access_log access_log;
#endif
#ifdef LWS_WITH_CGI
struct lws_cgi *cgi; /* wsi being cgi master have one of these */
#endif
enum http_version request_version;
enum http_connection_type connection_type;
lws_filepos_t tx_content_length;
lws_filepos_t tx_content_remain;
lws_filepos_t rx_content_length;
lws_filepos_t rx_content_remain;
#if defined(LWS_WITH_HTTP_PROXY)
unsigned int perform_rewrite:1;
#endif
};
#ifndef LWS_NO_CLIENT
enum lws_chunk_parser {
ELCP_HEX,
ELCP_CR,
ELCP_CONTENT,
ELCP_POST_CR,
ELCP_POST_LF,
};
#endif
enum lws_parse_urldecode_results {
LPUR_CONTINUE,
LPUR_SWALLOW,
LPUR_FORBID,
LPUR_EXCESSIVE,
};
int
lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
void
_lws_header_table_reset(struct allocated_headers *ah);
LWS_EXTERN int
_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah);

View File

@ -0,0 +1,182 @@
/*
* libwebsockets - server access log handling
*
* Copyright (C) 2010-2017 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"
/*
* Produce Apache-compatible log string for wsi, like this:
*
* 2.31.234.19 - - [27/Mar/2016:03:22:44 +0800]
* "GET /aep-screen.png HTTP/1.1"
* 200 152987 "https://libwebsockets.org/index.html"
* "Mozilla/5.0 (Macint... Chrome/49.0.2623.87 Safari/537.36"
*
*/
extern const char * const method_names[];
static const char * const hver[] = {
"HTTP/1.0", "HTTP/1.1", "HTTP/2"
};
void
lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth)
{
#ifdef LWS_WITH_IPV6
char ads[INET6_ADDRSTRLEN];
#else
char ads[INET_ADDRSTRLEN];
#endif
char da[64];
const char *pa, *me;
struct tm *tmp;
time_t t = time(NULL);
int l = 256, m;
if (!wsi->vhost)
return;
/* only worry about preparing it if we store it */
if (wsi->vhost->log_fd == (int)LWS_INVALID_FILE)
return;
if (wsi->access_log_pending)
lws_access_log(wsi);
wsi->http.access_log.header_log = lws_malloc(l, "access log");
if (wsi->http.access_log.header_log) {
tmp = localtime(&t);
if (tmp)
strftime(da, sizeof(da), "%d/%b/%Y:%H:%M:%S %z", tmp);
else
strcpy(da, "01/Jan/1970:00:00:00 +0000");
pa = lws_get_peer_simple(wsi, ads, sizeof(ads));
if (!pa)
pa = "(unknown)";
if (wsi->http2_substream)
me = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
else
me = method_names[meth];
if (!me)
me = "(null)";
lws_snprintf(wsi->http.access_log.header_log, l,
"%s - - [%s] \"%s %s %s\"",
pa, da, me, uri_ptr,
hver[wsi->http.request_version]);
l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT);
if (l) {
wsi->http.access_log.user_agent = lws_malloc(l + 2, "access log");
if (!wsi->http.access_log.user_agent) {
lwsl_err("OOM getting user agent\n");
lws_free_set_NULL(wsi->http.access_log.header_log);
return;
}
lws_hdr_copy(wsi, wsi->http.access_log.user_agent,
l + 1, WSI_TOKEN_HTTP_USER_AGENT);
for (m = 0; m < l; m++)
if (wsi->http.access_log.user_agent[m] == '\"')
wsi->http.access_log.user_agent[m] = '\'';
}
l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER);
if (l) {
wsi->http.access_log.referrer = lws_malloc(l + 2, "referrer");
if (!wsi->http.access_log.referrer) {
lwsl_err("OOM getting user agent\n");
lws_free_set_NULL(wsi->http.access_log.user_agent);
lws_free_set_NULL(wsi->http.access_log.header_log);
return;
}
lws_hdr_copy(wsi, wsi->http.access_log.referrer,
l + 1, WSI_TOKEN_HTTP_REFERER);
for (m = 0; m < l; m++)
if (wsi->http.access_log.referrer[m] == '\"')
wsi->http.access_log.referrer[m] = '\'';
}
wsi->access_log_pending = 1;
}
}
int
lws_access_log(struct lws *wsi)
{
char *p = wsi->http.access_log.user_agent, ass[512],
*p1 = wsi->http.access_log.referrer;
int l;
if (!wsi->vhost)
return 0;
if (wsi->vhost->log_fd == (int)LWS_INVALID_FILE)
return 0;
if (!wsi->access_log_pending)
return 0;
if (!wsi->http.access_log.header_log)
return 0;
if (!p)
p = "";
if (!p1)
p1 = "";
/*
* We do this in two parts to restrict an oversize referrer such that
* we will always have space left to append an empty useragent, while
* maintaining the structure of the log text
*/
l = lws_snprintf(ass, sizeof(ass) - 7, "%s %d %lu \"%s",
wsi->http.access_log.header_log,
wsi->http.access_log.response, wsi->http.access_log.sent, p1);
if (strlen(p) > sizeof(ass) - 6 - l)
p[sizeof(ass) - 6 - l] = '\0';
l += lws_snprintf(ass + l, sizeof(ass) - 1 - l, "\" \"%s\"\n", p);
if (write(wsi->vhost->log_fd, ass, l) != l)
lwsl_err("Failed to write log\n");
if (wsi->http.access_log.header_log) {
lws_free(wsi->http.access_log.header_log);
wsi->http.access_log.header_log = NULL;
}
if (wsi->http.access_log.user_agent) {
lws_free(wsi->http.access_log.user_agent);
wsi->http.access_log.user_agent = NULL;
}
if (wsi->http.access_log.referrer) {
lws_free(wsi->http.access_log.referrer);
wsi->http.access_log.referrer = NULL;
}
wsi->access_log_pending = 0;
return 0;
}

View File

@ -51,7 +51,7 @@
* MA 02110-1301 USA * MA 02110-1301 USA
*/ */
#include "private-libwebsockets.h" #include "core/private.h"
#include <zlib.h> #include <zlib.h>
@ -241,13 +241,13 @@ lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
if (priv->hdr.filename_len != len) if (priv->hdr.filename_len != len)
goto next; goto next;
if (len >= sizeof(buf) - 1) if (len >= (int)sizeof(buf) - 1)
return LWS_FZ_ERR_NAME_TOO_LONG; return LWS_FZ_ERR_NAME_TOO_LONG;
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd, if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
&amount, buf, len)) &amount, buf, len))
return LWS_FZ_ERR_NAME_READ; return LWS_FZ_ERR_NAME_READ;
if (amount != len) if ((int)amount != len)
return LWS_FZ_ERR_NAME_READ; return LWS_FZ_ERR_NAME_READ;
buf[len] = '\0'; buf[len] = '\0';
@ -348,9 +348,8 @@ lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
m = sizeof(rp) - 1; m = sizeof(rp) - 1;
if ((vpath - vfs_path - 1) < m) if ((vpath - vfs_path - 1) < m)
m = vpath - vfs_path - 1; m = lws_ptr_diff(vpath, vfs_path) - 1;
strncpy(rp, vfs_path, m); lws_strncpy(rp, vfs_path, m + 1);
rp[m] = '\0';
/* open the zip file itself using the incoming fops, not fops_zip */ /* open the zip file itself using the incoming fops, not fops_zip */
@ -363,7 +362,7 @@ lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
if (*vpath == '/') if (*vpath == '/')
vpath++; vpath++;
m = lws_fops_zip_scan(priv, vpath, strlen(vpath)); m = lws_fops_zip_scan(priv, vpath, (int)strlen(vpath));
if (m) { if (m) {
lwsl_err("unable to find record matching '%s' %d\n", vpath, m); lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
goto bail2; goto bail2;
@ -565,7 +564,7 @@ spin:
switch (ret) { switch (ret) {
case Z_NEED_DICT: case Z_NEED_DICT:
ret = Z_DATA_ERROR; ret = Z_DATA_ERROR;
/* and fall through */ /* fallthru */
case Z_DATA_ERROR: case Z_DATA_ERROR:
case Z_MEM_ERROR: case Z_MEM_ERROR:

View File

@ -19,8 +19,7 @@
* MA 02110-1301 USA * MA 02110-1301 USA
*/ */
#include "private-libwebsockets.h" #include "core/private.h"
#include "../misc/lejp.h"
#ifndef _WIN32 #ifndef _WIN32
/* this is needed for Travis CI */ /* this is needed for Travis CI */
@ -40,6 +39,7 @@ static const char * const paths_global[] = {
"global.timeout-secs", "global.timeout-secs",
"global.reject-service-keywords[].*", "global.reject-service-keywords[].*",
"global.reject-service-keywords[]", "global.reject-service-keywords[]",
"global.default-alpn",
}; };
enum lejp_global_paths { enum lejp_global_paths {
@ -52,7 +52,8 @@ enum lejp_global_paths {
LWJPGP_PINGPONG_SECS, LWJPGP_PINGPONG_SECS,
LWJPGP_TIMEOUT_SECS, LWJPGP_TIMEOUT_SECS,
LWJPGP_REJECT_SERVICE_KEYWORDS_NAME, LWJPGP_REJECT_SERVICE_KEYWORDS_NAME,
LWJPGP_REJECT_SERVICE_KEYWORDS LWJPGP_REJECT_SERVICE_KEYWORDS,
LWJPGP_DEFAULT_ALPN,
}; };
static const char * const paths_vhosts[] = { static const char * const paths_vhosts[] = {
@ -100,6 +101,10 @@ static const char * const paths_vhosts[] = {
"vhosts[].client-ssl-ca", "vhosts[].client-ssl-ca",
"vhosts[].client-ssl-ciphers", "vhosts[].client-ssl-ciphers",
"vhosts[].onlyraw", "vhosts[].onlyraw",
"vhosts[].client-cert-required",
"vhosts[].ignore-missing-cert",
"vhosts[].error-document-404",
"vhosts[].alpn",
}; };
enum lejp_vhost_paths { enum lejp_vhost_paths {
@ -147,6 +152,10 @@ enum lejp_vhost_paths {
LEJPVP_CLIENT_SSL_CA, LEJPVP_CLIENT_SSL_CA,
LEJPVP_CLIENT_CIPHERS, LEJPVP_CLIENT_CIPHERS,
LEJPVP_FLAG_ONLYRAW, LEJPVP_FLAG_ONLYRAW,
LEJPVP_FLAG_CLIENT_CERT_REQUIRED,
LEJPVP_IGNORE_MISSING_CERT,
LEJPVP_ERROR_DOCUMENT_404,
LEJPVP_ALPN,
}; };
static const char * const parser_errs[] = { static const char * const parser_errs[] = {
@ -216,7 +225,7 @@ arg_to_bool(const char *s)
if (n) if (n)
return 1; return 1;
for (n = 0; n < ARRAY_SIZE(on); n++) for (n = 0; n < (int)ARRAY_SIZE(on); n++)
if (!strcasecmp(s, on[n])) if (!strcasecmp(s, on[n]))
return 1; return 1;
@ -285,6 +294,10 @@ lejp_globals_cb(struct lejp_ctx *ctx, char reason)
a->info->timeout_secs = atoi(ctx->buf); a->info->timeout_secs = atoi(ctx->buf);
return 0; return 0;
case LWJPGP_DEFAULT_ALPN:
a->info->alpn = a->p;
break;
default: default:
return 0; return 0;
} }
@ -312,20 +325,39 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
#endif #endif
if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) { if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
uint32_t i[4];
const char *ss;
/* set the defaults for this vhost */ /* set the defaults for this vhost */
a->valid = 1; a->valid = 1;
a->head = NULL; a->head = NULL;
a->last = NULL; a->last = NULL;
a->info->port = 0;
a->info->iface = NULL; i[0] = a->info->count_threads;
i[1] = a->info->options & (
LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME |
LWS_SERVER_OPTION_LIBUV |
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN |
LWS_SERVER_OPTION_LIBEVENT |
LWS_SERVER_OPTION_LIBEV
);
ss = a->info->server_string;
i[2] = a->info->ws_ping_pong_interval;
i[3] = a->info->timeout_secs;
memset(a->info, 0, sizeof(*a->info));
a->info->count_threads = i[0];
a->info->options = i[1];
a->info->server_string = ss;
a->info->ws_ping_pong_interval = i[2];
a->info->timeout_secs = i[3];
a->info->protocols = a->protocols; a->info->protocols = a->protocols;
a->info->extensions = a->extensions; a->info->extensions = a->extensions;
a->info->ssl_cert_filepath = NULL; #if defined(LWS_WITH_TLS)
a->info->ssl_private_key_filepath = NULL;
a->info->ssl_ca_filepath = NULL;
a->info->client_ssl_cert_filepath = NULL;
a->info->client_ssl_private_key_filepath = NULL;
a->info->client_ssl_ca_filepath = NULL;
a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:" a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-GCM-SHA384:" "ECDHE-RSA-AES256-GCM-SHA384:"
"DHE-RSA-AES256-GCM-SHA384:" "DHE-RSA-AES256-GCM-SHA384:"
@ -339,7 +371,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
"!DHE-RSA-AES256-SHA256:" "!DHE-RSA-AES256-SHA256:"
"!AES256-GCM-SHA384:" "!AES256-GCM-SHA384:"
"!AES256-SHA256"; "!AES256-SHA256";
a->info->timeout_secs = 5; #endif
a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:" a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-GCM-SHA384:" "ECDHE-RSA-AES256-GCM-SHA384:"
"DHE-RSA-AES256-GCM-SHA384:" "DHE-RSA-AES256-GCM-SHA384:"
@ -353,13 +385,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
"!DHE-RSA-AES256-SHA256:" "!DHE-RSA-AES256-SHA256:"
"!AES256-GCM-SHA384:" "!AES256-GCM-SHA384:"
"!AES256-SHA256"; "!AES256-SHA256";
a->info->pvo = NULL;
a->info->headers = NULL;
a->info->keepalive_timeout = 5; a->info->keepalive_timeout = 5;
a->info->log_filepath = NULL;
a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK |
LWS_SERVER_OPTION_STS | LWS_SERVER_OPTION_ONLY_RAW);
a->enable_client_ssl = 0;
} }
if (reason == LEJPCB_OBJECT_START && if (reason == LEJPCB_OBJECT_START &&
@ -379,7 +405,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->pvo->next = a->info->pvo; a->pvo->next = a->info->pvo;
a->info->pvo = a->pvo; a->info->pvo = a->pvo;
a->pvo->name = a->p; a->pvo->name = a->p;
lwsl_notice(" adding protocol %s\n", a->p); lwsl_info(" adding protocol %s\n", a->p);
a->p += n; a->p += n;
a->pvo->value = a->p; a->pvo->value = a->p;
a->pvo->options = NULL; a->pvo->options = NULL;
@ -431,6 +457,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
} }
a->any_vhosts = 1; a->any_vhosts = 1;
#if defined(LWS_WITH_TLS)
if (a->enable_client_ssl) { if (a->enable_client_ssl) {
const char *cert_filepath = a->info->client_ssl_cert_filepath; const char *cert_filepath = a->info->client_ssl_cert_filepath;
const char *private_key_filepath = a->info->client_ssl_private_key_filepath; const char *private_key_filepath = a->info->client_ssl_private_key_filepath;
@ -444,6 +471,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
lws_init_vhost_client_ssl(a->info, vhost); lws_init_vhost_client_ssl(a->info, vhost);
} }
#endif
return 0; return 0;
} }
@ -474,7 +502,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
if (a->last) if (a->last)
a->last->mount_next = m; a->last->mount_next = m;
for (n = 0; n < ARRAY_SIZE(mount_protocols); n++) for (n = 0; n < (int)ARRAY_SIZE(mount_protocols); n++)
if (!strncmp(a->m.origin, mount_protocols[n], if (!strncmp(a->m.origin, mount_protocols[n],
strlen(mount_protocols[n]))) { strlen(mount_protocols[n]))) {
lwsl_info("----%s\n", a->m.origin); lwsl_info("----%s\n", a->m.origin);
@ -484,7 +512,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
break; break;
} }
if (n == ARRAY_SIZE(mount_protocols)) { if (n == (int)ARRAY_SIZE(mount_protocols)) {
lwsl_err("unsupported protocol:// %s\n", a->m.origin); lwsl_err("unsupported protocol:// %s\n", a->m.origin);
return 1; return 1;
} }
@ -573,9 +601,11 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
case LEJPVP_KEEPALIVE_TIMEOUT: case LEJPVP_KEEPALIVE_TIMEOUT:
a->info->keepalive_timeout = atoi(ctx->buf); a->info->keepalive_timeout = atoi(ctx->buf);
return 0; return 0;
#if defined(LWS_WITH_TLS)
case LEJPVP_CLIENT_CIPHERS: case LEJPVP_CLIENT_CIPHERS:
a->info->client_ssl_cipher_list = a->p; a->info->client_ssl_cipher_list = a->p;
break; break;
#endif
case LEJPVP_CIPHERS: case LEJPVP_CIPHERS:
a->info->ssl_cipher_list = a->p; a->info->ssl_cipher_list = a->p;
break; break;
@ -651,6 +681,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
case LEJPVP_ENABLE_CLIENT_SSL: case LEJPVP_ENABLE_CLIENT_SSL:
a->enable_client_ssl = arg_to_bool(ctx->buf); a->enable_client_ssl = arg_to_bool(ctx->buf);
return 0; return 0;
#if defined(LWS_WITH_TLS)
case LEJPVP_CLIENT_SSL_KEY: case LEJPVP_CLIENT_SSL_KEY:
a->info->client_ssl_private_key_filepath = a->p; a->info->client_ssl_private_key_filepath = a->p;
break; break;
@ -660,6 +691,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
case LEJPVP_CLIENT_SSL_CA: case LEJPVP_CLIENT_SSL_CA:
a->info->client_ssl_ca_filepath = a->p; a->info->client_ssl_ca_filepath = a->p;
break; break;
#endif
case LEJPVP_NOIPV6: case LEJPVP_NOIPV6:
if (arg_to_bool(ctx->buf)) if (arg_to_bool(ctx->buf))
@ -683,6 +715,24 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE); a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
return 0; return 0;
case LEJPVP_FLAG_CLIENT_CERT_REQUIRED:
if (arg_to_bool(ctx->buf))
a->info->options |=
LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
return 0;
case LEJPVP_IGNORE_MISSING_CERT:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_IGNORE_MISSING_CERT;
else
a->info->options &= ~(LWS_SERVER_OPTION_IGNORE_MISSING_CERT);
return 0;
case LEJPVP_ERROR_DOCUMENT_404:
a->info->error_document_404 = a->p;
break;
case LEJPVP_SSL_OPTION_SET: case LEJPVP_SSL_OPTION_SET:
a->info->ssl_options_set |= atol(ctx->buf); a->info->ssl_options_set |= atol(ctx->buf);
return 0; return 0;
@ -690,6 +740,10 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->info->ssl_options_clear |= atol(ctx->buf); a->info->ssl_options_clear |= atol(ctx->buf);
return 0; return 0;
case LEJPVP_ALPN:
a->info->alpn = a->p;
break;
default: default:
return 0; return 0;
} }
@ -701,7 +755,7 @@ dostring:
n = p1 - p; n = p1 - p;
if (n > a->end - a->p) if (n > a->end - a->p)
n = a->end - a->p; n = a->end - a->p;
strncpy(a->p, p, n); lws_strncpy(a->p, p, n + 1);
a->p += n; a->p += n;
a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR); a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR);
p += n + strlen(ESC_INSTALL_DATADIR); p += n + strlen(ESC_INSTALL_DATADIR);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,177 @@
/*
* 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>
static int
rops_handle_POLLIN_listen(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
struct lws_context *context = wsi->context;
lws_sockfd_type accept_fd = LWS_SOCK_INVALID;
lws_sock_file_fd_type fd;
int opts = LWS_ADOPT_SOCKET | LWS_ADOPT_ALLOW_SSL;
struct sockaddr_storage cli_addr;
socklen_t clilen;
/* pollin means a client has connected to us then
*
* pollout is a hack on esp32 for background accepts signalling
* they completed
*/
do {
struct lws *cwsi;
if (!(pollfd->revents & (LWS_POLLIN | LWS_POLLOUT)) ||
!(pollfd->events & LWS_POLLIN))
break;
#if defined(LWS_WITH_TLS)
/*
* can we really accept it, with regards to SSL limit?
* another vhost may also have had POLLIN on his
* listener this round and used it up already
*/
if (wsi->vhost->tls.use_ssl &&
context->simultaneous_ssl_restriction &&
context->simultaneous_ssl ==
context->simultaneous_ssl_restriction)
/*
* no... ignore it, he won't come again until
* we are below the simultaneous_ssl_restriction
* limit and POLLIN is enabled on him again
*/
break;
#endif
/* listen socket got an unencrypted connection... */
clilen = sizeof(cli_addr);
lws_latency_pre(context, wsi);
/*
* We cannot identify the peer who is in the listen
* socket connect queue before we accept it; even if
* we could, not accepting it due to PEER_LIMITS would
* block the connect queue for other legit peers.
*/
accept_fd = accept((int)pollfd->fd,
(struct sockaddr *)&cli_addr, &clilen);
lws_latency(context, wsi, "listener accept",
(int)accept_fd, accept_fd != LWS_SOCK_INVALID);
if (accept_fd == LWS_SOCK_INVALID) {
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK) {
break;
}
lwsl_err("ERROR on accept: %s\n",
strerror(LWS_ERRNO));
break;
}
lws_plat_set_socket_options(wsi->vhost, accept_fd);
#if defined(LWS_WITH_IPV6)
lwsl_debug("accepted new conn port %u on fd=%d\n",
((cli_addr.ss_family == AF_INET6) ?
ntohs(((struct sockaddr_in6 *) &cli_addr)->sin6_port) :
ntohs(((struct sockaddr_in *) &cli_addr)->sin_port)),
accept_fd);
#else
lwsl_debug("accepted new conn port %u on fd=%d\n",
ntohs(((struct sockaddr_in *) &cli_addr)->sin_port),
accept_fd);
#endif
/*
* look at who we connected to and give user code a
* chance to reject based on client IP. There's no
* protocol selected yet so we issue this to
* protocols[0]
*/
if ((wsi->vhost->protocols[0].callback)(wsi,
LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
NULL,
(void *)(lws_intptr_t)accept_fd, 0)) {
lwsl_debug("Callback denied net connection\n");
compatible_close(accept_fd);
break;
}
if (!(wsi->vhost->options & LWS_SERVER_OPTION_ONLY_RAW))
opts |= LWS_ADOPT_HTTP;
else
opts = LWS_ADOPT_SOCKET;
fd.sockfd = accept_fd;
cwsi = lws_adopt_descriptor_vhost(wsi->vhost, opts, fd,
NULL, NULL);
if (!cwsi)
/* already closed cleanly as necessary */
return LWS_HPI_RET_WSI_ALREADY_DIED;
if (lws_server_socket_service_ssl(cwsi, accept_fd)) {
lws_close_free_wsi(cwsi, LWS_CLOSE_STATUS_NOSTATUS,
"listen svc fail");
return LWS_HPI_RET_WSI_ALREADY_DIED;
}
lwsl_info("%s: new wsi %p: wsistate 0x%x, role_ops %s\n",
__func__, cwsi, cwsi->wsistate, cwsi->role_ops->name);
} while (pt->fds_count < context->fd_limit_per_thread - 1 &&
wsi->position_in_fds_table != LWS_NO_FDS_POS &&
lws_poll_listen_fd(&pt->fds[wsi->position_in_fds_table]) > 0);
return LWS_HPI_RET_HANDLED;
}
int rops_handle_POLLOUT_listen(struct lws *wsi)
{
return LWS_HP_RET_USER_SERVICE;
}
struct lws_role_ops role_ops_listen = {
/* role name */ "listen",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_listen,
/* handle_POLLOUT */ rops_handle_POLLOUT_listen,
/* perform_user_POLLOUT */ NULL,
/* callback_on_writable */ NULL,
/* tx_credit */ NULL,
/* write_role_protocol */ NULL,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ NULL,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,
/* destroy_role */ NULL,
/* writeable cb clnt, srv */ { 0, 0 },
/* close cb clnt, srv */ { 0, 0 },
/* file_handle */ 0,
};

View File

@ -0,0 +1,81 @@
/*
* 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>
static int
rops_handle_POLLIN_pipe(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
#if !defined(WIN32) && !defined(_WIN32)
char s[100];
int n;
/*
* discard the byte(s) that signaled us
* We really don't care about the number of bytes, but coverity
* thinks we should.
*/
n = read(wsi->desc.sockfd, s, sizeof(s));
(void)n;
if (n < 0)
return LWS_HPI_RET_PLEASE_CLOSE_ME;
#endif
/*
* the poll() wait, or the event loop for libuv etc is a
* process-wide resource that we interrupted. So let every
* protocol that may be interested in the pipe event know that
* it happened.
*/
if (lws_broadcast(wsi->context, LWS_CALLBACK_EVENT_WAIT_CANCELLED,
NULL, 0)) {
lwsl_info("closed in event cancel\n");
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
return LWS_HPI_RET_HANDLED;
}
struct lws_role_ops role_ops_pipe = {
/* role name */ "pipe",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_pipe,
/* handle_POLLOUT */ NULL,
/* perform_user_POLLOUT */ NULL,
/* callback_on_writable */ NULL,
/* tx_credit */ NULL,
/* write_role_protocol */ NULL,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ NULL,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,
/* destroy_role */ NULL,
/* writeable cb clnt, srv */ { 0, 0 },
/* close cb clnt, srv */ { 0, 0 },
/* file_handle */ 1,
};

282
thirdparty/libwebsockets/roles/private.h vendored Normal file
View File

@ -0,0 +1,282 @@
/*
* 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
*
* This is included from core/private.h
*/
typedef uint32_t lws_wsi_state_t;
/*
* The wsi->role_ops pointer decides almost everything about what role the wsi
* will play, h2, raw, ws, etc.
*
* However there are a few additional flags needed that vary, such as if the
* role is a client or server side, if it has that concept. And the connection
* fulfilling the role, has a separate dynamic state.
*
* 31 16 15 0
* [ role flags ] [ state ]
*
* The role flags part is generally invariant for the lifetime of the wsi,
* although it can change if the connection role itself does, eg, if the
* connection upgrades from H1 -> WS1 the role flags may be changed at that
* point.
*
* The state part reflects the dynamic connection state, and the states are
* reused between roles.
*
* None of the internal role or state representations are made available outside
* of lws internals. Even for lws internals, if you add stuff here, please keep
* the constants inside this header only by adding necessary helpers here and
* use the helpers in the actual code. This is to ease any future refactors.
*
* Notice LWSIFR_ENCAP means we have a parent wsi that actually carries our
* data as a stream inside a different protocol.
*/
#define _RS 16
#define LWSIFR_CLIENT (0x1000 << _RS) /* client side */
#define LWSIFR_SERVER (0x2000 << _RS) /* server side */
#define LWSIFR_P_ENCAP_H2 (0x0100 << _RS) /* we are encapsulated by h2 */
enum lwsi_role {
LWSI_ROLE_MASK = (0xffff << _RS),
LWSI_ROLE_ENCAP_MASK = (0x0f00 << _RS),
};
#define lwsi_role(wsi) (wsi->wsistate & LWSI_ROLE_MASK)
#if !defined (_DEBUG)
#define lwsi_set_role(wsi, role) wsi->wsistate = \
(wsi->wsistate & (~LWSI_ROLE_MASK)) | role
#else
void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role);
#endif
#define lwsi_role_client(wsi) (!!(wsi->wsistate & LWSIFR_CLIENT))
#define lwsi_role_server(wsi) (!!(wsi->wsistate & LWSIFR_SERVER))
#define lwsi_role_h2_ENCAPSULATION(wsi) \
((wsi->wsistate & LWSI_ROLE_ENCAP_MASK) == LWSIFR_P_ENCAP_H2)
/* Pollout wants a callback in this state */
#define LWSIFS_POCB (0x100)
/* Before any protocol connection was established */
#define LWSIFS_NOT_EST (0x200)
enum lwsi_state {
/* Phase 1: pre-transport */
LRS_UNCONNECTED = LWSIFS_NOT_EST | 0,
LRS_WAITING_CONNECT = LWSIFS_NOT_EST | 1,
/* Phase 2: establishing intermediaries on top of transport */
LRS_WAITING_PROXY_REPLY = LWSIFS_NOT_EST | 2,
LRS_WAITING_SSL = LWSIFS_NOT_EST | 3,
LRS_WAITING_SOCKS_GREETING_REPLY = LWSIFS_NOT_EST | 4,
LRS_WAITING_SOCKS_CONNECT_REPLY = LWSIFS_NOT_EST | 5,
LRS_WAITING_SOCKS_AUTH_REPLY = LWSIFS_NOT_EST | 6,
/* Phase 3: establishing tls tunnel */
LRS_SSL_INIT = LWSIFS_NOT_EST | 7,
LRS_SSL_ACK_PENDING = LWSIFS_NOT_EST | 8,
LRS_PRE_WS_SERVING_ACCEPT = LWSIFS_NOT_EST | 9,
/* Phase 4: connected */
LRS_WAITING_SERVER_REPLY = LWSIFS_NOT_EST | 10,
LRS_H2_AWAIT_PREFACE = LWSIFS_NOT_EST | 11,
LRS_H2_AWAIT_SETTINGS = LWSIFS_NOT_EST |
LWSIFS_POCB | 12,
/* Phase 5: protocol logically established */
LRS_H2_CLIENT_SEND_SETTINGS = LWSIFS_POCB | 13,
LRS_H2_WAITING_TO_SEND_HEADERS = LWSIFS_POCB | 14,
LRS_DEFERRING_ACTION = LWSIFS_POCB | 15,
LRS_IDLING = 16,
LRS_H1C_ISSUE_HANDSHAKE = 17,
LRS_H1C_ISSUE_HANDSHAKE2 = 18,
LRS_ISSUE_HTTP_BODY = 19,
LRS_ISSUING_FILE = 20,
LRS_HEADERS = 21,
LRS_BODY = 22,
LRS_ESTABLISHED = LWSIFS_POCB | 23,
/* we are established, but we have embarked on serving a single
* transaction. Other transaction input may be pending, but we will
* not service it while we are busy dealing with the current
* transaction.
*
* When we complete the current transaction, we would reset our state
* back to ESTABLISHED and start to process the next transaction.
*/
LRS_DOING_TRANSACTION = LWSIFS_POCB | 24,
/* Phase 6: finishing */
LRS_WAITING_TO_SEND_CLOSE = LWSIFS_POCB | 25,
LRS_RETURNED_CLOSE = LWSIFS_POCB | 26,
LRS_AWAITING_CLOSE_ACK = LWSIFS_POCB | 27,
LRS_FLUSHING_BEFORE_CLOSE = LWSIFS_POCB | 28,
LRS_SHUTDOWN = 29,
/* Phase 7: dead */
LRS_DEAD_SOCKET = 30,
LRS_MASK = 0xffff
};
#define lwsi_state(wsi) ((enum lwsi_state)(wsi->wsistate & LRS_MASK))
#define lwsi_state_PRE_CLOSE(wsi) ((enum lwsi_state)(wsi->wsistate_pre_close & LRS_MASK))
#define lwsi_state_est(wsi) (!(wsi->wsistate & LWSIFS_NOT_EST))
#define lwsi_state_est_PRE_CLOSE(wsi) (!(wsi->wsistate_pre_close & LWSIFS_NOT_EST))
#define lwsi_state_can_handle_POLLOUT(wsi) (wsi->wsistate & LWSIFS_POCB)
#if !defined (_DEBUG)
#define lwsi_set_state(wsi, lrs) wsi->wsistate = \
(wsi->wsistate & (~LRS_MASK)) | lrs
#else
void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs);
#endif
/*
* internal role-specific ops
*/
struct lws_context_per_thread;
struct lws_role_ops {
const char *name;
const char *alpn;
/*
* After http headers have parsed, this is the last chance for a role
* to upgrade the connection to something else using the headers.
* ws-over-h2 is upgraded from h2 like this.
*/
int (*check_upgrades)(struct lws *wsi);
/* role-specific context init during context creation */
int (*init_context)(struct lws_context *context,
const struct lws_context_creation_info *info);
/* role-specific per-vhost init during vhost creation */
int (*init_vhost)(struct lws_vhost *vh,
const struct lws_context_creation_info *info);
/* role-specific per-vhost destructor during vhost destroy */
int (*destroy_vhost)(struct lws_vhost *vh);
/* generic 1Hz callback for the role itself */
int (*periodic_checks)(struct lws_context *context, int tsi,
time_t now);
/* chance for the role to force POLLIN without network activity */
int (*service_flag_pending)(struct lws_context *context, int tsi);
/* an fd using this role has POLLIN signalled */
int (*handle_POLLIN)(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd);
/* an fd using the role wanted a POLLOUT callback and now has it */
int (*handle_POLLOUT)(struct lws *wsi);
/* perform user pollout */
int (*perform_user_POLLOUT)(struct lws *wsi);
/* do effective callback on writeable */
int (*callback_on_writable)(struct lws *wsi);
/* connection-specific tx credit in bytes */
lws_fileofs_t (*tx_credit)(struct lws *wsi);
/* role-specific write formatting */
int (*write_role_protocol)(struct lws *wsi, unsigned char *buf,
size_t len, enum lws_write_protocol *wp);
/* get encapsulation parent */
struct lws * (*encapsulation_parent)(struct lws *wsi);
/* role-specific destructor */
int (*alpn_negotiated)(struct lws *wsi, const char *alpn);
/* chance for the role to handle close in the protocol */
int (*close_via_role_protocol)(struct lws *wsi,
enum lws_close_status reason);
/* role-specific close processing */
int (*close_role)(struct lws_context_per_thread *pt, struct lws *wsi);
/* role-specific connection close processing */
int (*close_kill_connection)(struct lws *wsi,
enum lws_close_status reason);
/* role-specific destructor */
int (*destroy_role)(struct lws *wsi);
/*
* the callback reasons for WRITEABLE for client, server
* (just client applies if no concept of client or server)
*/
uint16_t writeable_cb[2];
/*
* the callback reasons for CLOSE for client, server
* (just client applies if no concept of client or server)
*/
uint16_t close_cb[2];
unsigned int file_handle:1; /* role operates on files not sockets */
};
/* core roles */
extern struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, role_ops_listen,
role_ops_pipe;
/* bring in role private declarations */
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
#include "roles/http/private.h"
#else
#define lwsi_role_http(wsi) (0)
#endif
#if defined(LWS_ROLE_H1)
#include "roles/h1/private.h"
#else
#define lwsi_role_h1(wsi) (0)
#endif
#if defined(LWS_ROLE_H2)
#include "roles/h2/private.h"
#else
#define lwsi_role_h2(wsi) (0)
#endif
#if defined(LWS_ROLE_WS)
#include "roles/ws/private.h"
#else
#define lwsi_role_ws(wsi) (0)
#endif
#if defined(LWS_ROLE_CGI)
#include "roles/cgi/private.h"
#else
#define lwsi_role_cgi(wsi) (0)
#endif
enum {
LWS_HP_RET_BAIL_OK,
LWS_HP_RET_BAIL_DIE,
LWS_HP_RET_USER_SERVICE,
LWS_HPI_RET_WSI_ALREADY_DIED, /* we closed it */
LWS_HPI_RET_HANDLED, /* no probs */
LWS_HPI_RET_PLEASE_CLOSE_ME, /* close it for us */
LWS_UPG_RET_DONE,
LWS_UPG_RET_CONTINUE,
LWS_UPG_RET_BAIL
};

View File

@ -0,0 +1,223 @@
/*
* 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>
static int
rops_handle_POLLIN_raw_skt(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
struct lws_tokens ebuf;
int n, buffered;
/* pending truncated sends have uber priority */
if (wsi->trunc_len) {
if (!(pollfd->revents & LWS_POLLOUT))
return LWS_HPI_RET_HANDLED;
if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
wsi->trunc_len) < 0)
goto fail;
/*
* we can't afford to allow input processing to send
* something new, so spin around he event loop until
* he doesn't have any partials
*/
return LWS_HPI_RET_HANDLED;
}
if ((pollfd->revents & pollfd->events & LWS_POLLIN) &&
/* any tunnel has to have been established... */
lwsi_state(wsi) != LRS_SSL_ACK_PENDING &&
!(wsi->favoured_pollin &&
(pollfd->revents & pollfd->events & LWS_POLLOUT))) {
buffered = lws_buflist_aware_read(pt, wsi, &ebuf);
switch (ebuf.len) {
case 0:
lwsl_info("%s: read 0 len\n", __func__);
wsi->seen_zero_length_recv = 1;
lws_change_pollfd(wsi, LWS_POLLIN, 0);
/*
* we need to go to fail here, since it's the only
* chance we get to understand that the socket has
* closed
*/
// goto try_pollout;
goto fail;
case LWS_SSL_CAPABLE_ERROR:
goto fail;
case LWS_SSL_CAPABLE_MORE_SERVICE:
goto try_pollout;
}
n = user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_RAW_RX,
wsi->user_space, ebuf.token,
ebuf.len);
if (n < 0) {
lwsl_info("LWS_CALLBACK_RAW_RX_fail\n");
goto fail;
}
if (lws_buflist_aware_consume(wsi, &ebuf, ebuf.len, buffered))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
} else
if (wsi->favoured_pollin &&
(pollfd->revents & pollfd->events & LWS_POLLOUT))
/* we balanced the last favouring of pollin */
wsi->favoured_pollin = 0;
try_pollout:
/* this handles POLLOUT for http serving fragments */
if (!(pollfd->revents & LWS_POLLOUT))
return LWS_HPI_RET_HANDLED;
/* one shot */
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_notice("%s a\n", __func__);
goto fail;
}
/* clear back-to-back write detection */
wsi->could_have_pending = 0;
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_WRITEABLE_CB, 1);
#if defined(LWS_WITH_STATS)
if (wsi->active_writable_req_us) {
uint64_t ul = time_in_microseconds() -
wsi->active_writable_req_us;
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_MS_WRITABLE_DELAY, ul);
lws_stats_atomic_max(wsi->context, pt,
LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
wsi->active_writable_req_us = 0;
}
#endif
n = user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_RAW_WRITEABLE,
wsi->user_space, NULL, 0);
if (n < 0) {
lwsl_info("writeable_fail\n");
goto fail;
}
return LWS_HPI_RET_HANDLED;
fail:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "raw svc fail");
return LWS_HPI_RET_WSI_ALREADY_DIED;
}
static int
rops_handle_POLLIN_raw_file(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
int n;
if (pollfd->revents & LWS_POLLOUT) {
n = lws_callback_as_writeable(wsi);
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_info("failed at set pollfd\n");
return LWS_HPI_RET_WSI_ALREADY_DIED;
}
if (n)
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
if (pollfd->revents & LWS_POLLIN) {
if (user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_RAW_RX_FILE,
wsi->user_space, NULL, 0)) {
lwsl_debug("raw rx callback closed it\n");
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
}
if (pollfd->revents & LWS_POLLHUP)
return LWS_HPI_RET_PLEASE_CLOSE_ME;
return LWS_HPI_RET_HANDLED;
}
struct lws_role_ops role_ops_raw_skt = {
/* role name */ "raw-skt",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_raw_skt,
/* handle_POLLOUT */ NULL,
/* perform_user_POLLOUT */ NULL,
/* callback_on_writable */ NULL,
/* tx_credit */ NULL,
/* write_role_protocol */ NULL,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ NULL,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,
/* destroy_role */ NULL,
/* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE, 0 },
/* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE, 0 },
/* file_handle */ 0,
};
struct lws_role_ops role_ops_raw_file = {
/* role name */ "raw-file",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_raw_file,
/* handle_POLLOUT */ NULL,
/* perform_user_POLLOUT */ NULL,
/* callback_on_writable */ NULL,
/* tx_credit */ NULL,
/* write_role_protocol */ NULL,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ NULL,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,
/* destroy_role */ NULL,
/* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE_FILE, 0 },
/* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE_FILE, 0 },
/* file_handle */ 1,
};

View File

@ -19,31 +19,38 @@
* MA 02110-1301 USA * MA 02110-1301 USA
*/ */
#include "private-libwebsockets.h" #include "core/private.h"
/* /*
* parsers.c: lws_rx_sm() needs to be roughly kept in * parsers.c: lws_ws_rx_sm() needs to be roughly kept in
* sync with changes here, esp related to ext draining * sync with changes here, esp related to ext draining
*/ */
int lws_client_rx_sm(struct lws *wsi, unsigned char c) int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
{ {
int callback_action = LWS_CALLBACK_CLIENT_RECEIVE; int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
int handled, n, m, rx_draining_ext = 0; int handled, m;
unsigned short close_code; unsigned short close_code;
struct lws_tokens eff_buf; struct lws_tokens ebuf;
unsigned char *pp; unsigned char *pp;
#if !defined(LWS_WITHOUT_EXTENSIONS)
int rx_draining_ext = 0, n;
#endif
if (wsi->u.ws.rx_draining_ext) { ebuf.token = NULL;
ebuf.len = 0;
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
assert(!c); assert(!c);
eff_buf.token = NULL;
eff_buf.token_len = 0;
lws_remove_wsi_from_draining_ext_list(wsi); lws_remove_wsi_from_draining_ext_list(wsi);
rx_draining_ext = 1; rx_draining_ext = 1;
lwsl_debug("%s: doing draining flow\n", __func__); lwsl_debug("%s: doing draining flow\n", __func__);
goto drain_extension; goto drain_extension;
} }
#endif
if (wsi->socket_is_permanently_unusable) if (wsi->socket_is_permanently_unusable)
return -1; return -1;
@ -51,35 +58,38 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
switch (wsi->lws_rx_parse_state) { switch (wsi->lws_rx_parse_state) {
case LWS_RXPS_NEW: case LWS_RXPS_NEW:
/* control frames (PING) may interrupt checkable sequences */ /* control frames (PING) may interrupt checkable sequences */
wsi->u.ws.defeat_check_utf8 = 0; wsi->ws->defeat_check_utf8 = 0;
switch (wsi->ietf_spec_revision) { switch (wsi->ws->ietf_spec_revision) {
case 13: case 13:
wsi->u.ws.opcode = c & 0xf; wsi->ws->opcode = c & 0xf;
/* revisit if an extension wants them... */ /* revisit if an extension wants them... */
switch (wsi->u.ws.opcode) { switch (wsi->ws->opcode) {
case LWSWSOPC_TEXT_FRAME: case LWSWSOPC_TEXT_FRAME:
wsi->u.ws.rsv_first_msg = (c & 0x70); wsi->ws->rsv_first_msg = (c & 0x70);
wsi->u.ws.continuation_possible = 1; wsi->ws->continuation_possible = 1;
wsi->u.ws.check_utf8 = lws_check_opt( wsi->ws->check_utf8 = lws_check_opt(
wsi->context->options, wsi->context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8); LWS_SERVER_OPTION_VALIDATE_UTF8);
wsi->u.ws.utf8 = 0; wsi->ws->utf8 = 0;
wsi->ws->first_fragment = 1;
break; break;
case LWSWSOPC_BINARY_FRAME: case LWSWSOPC_BINARY_FRAME:
wsi->u.ws.rsv_first_msg = (c & 0x70); wsi->ws->rsv_first_msg = (c & 0x70);
wsi->u.ws.check_utf8 = 0; wsi->ws->check_utf8 = 0;
wsi->u.ws.continuation_possible = 1; wsi->ws->continuation_possible = 1;
wsi->ws->first_fragment = 1;
break; break;
case LWSWSOPC_CONTINUATION: case LWSWSOPC_CONTINUATION:
if (!wsi->u.ws.continuation_possible) { if (!wsi->ws->continuation_possible) {
lwsl_info("disordered continuation\n"); lwsl_info("disordered continuation\n");
return -1; return -1;
} }
wsi->ws->first_fragment = 0;
break; break;
case LWSWSOPC_CLOSE: case LWSWSOPC_CLOSE:
wsi->u.ws.check_utf8 = 0; wsi->ws->check_utf8 = 0;
wsi->u.ws.utf8 = 0; wsi->ws->utf8 = 0;
break; break;
case 3: case 3:
case 4: case 4:
@ -94,45 +104,45 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
lwsl_info("illegal opcode\n"); lwsl_info("illegal opcode\n");
return -1; return -1;
default: default:
wsi->u.ws.defeat_check_utf8 = 1; wsi->ws->defeat_check_utf8 = 1;
break; break;
} }
wsi->u.ws.rsv = (c & 0x70); wsi->ws->rsv = (c & 0x70);
/* revisit if an extension wants them... */ /* revisit if an extension wants them... */
if ( if (
#ifndef LWS_NO_EXTENSIONS #if !defined(LWS_WITHOUT_EXTENSIONS)
!wsi->count_act_ext && !wsi->ws->count_act_ext &&
#endif #endif
wsi->u.ws.rsv) { wsi->ws->rsv) {
lwsl_info("illegal rsv bits set\n"); lwsl_info("illegal rsv bits set\n");
return -1; return -1;
} }
wsi->u.ws.final = !!((c >> 7) & 1); wsi->ws->final = !!((c >> 7) & 1);
lwsl_ext("%s: This RX frame Final %d\n", __func__, lwsl_ext("%s: This RX frame Final %d\n", __func__,
wsi->u.ws.final); wsi->ws->final);
if (wsi->u.ws.owed_a_fin && if (wsi->ws->owed_a_fin &&
(wsi->u.ws.opcode == LWSWSOPC_TEXT_FRAME || (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME)) { wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
lwsl_info("hey you owed us a FIN\n"); lwsl_info("hey you owed us a FIN\n");
return -1; return -1;
} }
if ((!(wsi->u.ws.opcode & 8)) && wsi->u.ws.final) { if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) {
wsi->u.ws.continuation_possible = 0; wsi->ws->continuation_possible = 0;
wsi->u.ws.owed_a_fin = 0; wsi->ws->owed_a_fin = 0;
} }
if ((wsi->u.ws.opcode & 8) && !wsi->u.ws.final) { if ((wsi->ws->opcode & 8) && !wsi->ws->final) {
lwsl_info("control msg can't be fragmented\n"); lwsl_info("control msg can't be fragmented\n");
return -1; return -1;
} }
if (!wsi->u.ws.final) if (!wsi->ws->final)
wsi->u.ws.owed_a_fin = 1; wsi->ws->owed_a_fin = 1;
switch (wsi->u.ws.opcode) { switch (wsi->ws->opcode) {
case LWSWSOPC_TEXT_FRAME: case LWSWSOPC_TEXT_FRAME:
case LWSWSOPC_BINARY_FRAME: case LWSWSOPC_BINARY_FRAME:
wsi->u.ws.frame_is_binary = wsi->u.ws.opcode == wsi->ws->frame_is_binary = wsi->ws->opcode ==
LWSWSOPC_BINARY_FRAME; LWSWSOPC_BINARY_FRAME;
break; break;
} }
@ -141,38 +151,38 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
default: default:
lwsl_err("unknown spec version %02d\n", lwsl_err("unknown spec version %02d\n",
wsi->ietf_spec_revision); wsi->ws->ietf_spec_revision);
break; break;
} }
break; break;
case LWS_RXPS_04_FRAME_HDR_LEN: case LWS_RXPS_04_FRAME_HDR_LEN:
wsi->u.ws.this_frame_masked = !!(c & 0x80); wsi->ws->this_frame_masked = !!(c & 0x80);
switch (c & 0x7f) { switch (c & 0x7f) {
case 126: case 126:
/* control frames are not allowed to have big lengths */ /* control frames are not allowed to have big lengths */
if (wsi->u.ws.opcode & 8) if (wsi->ws->opcode & 8)
goto illegal_ctl_length; goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
break; break;
case 127: case 127:
/* control frames are not allowed to have big lengths */ /* control frames are not allowed to have big lengths */
if (wsi->u.ws.opcode & 8) if (wsi->ws->opcode & 8)
goto illegal_ctl_length; goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
break; break;
default: default:
wsi->u.ws.rx_packet_length = c; wsi->ws->rx_packet_length = c & 0x7f;
if (wsi->u.ws.this_frame_masked) if (wsi->ws->this_frame_masked)
wsi->lws_rx_parse_state = wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1; LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else { else {
if (c) if (wsi->ws->rx_packet_length) {
wsi->lws_rx_parse_state = wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; LWS_RXPS_WS_FRAME_PAYLOAD;
else { } else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW; wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill; goto spill;
} }
@ -182,18 +192,18 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
break; break;
case LWS_RXPS_04_FRAME_HDR_LEN16_2: case LWS_RXPS_04_FRAME_HDR_LEN16_2:
wsi->u.ws.rx_packet_length = c << 8; wsi->ws->rx_packet_length = c << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
break; break;
case LWS_RXPS_04_FRAME_HDR_LEN16_1: case LWS_RXPS_04_FRAME_HDR_LEN16_1:
wsi->u.ws.rx_packet_length |= c; wsi->ws->rx_packet_length |= c;
if (wsi->u.ws.this_frame_masked) if (wsi->ws->this_frame_masked)
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1; wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else { else {
if (wsi->u.ws.rx_packet_length) if (wsi->ws->rx_packet_length)
wsi->lws_rx_parse_state = wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; LWS_RXPS_WS_FRAME_PAYLOAD;
else { else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW; wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill; goto spill;
@ -208,58 +218,58 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
return -1; return -1;
} }
#if defined __LP64__ #if defined __LP64__
wsi->u.ws.rx_packet_length = ((size_t)c) << 56; wsi->ws->rx_packet_length = ((size_t)c) << 56;
#else #else
wsi->u.ws.rx_packet_length = 0; wsi->ws->rx_packet_length = 0;
#endif #endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
break; break;
case LWS_RXPS_04_FRAME_HDR_LEN64_7: case LWS_RXPS_04_FRAME_HDR_LEN64_7:
#if defined __LP64__ #if defined __LP64__
wsi->u.ws.rx_packet_length |= ((size_t)c) << 48; wsi->ws->rx_packet_length |= ((size_t)c) << 48;
#endif #endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
break; break;
case LWS_RXPS_04_FRAME_HDR_LEN64_6: case LWS_RXPS_04_FRAME_HDR_LEN64_6:
#if defined __LP64__ #if defined __LP64__
wsi->u.ws.rx_packet_length |= ((size_t)c) << 40; wsi->ws->rx_packet_length |= ((size_t)c) << 40;
#endif #endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
break; break;
case LWS_RXPS_04_FRAME_HDR_LEN64_5: case LWS_RXPS_04_FRAME_HDR_LEN64_5:
#if defined __LP64__ #if defined __LP64__
wsi->u.ws.rx_packet_length |= ((size_t)c) << 32; wsi->ws->rx_packet_length |= ((size_t)c) << 32;
#endif #endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
break; break;
case LWS_RXPS_04_FRAME_HDR_LEN64_4: case LWS_RXPS_04_FRAME_HDR_LEN64_4:
wsi->u.ws.rx_packet_length |= ((size_t)c) << 24; wsi->ws->rx_packet_length |= ((size_t)c) << 24;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
break; break;
case LWS_RXPS_04_FRAME_HDR_LEN64_3: case LWS_RXPS_04_FRAME_HDR_LEN64_3:
wsi->u.ws.rx_packet_length |= ((size_t)c) << 16; wsi->ws->rx_packet_length |= ((size_t)c) << 16;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
break; break;
case LWS_RXPS_04_FRAME_HDR_LEN64_2: case LWS_RXPS_04_FRAME_HDR_LEN64_2:
wsi->u.ws.rx_packet_length |= ((size_t)c) << 8; wsi->ws->rx_packet_length |= ((size_t)c) << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
break; break;
case LWS_RXPS_04_FRAME_HDR_LEN64_1: case LWS_RXPS_04_FRAME_HDR_LEN64_1:
wsi->u.ws.rx_packet_length |= (size_t)c; wsi->ws->rx_packet_length |= (size_t)c;
if (wsi->u.ws.this_frame_masked) if (wsi->ws->this_frame_masked)
wsi->lws_rx_parse_state = wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1; LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else { else {
if (wsi->u.ws.rx_packet_length) if (wsi->ws->rx_packet_length)
wsi->lws_rx_parse_state = wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; LWS_RXPS_WS_FRAME_PAYLOAD;
else { else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW; wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill; goto spill;
@ -268,53 +278,53 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
break; break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_1: case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
wsi->u.ws.mask[0] = c; wsi->ws->mask[0] = c;
if (c) if (c)
wsi->u.ws.all_zero_nonce = 0; wsi->ws->all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2; wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
break; break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_2: case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
wsi->u.ws.mask[1] = c; wsi->ws->mask[1] = c;
if (c) if (c)
wsi->u.ws.all_zero_nonce = 0; wsi->ws->all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3; wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
break; break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_3: case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
wsi->u.ws.mask[2] = c; wsi->ws->mask[2] = c;
if (c) if (c)
wsi->u.ws.all_zero_nonce = 0; wsi->ws->all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4; wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
break; break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_4: case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
wsi->u.ws.mask[3] = c; wsi->ws->mask[3] = c;
if (c) if (c)
wsi->u.ws.all_zero_nonce = 0; wsi->ws->all_zero_nonce = 0;
if (wsi->u.ws.rx_packet_length) if (wsi->ws->rx_packet_length)
wsi->lws_rx_parse_state = wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; LWS_RXPS_WS_FRAME_PAYLOAD;
else { else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW; wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill; goto spill;
} }
break; break;
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED: case LWS_RXPS_WS_FRAME_PAYLOAD:
assert(wsi->u.ws.rx_ubuf); assert(wsi->ws->rx_ubuf);
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->u.ws.rx_draining_ext) if (wsi->ws->rx_draining_ext)
goto drain_extension; goto drain_extension;
#endif
if (wsi->ws->this_frame_masked && !wsi->ws->all_zero_nonce)
c ^= wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
if (wsi->u.ws.this_frame_masked && !wsi->u.ws.all_zero_nonce) wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] = c;
c ^= wsi->u.ws.mask[(wsi->u.ws.mask_idx++) & 3];
wsi->u.ws.rx_ubuf[LWS_PRE + (wsi->u.ws.rx_ubuf_head++)] = c; if (--wsi->ws->rx_packet_length == 0) {
if (--wsi->u.ws.rx_packet_length == 0) {
/* spill because we have the whole frame */ /* spill because we have the whole frame */
wsi->lws_rx_parse_state = LWS_RXPS_NEW; wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill; goto spill;
@ -325,11 +335,11 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
* supposed to default to context->pt_serv_buf_size * supposed to default to context->pt_serv_buf_size
*/ */
if (!wsi->protocol->rx_buffer_size && if (!wsi->protocol->rx_buffer_size &&
wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size) wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size)
break; break;
if (wsi->protocol->rx_buffer_size && if (wsi->protocol->rx_buffer_size &&
wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size) wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size)
break; break;
/* spill because we filled our rx buffer */ /* spill because we filled our rx buffer */
@ -342,18 +352,18 @@ spill:
* layer? If so service it and hide it from the user callback * layer? If so service it and hide it from the user callback
*/ */
switch (wsi->u.ws.opcode) { switch (wsi->ws->opcode) {
case LWSWSOPC_CLOSE: case LWSWSOPC_CLOSE:
pp = (unsigned char *)&wsi->u.ws.rx_ubuf[LWS_PRE]; pp = (unsigned char *)&wsi->ws->rx_ubuf[LWS_PRE];
if (lws_check_opt(wsi->context->options, if (lws_check_opt(wsi->context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8) && LWS_SERVER_OPTION_VALIDATE_UTF8) &&
wsi->u.ws.rx_ubuf_head > 2 && wsi->ws->rx_ubuf_head > 2 &&
lws_check_utf8(&wsi->u.ws.utf8, pp + 2, lws_check_utf8(&wsi->ws->utf8, pp + 2,
wsi->u.ws.rx_ubuf_head - 2)) wsi->ws->rx_ubuf_head - 2))
goto utf8_fail; goto utf8_fail;
/* is this an acknowledgement of our close? */ /* is this an acknowledgment of our close? */
if (wsi->state == LWSS_AWAITING_CLOSE_ACK) { if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
/* /*
* fine he has told us he is closing too, let's * fine he has told us he is closing too, let's
* finish our close * finish our close
@ -363,8 +373,8 @@ spill:
} }
lwsl_parser("client sees server close len = %d\n", lwsl_parser("client sees server close len = %d\n",
wsi->u.ws.rx_ubuf_head); wsi->ws->rx_ubuf_head);
if (wsi->u.ws.rx_ubuf_head >= 2) { if (wsi->ws->rx_ubuf_head >= 2) {
close_code = (pp[0] << 8) | pp[1]; close_code = (pp[0] << 8) | pp[1];
if (close_code < 1000 || if (close_code < 1000 ||
close_code == 1004 || close_code == 1004 ||
@ -384,39 +394,32 @@ spill:
wsi->protocol->callback, wsi, wsi->protocol->callback, wsi,
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE, LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
wsi->user_space, pp, wsi->user_space, pp,
wsi->u.ws.rx_ubuf_head)) wsi->ws->rx_ubuf_head))
return -1; return -1;
if (lws_partial_buffered(wsi)) memcpy(wsi->ws->ping_payload_buf + LWS_PRE, pp,
/* wsi->ws->rx_ubuf_head);
* if we're in the middle of something, wsi->ws->close_in_ping_buffer_len = wsi->ws->rx_ubuf_head;
* we can't do a normal close response and
* have to just close our end. lwsl_info("%s: scheduling return close as ack\n", __func__);
*/ __lws_change_pollfd(wsi, LWS_POLLIN, 0);
wsi->socket_is_permanently_unusable = 1; lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 3);
else wsi->waiting_to_send_close_frame = 1;
/* wsi->close_needs_ack = 0;
* parrot the close packet payload back lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
* we do not care about how it went, we are closing lws_callback_on_writable(wsi);
* immediately afterwards handled = 1;
*/ break;
lws_write(wsi, (unsigned char *)
&wsi->u.ws.rx_ubuf[LWS_PRE],
wsi->u.ws.rx_ubuf_head,
LWS_WRITE_CLOSE);
wsi->state = LWSS_RETURNED_CLOSE_ALREADY;
/* close the connection */
return -1;
case LWSWSOPC_PING: case LWSWSOPC_PING:
lwsl_info("received %d byte ping, sending pong\n", lwsl_info("received %d byte ping, sending pong\n",
wsi->u.ws.rx_ubuf_head); wsi->ws->rx_ubuf_head);
/* he set a close reason on this guy, ignore PING */ /* he set a close reason on this guy, ignore PING */
if (wsi->u.ws.close_in_ping_buffer_len) if (wsi->ws->close_in_ping_buffer_len)
goto ping_drop; goto ping_drop;
if (wsi->u.ws.ping_pending_flag) { if (wsi->ws->ping_pending_flag) {
/* /*
* there is already a pending ping payload * there is already a pending ping payload
* we should just log and drop * we should just log and drop
@ -426,30 +429,30 @@ spill:
} }
/* control packets can only be < 128 bytes long */ /* control packets can only be < 128 bytes long */
if (wsi->u.ws.rx_ubuf_head > 128 - 3) { if (wsi->ws->rx_ubuf_head > 128 - 3) {
lwsl_parser("DROP PING payload too large\n"); lwsl_parser("DROP PING payload too large\n");
goto ping_drop; goto ping_drop;
} }
/* stash the pong payload */ /* stash the pong payload */
memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE, memcpy(wsi->ws->ping_payload_buf + LWS_PRE,
&wsi->u.ws.rx_ubuf[LWS_PRE], &wsi->ws->rx_ubuf[LWS_PRE],
wsi->u.ws.rx_ubuf_head); wsi->ws->rx_ubuf_head);
wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head; wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head;
wsi->u.ws.ping_pending_flag = 1; wsi->ws->ping_pending_flag = 1;
/* get it sent as soon as possible */ /* get it sent as soon as possible */
lws_callback_on_writable(wsi); lws_callback_on_writable(wsi);
ping_drop: ping_drop:
wsi->u.ws.rx_ubuf_head = 0; wsi->ws->rx_ubuf_head = 0;
handled = 1; handled = 1;
break; break;
case LWSWSOPC_PONG: case LWSWSOPC_PONG:
lwsl_info("client receied pong\n"); lwsl_info("client receied pong\n");
lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE], lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
wsi->u.ws.rx_ubuf_head); wsi->ws->rx_ubuf_head);
if (wsi->pending_timeout == if (wsi->pending_timeout ==
PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) { PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
@ -467,30 +470,11 @@ ping_drop:
break; break;
default: default:
/* not handled or failed */
lwsl_ext("Unhandled ext opc 0x%x\n", wsi->ws->opcode);
wsi->ws->rx_ubuf_head = 0;
lwsl_parser("Reserved opc 0x%2X\n", wsi->u.ws.opcode); return -1;
/*
* It's something special we can't understand here.
* Pass the payload up to the extension's parsing
* state machine.
*/
eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
if (lws_ext_cb_active(wsi,
LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
&eff_buf, 0) <= 0) {
/* not handled or failed */
lwsl_ext("Unhandled ext opc 0x%x\n",
wsi->u.ws.opcode);
wsi->u.ws.rx_ubuf_head = 0;
return 0;
}
handled = 1;
break;
} }
/* /*
@ -501,53 +485,71 @@ ping_drop:
if (handled) if (handled)
goto already_done; goto already_done;
eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
eff_buf.token_len = wsi->u.ws.rx_ubuf_head; ebuf.len = wsi->ws->rx_ubuf_head;
if (wsi->u.ws.opcode == LWSWSOPC_PONG && !eff_buf.token_len) if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len)
goto already_done; goto already_done;
#if !defined(LWS_WITHOUT_EXTENSIONS)
drain_extension: drain_extension:
lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len); lwsl_ext("%s: passing %d to ext\n", __func__, ebuf.len);
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0); n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
lwsl_ext("Ext RX returned %d\n", n); lwsl_ext("Ext RX returned %d\n", n);
if (n < 0) { if (n < 0) {
wsi->socket_is_permanently_unusable = 1; wsi->socket_is_permanently_unusable = 1;
return -1; return -1;
} }
#endif
lwsl_debug("post inflate ebuf len %d\n", ebuf.len);
lwsl_ext("post inflate eff_buf len %d\n", eff_buf.token_len); if (
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (rx_draining_ext && !eff_buf.token_len) { rx_draining_ext &&
#endif
!ebuf.len) {
lwsl_debug(" --- ending drain on 0 read result\n"); lwsl_debug(" --- ending drain on 0 read result\n");
goto already_done; goto already_done;
} }
if (wsi->u.ws.check_utf8 && !wsi->u.ws.defeat_check_utf8) { if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
if (lws_check_utf8(&wsi->u.ws.utf8, if (lws_check_utf8(&wsi->ws->utf8,
(unsigned char *)eff_buf.token, (unsigned char *)ebuf.token,
eff_buf.token_len)) ebuf.len)) {
lws_close_reason(wsi,
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
(uint8_t *)"bad utf8", 8);
goto utf8_fail; goto utf8_fail;
}
/* we are ending partway through utf-8 character? */ /* we are ending partway through utf-8 character? */
if (!wsi->u.ws.rx_packet_length && wsi->u.ws.final && if (!wsi->ws->rx_packet_length && wsi->ws->final &&
wsi->u.ws.utf8 && !n) { wsi->ws->utf8
#if !defined(LWS_WITHOUT_EXTENSIONS)
&& !n
#endif
) {
lwsl_info("FINAL utf8 error\n"); lwsl_info("FINAL utf8 error\n");
lws_close_reason(wsi,
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
(uint8_t *)"partial utf8", 12);
utf8_fail: utf8_fail:
lwsl_info("utf8 error\n"); lwsl_info("utf8 error\n");
lwsl_hexdump_info(ebuf.token, ebuf.len);
return -1; return -1;
} }
} }
if (eff_buf.token_len < 0 && if (ebuf.len < 0 &&
callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG) callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
goto already_done; goto already_done;
if (!eff_buf.token) if (!ebuf.token)
goto already_done; goto already_done;
eff_buf.token[eff_buf.token_len] = '\0'; ebuf.token[ebuf.len] = '\0';
if (!wsi->protocol->callback) if (!wsi->protocol->callback)
goto already_done; goto already_done;
@ -555,7 +557,12 @@ utf8_fail:
if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG) if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
lwsl_info("Client doing pong callback\n"); lwsl_info("Client doing pong callback\n");
if (n && eff_buf.token_len) if (
/* coverity says dead code otherwise */
#if !defined(LWS_WITHOUT_EXTENSIONS)
n &&
#endif
ebuf.len)
/* extension had more... main loop will come back /* extension had more... main loop will come back
* we want callback to be done with this set, if so, * we want callback to be done with this set, if so,
* because lws_is_final() hides it was final until the * because lws_is_final() hides it was final until the
@ -565,21 +572,26 @@ utf8_fail:
else else
lws_remove_wsi_from_draining_ext_list(wsi); lws_remove_wsi_from_draining_ext_list(wsi);
if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY || if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION || lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
wsi->state == LWSS_AWAITING_CLOSE_ACK) lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
goto already_done; goto already_done;
m = wsi->protocol->callback(wsi, m = wsi->protocol->callback(wsi,
(enum lws_callback_reasons)callback_action, (enum lws_callback_reasons)callback_action,
wsi->user_space, eff_buf.token, eff_buf.token_len); wsi->user_space, ebuf.token, ebuf.len);
wsi->ws->first_fragment = 0;
// lwsl_notice("%s: bulk ws rx: input used %d, output %d\n",
// __func__, wsi->ws->rx_ubuf_head, ebuf.len);
/* if user code wants to close, let caller know */ /* if user code wants to close, let caller know */
if (m) if (m)
return 1; return 1;
already_done: already_done:
wsi->u.ws.rx_ubuf_head = 0; wsi->ws->rx_ubuf_head = 0;
break; break;
default: default:
lwsl_err("client rx illegal state\n"); lwsl_err("client rx illegal state\n");

View File

@ -0,0 +1,629 @@
/*
* 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>
/*
* In-place str to lower case
*/
static void
strtolower(char *s)
{
while (*s) {
#ifdef LWS_PLAT_OPTEE
int tolower_optee(int c);
*s = tolower_optee((int)*s);
#else
*s = tolower((int)*s);
#endif
s++;
}
}
int
lws_create_client_ws_object(struct lws_client_connect_info *i, struct lws *wsi)
{
int v = SPEC_LATEST_SUPPORTED;
/* allocate the ws struct for the wsi */
wsi->ws = lws_zalloc(sizeof(*wsi->ws), "client ws struct");
if (!wsi->ws) {
lwsl_notice("OOM\n");
return 1;
}
/* -1 means just use latest supported */
if (i->ietf_version_or_minus_one != -1 &&
i->ietf_version_or_minus_one)
v = i->ietf_version_or_minus_one;
wsi->ws->ietf_spec_revision = v;
return 0;
}
#if !defined(LWS_NO_CLIENT)
int
lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
{
if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) &&
(lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) &&
(lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) &&
!lwsi_role_client(wsi))
return 0;
// lwsl_notice("%s: hs client gets %d in\n", __func__, (int)len);
while (len) {
/*
* we were accepting input but now we stopped doing so
*/
if (lws_is_flowcontrolled(wsi)) {
//lwsl_notice("%s: caching %ld\n", __func__, (long)len);
lws_rxflow_cache(wsi, *buf, 0, (int)len);
*buf += len;
return 0;
}
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
int m;
//lwsl_notice("%s: draining ext\n", __func__);
if (lwsi_role_client(wsi))
m = lws_ws_client_rx_sm(wsi, 0);
else
m = lws_ws_rx_sm(wsi, 0, 0);
if (m < 0)
return -1;
continue;
}
#endif
/* caller will account for buflist usage */
if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
lwsl_notice("%s: client_rx_sm exited, DROPPING %d\n",
__func__, (int)len);
return -1;
}
len--;
}
// lwsl_notice("%s: finished with %ld\n", __func__, (long)len);
return 0;
}
#endif
char *
lws_generate_client_ws_handshake(struct lws *wsi, char *p)
{
char buf[128], hash[20], key_b64[40];
int n;
#if !defined(LWS_WITHOUT_EXTENSIONS)
const struct lws_extension *ext;
int ext_count = 0;
#endif
/*
* create the random key
*/
n = lws_get_random(wsi->context, hash, 16);
if (n != 16) {
lwsl_err("Unable to read from random dev %s\n",
SYSTEM_RANDOM_FILEPATH);
return NULL;
}
lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
p += sprintf(p, "Upgrade: websocket\x0d\x0a"
"Connection: Upgrade\x0d\x0a"
"Sec-WebSocket-Key: ");
strcpy(p, key_b64);
p += strlen(key_b64);
p += sprintf(p, "\x0d\x0a");
if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi,
_WSI_TOKEN_CLIENT_SENT_PROTOCOLS));
/* tell the server what extensions we could support */
#if !defined(LWS_WITHOUT_EXTENSIONS)
ext = wsi->vhost->ws.extensions;
while (ext && ext->callback) {
n = wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
wsi->user_space, (char *)ext->name, 0);
/*
* zero return from callback means go ahead and allow
* the extension, it's what we get if the callback is
* unhandled
*/
if (n) {
ext++;
continue;
}
/* apply it */
if (ext_count)
*p++ = ',';
else
p += sprintf(p, "Sec-WebSocket-Extensions: ");
p += sprintf(p, "%s", ext->client_offer);
ext_count++;
ext++;
}
if (ext_count)
p += sprintf(p, "\x0d\x0a");
#endif
if (wsi->ws->ietf_spec_revision)
p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
wsi->ws->ietf_spec_revision);
/* prepare the expected server accept response */
key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
key_b64);
lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);
lws_b64_encode_string(hash, 20,
wsi->http.ah->initial_handshake_hash_base64,
sizeof(wsi->http.ah->initial_handshake_hash_base64));
return p;
}
int
lws_client_ws_upgrade(struct lws *wsi, const char **cce)
{
int n, len, okay = 0;
struct lws_context *context = wsi->context;
const char *pc;
char *p;
#if !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
char *sb = (char *)&pt->serv_buf[0];
const struct lws_ext_options *opts;
const struct lws_extension *ext;
char ext_name[128];
const char *c, *a;
char ignore;
int more = 1;
#endif
if (wsi->client_h2_substream) {/* !!! client ws-over-h2 not there yet */
lwsl_warn("%s: client ws-over-h2 upgrade not supported yet\n",
__func__);
*cce = "HS: h2 / ws upgrade unsupported";
goto bail3;
}
if (wsi->http.ah->http_response == 401) {
lwsl_warn(
"lws_client_handshake: got bad HTTP response '%d'\n",
wsi->http.ah->http_response);
*cce = "HS: ws upgrade unauthorized";
goto bail3;
}
if (wsi->http.ah->http_response != 101) {
lwsl_warn(
"lws_client_handshake: got bad HTTP response '%d'\n",
wsi->http.ah->http_response);
*cce = "HS: ws upgrade response not 101";
goto bail3;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
lwsl_info("no ACCEPT\n");
*cce = "HS: ACCEPT missing";
goto bail3;
}
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
if (!p) {
lwsl_info("no UPGRADE\n");
*cce = "HS: UPGRADE missing";
goto bail3;
}
strtolower(p);
if (strcmp(p, "websocket")) {
lwsl_warn(
"lws_client_handshake: got bad Upgrade header '%s'\n", p);
*cce = "HS: Upgrade to something other than websocket";
goto bail3;
}
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION);
if (!p) {
lwsl_info("no Connection hdr\n");
*cce = "HS: CONNECTION missing";
goto bail3;
}
strtolower(p);
if (strcmp(p, "upgrade")) {
lwsl_warn("lws_client_int_s_hs: bad header %s\n", p);
*cce = "HS: UPGRADE malformed";
goto bail3;
}
pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
if (!pc) {
lwsl_parser("lws_client_int_s_hs: no protocol list\n");
} else
lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc);
/*
* confirm the protocol the server wants to talk was in the list
* of protocols we offered
*/
len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
if (!len) {
lwsl_info("%s: WSI_TOKEN_PROTOCOL is null\n", __func__);
/*
* no protocol name to work from,
* default to first protocol
*/
n = 0;
wsi->protocol = &wsi->vhost->protocols[0];
goto check_extensions;
}
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
len = (int)strlen(p);
while (pc && *pc && !okay) {
if (!strncmp(pc, p, len) &&
(pc[len] == ',' || pc[len] == '\0')) {
okay = 1;
continue;
}
while (*pc && *pc++ != ',')
;
while (*pc && *pc == ' ')
pc++;
}
if (!okay) {
lwsl_info("%s: got bad protocol %s\n", __func__, p);
*cce = "HS: PROTOCOL malformed";
goto bail2;
}
/*
* identify the selected protocol struct and set it
*/
n = 0;
/* keep client connection pre-bound protocol */
if (!lwsi_role_client(wsi))
wsi->protocol = NULL;
while (wsi->vhost->protocols[n].callback) {
if (!wsi->protocol &&
strcmp(p, wsi->vhost->protocols[n].name) == 0) {
wsi->protocol = &wsi->vhost->protocols[n];
break;
}
n++;
}
if (!wsi->vhost->protocols[n].callback) { /* no match */
/* if server, that's already fatal */
if (!lwsi_role_client(wsi)) {
lwsl_info("%s: fail protocol %s\n", __func__, p);
*cce = "HS: Cannot match protocol";
goto bail2;
}
/* for client, find the index of our pre-bound protocol */
n = 0;
while (wsi->vhost->protocols[n].callback) {
if (wsi->protocol && strcmp(wsi->protocol->name,
wsi->vhost->protocols[n].name) == 0) {
wsi->protocol = &wsi->vhost->protocols[n];
break;
}
n++;
}
if (!wsi->vhost->protocols[n].callback) {
if (wsi->protocol)
lwsl_err("Failed to match protocol %s\n",
wsi->protocol->name);
else
lwsl_err("No protocol on client\n");
goto bail2;
}
}
lwsl_debug("Selected protocol %s\n", wsi->protocol->name);
check_extensions:
/*
* stitch protocol choice into the vh protocol linked list
* We always insert ourselves at the start of the list
*
* X <-> B
* X <-> pAn <-> pB
*/
lws_vhost_lock(wsi->vhost);
wsi->same_vh_protocol_prev = /* guy who points to us */
&wsi->vhost->same_vh_protocol_list[n];
wsi->same_vh_protocol_next = /* old first guy is our next */
wsi->vhost->same_vh_protocol_list[n];
/* we become the new first guy */
wsi->vhost->same_vh_protocol_list[n] = wsi;
if (wsi->same_vh_protocol_next)
/* old first guy points back to us now */
wsi->same_vh_protocol_next->same_vh_protocol_prev =
&wsi->same_vh_protocol_next;
wsi->on_same_vh_list = 1;
lws_vhost_unlock(wsi->vhost);
#if !defined(LWS_WITHOUT_EXTENSIONS)
/* instantiate the accepted extensions */
if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
lwsl_ext("no client extensions allowed by server\n");
goto check_accept;
}
/*
* break down the list of server accepted extensions
* and go through matching them or identifying bogons
*/
if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size,
WSI_TOKEN_EXTENSIONS) < 0) {
lwsl_warn("ext list from server failed to copy\n");
*cce = "HS: EXT: list too big";
goto bail2;
}
c = sb;
n = 0;
ignore = 0;
a = NULL;
while (more) {
if (*c && (*c != ',' && *c != '\t')) {
if (*c == ';') {
ignore = 1;
if (!a)
a = c + 1;
}
if (ignore || *c == ' ') {
c++;
continue;
}
ext_name[n] = *c++;
if (n < (int)sizeof(ext_name) - 1)
n++;
continue;
}
ext_name[n] = '\0';
ignore = 0;
if (!*c)
more = 0;
else {
c++;
if (!n)
continue;
}
/* check we actually support it */
lwsl_notice("checking client ext %s\n", ext_name);
n = 0;
ext = wsi->vhost->ws.extensions;
while (ext && ext->callback) {
if (strcmp(ext_name, ext->name)) {
ext++;
continue;
}
n = 1;
lwsl_notice("instantiating client ext %s\n", ext_name);
/* instantiate the extension on this conn */
wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext;
/* allow him to construct his ext instance */
if (ext->callback(lws_get_context(wsi), ext, wsi,
LWS_EXT_CB_CLIENT_CONSTRUCT,
(void *)&wsi->ws->act_ext_user[wsi->ws->count_act_ext],
(void *)&opts, 0)) {
lwsl_info(" ext %s failed construction\n",
ext_name);
ext++;
continue;
}
/*
* allow the user code to override ext defaults if it
* wants to
*/
ext_name[0] = '\0';
if (user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_WS_EXT_DEFAULTS,
(char *)ext->name, ext_name,
sizeof(ext_name))) {
*cce = "HS: EXT: failed setting defaults";
goto bail2;
}
if (ext_name[0] &&
lws_ext_parse_options(ext, wsi, wsi->ws->act_ext_user[
wsi->ws->count_act_ext], opts, ext_name,
(int)strlen(ext_name))) {
lwsl_err("%s: unable to parse user defaults '%s'",
__func__, ext_name);
*cce = "HS: EXT: failed parsing defaults";
goto bail2;
}
/*
* give the extension the server options
*/
if (a && lws_ext_parse_options(ext, wsi,
wsi->ws->act_ext_user[wsi->ws->count_act_ext],
opts, a, lws_ptr_diff(c, a))) {
lwsl_err("%s: unable to parse remote def '%s'",
__func__, a);
*cce = "HS: EXT: failed parsing options";
goto bail2;
}
if (ext->callback(lws_get_context(wsi), ext, wsi,
LWS_EXT_CB_OPTION_CONFIRM,
wsi->ws->act_ext_user[wsi->ws->count_act_ext],
NULL, 0)) {
lwsl_err("%s: ext %s rejects server options %s",
__func__, ext->name, a);
*cce = "HS: EXT: Rejects server options";
goto bail2;
}
wsi->ws->count_act_ext++;
ext++;
}
if (n == 0) {
lwsl_warn("Unknown ext '%s'!\n", ext_name);
*cce = "HS: EXT: unknown ext";
goto bail2;
}
a = NULL;
n = 0;
}
check_accept:
#endif
/*
* Confirm his accept token is the one we precomputed
*/
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT);
if (strcmp(p, wsi->http.ah->initial_handshake_hash_base64)) {
lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p,
wsi->http.ah->initial_handshake_hash_base64);
*cce = "HS: Accept hash wrong";
goto bail2;
}
/* allocate the per-connection user memory (if any) */
if (lws_ensure_user_space(wsi)) {
lwsl_err("Problem allocating wsi user mem\n");
*cce = "HS: OOM";
goto bail2;
}
/*
* we seem to be good to go, give client last chance to check
* headers and OK it
*/
if (wsi->protocol->callback(wsi,
LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
wsi->user_space, NULL, 0)) {
*cce = "HS: Rejected by filter cb";
goto bail2;
}
/* clear his proxy connection timeout */
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
/* free up his parsing allocations */
lws_header_table_detach(wsi, 0);
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED,
&role_ops_ws);
lws_restart_ws_ping_pong_timer(wsi);
wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
/*
* create the frame buffer for this connection according to the
* size mentioned in the protocol definition. If 0 there, then
* use a big default for compatibility
*/
n = (int)wsi->protocol->rx_buffer_size;
if (!n)
n = context->pt_serv_buf_size;
n += LWS_PRE;
wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */,
"client frame buffer");
if (!wsi->ws->rx_ubuf) {
lwsl_err("Out of Mem allocating rx buffer %d\n", n);
*cce = "HS: OOM";
goto bail2;
}
wsi->ws->rx_ubuf_alloc = n;
lwsl_info("Allocating client RX buffer %d\n", n);
#if !defined(LWS_WITH_ESP32)
if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
(const char *)&n, sizeof n)) {
lwsl_warn("Failed to set SNDBUF to %d", n);
*cce = "HS: SO_SNDBUF failed";
goto bail3;
}
#endif
lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name);
/* call him back to inform him he is up */
if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED,
wsi->user_space, NULL, 0)) {
*cce = "HS: Rejected at CLIENT_ESTABLISHED";
goto bail3;
}
return 0;
bail3:
return 3;
bail2:
return 2;
}

1992
thirdparty/libwebsockets/roles/ws/ops-ws.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,164 @@
/*
* 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
*
* This is included from core/private.h if LWS_ROLE_WS
*/
extern struct lws_role_ops role_ops_ws;
#define lwsi_role_ws(wsi) (wsi->role_ops == &role_ops_ws)
enum lws_rx_parse_state {
LWS_RXPS_NEW,
LWS_RXPS_04_mask_1,
LWS_RXPS_04_mask_2,
LWS_RXPS_04_mask_3,
LWS_RXPS_04_FRAME_HDR_1,
LWS_RXPS_04_FRAME_HDR_LEN,
LWS_RXPS_04_FRAME_HDR_LEN16_2,
LWS_RXPS_04_FRAME_HDR_LEN16_1,
LWS_RXPS_04_FRAME_HDR_LEN64_8,
LWS_RXPS_04_FRAME_HDR_LEN64_7,
LWS_RXPS_04_FRAME_HDR_LEN64_6,
LWS_RXPS_04_FRAME_HDR_LEN64_5,
LWS_RXPS_04_FRAME_HDR_LEN64_4,
LWS_RXPS_04_FRAME_HDR_LEN64_3,
LWS_RXPS_04_FRAME_HDR_LEN64_2,
LWS_RXPS_04_FRAME_HDR_LEN64_1,
LWS_RXPS_07_COLLECT_FRAME_KEY_1,
LWS_RXPS_07_COLLECT_FRAME_KEY_2,
LWS_RXPS_07_COLLECT_FRAME_KEY_3,
LWS_RXPS_07_COLLECT_FRAME_KEY_4,
LWS_RXPS_WS_FRAME_PAYLOAD
};
enum lws_websocket_opcodes_07 {
LWSWSOPC_CONTINUATION = 0,
LWSWSOPC_TEXT_FRAME = 1,
LWSWSOPC_BINARY_FRAME = 2,
LWSWSOPC_NOSPEC__MUX = 7,
/* control extensions 8+ */
LWSWSOPC_CLOSE = 8,
LWSWSOPC_PING = 9,
LWSWSOPC_PONG = 0xa,
};
/* this is not usable directly by user code any more, lws_close_reason() */
#define LWS_WRITE_CLOSE 4
#define ALREADY_PROCESSED_IGNORE_CHAR 1
#define ALREADY_PROCESSED_NO_CB 2
#if !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_vhost_role_ws {
const struct lws_extension *extensions;
};
struct lws_pt_role_ws {
struct lws *rx_draining_ext_list;
struct lws *tx_draining_ext_list;
};
#endif
struct _lws_websocket_related {
char *rx_ubuf;
#if !defined(LWS_WITHOUT_EXTENSIONS)
const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE];
void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE];
struct lws *rx_draining_ext_list;
struct lws *tx_draining_ext_list;
#endif
/* Also used for close content... control opcode == < 128 */
uint8_t ping_payload_buf[128 - 3 + LWS_PRE];
uint8_t mask[4];
time_t time_next_ping_check;
size_t rx_packet_length;
uint32_t rx_ubuf_head;
uint32_t rx_ubuf_alloc;
uint8_t ping_payload_len;
uint8_t mask_idx;
uint8_t opcode;
uint8_t rsv;
uint8_t rsv_first_msg;
/* zero if no info, or length including 2-byte close code */
uint8_t close_in_ping_buffer_len;
uint8_t utf8;
uint8_t stashed_write_type;
uint8_t tx_draining_stashed_wp;
uint8_t ietf_spec_revision;
unsigned int final:1;
unsigned int frame_is_binary:1;
unsigned int all_zero_nonce:1;
unsigned int this_frame_masked:1;
unsigned int inside_frame:1; /* next write will be more of frame */
unsigned int clean_buffer:1; /* buffer not rewritten by extension */
unsigned int payload_is_close:1; /* process as PONG, but it is close */
unsigned int ping_pending_flag:1;
unsigned int continuation_possible:1;
unsigned int owed_a_fin:1;
unsigned int check_utf8:1;
unsigned int defeat_check_utf8:1;
unsigned int stashed_write_pending:1;
unsigned int send_check_ping:1;
unsigned int first_fragment:1;
unsigned int peer_has_sent_close:1;
#if !defined(LWS_WITHOUT_EXTENSIONS)
unsigned int extension_data_pending:1;
unsigned int rx_draining_ext:1;
unsigned int tx_draining_ext:1;
uint8_t count_act_ext;
#endif
};
int
lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len);
#if !defined(LWS_WITHOUT_EXTENSIONS)
LWS_VISIBLE void
lws_context_init_extensions(const struct lws_context_creation_info *info,
struct lws_context *context);
LWS_EXTERN int
lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
void *v, size_t len);
LWS_EXTERN int
lws_ext_cb_active(struct lws *wsi, int reason, void *buf, int len);
LWS_EXTERN int
lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi, int reason,
void *arg, int len);
#endif
int
handshake_0405(struct lws_context *context, struct lws *wsi);
int
lws_process_ws_upgrade(struct lws *wsi);
int
lws_server_init_wsi_for_ws(struct lws *wsi);

View File

@ -0,0 +1,836 @@
/*
* 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>
#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
#if !defined(LWS_WITHOUT_EXTENSIONS)
static int
lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
char ext_name[64], *args, *end = (*p) + budget - 1;
const struct lws_ext_options *opts, *po;
const struct lws_extension *ext;
struct lws_ext_option_arg oa;
int n, m, more = 1;
int ext_count = 0;
char ignore;
char *c;
/*
* Figure out which extensions the client has that we want to
* enable on this connection, and give him back the list
*/
if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS))
return 0;
/*
* break down the list of client extensions
* and go through them
*/
if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size,
WSI_TOKEN_EXTENSIONS) < 0)
return 1;
c = (char *)pt->serv_buf;
lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
wsi->ws->count_act_ext = 0;
ignore = 0;
n = 0;
args = NULL;
/*
* We may get a simple request
*
* Sec-WebSocket-Extensions: permessage-deflate
*
* or an elaborated one with requested options
*
* Sec-WebSocket-Extensions: permessage-deflate; \
* server_no_context_takeover; \
* client_no_context_takeover
*/
while (more) {
if (c >= (char *)pt->serv_buf + 255)
return -1;
if (*c && (*c != ',' && *c != '\t')) {
if (*c == ';') {
ignore = 1;
if (!args)
args = c + 1;
}
if (ignore || *c == ' ') {
c++;
continue;
}
ext_name[n] = *c++;
if (n < (int)sizeof(ext_name) - 1)
n++;
continue;
}
ext_name[n] = '\0';
ignore = 0;
if (!*c)
more = 0;
else {
c++;
if (!n)
continue;
}
while (args && *args && *args == ' ')
args++;
/* check a client's extension against our support */
ext = wsi->vhost->ws.extensions;
while (ext && ext->callback) {
if (strcmp(ext_name, ext->name)) {
ext++;
continue;
}
/*
* oh, we do support this one he asked for... but let's
* confirm he only gave it once
*/
for (m = 0; m < wsi->ws->count_act_ext; m++)
if (wsi->ws->active_extensions[m] == ext) {
lwsl_info("extension mentioned twice\n");
return 1; /* shenanigans */
}
/*
* ask user code if it's OK to apply it on this
* particular connection + protocol
*/
m = (wsi->protocol->callback)(wsi,
LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
wsi->user_space, ext_name, 0);
/*
* zero return from callback means go ahead and allow
* the extension, it's what we get if the callback is
* unhandled
*/
if (m) {
ext++;
continue;
}
/* apply it */
ext_count++;
/* instantiate the extension on this conn */
wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext;
/* allow him to construct his context */
if (ext->callback(lws_get_context(wsi), ext, wsi,
LWS_EXT_CB_CONSTRUCT,
(void *)&wsi->ws->act_ext_user[
wsi->ws->count_act_ext],
(void *)&opts, 0)) {
lwsl_info("ext %s failed construction\n",
ext_name);
ext_count--;
ext++;
continue;
}
if (ext_count > 1)
*(*p)++ = ',';
else
LWS_CPYAPP(*p,
"\x0d\x0aSec-WebSocket-Extensions: ");
*p += lws_snprintf(*p, (end - *p), "%s", ext_name);
/*
* The client may send a bunch of different option
* sets for the same extension, we are supposed to
* pick one we like the look of. The option sets are
* separated by comma.
*
* Actually we just either accept the first one or
* nothing.
*
* Go through the options trying to apply the
* recognized ones
*/
lwsl_info("ext args %s\n", args);
while (args && *args && *args != ',') {
while (*args == ' ')
args++;
po = opts;
while (po->name) {
/* only support arg-less options... */
if (po->type != EXTARG_NONE ||
strncmp(args, po->name,
strlen(po->name))) {
po++;
continue;
}
oa.option_name = NULL;
oa.option_index = (int)(po - opts);
oa.start = NULL;
oa.len = 0;
lwsl_info("setting '%s'\n", po->name);
if (!ext->callback(lws_get_context(wsi),
ext, wsi,
LWS_EXT_CB_OPTION_SET,
wsi->ws->act_ext_user[
wsi->ws->count_act_ext],
&oa, (end - *p))) {
*p += lws_snprintf(*p, (end - *p),
"; %s", po->name);
lwsl_debug("adding option %s\n",
po->name);
}
po++;
}
while (*args && *args != ',' && *args != ';')
args++;
if (*args == ';')
args++;
}
wsi->ws->count_act_ext++;
lwsl_parser("cnt_act_ext <- %d\n", wsi->ws->count_act_ext);
if (args && *args == ',')
more = 0;
ext++;
}
n = 0;
args = NULL;
}
return 0;
}
#endif
int
lws_process_ws_upgrade(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char protocol_list[128], protocol_name[64], *p;
int protocol_len, hit, n = 0, non_space_char_found = 0;
if (!wsi->protocol)
lwsl_err("NULL protocol at lws_read\n");
/*
* It's either websocket or h2->websocket
*
* Select the first protocol we support from the list
* the client sent us.
*
* Copy it to remove header fragmentation
*/
if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
WSI_TOKEN_PROTOCOL) < 0) {
lwsl_err("protocol list too long");
return 1;
}
protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
protocol_list[protocol_len] = '\0';
p = protocol_list;
hit = 0;
while (*p && !hit) {
n = 0;
non_space_char_found = 0;
while (n < (int)sizeof(protocol_name) - 1 &&
*p && *p != ',') {
/* ignore leading spaces */
if (!non_space_char_found && *p == ' ') {
n++;
continue;
}
non_space_char_found = 1;
protocol_name[n++] = *p++;
}
protocol_name[n] = '\0';
if (*p)
p++;
lwsl_debug("checking %s\n", protocol_name);
n = 0;
while (wsi->vhost->protocols[n].callback) {
lwsl_debug("try %s\n",
wsi->vhost->protocols[n].name);
if (wsi->vhost->protocols[n].name &&
!strcmp(wsi->vhost->protocols[n].name,
protocol_name)) {
wsi->protocol = &wsi->vhost->protocols[n];
hit = 1;
break;
}
n++;
}
}
/* we didn't find a protocol he wanted? */
if (!hit) {
if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
lwsl_notice("No protocol from \"%s\" supported\n",
protocol_list);
return 1;
}
/*
* some clients only have one protocol and
* do not send the protocol list header...
* allow it and match to the vhost's default
* protocol (which itself defaults to zero)
*/
lwsl_info("defaulting to prot handler %d\n",
wsi->vhost->default_protocol_index);
n = wsi->vhost->default_protocol_index;
wsi->protocol = &wsi->vhost->protocols[
(int)wsi->vhost->default_protocol_index];
}
/* allocate the ws struct for the wsi */
wsi->ws = lws_zalloc(sizeof(*wsi->ws), "ws struct");
if (!wsi->ws) {
lwsl_notice("OOM\n");
return 1;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
wsi->ws->ietf_spec_revision =
atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
/* allocate wsi->user storage */
if (lws_ensure_user_space(wsi)) {
lwsl_notice("problem with user space\n");
return 1;
}
/*
* Give the user code a chance to study the request and
* have the opportunity to deny it
*/
if ((wsi->protocol->callback)(wsi,
LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
wsi->user_space,
lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
lwsl_warn("User code denied connection\n");
return 1;
}
/*
* Perform the handshake according to the protocol version the
* client announced
*/
switch (wsi->ws->ietf_spec_revision) {
default:
lwsl_notice("Unknown client spec version %d\n",
wsi->ws->ietf_spec_revision);
wsi->ws->ietf_spec_revision = 13;
//return 1;
/* fallthru */
case 13:
#if defined(LWS_WITH_HTTP2)
if (wsi->h2_stream_carries_ws) {
if (lws_h2_ws_handshake(wsi)) {
lwsl_notice("h2 ws handshake failed\n");
return 1;
}
} else
#endif
{
lwsl_parser("lws_parse calling handshake_04\n");
if (handshake_0405(wsi->context, wsi)) {
lwsl_notice("hs0405 has failed the connection\n");
return 1;
}
}
break;
}
lws_same_vh_protocol_insert(wsi, n);
/*
* We are upgrading to ws, so http/1.1 + h2 and keepalive + pipelined
* header considerations about keeping the ah around no longer apply.
*
* However it's common for the first ws protocol data to have been
* coalesced with the browser upgrade request and to already be in the
* ah rx buffer.
*/
lws_pt_lock(pt, __func__);
if (wsi->h2_stream_carries_ws)
lws_role_transition(wsi, LWSIFR_SERVER | LWSIFR_P_ENCAP_H2,
LRS_ESTABLISHED, &role_ops_ws);
else
lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED,
&role_ops_ws);
lws_pt_unlock(pt);
lws_server_init_wsi_for_ws(wsi);
lwsl_parser("accepted v%02d connection\n", wsi->ws->ietf_spec_revision);
lwsl_info("%s: %p: dropping ah on ws upgrade\n", __func__, wsi);
lws_header_table_detach(wsi, 1);
return 0;
}
int
handshake_0405(struct lws_context *context, struct lws *wsi)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws_process_html_args args;
unsigned char hash[20];
int n, accept_len;
char *response;
char *p;
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
!lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
lwsl_info("handshake_04 missing pieces\n");
/* completed header processing, but missing some bits */
goto bail;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= MAX_WEBSOCKET_04_KEY_LEN) {
lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
goto bail;
}
/*
* since key length is restricted above (currently 128), cannot
* overflow
*/
n = sprintf((char *)pt->serv_buf,
"%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
lws_SHA1(pt->serv_buf, n, hash);
accept_len = lws_b64_encode_string((char *)hash, 20,
(char *)pt->serv_buf, context->pt_serv_buf_size);
if (accept_len < 0) {
lwsl_warn("Base64 encoded hash too long\n");
goto bail;
}
/* allocate the per-connection user memory (if any) */
if (lws_ensure_user_space(wsi))
goto bail;
/* create the response packet */
/* make a buffer big enough for everything */
response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + 256 + LWS_PRE;
p = response;
LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
"Upgrade: WebSocket\x0d\x0a"
"Connection: Upgrade\x0d\x0a"
"Sec-WebSocket-Accept: ");
strcpy(p, (char *)pt->serv_buf);
p += accept_len;
/* we can only return the protocol header if:
* - one came in, and ... */
if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
/* - it is not an empty string */
wsi->protocol->name &&
wsi->protocol->name[0]) {
LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
}
#if !defined(LWS_WITHOUT_EXTENSIONS)
/*
* Figure out which extensions the client has that we want to
* enable on this connection, and give him back the list.
*
* Give him a limited write bugdet
*/
if (lws_extension_server_handshake(wsi, &p, 192))
goto bail;
#endif
LWS_CPYAPP(p, "\x0d\x0a");
args.p = p;
args.max_len = lws_ptr_diff((char *)pt->serv_buf +
context->pt_serv_buf_size, p);
if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
LWS_CALLBACK_ADD_HEADERS,
wsi->user_space, &args, 0))
goto bail;
p = args.p;
/* end of response packet */
LWS_CPYAPP(p, "\x0d\x0a");
/* okay send the handshake response accepting the connection */
lwsl_parser("issuing resp pkt %d len\n",
lws_ptr_diff(p, response));
#if defined(DEBUG)
fwrite(response, 1, p - response, stderr);
#endif
n = lws_write(wsi, (unsigned char *)response, p - response,
LWS_WRITE_HTTP_HEADERS);
if (n != (p - response)) {
lwsl_info("%s: ERROR writing to socket %d\n", __func__, n);
goto bail;
}
/* alright clean up and set ourselves into established state */
lwsi_set_state(wsi, LRS_ESTABLISHED);
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
{
const char * uri_ptr =
lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
const struct lws_http_mount *hit =
lws_find_mount(wsi, uri_ptr, uri_len);
if (hit && hit->cgienv &&
wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO,
wsi->user_space, (void *)hit->cgienv, 0))
return 1;
}
return 0;
bail:
/* caller will free up his parsing allocations */
return -1;
}
/*
* Once we reach LWS_RXPS_WS_FRAME_PAYLOAD, we know how much
* to expect in that state and can deal with it in bulk more efficiently.
*/
static int
lws_ws_frame_rest_is_payload(struct lws *wsi, uint8_t **buf, size_t len)
{
uint8_t *buffer = *buf, mask[4];
struct lws_tokens ebuf;
unsigned int avail = (unsigned int)len;
#if !defined(LWS_WITHOUT_EXTENSIONS)
unsigned int old_packet_length = (int)wsi->ws->rx_packet_length;
#endif
int n = 0;
/*
* With zlib, we can give it as much input as we like. The pmd
* extension will draw it down in chunks (default 1024).
*
* If we try to restrict how much we give it, because we must go
* back to the event loop each time, we will drop the remainder...
*/
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (!wsi->ws->count_act_ext)
#endif
{
if (wsi->protocol->rx_buffer_size)
avail = (int)wsi->protocol->rx_buffer_size;
else
avail = wsi->context->pt_serv_buf_size;
}
/* do not consume more than we should */
if (avail > wsi->ws->rx_packet_length)
avail = (unsigned int)wsi->ws->rx_packet_length;
/* do not consume more than what is in the buffer */
if (avail > len)
avail = (unsigned int)len;
if (avail <= 0)
return 0;
ebuf.token = (char *)buffer;
ebuf.len = avail;
//lwsl_hexdump_notice(ebuf.token, ebuf.len);
if (!wsi->ws->all_zero_nonce) {
for (n = 0; n < 4; n++)
mask[n] = wsi->ws->mask[(wsi->ws->mask_idx + n) & 3];
/* deal with 4-byte chunks using unwrapped loop */
n = avail >> 2;
while (n--) {
*(buffer) = *(buffer) ^ mask[0];
buffer++;
*(buffer) = *(buffer) ^ mask[1];
buffer++;
*(buffer) = *(buffer) ^ mask[2];
buffer++;
*(buffer) = *(buffer) ^ mask[3];
buffer++;
}
/* and the remaining bytes bytewise */
for (n = 0; n < (int)(avail & 3); n++) {
*(buffer) = *(buffer) ^ mask[n];
buffer++;
}
wsi->ws->mask_idx = (wsi->ws->mask_idx + avail) & 3;
}
lwsl_info("%s: using %d of raw input (total %d on offer)\n", __func__,
avail, (int)len);
(*buf) += avail;
len -= avail;
#if !defined(LWS_WITHOUT_EXTENSIONS)
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
lwsl_info("%s: ext says %d / ebuf.len %d\n", __func__, n, ebuf.len);
#endif
/*
* ebuf may be pointing somewhere completely different now,
* it's the output
*/
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (n < 0) {
/*
* we may rely on this to get RX, just drop connection
*/
lwsl_notice("%s: LWS_EXT_CB_PAYLOAD_RX blew out\n", __func__);
wsi->socket_is_permanently_unusable = 1;
return -1;
}
#endif
wsi->ws->rx_packet_length -= avail;
#if !defined(LWS_WITHOUT_EXTENSIONS)
/*
* if we had an rx fragment right at the last compressed byte of the
* message, we can get a zero length inflated output, where no prior
* rx inflated output marked themselves with FIN, since there was
* raw ws payload still to drain at that time.
*
* Then we need to generate a zero length ws rx that can be understood
* as the message completion.
*/
if (!ebuf.len && /* zero-length inflation output */
!n && /* nothing left to drain from the inflator */
wsi->ws->count_act_ext && /* we are using pmd */
old_packet_length && /* we gave the inflator new input */
!wsi->ws->rx_packet_length && /* raw ws packet payload all gone */
wsi->ws->final && /* the raw ws packet is a FIN guy */
wsi->protocol->callback &&
!wsi->wsistate_pre_close) {
if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
LWS_CALLBACK_RECEIVE,
wsi->user_space, NULL, 0))
return -1;
return avail;
}
#endif
if (!ebuf.len)
return avail;
if (
#if !defined(LWS_WITHOUT_EXTENSIONS)
n &&
#endif
ebuf.len)
/* extension had more... main loop will come back */
lws_add_wsi_to_draining_ext_list(wsi);
else
lws_remove_wsi_from_draining_ext_list(wsi);
if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
if (lws_check_utf8(&wsi->ws->utf8,
(unsigned char *)ebuf.token, ebuf.len)) {
lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD,
(uint8_t *)"bad utf8", 8);
goto utf8_fail;
}
/* we are ending partway through utf-8 character? */
if (!wsi->ws->rx_packet_length && wsi->ws->final &&
wsi->ws->utf8 && !n) {
lwsl_info("FINAL utf8 error\n");
lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD,
(uint8_t *)"partial utf8", 12);
utf8_fail:
lwsl_info("utf8 error\n");
lwsl_hexdump_info(ebuf.token, ebuf.len);
return -1;
}
}
if (wsi->protocol->callback && !wsi->wsistate_pre_close)
if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
LWS_CALLBACK_RECEIVE,
wsi->user_space,
ebuf.token, ebuf.len))
return -1;
wsi->ws->first_fragment = 0;
#if !defined(LWS_WITHOUT_EXTENSIONS)
lwsl_info("%s: input used %d, output %d, rem len %d, rx_draining_ext %d\n",
__func__, avail, ebuf.len, (int)len, wsi->ws->rx_draining_ext);
#endif
return avail; /* how much we used from the input */
}
int
lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
{
int m, bulk = 0;
lwsl_debug("%s: received %d byte packet\n", __func__, (int)len);
//lwsl_hexdump_notice(*buf, len);
/* let the rx protocol state machine have as much as it needs */
while (len) {
/*
* we were accepting input but now we stopped doing so
*/
if (wsi->rxflow_bitmap) {
lwsl_info("%s: doing rxflow\n", __func__);
lws_rxflow_cache(wsi, *buf, 0, (int)len);
lwsl_parser("%s: cached %ld\n", __func__, (long)len);
*buf += len; /* stashing it is taking care of it */
return 1;
}
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
lwsl_debug("%s: draining rx ext\n", __func__);
m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
if (m < 0)
return -1;
continue;
}
#endif
/* consume payload bytes efficiently */
while (wsi->lws_rx_parse_state == LWS_RXPS_WS_FRAME_PAYLOAD &&
(wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
wsi->ws->opcode == LWSWSOPC_BINARY_FRAME ||
wsi->ws->opcode == LWSWSOPC_CONTINUATION) &&
len) {
uint8_t *bin = *buf;
bulk = 1;
m = lws_ws_frame_rest_is_payload(wsi, buf, len);
assert((int)lws_ptr_diff(*buf, bin) <= (int)len);
len -= lws_ptr_diff(*buf, bin);
if (!m) {
break;
}
if (m < 0) {
lwsl_info("%s: rest_is_payload bailed\n",
__func__);
return -1;
}
}
if (!bulk) {
/* process the byte */
m = lws_ws_rx_sm(wsi, 0, *(*buf)++);
len--;
} else {
/*
* We already handled this byte in bulk, just deal
* with the ramifications
*/
#if !defined(LWS_WITHOUT_EXTENSIONS)
lwsl_debug("%s: coming out of bulk with len %d, "
"wsi->ws->rx_draining_ext %d\n",
__func__, (int)len,
wsi->ws->rx_draining_ext);
#endif
m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR |
ALREADY_PROCESSED_NO_CB, 0);
}
if (m < 0) {
lwsl_info("%s: lws_ws_rx_sm bailed %d\n", __func__,
bulk);
return -1;
}
bulk = 0;
}
lwsl_debug("%s: exit with %d unused\n", __func__, (int)len);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,214 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* RFC7233 ranges parser
*
* Copyright (C) 2016 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 "private-libwebsockets.h"
/*
* RFC7233 examples
*
* o The first 500 bytes (byte offsets 0-499, inclusive):
*
* bytes=0-499
*
* o The second 500 bytes (byte offsets 500-999, inclusive):
*
* bytes=500-999
*
* o The final 500 bytes (byte offsets 9500-9999, inclusive):
*
* bytes=-500
*
* Or:
*
* bytes=9500-
*
* o The first and last bytes only (bytes 0 and 9999):
*
* bytes=0-0,-1
*
* o Other valid (but not canonical) specifications of the second 500
* bytes (byte offsets 500-999, inclusive):
*
* bytes=500-600,601-999
* bytes=500-700,601-999
*/
/*
* returns 1 if the range struct represents a usable range
* if no ranges header, you get one of these for the whole
* file. Otherwise you get one for each valid range in the
* header.
*
* returns 0 if no further valid range forthcoming; rp->state
* may be LWSRS_SYNTAX or LWSRS_COMPLETED
*/
int
lws_ranges_next(struct lws_range_parsing *rp)
{
static const char * const beq = "bytes=";
char c;
while (1) {
c = rp->buf[rp->pos];
switch (rp->state) {
case LWSRS_SYNTAX:
case LWSRS_COMPLETED:
return 0;
case LWSRS_NO_ACTIVE_RANGE:
rp->state = LWSRS_COMPLETED;
return 0;
case LWSRS_BYTES_EQ: // looking for "bytes="
if (c != beq[rp->pos]) {
rp->state = LWSRS_SYNTAX;
return -1;
}
if (rp->pos == 5)
rp->state = LWSRS_FIRST;
break;
case LWSRS_FIRST:
rp->start = 0;
rp->end = 0;
rp->start_valid = 0;
rp->end_valid = 0;
rp->state = LWSRS_STARTING;
// fallthru
case LWSRS_STARTING:
if (c == '-') {
rp->state = LWSRS_ENDING;
break;
}
if (!(c >= '0' && c <= '9')) {
rp->state = LWSRS_SYNTAX;
return 0;
}
rp->start = (rp->start * 10) + (c - '0');
rp->start_valid = 1;
break;
case LWSRS_ENDING:
if (c == ',' || c == '\0') {
rp->state = LWSRS_FIRST;
if (c == ',')
rp->pos++;
/*
* By the end of this, start and end are
* always valid if the range still is
*/
if (!rp->start_valid) { /* eg, -500 */
if (rp->end > rp->extent)
rp->end = rp->extent;
rp->start = rp->extent - rp->end;
rp->end = rp->extent - 1;
} else
if (!rp->end_valid)
rp->end = rp->extent - 1;
rp->did_try = 1;
/* end must be >= start or ignore it */
if (rp->end < rp->start) {
if (c == ',')
break;
rp->state = LWSRS_COMPLETED;
return 0;
}
return 1; /* issue range */
}
if (!(c >= '0' && c <= '9')) {
rp->state = LWSRS_SYNTAX;
return 0;
}
rp->end = (rp->end * 10) + (c - '0');
rp->end_valid = 1;
break;
}
rp->pos++;
}
}
void
lws_ranges_reset(struct lws_range_parsing *rp)
{
rp->pos = 0;
rp->ctr = 0;
rp->start = 0;
rp->end = 0;
rp->start_valid = 0;
rp->end_valid = 0;
rp->state = LWSRS_BYTES_EQ;
}
/*
* returns count of valid ranges
*/
int
lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp,
unsigned long long extent)
{
rp->agg = 0;
rp->send_ctr = 0;
rp->inside = 0;
rp->count_ranges = 0;
rp->did_try = 0;
lws_ranges_reset(rp);
rp->state = LWSRS_COMPLETED;
rp->extent = extent;
if (lws_hdr_copy(wsi, (char *)rp->buf, sizeof(rp->buf),
WSI_TOKEN_HTTP_RANGE) <= 0)
return 0;
rp->state = LWSRS_BYTES_EQ;
while (lws_ranges_next(rp)) {
rp->count_ranges++;
rp->agg += rp->end - rp->start + 1;
}
lwsl_debug("%s: count %d\n", __func__, rp->count_ranges);
lws_ranges_reset(rp);
if (rp->did_try && !rp->count_ranges)
return -1; /* "not satisfiable */
lws_ranges_next(rp);
return rp->count_ranges;
}

View File

@ -1,360 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2013 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 "private-libwebsockets.h"
#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
#ifndef LWS_NO_EXTENSIONS
static int
lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
char ext_name[64], *args, *end = (*p) + budget - 1;
const struct lws_ext_options *opts, *po;
const struct lws_extension *ext;
struct lws_ext_option_arg oa;
int n, m, more = 1;
int ext_count = 0;
char ignore;
char *c;
/*
* Figure out which extensions the client has that we want to
* enable on this connection, and give him back the list
*/
if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS))
return 0;
/*
* break down the list of client extensions
* and go through them
*/
if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size,
WSI_TOKEN_EXTENSIONS) < 0)
return 1;
c = (char *)pt->serv_buf;
lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
wsi->count_act_ext = 0;
ignore = 0;
n = 0;
args = NULL;
/*
* We may get a simple request
*
* Sec-WebSocket-Extensions: permessage-deflate
*
* or an elaborated one with requested options
*
* Sec-WebSocket-Extensions: permessage-deflate; \
* server_no_context_takeover; \
* client_no_context_takeover
*/
while (more) {
if (*c && (*c != ',' && *c != '\t')) {
if (*c == ';') {
ignore = 1;
args = c + 1;
}
if (ignore || *c == ' ') {
c++;
continue;
}
ext_name[n] = *c++;
if (n < sizeof(ext_name) - 1)
n++;
continue;
}
ext_name[n] = '\0';
ignore = 0;
if (!*c)
more = 0;
else {
c++;
if (!n)
continue;
}
while (args && *args && *args == ' ')
args++;
/* check a client's extension against our support */
ext = wsi->vhost->extensions;
while (ext && ext->callback) {
if (strcmp(ext_name, ext->name)) {
ext++;
continue;
}
/*
* oh, we do support this one he asked for... but let's
* confirm he only gave it once
*/
for (m = 0; m < wsi->count_act_ext; m++)
if (wsi->active_extensions[m] == ext) {
lwsl_info("extension mentioned twice\n");
return 1; /* shenanigans */
}
/*
* ask user code if it's OK to apply it on this
* particular connection + protocol
*/
m = (wsi->protocol->callback)(wsi,
LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
wsi->user_space, ext_name, 0);
/*
* zero return from callback means go ahead and allow
* the extension, it's what we get if the callback is
* unhandled
*/
if (m) {
ext++;
continue;
}
/* apply it */
ext_count++;
/* instantiate the extension on this conn */
wsi->active_extensions[wsi->count_act_ext] = ext;
/* allow him to construct his context */
if (ext->callback(lws_get_context(wsi), ext, wsi,
LWS_EXT_CB_CONSTRUCT,
(void *)&wsi->act_ext_user[
wsi->count_act_ext],
(void *)&opts, 0)) {
lwsl_info("ext %s failed construction\n",
ext_name);
ext_count--;
ext++;
continue;
}
if (ext_count > 1)
*(*p)++ = ',';
else
LWS_CPYAPP(*p,
"\x0d\x0aSec-WebSocket-Extensions: ");
*p += lws_snprintf(*p, (end - *p), "%s", ext_name);
/*
* go through the options trying to apply the
* recognized ones
*/
lwsl_debug("ext args %s", args);
while (args && *args && *args != ',') {
while (*args == ' ')
args++;
po = opts;
while (po->name) {
lwsl_debug("'%s' '%s'\n", po->name, args);
/* only support arg-less options... */
if (po->type == EXTARG_NONE &&
!strncmp(args, po->name,
strlen(po->name))) {
oa.option_name = NULL;
oa.option_index = po - opts;
oa.start = NULL;
lwsl_debug("setting %s\n", po->name);
if (!ext->callback(
lws_get_context(wsi), ext, wsi,
LWS_EXT_CB_OPTION_SET,
wsi->act_ext_user[
wsi->count_act_ext],
&oa, (end - *p))) {
*p += lws_snprintf(*p, (end - *p), "; %s", po->name);
lwsl_debug("adding option %s\n", po->name);
}
}
po++;
}
while (*args && *args != ',' && *args != ';')
args++;
}
wsi->count_act_ext++;
lwsl_parser("count_act_ext <- %d\n",
wsi->count_act_ext);
ext++;
}
n = 0;
args = NULL;
}
return 0;
}
#endif
int
handshake_0405(struct lws_context *context, struct lws *wsi)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws_process_html_args args;
unsigned char hash[20];
int n, accept_len;
char *response;
char *p;
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
!lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
lwsl_parser("handshake_04 missing pieces\n");
/* completed header processing, but missing some bits */
goto bail;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= MAX_WEBSOCKET_04_KEY_LEN) {
lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
goto bail;
}
/*
* since key length is restricted above (currently 128), cannot
* overflow
*/
n = sprintf((char *)pt->serv_buf,
"%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
lws_SHA1(pt->serv_buf, n, hash);
accept_len = lws_b64_encode_string((char *)hash, 20,
(char *)pt->serv_buf, context->pt_serv_buf_size);
if (accept_len < 0) {
lwsl_warn("Base64 encoded hash too long\n");
goto bail;
}
/* allocate the per-connection user memory (if any) */
if (lws_ensure_user_space(wsi))
goto bail;
/* create the response packet */
/* make a buffer big enough for everything */
response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE;
p = response;
LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
"Upgrade: WebSocket\x0d\x0a"
"Connection: Upgrade\x0d\x0a"
"Sec-WebSocket-Accept: ");
strcpy(p, (char *)pt->serv_buf);
p += accept_len;
/* we can only return the protocol header if:
* - one came in, and ... */
if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
/* - it is not an empty string */
wsi->protocol->name &&
wsi->protocol->name[0]) {
LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
}
#ifndef LWS_NO_EXTENSIONS
/*
* Figure out which extensions the client has that we want to
* enable on this connection, and give him back the list.
*
* Give him a limited write bugdet
*/
if (lws_extension_server_handshake(wsi, &p, 192))
goto bail;
#endif
LWS_CPYAPP(p, "\x0d\x0a");
args.p = p;
args.max_len = ((char *)pt->serv_buf + context->pt_serv_buf_size) - p;
if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
LWS_CALLBACK_ADD_HEADERS,
wsi->user_space, &args, 0))
goto bail;
p = args.p;
/* end of response packet */
LWS_CPYAPP(p, "\x0d\x0a");
if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX,
response, p - response)) {
/* okay send the handshake response accepting the connection */
lwsl_parser("issuing resp pkt %d len\n", (int)(p - response));
#if defined(DEBUG) && ! defined(LWS_WITH_ESP8266)
fwrite(response, 1, p - response, stderr);
#endif
n = lws_write(wsi, (unsigned char *)response,
p - response, LWS_WRITE_HTTP_HEADERS);
if (n != (p - response)) {
lwsl_debug("handshake_0405: ERROR writing to socket\n");
goto bail;
}
}
/* alright clean up and set ourselves into established state */
wsi->state = LWSS_ESTABLISHED;
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
{
const char * uri_ptr =
lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
const struct lws_http_mount *hit =
lws_find_mount(wsi, uri_ptr, uri_len);
if (hit && hit->cgienv &&
wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO,
wsi->user_space, (void *)hit->cgienv, 0))
return 1;
}
return 0;
bail:
/* caller will free up his parsing allocations */
return -1;
}

View File

@ -1,477 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 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 "private-libwebsockets.h"
extern int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
extern void
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info);
#if !defined(LWS_WITH_MBEDTLS)
static int
OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
SSL *ssl;
int n;
struct lws *wsi;
ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
SSL_get_ex_data_X509_STORE_CTX_idx());
/*
* !!! nasty openssl requires the index to come as a library-scope
* static
*/
wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
n = wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION,
x509_ctx, ssl, preverify_ok);
/* convert return code from 0 = OK to 1 = OK */
return !n;
}
#endif
static int
lws_context_ssl_init_ecdh(struct lws_vhost *vhost)
{
#ifdef LWS_SSL_SERVER_WITH_ECDH_CERT
EC_KEY *EC_key = NULL;
EVP_PKEY *pkey;
int KeyType;
X509 *x;
if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH))
return 0;
lwsl_notice(" Using ECDH certificate support\n");
/* Get X509 certificate from ssl context */
x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0);
if (!x) {
lwsl_err("%s: x is NULL\n", __func__);
return 1;
}
/* Get the public key from certificate */
pkey = X509_get_pubkey(x);
if (!pkey) {
lwsl_err("%s: pkey is NULL\n", __func__);
return 1;
}
/* Get the key type */
KeyType = EVP_PKEY_type(pkey->type);
if (EVP_PKEY_EC != KeyType) {
lwsl_notice("Key type is not EC\n");
return 0;
}
/* Get the key */
EC_key = EVP_PKEY_get1_EC_KEY(pkey);
/* Set ECDH parameter */
if (!EC_key) {
lwsl_err("%s: ECDH key is NULL \n", __func__);
return 1;
}
SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, EC_key);
EC_KEY_free(EC_key);
#endif
return 0;
}
static int
lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info,
struct lws_vhost *vhost)
{
#if defined(LWS_HAVE_OPENSSL_ECDH_H) && !defined(LWS_WITH_MBEDTLS)
EC_KEY *ecdh;
int ecdh_nid;
const char *ecdh_curve = "prime256v1";
if (info->ecdh_curve)
ecdh_curve = info->ecdh_curve;
ecdh_nid = OBJ_sn2nid(ecdh_curve);
if (NID_undef == ecdh_nid) {
lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve);
return 1;
}
ecdh = EC_KEY_new_by_curve_name(ecdh_nid);
if (NULL == ecdh) {
lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve);
return 1;
}
SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, ecdh);
EC_KEY_free(ecdh);
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve);
#else
#if !defined(LWS_WITH_MBEDTLS)
lwsl_notice(" OpenSSL doesn't support ECDH\n");
#endif
#endif
return 0;
}
#if !defined(LWS_WITH_MBEDTLS) && defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT)
static int
lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
{
struct lws_context *context = (struct lws_context *)arg;
struct lws_vhost *vhost, *vh;
const char *servername;
if (!ssl)
return SSL_TLSEXT_ERR_NOACK;
/*
* We can only get ssl accepted connections by using a vhost's ssl_ctx
* find out which listening one took us and only match vhosts on the
* same port.
*/
vh = context->vhost_list;
while (vh) {
if (!vh->being_destroyed && ssl && vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
break;
vh = vh->vhost_next;
}
if (!vh) {
assert(vh); /* can't match the incoming vh? */
return SSL_TLSEXT_ERR_OK;
}
servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (!servername) {
/* the client doesn't know what hostname it wants */
lwsl_info("SNI: Unknown ServerName: %s\n", servername);
return SSL_TLSEXT_ERR_OK;
}
vhost = lws_select_vhost(context, vh->listen_port, servername);
if (!vhost) {
lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port);
return SSL_TLSEXT_ERR_OK;
}
lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port);
/* select the ssl ctx from the selected vhost for this conn */
SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
return SSL_TLSEXT_ERR_OK;
}
#endif
LWS_VISIBLE int
lws_context_init_server_ssl(struct lws_context_creation_info *info,
struct lws_vhost *vhost)
{
struct lws_context *context = vhost->context;
struct lws wsi;
unsigned long error;
int n;
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
vhost->use_ssl = 0;
return 0;
}
/*
* If he is giving a cert filepath, take it as a sign he wants to use
* it on this vhost. User code can leave the cert filepath NULL and
* set the LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX option itself, in
* which case he's expected to set up the cert himself at
* LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
* provides the vhost SSL_CTX * in the user parameter.
*/
if (info->ssl_cert_filepath)
info->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX;
if (info->port != CONTEXT_PORT_NO_LISTEN) {
vhost->use_ssl = lws_check_opt(info->options,
LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX);
if (vhost->use_ssl && info->ssl_cipher_list)
lwsl_notice(" SSL ciphers: '%s'\n", info->ssl_cipher_list);
if (vhost->use_ssl)
lwsl_notice(" Using SSL mode\n");
else
lwsl_notice(" Using non-SSL mode\n");
}
/*
* give him a fake wsi with context + vhost set, so he can use
* lws_get_context() in the callback
*/
memset(&wsi, 0, sizeof(wsi));
wsi.vhost = vhost;
wsi.context = context;
(void)n;
(void)error;
/*
* Firefox insists on SSLv23 not SSLv3
* Konq disables SSLv2 by default now, SSLv23 works
*
* SSLv23_server_method() is the openssl method for "allow all TLS
* versions", compared to e.g. TLSv1_2_server_method() which only allows
* tlsv1.2. Unwanted versions must be disabled using SSL_CTX_set_options()
*/
#if !defined(LWS_WITH_MBEDTLS)
{
SSL_METHOD *method;
method = (SSL_METHOD *)SSLv23_server_method();
if (!method) {
error = ERR_get_error();
lwsl_err("problem creating ssl method %lu: %s\n",
error, ERR_error_string(error,
(char *)context->pt[0].serv_buf));
return 1;
}
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
if (!vhost->ssl_ctx) {
error = ERR_get_error();
lwsl_err("problem creating ssl context %lu: %s\n",
error, ERR_error_string(error,
(char *)context->pt[0].serv_buf));
return 1;
}
}
#else
{
const SSL_METHOD *method = TLSv1_2_server_method();
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
if (!vhost->ssl_ctx) {
lwsl_err("problem creating ssl context\n");
return 1;
}
}
#endif
#if !defined(LWS_WITH_MBEDTLS)
/* associate the lws context with the SSL_CTX */
SSL_CTX_set_ex_data(vhost->ssl_ctx,
openssl_SSL_CTX_private_data_index, (char *)vhost->context);
/* Disable SSLv2 and SSLv3 */
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_COMPRESSION);
#endif
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_DH_USE);
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
if (info->ssl_cipher_list)
SSL_CTX_set_cipher_list(vhost->ssl_ctx,
info->ssl_cipher_list);
#endif
/* as a server, are we requiring clients to identify themselves? */
if (lws_check_opt(info->options,
LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) {
int verify_options = SSL_VERIFY_PEER;
if (!lws_check_opt(info->options,
LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED))
verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
#if !defined(LWS_WITH_MBEDTLS)
SSL_CTX_set_session_id_context(vhost->ssl_ctx,
(unsigned char *)context, sizeof(void *));
/* absolutely require the client cert */
SSL_CTX_set_verify(vhost->ssl_ctx,
verify_options, OpenSSL_verify_callback);
#endif
}
#if !defined(LWS_WITH_MBEDTLS) && !defined(OPENSSL_NO_TLSEXT)
SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx,
lws_ssl_server_name_cb);
SSL_CTX_set_tlsext_servername_arg(vhost->ssl_ctx, context);
#endif
/*
* give user code a chance to load certs into the server
* allowing it to verify incoming client certs
*/
#if !defined(LWS_WITH_MBEDTLS)
if (info->ssl_ca_filepath &&
!SSL_CTX_load_verify_locations(vhost->ssl_ctx,
info->ssl_ca_filepath, NULL)) {
lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", __func__);
}
#endif
if (vhost->use_ssl) {
if (lws_context_ssl_init_ecdh_curve(info, vhost))
return -1;
vhost->protocols[0].callback(&wsi,
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS,
vhost->ssl_ctx, NULL, 0);
}
if (lws_check_opt(info->options, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT))
/* Normally SSL listener rejects non-ssl, optionally allow */
vhost->allow_non_ssl_on_ssl_port = 1;
if (info->ssl_options_set)
SSL_CTX_set_options(vhost->ssl_ctx, info->ssl_options_set);
/* SSL_clear_options introduced in 0.9.8m */
#if !defined(LWS_WITH_MBEDTLS)
#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL)
if (info->ssl_options_clear)
SSL_CTX_clear_options(vhost->ssl_ctx, info->ssl_options_clear);
#endif
#endif
lwsl_info(" SSL options 0x%lX\n", SSL_CTX_get_options(vhost->ssl_ctx));
if (vhost->use_ssl && info->ssl_cert_filepath) {
/*
* 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 !defined(LWS_WITH_MBEDTLS)
/* set the local certificate from CertFile */
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx,
info->ssl_cert_filepath);
if (n != 1) {
error = ERR_get_error();
lwsl_err("problem getting cert '%s' %lu: %s\n",
info->ssl_cert_filepath,
error,
ERR_error_string(error,
(char *)context->pt[0].serv_buf));
return 1;
}
lws_ssl_bind_passphrase(vhost->ssl_ctx, info);
#else
uint8_t *p;
lws_filepos_t flen;
int err;
if (alloc_pem_to_der_file(vhost->context, info->ssl_cert_filepath, &p,
&flen)) {
lwsl_err("couldn't find cert file %s\n",
info->ssl_cert_filepath);
return 1;
}
err = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, flen, p);
if (!err) {
lwsl_err("Problem loading cert\n");
return 1;
}
#if !defined(LWS_WITH_ESP32)
free(p);
p = NULL;
#endif
if (info->ssl_private_key_filepath) {
if (alloc_pem_to_der_file(vhost->context,
info->ssl_private_key_filepath, &p, &flen)) {
lwsl_err("couldn't find cert file %s\n",
info->ssl_cert_filepath);
return 1;
}
err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen);
if (!err) {
lwsl_err("Problem loading key\n");
return 1;
}
}
#if !defined(LWS_WITH_ESP32)
free(p);
p = NULL;
#endif
#endif
if (info->ssl_private_key_filepath != NULL) {
#if !defined(LWS_WITH_MBEDTLS)
/* set the private key from KeyFile */
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx,
info->ssl_private_key_filepath,
SSL_FILETYPE_PEM) != 1) {
error = ERR_get_error();
lwsl_err("ssl problem getting key '%s' %lu: %s\n",
info->ssl_private_key_filepath, error,
ERR_error_string(error,
(char *)context->pt[0].serv_buf));
return 1;
}
#endif
} else
if (vhost->protocols[0].callback(&wsi,
LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
vhost->ssl_ctx, NULL, 0)) {
lwsl_err("ssl private key not set\n");
return 1;
}
#if !defined(LWS_WITH_MBEDTLS)
/* verify private key */
if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) {
lwsl_err("Private SSL key doesn't match cert\n");
return 1;
}
#endif
}
if (vhost->use_ssl) {
if (lws_context_ssl_init_ecdh(vhost))
return 1;
/*
* SSL is happy and has a cert it's content with
* If we're supporting HTTP2, initialize that
*/
lws_context_init_http2_ssl(vhost);
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,973 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 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 "private-libwebsockets.h"
#include <errno.h>
int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
lws_filepos_t len;
lws_fop_flags_t flags = LWS_O_RDONLY;
lws_fop_fd_t fops_fd = lws_vfs_file_open(
lws_get_fops(context), filename, &flags);
int ret = 1;
if (!fops_fd)
return 1;
len = lws_vfs_get_length(fops_fd);
*buf = lws_malloc((size_t)len, "lws_alloc_vfs_file");
if (!*buf)
goto bail;
if (lws_vfs_file_read(fops_fd, amount, *buf, len))
goto bail;
ret = 0;
bail:
lws_vfs_file_close(&fops_fd);
return ret;
}
#if defined(LWS_WITH_MBEDTLS)
#if defined(LWS_WITH_ESP32)
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
nvs_handle nvh;
size_t s;
int n = 0;
ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
n = 1;
goto bail;
}
*buf = lws_malloc(s, "alloc_file");
if (!*buf) {
n = 2;
goto bail;
}
if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
lws_free(*buf);
n = 1;
goto bail;
}
*amount = s;
bail:
nvs_close(nvh);
return n;
}
#else
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
FILE *f;
size_t s;
int n = 0;
f = fopen(filename, "rb");
if (f == NULL) {
n = 1;
goto bail;
}
if (fseek(f, 0, SEEK_END) != 0) {
n = 1;
goto bail;
}
s = ftell(f);
if (s == -1) {
n = 1;
goto bail;
}
if (fseek(f, 0, SEEK_SET) != 0) {
n = 1;
goto bail;
}
*buf = lws_malloc(s, "alloc_file");
if (!*buf) {
n = 2;
goto bail;
}
if (fread(*buf, s, 1, f) != 1) {
lws_free(*buf);
n = 1;
goto bail;
}
*amount = s;
bail:
if (f)
fclose(f);
return n;
}
#endif
int alloc_pem_to_der_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
uint8_t *pem, *p, *q, *end;
lws_filepos_t len;
int n;
n = alloc_file(context, filename, &pem, &len);
if (n)
return n;
/* trim the first line */
p = pem;
end = p + len;
if (strncmp((char *)p, "-----", 5))
goto bail;
p += 5;
while (p < end && *p != '\n' && *p != '-')
p++;
if (*p != '-')
goto bail;
while (p < end && *p != '\n')
p++;
if (p >= end)
goto bail;
p++;
/* trim the last line */
q = end - 2;
while (q > pem && *q != '\n')
q--;
if (*q != '\n')
goto bail;
*q = '\0';
*amount = lws_b64_decode_string((char *)p, (char *)pem, len);
*buf = pem;
return 0;
bail:
lws_free(pem);
return 4;
}
#endif
int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
int lws_ssl_get_error(struct lws *wsi, int n)
{
int m;
if (!wsi->ssl)
return 99;
m = SSL_get_error(wsi->ssl, n);
lwsl_debug("%s: %p %d -> %d\n", __func__, wsi->ssl, n, m);
return m;
}
/* Copies a string describing the code returned by lws_ssl_get_error(),
* which may also contain system error information in the case of SSL_ERROR_SYSCALL,
* into buf up to len.
* Returns a pointer to buf.
*
* Note: the lws_ssl_get_error() code is *not* an error code that can be passed
* to ERR_error_string(),
*
* ret is the return value originally passed to lws_ssl_get_error(), needed to disambiguate
* SYS_ERROR_SYSCALL.
*
* See man page for SSL_get_error().
*
* Not thread safe, uses strerror()
*/
char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) {
switch (status) {
case SSL_ERROR_NONE: return strncpy(buf, "SSL_ERROR_NONE", len);
case SSL_ERROR_ZERO_RETURN: return strncpy(buf, "SSL_ERROR_ZERO_RETURN", len);
case SSL_ERROR_WANT_READ: return strncpy(buf, "SSL_ERROR_WANT_READ", len);
case SSL_ERROR_WANT_WRITE: return strncpy(buf, "SSL_ERROR_WANT_WRITE", len);
case SSL_ERROR_WANT_CONNECT: return strncpy(buf, "SSL_ERROR_WANT_CONNECT", len);
case SSL_ERROR_WANT_ACCEPT: return strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len);
case SSL_ERROR_WANT_X509_LOOKUP: return 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_ssl_elaborate_error(void)
{
#if defined(LWS_WITH_MBEDTLS)
#else
char buf[256];
u_long err;
while ((err = ERR_get_error()) != 0) {
ERR_error_string_n(err, buf, sizeof(buf));
lwsl_info("*** %s\n", buf);
}
#endif
}
#if !defined(LWS_WITH_MBEDTLS)
static int
lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag, void *userdata)
{
struct lws_context_creation_info * info =
(struct lws_context_creation_info *)userdata;
strncpy(buf, info->ssl_private_key_password, size);
buf[size - 1] = '\0';
return strlen(buf);
}
void
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info)
{
if (!info->ssl_private_key_password)
return;
/*
* password provided, set ssl callback and user data
* for checking password which will be trigered during
* SSL_CTX_use_PrivateKey_file function
*/
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)info);
SSL_CTX_set_default_passwd_cb(ssl_ctx, lws_context_init_ssl_pem_passwd_cb);
}
#endif
int
lws_context_init_ssl_library(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
#if defined(LWS_WITH_MBEDTLS)
lwsl_info(" Compiled with MbedTLS support\n");
#else
lwsl_info(" Compiled with OpenSSL support\n");
#endif
#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 !defined(LWS_WITH_MBEDTLS)
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
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;
}
LWS_VISIBLE void
lws_ssl_destroy(struct lws_vhost *vhost)
{
if (!lws_check_opt(vhost->context->options,
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
return;
if (vhost->ssl_ctx)
SSL_CTX_free(vhost->ssl_ctx);
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
SSL_CTX_free(vhost->ssl_client_ctx);
#if defined(LWS_WITH_MBEDTLS)
if (vhost->x509_client_CA)
X509_free(vhost->x509_client_CA);
#else
// after 1.1.0 no need
#if (OPENSSL_VERSION_NUMBER < 0x10100000)
// <= 1.0.1f = old api, 1.0.1g+ = new api
#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
ERR_remove_state(0);
#else
#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
!defined(LIBRESSL_VERSION_NUMBER) && \
!defined(OPENSSL_IS_BORINGSSL)
ERR_remove_thread_state();
#else
ERR_remove_thread_state(NULL);
#endif
#endif
// after 1.1.0 no need
#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
SSL_COMP_free_compression_methods();
#endif
ERR_free_strings();
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
#endif
#endif
}
int
lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws *wsi, *wsi_next;
wsi = pt->pending_read_list;
while (wsi) {
wsi_next = wsi->pending_read_list_next;
pt->fds[wsi->position_in_fds_table].revents |=
pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN)
return 1;
wsi = wsi_next;
}
return 0;
}
LWS_VISIBLE void
lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
if (!wsi->pending_read_list_prev &&
!wsi->pending_read_list_next &&
pt->pending_read_list != wsi)
/* we are not on the list */
return;
/* point previous guy's next to our next */
if (!wsi->pending_read_list_prev)
pt->pending_read_list = wsi->pending_read_list_next;
else
wsi->pending_read_list_prev->pending_read_list_next =
wsi->pending_read_list_next;
/* point next guy's previous to our previous */
if (wsi->pending_read_list_next)
wsi->pending_read_list_next->pending_read_list_prev =
wsi->pending_read_list_prev;
wsi->pending_read_list_prev = NULL;
wsi->pending_read_list_next = NULL;
}
LWS_VISIBLE int
lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n = 0, m;
if (!wsi->ssl)
return lws_ssl_capable_read_no_ssl(wsi, buf, len);
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
errno = 0;
n = SSL_read(wsi->ssl, buf, len);
#if defined(LWS_WITH_ESP32)
if (!n && errno == ENOTCONN) {
lwsl_debug("%p: SSL_read ENOTCONN\n", wsi);
return LWS_SSL_CAPABLE_ERROR;
}
#endif
#if defined(LWS_WITH_STATS)
if (!wsi->seen_rx) {
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_MS_SSL_RX_DELAY,
time_in_microseconds() - wsi->accept_start_us);
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_SSL_CONNS_HAD_RX, 1);
wsi->seen_rx = 1;
}
#endif
lwsl_debug("%p: SSL_read says %d\n", wsi, n);
/* manpage: returning 0 means connection shut down */
if (!n || (n == -1 && errno == ENOTCONN)) {
wsi->socket_is_permanently_unusable = 1;
return LWS_SSL_CAPABLE_ERROR;
}
if (n < 0) {
m = lws_ssl_get_error(wsi, n);
lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno);
if (m == SSL_ERROR_ZERO_RETURN ||
m == SSL_ERROR_SYSCALL)
return LWS_SSL_CAPABLE_ERROR;
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
lwsl_debug("%s: WANT_READ\n", __func__);
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
lwsl_debug("%s: WANT_WRITE\n", __func__);
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
wsi->socket_is_permanently_unusable = 1;
return LWS_SSL_CAPABLE_ERROR;
}
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
lws_restart_ws_ping_pong_timer(wsi);
/*
* if it was our buffer that limited what we read,
* check if SSL has additional data pending inside SSL buffers.
*
* Because these won't signal at the network layer with POLLIN
* and if we don't realize, this data will sit there forever
*/
if (n != len)
goto bail;
if (!wsi->ssl)
goto bail;
if (!SSL_pending(wsi->ssl))
goto bail;
if (wsi->pending_read_list_next)
return n;
if (wsi->pending_read_list_prev)
return n;
if (pt->pending_read_list == wsi)
return n;
/* add us to the linked list of guys with pending ssl */
if (pt->pending_read_list)
pt->pending_read_list->pending_read_list_prev = wsi;
wsi->pending_read_list_next = pt->pending_read_list;
wsi->pending_read_list_prev = NULL;
pt->pending_read_list = wsi;
return n;
bail:
lws_ssl_remove_wsi_from_buffered_list(wsi);
return n;
}
LWS_VISIBLE int
lws_ssl_pending(struct lws *wsi)
{
if (!wsi->ssl)
return 0;
return SSL_pending(wsi->ssl);
}
LWS_VISIBLE int
lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
{
int n, m;
if (!wsi->ssl)
return lws_ssl_capable_write_no_ssl(wsi, buf, len);
n = SSL_write(wsi->ssl, buf, len);
if (n > 0)
return n;
m = lws_ssl_get_error(wsi, n);
if (m != SSL_ERROR_SYSCALL) {
if (SSL_want_read(wsi->ssl)) {
lwsl_notice("%s: want read\n", __func__);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
if (SSL_want_write(wsi->ssl)) {
lws_set_blocking_send(wsi);
lwsl_notice("%s: want write\n", __func__);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
}
lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL));
lws_ssl_elaborate_error();
wsi->socket_is_permanently_unusable = 1;
return LWS_SSL_CAPABLE_ERROR;
}
static int
lws_gate_accepts(struct lws_context *context, int on)
{
struct lws_vhost *v = context->vhost_list;
lwsl_info("gating accepts %d\n", on);
context->ssl_gate_accepts = !on;
#if defined(LWS_WITH_STATS)
context->updated = 1;
#endif
while (v) {
if (v->use_ssl && v->lserv_wsi) /* gate ability to accept incoming connections */
if (lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on,
(LWS_POLLIN) * on))
lwsl_info("Unable to set accept POLLIN %d\n", on);
v = v->vhost_next;
}
return 0;
}
void
lws_ssl_info_callback(const SSL *ssl, int where, int ret)
{
struct lws *wsi;
struct lws_context *context;
struct lws_ssl_info si;
context = (struct lws_context *)SSL_CTX_get_ex_data(
SSL_get_SSL_CTX(ssl),
openssl_SSL_CTX_private_data_index);
if (!context)
return;
wsi = wsi_from_fd(context, SSL_get_fd(ssl));
if (!wsi)
return;
if (!(where & wsi->vhost->ssl_info_event_mask))
return;
si.where = where;
si.ret = ret;
if (user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_SSL_INFO,
wsi->user_space, &si, 0))
lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1);
}
LWS_VISIBLE int
lws_ssl_close(struct lws *wsi)
{
lws_sockfd_type n;
if (!wsi->ssl)
return 0; /* not handled */
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
/* kill ssl callbacks, becausse we will remove the fd from the
* table linking it to the wsi
*/
if (wsi->vhost->ssl_info_event_mask)
SSL_set_info_callback(wsi->ssl, NULL);
#endif
n = SSL_get_fd(wsi->ssl);
if (!wsi->socket_is_permanently_unusable)
SSL_shutdown(wsi->ssl);
compatible_close(n);
SSL_free(wsi->ssl);
wsi->ssl = NULL;
if (wsi->context->simultaneous_ssl_restriction &&
wsi->context->simultaneous_ssl-- ==
wsi->context->simultaneous_ssl_restriction)
/* we made space and can do an accept */
lws_gate_accepts(wsi->context, 1);
#if defined(LWS_WITH_STATS)
wsi->context->updated = 1;
#endif
return 1; /* handled */
}
/* leave all wsi close processing to the caller */
LWS_VISIBLE int
lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
{
struct lws_context *context = wsi->context;
struct lws_vhost *vh;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n, m;
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
BIO *bio;
#endif
char buf[256];
(void)buf;
if (!LWS_SSL_ENABLED(wsi->vhost))
return 0;
switch (wsi->mode) {
case LWSCM_SSL_INIT:
case LWSCM_SSL_INIT_RAW:
if (wsi->ssl)
lwsl_err("%s: leaking ssl\n", __func__);
if (accept_fd == LWS_SOCK_INVALID)
assert(0);
if (context->simultaneous_ssl_restriction &&
context->simultaneous_ssl >= context->simultaneous_ssl_restriction) {
lwsl_notice("unable to deal with SSL connection\n");
return 1;
}
errno = 0;
wsi->ssl = SSL_new(wsi->vhost->ssl_ctx);
if (wsi->ssl == NULL) {
lwsl_err("SSL_new failed: %d (errno %d)\n",
lws_ssl_get_error(wsi, 0), errno);
lws_ssl_elaborate_error();
if (accept_fd != LWS_SOCK_INVALID)
compatible_close(accept_fd);
goto fail;
}
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
if (wsi->vhost->ssl_info_event_mask)
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
#endif
if (context->simultaneous_ssl_restriction &&
++context->simultaneous_ssl == context->simultaneous_ssl_restriction)
/* that was the last allowed SSL connection */
lws_gate_accepts(context, 0);
#if defined(LWS_WITH_STATS)
context->updated = 1;
#endif
#if !defined(LWS_WITH_MBEDTLS)
SSL_set_ex_data(wsi->ssl,
openssl_websocket_private_data_index, wsi);
#endif
SSL_set_fd(wsi->ssl, accept_fd);
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
CyaSSL_set_using_nonblock(wsi->ssl, 1);
#else
wolfSSL_set_using_nonblock(wsi->ssl, 1);
#endif
#else
#if defined(LWS_WITH_MBEDTLS)
lws_plat_set_socket_options(wsi->vhost, accept_fd);
#else
SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
bio = SSL_get_rbio(wsi->ssl);
if (bio)
BIO_set_nbio(bio, 1); /* nonblocking */
else
lwsl_notice("NULL rbio\n");
bio = SSL_get_wbio(wsi->ssl);
if (bio)
BIO_set_nbio(bio, 1); /* nonblocking */
else
lwsl_notice("NULL rbio\n");
#endif
#endif
/*
* we are not accepted yet, but we need to enter ourselves
* as a live connection. That way we can retry when more
* pieces come if we're not sorted yet
*/
if (wsi->mode == LWSCM_SSL_INIT)
wsi->mode = LWSCM_SSL_ACK_PENDING;
else
wsi->mode = LWSCM_SSL_ACK_PENDING_RAW;
if (insert_wsi_socket_into_fds(context, wsi)) {
lwsl_err("%s: failed to insert into fds\n", __func__);
goto fail;
}
lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
context->timeout_secs);
lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n");
/* fallthru */
case LWSCM_SSL_ACK_PENDING:
case LWSCM_SSL_ACK_PENDING_RAW:
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_err("%s: lws_change_pollfd failed\n", __func__);
goto fail;
}
lws_latency_pre(context, wsi);
if (wsi->vhost->allow_non_ssl_on_ssl_port) {
n = recv(wsi->desc.sockfd, (char *)pt->serv_buf,
context->pt_serv_buf_size, MSG_PEEK);
/*
* optionally allow non-SSL connect on SSL listening socket
* This is disabled by default, if enabled it goes around any
* SSL-level access control (eg, client-side certs) so leave
* it disabled unless you know it's not a problem for you
*/
if (n >= 1 && pt->serv_buf[0] >= ' ') {
/*
* TLS content-type for Handshake is 0x16, and
* for ChangeCipherSpec Record, it's 0x14
*
* A non-ssl session will start with the HTTP
* method in ASCII. If we see it's not a legit
* SSL handshake kill the SSL for this
* connection and try to handle as a HTTP
* connection upgrade directly.
*/
wsi->use_ssl = 0;
SSL_shutdown(wsi->ssl);
SSL_free(wsi->ssl);
wsi->ssl = NULL;
if (lws_check_opt(context->options,
LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS))
wsi->redirect_to_https = 1;
goto accepted;
}
if (!n) /*
* connection is gone, or nothing to read
* if it's gone, we will timeout on
* PENDING_TIMEOUT_SSL_ACCEPT
*/
break;
if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK)) {
/*
* well, we get no way to know ssl or not
* so go around again waiting for something
* to come and give us a hint, or timeout the
* connection.
*/
m = SSL_ERROR_WANT_READ;
goto go_again;
}
}
/* normal SSL connection processing path */
#if defined(LWS_WITH_STATS)
if (!wsi->accept_start_us)
wsi->accept_start_us = time_in_microseconds();
#endif
errno = 0;
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1);
n = SSL_accept(wsi->ssl);
lws_latency(context, wsi,
"SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1);
lwsl_info("SSL_accept says %d\n", n);
if (n == 1)
goto accepted;
m = lws_ssl_get_error(wsi, n);
#if defined(LWS_WITH_MBEDTLS)
if (m == SSL_ERROR_SYSCALL && errno == 11)
m = SSL_ERROR_WANT_READ;
#endif
if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
goto failed;
go_again:
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__);
goto fail;
}
lwsl_info("SSL_ERROR_WANT_READ\n");
break;
}
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
lwsl_debug("%s: WANT_WRITE\n", __func__);
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__);
goto fail;
}
break;
}
failed:
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
wsi->socket_is_permanently_unusable = 1;
lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd,
lws_ssl_get_error_string(m, n, buf, sizeof(buf)));
lws_ssl_elaborate_error();
goto fail;
accepted:
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1);
#if defined(LWS_WITH_STATS)
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY,
time_in_microseconds() - wsi->accept_start_us);
wsi->accept_start_us = time_in_microseconds();
#endif
/* adapt our vhost to match the SNI SSL_CTX that was chosen */
vh = context->vhost_list;
while (vh) {
if (!vh->being_destroyed && wsi->ssl &&
vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) {
lwsl_info("setting wsi to vh %s\n", vh->name);
wsi->vhost = vh;
break;
}
vh = vh->vhost_next;
}
/* OK, we are accepted... give him some time to negotiate */
lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
context->timeout_secs);
if (wsi->mode == LWSCM_SSL_ACK_PENDING_RAW)
wsi->mode = LWSCM_RAW;
else
wsi->mode = LWSCM_HTTP_SERVING;
#if defined(LWS_WITH_HTTP2)
if (lws_h2_configure_if_upgraded(wsi))
goto fail;
#endif
lwsl_debug("accepted new SSL conn\n");
break;
}
return 0;
fail:
return 1;
}
void
lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
{
if (vhost->ssl_ctx)
SSL_CTX_free(vhost->ssl_ctx);
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
SSL_CTX_free(vhost->ssl_client_ctx);
}
void
lws_ssl_context_destroy(struct lws_context *context)
{
#if !defined(LWS_WITH_MBEDTLS)
// after 1.1.0 no need
#if (OPENSSL_VERSION_NUMBER < 0x10100000)
// <= 1.0.1f = old api, 1.0.1g+ = new api
#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
ERR_remove_state(0);
#else
#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
!defined(LIBRESSL_VERSION_NUMBER) && \
!defined(OPENSSL_IS_BORINGSSL)
ERR_remove_thread_state();
#else
ERR_remove_thread_state(NULL);
#endif
#endif
// after 1.1.0 no need
#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
SSL_COMP_free_compression_methods();
#endif
ERR_free_strings();
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
#endif
#endif
}

View File

@ -0,0 +1,202 @@
/*
* libwebsockets - generic hash and HMAC api hiding the backend
*
* Copyright (C) 2017 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
*
* lws_genhash provides a hash / hmac abstraction api in lws that works the
* same whether you are using openssl or mbedtls hash functions underneath.
*/
#include "libwebsockets.h"
#include <mbedtls/version.h>
#if (MBEDTLS_VERSION_NUMBER >= 0x02070000)
#define MBA(fn) fn##_ret
#else
#define MBA(fn) fn
#endif
size_t
lws_genhash_size(enum lws_genhash_types type)
{
switch(type) {
case LWS_GENHASH_TYPE_SHA1:
return 20;
case LWS_GENHASH_TYPE_SHA256:
return 32;
case LWS_GENHASH_TYPE_SHA384:
return 48;
case LWS_GENHASH_TYPE_SHA512:
return 64;
}
return 0;
}
int
lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type)
{
ctx->type = type;
switch (ctx->type) {
case LWS_GENHASH_TYPE_SHA1:
mbedtls_sha1_init(&ctx->u.sha1);
MBA(mbedtls_sha1_starts)(&ctx->u.sha1);
break;
case LWS_GENHASH_TYPE_SHA256:
mbedtls_sha256_init(&ctx->u.sha256);
MBA(mbedtls_sha256_starts)(&ctx->u.sha256, 0);
break;
case LWS_GENHASH_TYPE_SHA384:
mbedtls_sha512_init(&ctx->u.sha512);
MBA(mbedtls_sha512_starts)(&ctx->u.sha512, 1 /* is384 */);
break;
case LWS_GENHASH_TYPE_SHA512:
mbedtls_sha512_init(&ctx->u.sha512);
MBA(mbedtls_sha512_starts)(&ctx->u.sha512, 0);
break;
default:
return 1;
}
return 0;
}
int
lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len)
{
switch (ctx->type) {
case LWS_GENHASH_TYPE_SHA1:
MBA(mbedtls_sha1_update)(&ctx->u.sha1, in, len);
break;
case LWS_GENHASH_TYPE_SHA256:
MBA(mbedtls_sha256_update)(&ctx->u.sha256, in, len);
break;
case LWS_GENHASH_TYPE_SHA384:
MBA(mbedtls_sha512_update)(&ctx->u.sha512, in, len);
break;
case LWS_GENHASH_TYPE_SHA512:
MBA(mbedtls_sha512_update)(&ctx->u.sha512, in, len);
break;
}
return 0;
}
int
lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result)
{
switch (ctx->type) {
case LWS_GENHASH_TYPE_SHA1:
MBA(mbedtls_sha1_finish)(&ctx->u.sha1, result);
mbedtls_sha1_free(&ctx->u.sha1);
break;
case LWS_GENHASH_TYPE_SHA256:
MBA(mbedtls_sha256_finish)(&ctx->u.sha256, result);
mbedtls_sha256_free(&ctx->u.sha256);
break;
case LWS_GENHASH_TYPE_SHA384:
MBA(mbedtls_sha512_finish)(&ctx->u.sha512, result);
mbedtls_sha512_free(&ctx->u.sha512);
break;
case LWS_GENHASH_TYPE_SHA512:
MBA(mbedtls_sha512_finish)(&ctx->u.sha512, result);
mbedtls_sha512_free(&ctx->u.sha512);
break;
}
return 0;
}
size_t
lws_genhmac_size(enum lws_genhmac_types type)
{
switch(type) {
case LWS_GENHMAC_TYPE_SHA256:
return 32;
case LWS_GENHMAC_TYPE_SHA384:
return 48;
case LWS_GENHMAC_TYPE_SHA512:
return 64;
}
return 0;
}
int
lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type,
const uint8_t *key, size_t key_len)
{
int t;
ctx->type = type;
switch (type) {
case LWS_GENHMAC_TYPE_SHA256:
t = MBEDTLS_MD_SHA256;
break;
case LWS_GENHMAC_TYPE_SHA384:
t = MBEDTLS_MD_SHA384;
break;
case LWS_GENHMAC_TYPE_SHA512:
t = MBEDTLS_MD_SHA512;
break;
default:
return -1;
}
ctx->hmac = mbedtls_md_info_from_type(t);
if (!ctx->hmac)
return -1;
if (mbedtls_md_init_ctx(&ctx->ctx, ctx->hmac))
return -1;
if (mbedtls_md_hmac_starts(&ctx->ctx, key, key_len)) {
mbedtls_md_free(&ctx->ctx);
ctx->hmac = NULL;
return -1;
}
return 0;
}
int
lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len)
{
if (mbedtls_md_hmac_update(&ctx->ctx, in, len))
return -1;
return 0;
}
int
lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result)
{
int n = 0;
if (result)
n = mbedtls_md_hmac_finish(&ctx->ctx, result);
mbedtls_md_free(&ctx->ctx);
ctx->hmac = NULL;
if (n)
return -1;
return 0;
}

View File

@ -0,0 +1,329 @@
/*
* libwebsockets - generic RSA api hiding the backend
*
* Copyright (C) 2017 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
*
* lws_genhash provides a hash / hmac abstraction api in lws that works the
* same whether you are using openssl or mbedtls hash functions underneath.
*/
#include "core/private.h"
LWS_VISIBLE void
lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el)
{
int n;
for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
if (el->e[n].buf)
lws_free_set_NULL(el->e[n].buf);
}
LWS_VISIBLE int
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el)
{
int n;
memset(ctx, 0, sizeof(*ctx));
ctx->ctx = lws_zalloc(sizeof(*ctx->ctx), "genrsa");
if (!ctx->ctx)
return 1;
mbedtls_rsa_init(ctx->ctx, MBEDTLS_RSA_PKCS_V15, 0);
{
mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
&ctx->ctx->E, &ctx->ctx->N, &ctx->ctx->D, &ctx->ctx->P,
&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
&ctx->ctx->QP,
};
for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
if (el->e[n].buf &&
mbedtls_mpi_read_binary(mpi[n], el->e[n].buf,
el->e[n].len)) {
lwsl_notice("mpi load failed\n");
lws_free_set_NULL(ctx->ctx);
return -1;
}
}
ctx->ctx->len = el->e[JWK_KEY_N].len;
return 0;
}
static int
_rngf(void *context, unsigned char *buf, size_t len)
{
if ((size_t)lws_get_random(context, buf, len) == len)
return 0;
return -1;
}
LWS_VISIBLE int
lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
struct lws_genrsa_elements *el, int bits)
{
int n;
memset(ctx, 0, sizeof(*ctx));
ctx->ctx = lws_zalloc(sizeof(*ctx->ctx), "genrsa");
if (!ctx->ctx)
return -1;
mbedtls_rsa_init(ctx->ctx, MBEDTLS_RSA_PKCS_V15, 0);
n = mbedtls_rsa_gen_key(ctx->ctx, _rngf, context, bits, 65537);
if (n) {
lwsl_err("mbedtls_rsa_gen_key failed 0x%x\n", -n);
goto cleanup_1;
}
{
mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
&ctx->ctx->E, &ctx->ctx->N, &ctx->ctx->D, &ctx->ctx->P,
&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
&ctx->ctx->QP,
};
for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
if (mbedtls_mpi_size(mpi[n])) {
el->e[n].buf = lws_malloc(
mbedtls_mpi_size(mpi[n]), "genrsakey");
if (!el->e[n].buf)
goto cleanup;
el->e[n].len = mbedtls_mpi_size(mpi[n]);
mbedtls_mpi_write_binary(mpi[n], el->e[n].buf,
el->e[n].len);
}
}
return 0;
cleanup:
for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
if (el->e[n].buf)
lws_free_set_NULL(el->e[n].buf);
cleanup_1:
lws_free(ctx->ctx);
return -1;
}
LWS_VISIBLE int
lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out, size_t out_max)
{
size_t olen = 0;
int n;
ctx->ctx->len = in_len;
n = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx->ctx, NULL, NULL,
MBEDTLS_RSA_PUBLIC,
&olen, in, out, out_max);
if (n) {
lwsl_notice("%s: -0x%x\n", __func__, -n);
return -1;
}
return olen;
}
LWS_VISIBLE int
lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out)
{
int n;
ctx->ctx->len = in_len;
n = mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx->ctx, NULL, NULL,
MBEDTLS_RSA_PRIVATE,
in_len, in, out);
if (n) {
lwsl_notice("%s: -0x%x\n", __func__, -n);
return -1;
}
return 0;
}
static int
lws_genrsa_genrsa_hash_to_mbed_hash(enum lws_genhash_types hash_type)
{
int h = -1;
switch (hash_type) {
case LWS_GENHASH_TYPE_SHA1:
h = MBEDTLS_MD_SHA1;
break;
case LWS_GENHASH_TYPE_SHA256:
h = MBEDTLS_MD_SHA256;
break;
case LWS_GENHASH_TYPE_SHA384:
h = MBEDTLS_MD_SHA384;
break;
case LWS_GENHASH_TYPE_SHA512:
h = MBEDTLS_MD_SHA512;
break;
}
return h;
}
LWS_VISIBLE int
lws_genrsa_public_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type, const uint8_t *sig,
size_t sig_len)
{
int n, h = lws_genrsa_genrsa_hash_to_mbed_hash(hash_type);
if (h < 0)
return -1;
n = mbedtls_rsa_rsassa_pkcs1_v15_verify(ctx->ctx, NULL, NULL,
MBEDTLS_RSA_PUBLIC,
h, 0, in, sig);
if (n < 0) {
lwsl_notice("%s: -0x%x\n", __func__, -n);
return -1;
}
return n;
}
LWS_VISIBLE int
lws_genrsa_public_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type, uint8_t *sig,
size_t sig_len)
{
int n, h = lws_genrsa_genrsa_hash_to_mbed_hash(hash_type);
if (h < 0)
return -1;
/*
* The "sig" buffer must be as large as the size of ctx->N
* (eg. 128 bytes if RSA-1024 is used).
*/
if (sig_len < ctx->ctx->len)
return -1;
n = mbedtls_rsa_rsassa_pkcs1_v15_sign(ctx->ctx, NULL, NULL,
MBEDTLS_RSA_PRIVATE, h, 0, in,
sig);
if (n < 0) {
lwsl_notice("%s: -0x%x\n", __func__, -n);
return -1;
}
return ctx->ctx->len;
}
LWS_VISIBLE int
lws_genrsa_render_pkey_asn1(struct lws_genrsa_ctx *ctx, int _private,
uint8_t *pkey_asn1, size_t pkey_asn1_len)
{
uint8_t *p = pkey_asn1, *totlen, *end = pkey_asn1 + pkey_asn1_len - 1;
mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
&ctx->ctx->N, &ctx->ctx->E, &ctx->ctx->D, &ctx->ctx->P,
&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
&ctx->ctx->QP,
};
int n;
/* 30 82 - sequence
* 09 29 <-- length(0x0929) less 4 bytes
* 02 01 <- length (1)
* 00
* 02 82
* 02 01 <- length (513) N
* ...
*
* 02 03 <- length (3) E
* 01 00 01
*
* 02 82
* 02 00 <- length (512) D P Q EXP1 EXP2 COEFF
*
* */
*p++ = 0x30;
*p++ = 0x82;
totlen = p;
p += 2;
*p++ = 0x02;
*p++ = 0x01;
*p++ = 0x00;
for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++) {
int m = mbedtls_mpi_size(mpi[n]);
uint8_t *elen;
*p++ = 0x02;
elen = p;
if (m < 0x7f)
*p++ = m;
else {
*p++ = 0x82;
*p++ = m >> 8;
*p++ = m & 0xff;
}
if (p + m > end)
return -1;
mbedtls_mpi_write_binary(mpi[n], p, m);
if (p[0] & 0x80) {
p[0] = 0x00;
mbedtls_mpi_write_binary(mpi[n], &p[1], m);
m++;
}
if (m < 0x7f)
*elen = m;
else {
*elen++ = 0x82;
*elen++ = m >> 8;
*elen = m & 0xff;
}
p += m;
}
n = lws_ptr_diff(p, pkey_asn1);
*totlen++ = (n - 4) >> 8;
*totlen = (n - 4) & 0xff;
return n;
}
LWS_VISIBLE void
lws_genrsa_destroy(struct lws_genrsa_ctx *ctx)
{
if (!ctx->ctx)
return;
mbedtls_rsa_free(ctx->ctx);
lws_free(ctx->ctx);
ctx->ctx = NULL;
}

View File

@ -0,0 +1,240 @@
/*
* libwebsockets - mbedtls-specific client TLS code
*
* Copyright (C) 2010-2017 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"
static int
OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
return 0;
}
int
lws_ssl_client_bio_create(struct lws *wsi)
{
X509_VERIFY_PARAM *param;
char hostname[128], *p;
const char *alpn_comma = wsi->context->tls.alpn_default;
struct alpn_ctx protos;
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
_WSI_TOKEN_CLIENT_HOST) <= 0) {
lwsl_err("%s: Unable to get hostname\n", __func__);
return -1;
}
/*
* remove any :port part on the hostname... necessary for network
* connection but typical certificates do not contain it
*/
p = hostname;
while (*p) {
if (*p == ':') {
*p = '\0';
break;
}
p++;
}
wsi->tls.ssl = SSL_new(wsi->vhost->tls.ssl_client_ctx);
if (!wsi->tls.ssl)
return -1;
if (wsi->vhost->tls.ssl_info_event_mask)
SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback);
if (!(wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
param = SSL_get0_param(wsi->tls.ssl);
/* Enable automatic hostname checks */
// X509_VERIFY_PARAM_set_hostflags(param,
// X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
X509_VERIFY_PARAM_set1_host(param, hostname, 0);
}
if (wsi->vhost->tls.alpn)
alpn_comma = wsi->vhost->tls.alpn;
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
_WSI_TOKEN_CLIENT_ALPN) > 0)
alpn_comma = hostname;
lwsl_info("%s: %p: client conn sending ALPN list '%s'\n",
__func__, wsi, alpn_comma);
protos.len = lws_alpn_comma_to_openssl(alpn_comma, protos.data,
sizeof(protos.data) - 1);
/* with mbedtls, protos is not pointed to after exit from this call */
SSL_set_alpn_select_cb(wsi->tls.ssl, &protos);
/*
* use server name indication (SNI), if supported,
* when establishing connection
*/
SSL_set_verify(wsi->tls.ssl, SSL_VERIFY_PEER,
OpenSSL_client_verify_callback);
SSL_set_fd(wsi->tls.ssl, wsi->desc.sockfd);
return 0;
}
int ERR_get_error(void)
{
return 0;
}
enum lws_ssl_capable_status
lws_tls_client_connect(struct lws *wsi)
{
int m, n = SSL_connect(wsi->tls.ssl);
const unsigned char *prot;
unsigned int len;
if (n == 1) {
SSL_get0_alpn_selected(wsi->tls.ssl, &prot, &len);
lws_role_call_alpn_negotiated(wsi, (const char *)prot);
lwsl_info("client connect OK\n");
return LWS_SSL_CAPABLE_DONE;
}
m = SSL_get_error(wsi->tls.ssl, n);
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl))
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl))
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
if (!n) /* we don't know what he wants, but he says to retry */
return LWS_SSL_CAPABLE_MORE_SERVICE;
return LWS_SSL_CAPABLE_ERROR;
}
int
lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len)
{
int n;
X509 *peer = SSL_get_peer_certificate(wsi->tls.ssl);
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char *sb = (char *)&pt->serv_buf[0];
if (!peer) {
lwsl_info("peer did not provide cert\n");
return -1;
}
lwsl_info("peer provided cert\n");
n = SSL_get_verify_result(wsi->tls.ssl);
lws_latency(wsi->context, wsi,
"SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0);
lwsl_debug("get_verify says %d\n", n);
if (n == X509_V_OK)
return 0;
if (n == X509_V_ERR_HOSTNAME_MISMATCH &&
(wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
lwsl_info("accepting certificate for invalid hostname\n");
return 0;
}
if (n == X509_V_ERR_INVALID_CA &&
(wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED)) {
lwsl_info("accepting certificate from untrusted CA\n");
return 0;
}
if ((n == X509_V_ERR_CERT_NOT_YET_VALID ||
n == X509_V_ERR_CERT_HAS_EXPIRED) &&
(wsi->tls.use_ssl & LCCSCF_ALLOW_EXPIRED)) {
lwsl_info("accepting expired or not yet valid certificate\n");
return 0;
}
lws_snprintf(ebuf, ebuf_len,
"server's cert didn't look good, X509_V_ERR = %d: %s\n",
n, ERR_error_string(n, sb));
lwsl_info("%s\n", ebuf);
lws_ssl_elaborate_error();
return -1;
}
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 char *cert_filepath,
const char *private_key_filepath)
{
X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len);
SSL_METHOD *method = (SSL_METHOD *)TLS_client_method();
unsigned long error;
lws_filepos_t len;
uint8_t *buf;
if (!method) {
error = ERR_get_error();
lwsl_err("problem creating ssl method %lu: %s\n",
error, ERR_error_string(error,
(char *)vh->context->pt[0].serv_buf));
return 1;
}
/* create context */
vh->tls.ssl_client_ctx = SSL_CTX_new(method);
if (!vh->tls.ssl_client_ctx) {
error = ERR_get_error();
lwsl_err("problem creating ssl context %lu: %s\n",
error, ERR_error_string(error,
(char *)vh->context->pt[0].serv_buf));
return 1;
}
if (!ca_filepath)
return 0;
if (alloc_file(vh->context, ca_filepath, &buf, &len)) {
lwsl_err("Load CA cert file %s failed\n", ca_filepath);
return 1;
}
vh->tls.x509_client_CA = d2i_X509(NULL, buf, len);
free(buf);
if (!vh->tls.x509_client_CA) {
lwsl_err("client CA: x509 parse failed\n");
return 1;
}
if (!vh->tls.ssl_ctx)
SSL_CTX_add_client_CA(vh->tls.ssl_client_ctx, vh->tls.x509_client_CA);
else
SSL_CTX_add_client_CA(vh->tls.ssl_ctx, vh->tls.x509_client_CA);
lwsl_notice("client loaded CA for verification %s\n", ca_filepath);
return 0;
}

View File

@ -0,0 +1,694 @@
/*
* libwebsockets - mbedTLS-specific server functions
*
* Copyright (C) 2010-2017 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 <mbedtls/x509_csr.h>
int
lws_tls_server_client_cert_verify_config(struct lws_vhost *vh)
{
int verify_options = SSL_VERIFY_PEER;
/* as a server, are we requiring clients to identify themselves? */
if (!lws_check_opt(vh->options,
LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) {
lwsl_notice("no client cert required\n");
return 0;
}
/*
* The wrapper has this messed-up mapping:
*
* else if (ctx->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
* mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
*
* ie the meaning is inverted. So where we should test for ! we don't
*/
if (lws_check_opt(vh->options, LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED))
verify_options = SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
lwsl_notice("%s: vh %s requires client cert %d\n", __func__, vh->name,
verify_options);
SSL_CTX_set_verify(vh->tls.ssl_ctx, verify_options, NULL);
return 0;
}
static int
lws_mbedtls_sni_cb(void *arg, mbedtls_ssl_context *mbedtls_ctx,
const unsigned char *servername, size_t len)
{
SSL *ssl = SSL_SSL_from_mbedtls_ssl_context(mbedtls_ctx);
struct lws_context *context = (struct lws_context *)arg;
struct lws_vhost *vhost, *vh;
lwsl_notice("%s: %s\n", __func__, servername);
/*
* We can only get ssl accepted connections by using a vhost's ssl_ctx
* find out which listening one took us and only match vhosts on the
* same port.
*/
vh = context->vhost_list;
while (vh) {
if (!vh->being_destroyed &&
vh->tls.ssl_ctx == SSL_get_SSL_CTX(ssl))
break;
vh = vh->vhost_next;
}
if (!vh) {
assert(vh); /* can't match the incoming vh? */
return 0;
}
vhost = lws_select_vhost(context, vh->listen_port,
(const char *)servername);
if (!vhost) {
lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port);
return 0;
}
lwsl_info("SNI: Found: %s:%d at vhost '%s'\n", servername,
vh->listen_port, vhost->name);
/* select the ssl ctx from the selected vhost for this conn */
SSL_set_SSL_CTX(ssl, vhost->tls.ssl_ctx);
return 0;
}
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)
{
int n, f = 0;
const char *filepath = private_key;
uint8_t *mem = NULL, *p = NULL;
size_t mem_len = 0;
lws_filepos_t flen;
long err;
if ((!cert || !private_key) && (!mem_cert || !mem_privkey)) {
lwsl_notice("%s: no usable input\n", __func__);
return 0;
}
n = lws_tls_generic_cert_checks(vhost, cert, private_key);
if (n == LWS_TLS_EXTANT_NO && (!mem_cert || !mem_privkey))
return 0;
/*
* we can't read the root-privs files. But if mem_cert is provided,
* we should use that.
*/
if (n == LWS_TLS_EXTANT_NO)
n = LWS_TLS_EXTANT_ALTERNATIVE;
if (n == LWS_TLS_EXTANT_ALTERNATIVE && (!mem_cert || !mem_privkey))
return 1; /* no alternative */
if (n == LWS_TLS_EXTANT_ALTERNATIVE) {
/*
* Although we have prepared update certs, we no longer have
* the rights to read our own cert + key we saved.
*
* If we were passed copies in memory buffers, use those
* instead.
*
* The passed memory-buffer cert image is in DER, and the
* memory-buffer private key image is PEM.
*/
/* mem cert is already DER */
p = (uint8_t *)mem_cert;
flen = len_mem_cert;
/* mem private key is PEM, so go through the motions */
mem = (uint8_t *)mem_privkey;
mem_len = mem_privkey_len;
filepath = NULL;
} else {
if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, NULL,
0, &p, &flen)) {
lwsl_err("couldn't find cert file %s\n", cert);
return 1;
}
f = 1;
}
err = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, flen, p);
if (!err) {
free(p);
lwsl_err("Problem loading cert\n");
return 1;
}
if (f)
free(p);
p = NULL;
if (private_key || n == LWS_TLS_EXTANT_ALTERNATIVE) {
if (lws_tls_alloc_pem_to_der_file(vhost->context, filepath,
(char *)mem, mem_len, &p,
&flen)) {
lwsl_err("couldn't find private key file %s\n",
private_key);
return 1;
}
err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->tls.ssl_ctx, p, flen);
if (!err) {
free(p);
lwsl_err("Problem loading key\n");
return 1;
}
}
if (p && !mem_privkey) {
free(p);
p = NULL;
}
if (!private_key && !mem_privkey &&
vhost->protocols[0].callback(wsi,
LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
vhost->tls.ssl_ctx, NULL, 0)) {
lwsl_err("ssl private key not set\n");
return 1;
}
vhost->tls.skipped_certs = 0;
return 0;
}
int
lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info,
struct lws_vhost *vhost, struct lws *wsi)
{
const SSL_METHOD *method = TLS_server_method();
uint8_t *p;
lws_filepos_t flen;
int n;
vhost->tls.ssl_ctx = SSL_CTX_new(method); /* create context */
if (!vhost->tls.ssl_ctx) {
lwsl_err("problem creating ssl context\n");
return 1;
}
if (!vhost->tls.use_ssl || !info->ssl_cert_filepath)
return 0;
if (info->ssl_ca_filepath) {
lwsl_notice("%s: vh %s: loading CA filepath %s\n", __func__,
vhost->name, info->ssl_ca_filepath);
if (lws_tls_alloc_pem_to_der_file(vhost->context,
info->ssl_ca_filepath, NULL, 0, &p, &flen)) {
lwsl_err("couldn't find client CA file %s\n",
info->ssl_ca_filepath);
return 1;
}
if (SSL_CTX_add_client_CA_ASN1(vhost->tls.ssl_ctx, (int)flen, p) != 1) {
lwsl_err("%s: SSL_CTX_add_client_CA_ASN1 unhappy\n",
__func__);
free(p);
return 1;
}
free(p);
}
n = lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath,
info->ssl_private_key_filepath, NULL,
0, NULL, 0);
if (n)
return n;
return 0;
}
int
lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd)
{
errno = 0;
wsi->tls.ssl = SSL_new(wsi->vhost->tls.ssl_ctx);
if (wsi->tls.ssl == NULL) {
lwsl_err("SSL_new failed: errno %d\n", errno);
lws_ssl_elaborate_error();
return 1;
}
SSL_set_fd(wsi->tls.ssl, accept_fd);
if (wsi->vhost->tls.ssl_info_event_mask)
SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback);
SSL_set_sni_callback(wsi->tls.ssl, lws_mbedtls_sni_cb, wsi->context);
return 0;
}
int
lws_tls_server_abort_connection(struct lws *wsi)
{
__lws_tls_shutdown(wsi);
SSL_free(wsi->tls.ssl);
return 0;
}
enum lws_ssl_capable_status
lws_tls_server_accept(struct lws *wsi)
{
union lws_tls_cert_info_results ir;
int m, n;
n = SSL_accept(wsi->tls.ssl);
if (n == 1) {
if (strstr(wsi->vhost->name, ".invalid")) {
lwsl_notice("%s: vhost has .invalid, rejecting accept\n", __func__);
return LWS_SSL_CAPABLE_ERROR;
}
n = lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME, &ir,
sizeof(ir.ns.name));
if (!n)
lwsl_notice("%s: client cert CN '%s'\n",
__func__, ir.ns.name);
else
lwsl_info("%s: couldn't get client cert CN\n", __func__);
return LWS_SSL_CAPABLE_DONE;
}
m = SSL_get_error(wsi->tls.ssl, n);
lwsl_debug("%s: %p: accept SSL_get_error %d errno %d\n", __func__,
wsi, m, errno);
// mbedtls wrapper only
if (m == SSL_ERROR_SYSCALL && errno == 11)
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
return LWS_SSL_CAPABLE_ERROR;
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) {
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__);
return LWS_SSL_CAPABLE_ERROR;
}
lwsl_info("SSL_ERROR_WANT_READ\n");
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
}
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) {
lwsl_debug("%s: WANT_WRITE\n", __func__);
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__);
return LWS_SSL_CAPABLE_ERROR;
}
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
}
return LWS_SSL_CAPABLE_ERROR;
}
#if defined(LWS_WITH_ACME)
/*
* mbedtls doesn't support SAN for cert creation. So we use a known-good
* tls-sni-01 cert from OpenSSL that worked on Let's Encrypt, and just replace
* the pubkey n part and the signature part.
*
* This will need redoing for tls-sni-02...
*/
static uint8_t ss_cert_leadin[] = {
0x30, 0x82,
0x05, 0x56, /* total length: LEN1 (+2 / +3) (correct for 513 + 512)*/
0x30, 0x82, /* length: LEN2 (+6 / +7) (correct for 513) */
0x03, 0x3e,
/* addition: v3 cert (+5 bytes)*/
0xa0, 0x03,
0x02, 0x01, 0x02,
0x02, 0x01, 0x01,
0x30, 0x0d, 0x06, 0x09, 0x2a,
0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x3f,
0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47,
0x42, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b,
0x73, 0x6f, 0x6d, 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x31,
0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11, 0x74, 0x65,
0x6d, 0x70, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x69, 0x6e, 0x76, 0x61,
0x6c, 0x69, 0x64, 0x30, 0x1e, 0x17, 0x0d,
/* from 2017-10-29 ... */
0x31, 0x37, 0x31, 0x30, 0x32, 0x39, 0x31, 0x31, 0x34, 0x39, 0x34, 0x35,
0x5a, 0x17, 0x0d,
/* thru 2049-10-29 we immediately discard the private key, no worries */
0x34, 0x39, 0x31, 0x30, 0x32, 0x39, 0x31, 0x32, 0x34, 0x39, 0x34, 0x35,
0x5a,
0x30, 0x3f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x47, 0x42, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a,
0x0c, 0x0b, 0x73, 0x6f, 0x6d, 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e,
0x79, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11,
0x74, 0x65, 0x6d, 0x70, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x69, 0x6e,
0x76, 0x61, 0x6c, 0x69, 0x64, 0x30,
0x82,
0x02, 0x22, /* LEN3 (+C3 / C4) */
0x30, 0x0d, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
0x03,
0x82,
0x02, 0x0f, /* LEN4 (+D6 / D7) */
0x00, 0x30, 0x82,
0x02, 0x0a, /* LEN5 (+ DB / DC) */
0x02, 0x82,
//0x02, 0x01, /* length of n in bytes (including leading 00 if any) */
},
/* 1 + (keybits / 8) bytes N */
ss_cert_san_leadin[] = {
/* e - fixed */
0x02, 0x03, 0x01, 0x00, 0x01,
0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x59, 0x06, 0x03, 0x55, 0x1d,
0x11, 0x04, 0x52, 0x30, 0x50, /* <-- SAN length + 2 */
0x82, 0x4e, /* <-- SAN length */
},
/* 78 bytes of SAN (tls-sni-01)
0x61, 0x64, 0x34, 0x31, 0x61, 0x66, 0x62, 0x65, 0x30, 0x63, 0x61, 0x34,
0x36, 0x34, 0x32, 0x66, 0x30, 0x61, 0x34, 0x34, 0x39, 0x64, 0x39, 0x63,
0x61, 0x37, 0x36, 0x65, 0x62, 0x61, 0x61, 0x62, 0x2e, 0x32, 0x38, 0x39,
0x34, 0x64, 0x34, 0x31, 0x36, 0x63, 0x39, 0x38, 0x33, 0x66, 0x31, 0x32,
0x65, 0x64, 0x37, 0x33, 0x31, 0x61, 0x33, 0x30, 0x66, 0x35, 0x63, 0x34,
0x34, 0x37, 0x37, 0x66, 0x65, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x69,
0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, */
/* end of LEN2 area */
ss_cert_sig_leadin[] = {
/* it's saying that the signature is SHA256 + RSA */
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
0x82,
0x02, 0x01,
0x00,
};
/* (keybits / 8) bytes signature to end of LEN1 area */
#define SAN_A_LENGTH 78
LWS_VISIBLE int
lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a,
const char *san_b)
{
int buflen = 0x560;
uint8_t *buf = lws_malloc(buflen, "tmp cert buf"), *p = buf, *pkey_asn1;
struct lws_genrsa_ctx ctx;
struct lws_genrsa_elements el;
uint8_t digest[32];
struct lws_genhash_ctx hash_ctx;
int pkey_asn1_len = 3 * 1024;
int n, m, keybits = lws_plat_recommended_rsa_bits(), adj;
if (!buf)
return 1;
n = lws_genrsa_new_keypair(vhost->context, &ctx, &el, keybits);
if (n < 0) {
lws_jwk_destroy_genrsa_elements(&el);
goto bail1;
}
n = sizeof(ss_cert_leadin);
memcpy(p, ss_cert_leadin, n);
p += n;
adj = (0x0556 - 0x401) + (keybits / 4) + 1;
buf[2] = adj >> 8;
buf[3] = adj & 0xff;
adj = (0x033e - 0x201) + (keybits / 8) + 1;
buf[6] = adj >> 8;
buf[7] = adj & 0xff;
adj = (0x0222 - 0x201) + (keybits / 8) + 1;
buf[0xc3] = adj >> 8;
buf[0xc4] = adj & 0xff;
adj = (0x020f - 0x201) + (keybits / 8) + 1;
buf[0xd6] = adj >> 8;
buf[0xd7] = adj & 0xff;
adj = (0x020a - 0x201) + (keybits / 8) + 1;
buf[0xdb] = adj >> 8;
buf[0xdc] = adj & 0xff;
*p++ = ((keybits / 8) + 1) >> 8;
*p++ = ((keybits / 8) + 1) & 0xff;
/* we need to drop 1 + (keybits / 8) bytes of n in here, 00 + key */
*p++ = 0x00;
memcpy(p, el.e[JWK_KEY_N].buf, el.e[JWK_KEY_N].len);
p += el.e[JWK_KEY_N].len;
memcpy(p, ss_cert_san_leadin, sizeof(ss_cert_san_leadin));
p += sizeof(ss_cert_san_leadin);
/* drop in 78 bytes of san_a */
memcpy(p, san_a, SAN_A_LENGTH);
p += SAN_A_LENGTH;
memcpy(p, ss_cert_sig_leadin, sizeof(ss_cert_sig_leadin));
p[17] = ((keybits / 8) + 1) >> 8;
p[18] = ((keybits / 8) + 1) & 0xff;
p += sizeof(ss_cert_sig_leadin);
/* hash the cert plaintext */
if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
goto bail2;
if (lws_genhash_update(&hash_ctx, buf, lws_ptr_diff(p, buf))) {
lws_genhash_destroy(&hash_ctx, NULL);
goto bail2;
}
if (lws_genhash_destroy(&hash_ctx, digest))
goto bail2;
/* sign the hash */
n = lws_genrsa_public_sign(&ctx, digest, LWS_GENHASH_TYPE_SHA256, p,
buflen - lws_ptr_diff(p, buf));
if (n < 0)
goto bail2;
p += n;
pkey_asn1 = lws_malloc(pkey_asn1_len, "mbed crt tmp");
if (!pkey_asn1)
goto bail2;
m = lws_genrsa_render_pkey_asn1(&ctx, 1, pkey_asn1, pkey_asn1_len);
if (m < 0) {
lws_free(pkey_asn1);
goto bail2;
}
// lwsl_hexdump_level(LLL_DEBUG, buf, lws_ptr_diff(p, buf));
n = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx,
lws_ptr_diff(p, buf), buf);
if (n != 1) {
lws_free(pkey_asn1);
lwsl_err("%s: generated cert failed to load 0x%x\n",
__func__, -n);
} else {
//lwsl_debug("private key\n");
//lwsl_hexdump_level(LLL_DEBUG, pkey_asn1, n);
/* and to use our generated private key */
n = SSL_CTX_use_PrivateKey_ASN1(0, vhost->tls.ssl_ctx, pkey_asn1, m);
lws_free(pkey_asn1);
if (n != 1) {
lwsl_err("%s: SSL_CTX_use_PrivateKey_ASN1 failed\n",
__func__);
}
}
lws_genrsa_destroy(&ctx);
lws_jwk_destroy_genrsa_elements(&el);
lws_free(buf);
return n != 1;
bail2:
lws_genrsa_destroy(&ctx);
lws_jwk_destroy_genrsa_elements(&el);
bail1:
lws_free(buf);
return -1;
}
void
lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost)
{
}
#if defined(LWS_WITH_JWS)
static int
_rngf(void *context, unsigned char *buf, size_t len)
{
if ((size_t)lws_get_random(context, buf, len) == len)
return 0;
return -1;
}
static const char *x5[] = { "C", "ST", "L", "O", "CN" };
/*
* CSR is output formatted as b64url(DER)
* Private key is output as a PEM in memory
*/
LWS_VISIBLE LWS_EXTERN int
lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[],
uint8_t *dcsr, size_t csr_len, char **privkey_pem,
size_t *privkey_len)
{
mbedtls_x509write_csr csr;
mbedtls_pk_context mpk;
int buf_size = 4096, n;
char subject[200], *p = subject, *end = p + sizeof(subject) - 1;
uint8_t *buf = malloc(buf_size); /* malloc because given to user code */
if (!buf)
return -1;
mbedtls_x509write_csr_init(&csr);
mbedtls_pk_init(&mpk);
if (mbedtls_pk_setup(&mpk, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA))) {
lwsl_notice("%s: pk_setup failed\n", __func__);
goto fail;
}
n = mbedtls_rsa_gen_key(mbedtls_pk_rsa(mpk), _rngf, context,
lws_plat_recommended_rsa_bits(), 65537);
if (n) {
lwsl_notice("%s: failed to generate keys\n", __func__);
goto fail1;
}
/* subject must be formatted like "C=TW,O=warmcat,CN=myserver" */
for (n = 0; n < (int)ARRAY_SIZE(x5); n++) {
if (p != subject)
*p++ = ',';
if (elements[n])
p += lws_snprintf(p, end - p, "%s=%s", x5[n],
elements[n]);
}
if (mbedtls_x509write_csr_set_subject_name(&csr, subject))
goto fail1;
mbedtls_x509write_csr_set_key(&csr, &mpk);
mbedtls_x509write_csr_set_md_alg(&csr, MBEDTLS_MD_SHA256);
/*
* data is written at the end of the buffer! Use the
* return value to determine where you should start
* using the buffer
*/
n = mbedtls_x509write_csr_der(&csr, buf, buf_size, _rngf, context);
if (n < 0) {
lwsl_notice("%s: write csr der failed\n", __func__);
goto fail1;
}
/* we have it in DER, we need it in b64URL */
n = lws_jws_base64_enc((char *)(buf + buf_size) - n, n,
(char *)dcsr, csr_len);
if (n < 0)
goto fail1;
/*
* okay, the CSR is done, last we need the private key in PEM
* re-use the DER CSR buf as the result buffer since we cn do it in
* one step
*/
if (mbedtls_pk_write_key_pem(&mpk, buf, buf_size)) {
lwsl_notice("write key pem failed\n");
goto fail1;
}
*privkey_pem = (char *)buf;
*privkey_len = strlen((const char *)buf);
mbedtls_pk_free(&mpk);
mbedtls_x509write_csr_free(&csr);
return n;
fail1:
mbedtls_pk_free(&mpk);
fail:
mbedtls_x509write_csr_free(&csr);
free(buf);
return -1;
}
#endif
#endif

View File

@ -0,0 +1,520 @@
/*
* libwebsockets - mbedTLS-specific lws apis
*
* Copyright (C) 2010-2017 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 <mbedtls/oid.h>
void
lws_ssl_elaborate_error(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)
{
if (!lws_check_opt(vhost->context->options,
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
return;
if (vhost->tls.ssl_ctx)
SSL_CTX_free(vhost->tls.ssl_ctx);
if (!vhost->tls.user_supplied_ssl_ctx && vhost->tls.ssl_client_ctx)
SSL_CTX_free(vhost->tls.ssl_client_ctx);
if (vhost->tls.x509_client_CA)
X509_free(vhost->tls.x509_client_CA);
}
LWS_VISIBLE int
lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n = 0, m;
if (!wsi->tls.ssl)
return lws_ssl_capable_read_no_ssl(wsi, buf, len);
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
errno = 0;
n = SSL_read(wsi->tls.ssl, buf, len);
#if defined(LWS_WITH_ESP32)
if (!n && errno == ENOTCONN) {
lwsl_debug("%p: SSL_read ENOTCONN\n", wsi);
return LWS_SSL_CAPABLE_ERROR;
}
#endif
#if defined(LWS_WITH_STATS)
if (!wsi->seen_rx) {
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_MS_SSL_RX_DELAY,
time_in_microseconds() - wsi->accept_start_us);
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_SSL_CONNS_HAD_RX, 1);
wsi->seen_rx = 1;
}
#endif
lwsl_debug("%p: SSL_read says %d\n", wsi, n);
/* manpage: returning 0 means connection shut down */
if (!n) {
wsi->socket_is_permanently_unusable = 1;
return LWS_SSL_CAPABLE_ERROR;
}
if (n < 0) {
m = SSL_get_error(wsi->tls.ssl, n);
lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno);
if (m == SSL_ERROR_ZERO_RETURN ||
m == SSL_ERROR_SYSCALL)
return LWS_SSL_CAPABLE_ERROR;
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) {
lwsl_debug("%s: WANT_READ\n", __func__);
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) {
lwsl_debug("%s: WANT_WRITE\n", __func__);
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
wsi->socket_is_permanently_unusable = 1;
return LWS_SSL_CAPABLE_ERROR;
}
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
lws_restart_ws_ping_pong_timer(wsi);
/*
* if it was our buffer that limited what we read,
* check if SSL has additional data pending inside SSL buffers.
*
* Because these won't signal at the network layer with POLLIN
* and if we don't realize, this data will sit there forever
*/
if (n != len)
goto bail;
if (!wsi->tls.ssl)
goto bail;
if (!SSL_pending(wsi->tls.ssl))
goto bail;
if (wsi->tls.pending_read_list_next)
return n;
if (wsi->tls.pending_read_list_prev)
return n;
if (pt->tls.pending_read_list == wsi)
return n;
/* add us to the linked list of guys with pending ssl */
if (pt->tls.pending_read_list)
pt->tls.pending_read_list->tls.pending_read_list_prev = wsi;
wsi->tls.pending_read_list_next = pt->tls.pending_read_list;
wsi->tls.pending_read_list_prev = NULL;
pt->tls.pending_read_list = wsi;
return n;
bail:
lws_ssl_remove_wsi_from_buffered_list(wsi);
return n;
}
LWS_VISIBLE int
lws_ssl_pending(struct lws *wsi)
{
if (!wsi->tls.ssl)
return 0;
return SSL_pending(wsi->tls.ssl);
}
LWS_VISIBLE int
lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
{
int n, m;
if (!wsi->tls.ssl)
return lws_ssl_capable_write_no_ssl(wsi, buf, len);
n = SSL_write(wsi->tls.ssl, buf, len);
if (n > 0)
return n;
m = SSL_get_error(wsi->tls.ssl, n);
if (m != SSL_ERROR_SYSCALL) {
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) {
lwsl_notice("%s: want read\n", __func__);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) {
lws_set_blocking_send(wsi);
lwsl_notice("%s: want write\n", __func__);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
}
lwsl_debug("%s failed: %d\n",__func__, m);
wsi->socket_is_permanently_unusable = 1;
return LWS_SSL_CAPABLE_ERROR;
}
int openssl_SSL_CTX_private_data_index;
void
lws_ssl_info_callback(const SSL *ssl, int where, int ret)
{
struct lws *wsi;
struct lws_context *context;
struct lws_ssl_info si;
context = (struct lws_context *)SSL_CTX_get_ex_data(
SSL_get_SSL_CTX(ssl),
openssl_SSL_CTX_private_data_index);
if (!context)
return;
wsi = wsi_from_fd(context, SSL_get_fd(ssl));
if (!wsi)
return;
if (!(where & wsi->vhost->tls.ssl_info_event_mask))
return;
si.where = where;
si.ret = ret;
if (user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_SSL_INFO,
wsi->user_space, &si, 0))
lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1);
}
LWS_VISIBLE int
lws_ssl_close(struct lws *wsi)
{
lws_sockfd_type n;
if (!wsi->tls.ssl)
return 0; /* not handled */
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
/* kill ssl callbacks, becausse we will remove the fd from the
* table linking it to the wsi
*/
if (wsi->vhost->tls.ssl_info_event_mask)
SSL_set_info_callback(wsi->tls.ssl, NULL);
#endif
n = SSL_get_fd(wsi->tls.ssl);
if (!wsi->socket_is_permanently_unusable)
SSL_shutdown(wsi->tls.ssl);
compatible_close(n);
SSL_free(wsi->tls.ssl);
wsi->tls.ssl = NULL;
if (!lwsi_role_client(wsi) &&
wsi->context->simultaneous_ssl_restriction &&
wsi->context->simultaneous_ssl-- ==
wsi->context->simultaneous_ssl_restriction)
/* we made space and can do an accept */
lws_gate_accepts(wsi->context, 1);
#if defined(LWS_WITH_STATS)
wsi->context->updated = 1;
#endif
return 1; /* handled */
}
void
lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
{
if (vhost->tls.ssl_ctx)
SSL_CTX_free(vhost->tls.ssl_ctx);
if (!vhost->tls.user_supplied_ssl_ctx && vhost->tls.ssl_client_ctx)
SSL_CTX_free(vhost->tls.ssl_client_ctx);
#if defined(LWS_WITH_ACME)
lws_tls_acme_sni_cert_destroy(vhost);
#endif
}
void
lws_ssl_context_destroy(struct lws_context *context)
{
}
lws_tls_ctx *
lws_tls_ctx_from_wsi(struct lws *wsi)
{
if (!wsi->tls.ssl)
return NULL;
return SSL_get_SSL_CTX(wsi->tls.ssl);
}
enum lws_ssl_capable_status
__lws_tls_shutdown(struct lws *wsi)
{
int n = SSL_shutdown(wsi->tls.ssl);
lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd);
switch (n) {
case 1: /* successful completion */
n = shutdown(wsi->desc.sockfd, SHUT_WR);
return LWS_SSL_CAPABLE_DONE;
case 0: /* needs a retry */
__lws_change_pollfd(wsi, 0, LWS_POLLIN);
return LWS_SSL_CAPABLE_MORE_SERVICE;
default: /* fatal error, or WANT */
n = SSL_get_error(wsi->tls.ssl, n);
if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
if (SSL_want_read(wsi->tls.ssl)) {
lwsl_debug("(wants read)\n");
__lws_change_pollfd(wsi, 0, LWS_POLLIN);
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
}
if (SSL_want_write(wsi->tls.ssl)) {
lwsl_debug("(wants write)\n");
__lws_change_pollfd(wsi, 0, LWS_POLLOUT);
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
}
}
return LWS_SSL_CAPABLE_ERROR;
}
}
static time_t
lws_tls_mbedtls_time_to_unix(mbedtls_x509_time *xtime)
{
struct tm t;
if (!xtime || !xtime->year || xtime->year < 0)
return (time_t)(long long)-1;
memset(&t, 0, sizeof(t));
t.tm_year = xtime->year - 1900;
t.tm_mon = xtime->mon - 1; /* mbedtls months are 1+, tm are 0+ */
t.tm_mday = xtime->day - 1; /* mbedtls days are 1+, tm are 0+ */
t.tm_hour = xtime->hour;
t.tm_min = xtime->min;
t.tm_sec = xtime->sec;
t.tm_isdst = -1;
return mktime(&t);
}
static int
lws_tls_mbedtls_get_x509_name(mbedtls_x509_name *name,
union lws_tls_cert_info_results *buf, size_t len)
{
while (name) {
if (MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid)) {
name = name->next;
continue;
}
if (len - 1 < name->val.len)
return -1;
memcpy(&buf->ns.name[0], name->val.p, name->val.len);
buf->ns.name[name->val.len] = '\0';
buf->ns.len = name->val.len;
return 0;
}
return -1;
}
static int
lws_tls_mbedtls_cert_info(mbedtls_x509_crt *x509, enum lws_tls_cert_info type,
union lws_tls_cert_info_results *buf, size_t len)
{
if (!x509)
return -1;
switch (type) {
case LWS_TLS_CERT_INFO_VALIDITY_FROM:
buf->time = lws_tls_mbedtls_time_to_unix(&x509->valid_from);
if (buf->time == (time_t)(long long)-1)
return -1;
break;
case LWS_TLS_CERT_INFO_VALIDITY_TO:
buf->time = lws_tls_mbedtls_time_to_unix(&x509->valid_to);
if (buf->time == (time_t)(long long)-1)
return -1;
break;
case LWS_TLS_CERT_INFO_COMMON_NAME:
return lws_tls_mbedtls_get_x509_name(&x509->subject, buf, len);
case LWS_TLS_CERT_INFO_ISSUER_NAME:
return lws_tls_mbedtls_get_x509_name(&x509->issuer, buf, len);
case LWS_TLS_CERT_INFO_USAGE:
buf->usage = x509->key_usage;
break;
case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY:
{
char *p = buf->ns.name;
size_t r = len, u;
switch (mbedtls_pk_get_type(&x509->pk)) {
case MBEDTLS_PK_RSA:
{
mbedtls_rsa_context *rsa = mbedtls_pk_rsa(x509->pk);
if (mbedtls_mpi_write_string(&rsa->N, 16, p, r, &u))
return -1;
r -= u;
p += u;
if (mbedtls_mpi_write_string(&rsa->E, 16, p, r, &u))
return -1;
p += u;
buf->ns.len = lws_ptr_diff(p, buf->ns.name);
break;
}
case MBEDTLS_PK_ECKEY:
{
mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(x509->pk);
if (mbedtls_mpi_write_string(&ecp->Q.X, 16, p, r, &u))
return -1;
r -= u;
p += u;
if (mbedtls_mpi_write_string(&ecp->Q.Y, 16, p, r, &u))
return -1;
r -= u;
p += u;
if (mbedtls_mpi_write_string(&ecp->Q.Z, 16, p, r, &u))
return -1;
p += u;
buf->ns.len = lws_ptr_diff(p, buf->ns.name);
break;
}
default:
lwsl_notice("%s: x509 has unsupported pubkey type %d\n",
__func__,
mbedtls_pk_get_type(&x509->pk));
return -1;
}
break;
}
default:
return -1;
}
return 0;
}
LWS_VISIBLE LWS_EXTERN 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)
{
mbedtls_x509_crt *x509 = ssl_ctx_get_mbedtls_x509_crt(vhost->tls.ssl_ctx);
return lws_tls_mbedtls_cert_info(x509, type, buf, len);
}
LWS_VISIBLE int
lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type,
union lws_tls_cert_info_results *buf, size_t len)
{
mbedtls_x509_crt *x509;
wsi = lws_get_network_wsi(wsi);
x509 = ssl_get_peer_mbedtls_x509_crt(wsi->tls.ssl);
if (!x509)
return -1;
switch (type) {
case LWS_TLS_CERT_INFO_VERIFIED:
buf->verified = SSL_get_verify_result(wsi->tls.ssl) == X509_V_OK;
return 0;
default:
return lws_tls_mbedtls_cert_info(x509, type, buf, len);
}
return -1;
}
static int
tops_fake_POLLIN_for_buffered_mbedtls(struct lws_context_per_thread *pt)
{
return lws_tls_fake_POLLIN_for_buffered(pt);
}
static int
tops_periodic_housekeeping_mbedtls(struct lws_context *context, time_t now)
{
int n;
n = lws_compare_time_t(context, now, context->tls.last_cert_check_s);
if ((!context->tls.last_cert_check_s || n > (24 * 60 * 60)) &&
!lws_tls_check_all_cert_lifetimes(context))
context->tls.last_cert_check_s = now;
return 0;
}
const struct lws_tls_ops tls_ops_mbedtls = {
/* fake_POLLIN_for_buffered */ tops_fake_POLLIN_for_buffered_mbedtls,
/* periodic_housekeeping */ tops_periodic_housekeeping_mbedtls,
};

View File

@ -203,6 +203,8 @@ struct ssl_st
const SSL_METHOD *method; const SSL_METHOD *method;
const char **alpn_protos;
RECORD_LAYER rlayer; RECORD_LAYER rlayer;
/* where we are */ /* where we are */

View File

@ -35,6 +35,22 @@
#define X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS (1 << 3) #define X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS (1 << 3)
#define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS (1 << 4) #define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS (1 << 4)
mbedtls_x509_crt *
ssl_ctx_get_mbedtls_x509_crt(SSL_CTX *ssl_ctx);
mbedtls_x509_crt *
ssl_get_peer_mbedtls_x509_crt(SSL *ssl);
int SSL_set_sni_callback(SSL *ssl, int(*cb)(void *, mbedtls_ssl_context *,
const unsigned char *, size_t), void *param);
void SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx);
int SSL_CTX_add_client_CA_ASN1(SSL_CTX *ssl, int len,
const unsigned char *d);
SSL *SSL_SSL_from_mbedtls_ssl_context(mbedtls_ssl_context *msc);
/** /**
* @brief create a SSL context * @brief create a SSL context
* *
@ -305,6 +321,7 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
void *arg), void *arg),
void *arg); void *arg);
void SSL_set_alpn_select_cb(SSL *ssl, void *arg);
/** /**
* @brief set the SSL context ALPN select protocol * @brief set the SSL context ALPN select protocol

View File

@ -19,19 +19,11 @@
extern "C" { extern "C" {
#endif #endif
/*
#include "esp_types.h"
#include "esp_log.h"
*/
#include "string.h" #include "string.h"
#include "stdlib.h"
/* GODOT ADDITION */ #if defined(LWS_HAVE_MALLOC_H)
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <stdlib.h>
#else
#include "malloc.h" #include "malloc.h"
#endif #endif
/* END GODOT ADDITION */
void *ssl_mem_zalloc(size_t size); void *ssl_mem_zalloc(size_t size);

View File

@ -19,6 +19,9 @@
#include "ssl_dbg.h" #include "ssl_dbg.h"
#include "ssl_port.h" #include "ssl_port.h"
char *
lws_strncpy(char *dest, const char *src, size_t size);
#define SSL_SEND_DATA_MAX_LENGTH 1460 #define SSL_SEND_DATA_MAX_LENGTH 1460
/** /**
@ -348,6 +351,9 @@ void SSL_free(SSL *ssl)
SSL_SESSION_free(ssl->session); SSL_SESSION_free(ssl->session);
if (ssl->alpn_protos)
ssl_mem_free(ssl->alpn_protos);
ssl_mem_free(ssl); ssl_mem_free(ssl);
} }
@ -1582,7 +1588,7 @@ void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_C
void ERR_error_string_n(unsigned long e, char *buf, size_t len) void ERR_error_string_n(unsigned long e, char *buf, size_t len)
{ {
strncpy(buf, "unknown", len); lws_strncpy(buf, "unknown", len);
} }
void ERR_free_strings(void) void ERR_free_strings(void)
@ -1591,11 +1597,34 @@ void ERR_free_strings(void)
char *ERR_error_string(unsigned long e, char *buf) char *ERR_error_string(unsigned long e, char *buf)
{ {
if (buf) { if (!buf)
strcpy(buf, "unknown"); return "unknown";
switch(e) {
case X509_V_ERR_INVALID_CA:
strcpy(buf, "CA is not trusted");
break;
case X509_V_ERR_HOSTNAME_MISMATCH:
strcpy(buf, "Hostname mismatch");
break;
case X509_V_ERR_CA_KEY_TOO_SMALL:
strcpy(buf, "CA key too small");
break;
case X509_V_ERR_CA_MD_TOO_WEAK:
strcpy(buf, "MD key too weak");
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
strcpy(buf, "Cert from the future");
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
strcpy(buf, "Cert expired");
break;
default:
strcpy(buf, "unknown");
break;
} }
return "unknown"; return buf;
} }
void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx)
@ -1619,15 +1648,16 @@ void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx)
*/ */
struct alpn_ctx { struct alpn_ctx {
unsigned char *data; unsigned char data[23];
unsigned short len; unsigned char len;
}; };
void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg) static void
_openssl_alpn_to_mbedtls(struct alpn_ctx *ac, char ***palpn_protos)
{ {
struct alpn_ctx *ac = arg;
unsigned char *p = ac->data, *q; unsigned char *p = ac->data, *q;
unsigned char len; unsigned char len;
char **alpn_protos;
int count = 0; int count = 0;
/* find out how many entries he gave us */ /* find out how many entries he gave us */
@ -1644,23 +1674,28 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
break; break;
} }
if (!len)
count++;
if (!count) if (!count)
return; return;
/* allocate space for count + 1 pointers and the data afterwards */ /* allocate space for count + 1 pointers and the data afterwards */
ctx->alpn_protos = ssl_mem_zalloc((count + 1) * sizeof(char *) + ac->len + 1); alpn_protos = ssl_mem_zalloc((count + 1) * sizeof(char *) + ac->len + 1);
if (!ctx->alpn_protos) if (!alpn_protos)
return; return;
*palpn_protos = alpn_protos;
/* convert to mbedtls format */ /* convert to mbedtls format */
q = (unsigned char *)ctx->alpn_protos + (count + 1) * sizeof(char *); q = (unsigned char *)alpn_protos + (count + 1) * sizeof(char *);
p = ac->data; p = ac->data;
count = 0; count = 0;
len = *p++; len = *p++;
ctx->alpn_protos[count] = (char *)q; alpn_protos[count] = (char *)q;
while (p - ac->data < ac->len) { while (p - ac->data < ac->len) {
if (len--) { if (len--) {
*q++ = *p++; *q++ = *p++;
@ -1669,11 +1704,33 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
*q++ = '\0'; *q++ = '\0';
count++; count++;
len = *p++; len = *p++;
ctx->alpn_protos[count] = (char *)q; alpn_protos[count] = (char *)q;
if (!len) if (!len)
break; break;
} }
ctx->alpn_protos[count] = NULL; /* last pointer ends list with NULL */ if (!len) {
*q++ = '\0';
count++;
len = *p++;
alpn_protos[count] = (char *)q;
}
alpn_protos[count] = NULL; /* last pointer ends list with NULL */
}
void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
{
struct alpn_ctx *ac = arg;
ctx->alpn_cb = cb; ctx->alpn_cb = cb;
_openssl_alpn_to_mbedtls(ac, (char ***)&ctx->alpn_protos);
}
void SSL_set_alpn_select_cb(SSL *ssl, void *arg)
{
struct alpn_ctx *ac = arg;
_openssl_alpn_to_mbedtls(ac, (char ***)&ssl->alpn_protos);
_ssl_set_alpn_list(ssl);
} }

View File

@ -17,6 +17,8 @@
#include "ssl_dbg.h" #include "ssl_dbg.h"
#include "ssl_port.h" #include "ssl_port.h"
#include <assert.h>
/** /**
* @brief show X509 certification information * @brief show X509 certification information
*/ */
@ -155,7 +157,7 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x)
{ {
SSL_ASSERT1(ctx); SSL_ASSERT1(ctx);
SSL_ASSERT1(x); SSL_ASSERT1(x);
assert(ctx);
if (ctx->client_CA == x) if (ctx->client_CA == x)
return 1; return 1;
@ -166,6 +168,28 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x)
return 1; return 1;
} }
/**
* @brief add CA client certification into the SSL
*/
int SSL_CTX_add_client_CA_ASN1(SSL_CTX *ctx, int len,
const unsigned char *d)
{
X509 *x;
x = d2i_X509(NULL, d, len);
if (!x) {
SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "d2i_X509() return NULL");
return 0;
}
SSL_ASSERT1(ctx);
X509_free(ctx->client_CA);
ctx->client_CA = x;
return 1;
}
/** /**
* @brief add CA client certification into the SSL * @brief add CA client certification into the SSL
*/ */

View File

@ -25,6 +25,8 @@
#include "mbedtls/error.h" #include "mbedtls/error.h"
#include "mbedtls/certs.h" #include "mbedtls/certs.h"
#include <libwebsockets.h>
#define X509_INFO_STRING_LENGTH 8192 #define X509_INFO_STRING_LENGTH 8192
struct ssl_pm struct ssl_pm
@ -41,6 +43,8 @@ struct ssl_pm
mbedtls_ssl_context ssl; mbedtls_ssl_context ssl;
mbedtls_entropy_context entropy; mbedtls_entropy_context entropy;
SSL *owner;
}; };
struct x509_pm struct x509_pm
@ -62,7 +66,7 @@ unsigned int max_content_len;
/*********************************************************************************************/ /*********************************************************************************************/
/************************************ SSL arch interface *************************************/ /************************************ SSL arch interface *************************************/
#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG //#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG
/* mbedtls debug level */ /* mbedtls debug level */
#define MBEDTLS_DEBUG_LEVEL 4 #define MBEDTLS_DEBUG_LEVEL 4
@ -79,13 +83,13 @@ static void ssl_platform_debug(void *ctx, int level,
This is a bit wasteful because the macros are compiled in with This is a bit wasteful because the macros are compiled in with
the full _FILE_ path in each case. the full _FILE_ path in each case.
*/ */
char *file_sep = rindex(file, '/'); // char *file_sep = rindex(file, '/');
if(file_sep) // if(file_sep)
file = file_sep + 1; // file = file_sep + 1;
SSL_DEBUG(SSL_DEBUG_ON, "%s:%d %s", file, line, str); printf("%s:%d %s", file, line, str);
} }
#endif //#endif
/** /**
* @brief create SSL low-level object * @brief create SSL low-level object
@ -109,6 +113,8 @@ int ssl_pm_new(SSL *ssl)
goto no_mem; goto no_mem;
} }
ssl_pm->owner = ssl;
if (!ssl->ctx->read_buffer_len) if (!ssl->ctx->read_buffer_len)
ssl->ctx->read_buffer_len = 2048; ssl->ctx->read_buffer_len = 2048;
@ -159,12 +165,12 @@ int ssl_pm_new(SSL *ssl)
mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg);
#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG //#ifdef CONFIG_OPENSSL_LOWLEVEL_DEBUG
mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL); // mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL);
// mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL);
//#else
mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL); mbedtls_ssl_conf_dbg(&ssl_pm->conf, ssl_platform_debug, NULL);
#else //#endif
mbedtls_ssl_conf_dbg(&ssl_pm->conf, NULL, NULL);
#endif
ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf); ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf);
if (ret) { if (ret) {
@ -261,7 +267,7 @@ static int mbedtls_handshake( mbedtls_ssl_context *ssl )
while (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) { while (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) {
ret = mbedtls_ssl_handshake_step(ssl); ret = mbedtls_ssl_handshake_step(ssl);
SSL_DEBUG(SSL_PLATFORM_DEBUG_LEVEL, "ssl ret %d state %d", ret, ssl->state); lwsl_info("%s: ssl ret -%x state %d\n", __func__, -ret, ssl->state);
if (ret != 0) if (ret != 0)
break; break;
@ -270,14 +276,21 @@ static int mbedtls_handshake( mbedtls_ssl_context *ssl )
return ret; return ret;
} }
#include <errno.h>
int ssl_pm_handshake(SSL *ssl) int ssl_pm_handshake(SSL *ssl)
{ {
int ret; int ret;
struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
ssl->err = 0;
errno = 0;
ret = ssl_pm_reload_crt(ssl); ret = ssl_pm_reload_crt(ssl);
if (ret) if (ret) {
printf("%s: cert reload failed\n", __func__);
return 0; return 0;
}
if (ssl_pm->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) { if (ssl_pm->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) {
ssl_speed_up_enter(); ssl_speed_up_enter();
@ -298,6 +311,7 @@ int ssl_pm_handshake(SSL *ssl)
* <0 = death * <0 = death
*/ */
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
ssl->err = ret;
SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_handshake() return -0x%x", -ret); SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_handshake() return -0x%x", -ret);
return 0; /* OpenSSL: did not complete but may be retried */ return 0; /* OpenSSL: did not complete but may be retried */
} }
@ -309,6 +323,14 @@ int ssl_pm_handshake(SSL *ssl)
return 1; /* openssl successful */ return 1; /* openssl successful */
} }
if (errno == 11) {
ssl->err = ret == MBEDTLS_ERR_SSL_WANT_READ;
return 0;
}
printf("%s: mbedtls_ssl_handshake() returned -0x%x\n", __func__, -ret);
/* it's had it */ /* it's had it */
ssl->err = SSL_ERROR_SYSCALL; ssl->err = SSL_ERROR_SYSCALL;
@ -316,6 +338,28 @@ int ssl_pm_handshake(SSL *ssl)
return -1; /* openssl death */ return -1; /* openssl death */
} }
mbedtls_x509_crt *
ssl_ctx_get_mbedtls_x509_crt(SSL_CTX *ssl_ctx)
{
struct x509_pm *x509_pm = (struct x509_pm *)ssl_ctx->cert->x509->x509_pm;
if (!x509_pm)
return NULL;
return x509_pm->x509_crt;
}
mbedtls_x509_crt *
ssl_get_peer_mbedtls_x509_crt(SSL *ssl)
{
struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm;
if (!x509_pm)
return NULL;
return x509_pm->ex_crt;
}
int ssl_pm_shutdown(SSL *ssl) int ssl_pm_shutdown(SSL *ssl)
{ {
int ret; int ret;
@ -351,8 +395,10 @@ int ssl_pm_read(SSL *ssl, void *buffer, int len)
ret = mbedtls_ssl_read(&ssl_pm->ssl, buffer, len); ret = mbedtls_ssl_read(&ssl_pm->ssl, buffer, len);
if (ret < 0) { if (ret < 0) {
// lwsl_notice("%s: mbedtls_ssl_read says -0x%x\n", __func__, -ret);
SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_read() return -0x%x", -ret); SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_read() return -0x%x", -ret);
if (ret == MBEDTLS_ERR_NET_CONN_RESET) if (ret == MBEDTLS_ERR_NET_CONN_RESET ||
ret <= MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE) /* fatal errors */
ssl->err = SSL_ERROR_SYSCALL; ssl->err = SSL_ERROR_SYSCALL;
ret = -1; ret = -1;
} }
@ -392,6 +438,7 @@ int ssl_pm_send(SSL *ssl, const void *buffer, int len)
if (ret < 0) { if (ret < 0) {
SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret); SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret);
switch (ret) { switch (ret) {
case MBEDTLS_ERR_NET_SEND_FAILED:
case MBEDTLS_ERR_NET_CONN_RESET: case MBEDTLS_ERR_NET_CONN_RESET:
ssl->err = SSL_ERROR_SYSCALL; ssl->err = SSL_ERROR_SYSCALL;
break; break;
@ -589,22 +636,27 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len)
} }
} }
load_buf = ssl_mem_malloc(len + 1); mbedtls_x509_crt_init(x509_pm->x509_crt);
if (!load_buf) { if (buffer[0] != 0x30) {
SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (load_buf)"); load_buf = ssl_mem_malloc(len + 1);
goto failed; if (!load_buf) {
SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (load_buf)");
goto failed;
}
ssl_memcpy(load_buf, buffer, len);
load_buf[len] = '\0';
ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len + 1);
ssl_mem_free(load_buf);
} else {
printf("parsing as der\n");
ret = mbedtls_x509_crt_parse_der(x509_pm->x509_crt, buffer, len);
} }
ssl_memcpy(load_buf, buffer, len);
load_buf[len] = '\0';
mbedtls_x509_crt_init(x509_pm->x509_crt);
ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len + 1);
ssl_mem_free(load_buf);
if (ret) { if (ret) {
SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_x509_crt_parse return -0x%x", -ret); printf("mbedtls_x509_crt_parse return -0x%x", -ret);
goto failed; goto failed;
} }
@ -707,46 +759,44 @@ void ssl_pm_set_bufflen(SSL *ssl, int len)
long ssl_pm_get_verify_result(const SSL *ssl) long ssl_pm_get_verify_result(const SSL *ssl)
{ {
uint32_t ret; uint32_t ret;
long verify_result; long verify_result;
struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl); ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl);
if (!ret)
return X509_V_OK;
if (!ret) if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED ||
return X509_V_OK; (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED))
verify_result = X509_V_ERR_INVALID_CA;
if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED || else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
(ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED)) verify_result = X509_V_ERR_HOSTNAME_MISMATCH;
// Allows us to use LCCSCF_ALLOW_SELFSIGNED to skip verification
verify_result = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH) else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) ||
verify_result = X509_V_ERR_HOSTNAME_MISMATCH; (ret & MBEDTLS_X509_BADCRL_BAD_KEY))
verify_result = X509_V_ERR_CA_KEY_TOO_SMALL;
else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) || else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) ||
(ret & MBEDTLS_X509_BADCRL_BAD_KEY)) (ret & MBEDTLS_X509_BADCRL_BAD_MD))
verify_result = X509_V_ERR_CA_KEY_TOO_SMALL; verify_result = X509_V_ERR_CA_MD_TOO_WEAK;
else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) || else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) ||
(ret & MBEDTLS_X509_BADCRL_BAD_MD)) (ret & MBEDTLS_X509_BADCRL_FUTURE))
verify_result = X509_V_ERR_CA_MD_TOO_WEAK; verify_result = X509_V_ERR_CERT_NOT_YET_VALID;
else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) || else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) ||
(ret & MBEDTLS_X509_BADCRL_FUTURE)) (ret & MBEDTLS_X509_BADCRL_EXPIRED))
verify_result = X509_V_ERR_CERT_NOT_YET_VALID; verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) || else
(ret & MBEDTLS_X509_BADCRL_EXPIRED)) verify_result = X509_V_ERR_UNSPECIFIED;
verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
else SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL,
verify_result = X509_V_ERR_UNSPECIFIED; "mbedtls_ssl_get_verify_result() return 0x%x", ret);
SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, return verify_result;
"mbedtls_ssl_get_verify_result() return 0x%x", ret);
return verify_result;
} }
/** /**
@ -779,6 +829,12 @@ int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
void _ssl_set_alpn_list(const SSL *ssl) void _ssl_set_alpn_list(const SSL *ssl)
{ {
if (ssl->alpn_protos) {
if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->alpn_protos))
fprintf(stderr, "mbedtls_ssl_conf_alpn_protocols failed\n");
return;
}
if (!ssl->ctx->alpn_protos) if (!ssl->ctx->alpn_protos)
return; return;
if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->ctx->alpn_protos)) if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->ctx->alpn_protos))
@ -797,3 +853,55 @@ void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data,
*len = 0; *len = 0;
} }
int SSL_set_sni_callback(SSL *ssl, int(*cb)(void *, mbedtls_ssl_context *,
const unsigned char *, size_t), void *param)
{
struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
mbedtls_ssl_conf_sni(&ssl_pm->conf, cb, param);
return 0;
}
SSL *SSL_SSL_from_mbedtls_ssl_context(mbedtls_ssl_context *msc)
{
struct ssl_pm *ssl_pm = (struct ssl_pm *)((char *)msc - offsetof(struct ssl_pm, ssl));
return ssl_pm->owner;
}
#include "ssl_cert.h"
void SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx)
{
struct ssl_pm *ssl_pm = ssl->ssl_pm;
struct x509_pm *x509_pm = (struct x509_pm *)ctx->cert->x509->x509_pm;
struct x509_pm *x509_pm_ca = (struct x509_pm *)ctx->client_CA->x509_pm;
struct pkey_pm *pkey_pm = (struct pkey_pm *)ctx->cert->pkey->pkey_pm;
int mode;
if (ssl->cert)
ssl_cert_free(ssl->cert);
ssl->ctx = ctx;
ssl->cert = __ssl_cert_new(ctx->cert);
if (ctx->verify_mode == SSL_VERIFY_PEER)
mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
else if (ctx->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
else if (ctx->verify_mode == SSL_VERIFY_CLIENT_ONCE)
mode = MBEDTLS_SSL_VERIFY_UNSET;
else
mode = MBEDTLS_SSL_VERIFY_NONE;
// printf("ssl: %p, client ca x509_crt %p, mbedtls mode %d\n", ssl, x509_pm_ca->x509_crt, mode);
/* apply new ctx cert to ssl */
ssl->verify_mode = ctx->verify_mode;
mbedtls_ssl_set_hs_ca_chain(&ssl_pm->ssl, x509_pm_ca->x509_crt, NULL);
mbedtls_ssl_set_hs_own_cert(&ssl_pm->ssl, x509_pm->x509_crt, pkey_pm->pkey);
mbedtls_ssl_set_hs_authmode(&ssl_pm->ssl, mode);
}

281
thirdparty/libwebsockets/tls/private.h vendored Normal file
View File

@ -0,0 +1,281 @@
/*
* 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
*
* This is included from core/private.h if LWS_WITH_TLS
*/
#if defined(LWS_WITH_TLS)
#if defined(USE_WOLFSSL)
#if defined(USE_OLD_CYASSL)
#if defined(_WIN32)
#include <IDE/WIN/user_settings.h>
#include <cyassl/ctaocrypt/settings.h>
#else
#include <cyassl/options.h>
#endif
#include <cyassl/openssl/ssl.h>
#include <cyassl/error-ssl.h>
#else
#if defined(_WIN32)
#include <IDE/WIN/user_settings.h>
#include <wolfssl/wolfcrypt/settings.h>
#else
#include <wolfssl/options.h>
#endif
#include <wolfssl/openssl/ssl.h>
#include <wolfssl/error-ssl.h>
#define OPENSSL_NO_TLSEXT
#endif /* not USE_OLD_CYASSL */
#else /* WOLFSSL */
#if defined(LWS_WITH_ESP32)
#define OPENSSL_NO_TLSEXT
#undef MBEDTLS_CONFIG_FILE
#define MBEDTLS_CONFIG_FILE <mbedtls/esp_config.h>
#include <mbedtls/ssl.h>
#include <mbedtls/x509_crt.h>
#include "tls/mbedtls/wrapper/include/openssl/ssl.h" /* wrapper !!!! */
#else /* not esp32 */
#if defined(LWS_WITH_MBEDTLS)
#include <mbedtls/ssl.h>
#include <mbedtls/x509_crt.h>
#include <mbedtls/x509_csr.h>
#include "tls/mbedtls/wrapper/include/openssl/ssl.h" /* wrapper !!!! */
#else
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#ifdef LWS_HAVE_OPENSSL_ECDH_H
#include <openssl/ecdh.h>
#endif
#include <openssl/x509v3.h>
#endif /* not mbedtls */
#if defined(OPENSSL_VERSION_NUMBER)
#if (OPENSSL_VERSION_NUMBER < 0x0009080afL)
/* later openssl defines this to negate the presence of tlsext... but it was only
* introduced at 0.9.8j. Earlier versions don't know it exists so don't
* define it... making it look like the feature exists...
*/
#define OPENSSL_NO_TLSEXT
#endif
#endif
#endif /* not ESP32 */
#endif /* not USE_WOLFSSL */
#endif /* LWS_WITH_TLS */
enum lws_tls_extant {
LWS_TLS_EXTANT_NO,
LWS_TLS_EXTANT_YES,
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)
typedef SSL lws_tls_conn;
typedef SSL_CTX lws_tls_ctx;
typedef BIO lws_tls_bio;
typedef X509 lws_tls_x509;
#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 *pending_read_list; /* linked list */
};
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 *pending_read_list_prev, *pending_read_list_next;
unsigned int use_ssl;
unsigned int redirect_to_https:1;
};
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_ssl_elaborate_error(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 char *cert_filepath,
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);
#endif

View File

@ -0,0 +1,150 @@
/*
* libwebsockets - client-related ssl code independent of backend
*
* Copyright (C) 2010-2017 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"
int
lws_ssl_client_connect1(struct lws *wsi)
{
struct lws_context *context = wsi->context;
int n = 0;
lws_latency_pre(context, wsi);
n = lws_tls_client_connect(wsi);
lws_latency(context, wsi, "SSL_connect hs", n, n > 0);
switch (n) {
case LWS_SSL_CAPABLE_ERROR:
return -1;
case LWS_SSL_CAPABLE_DONE:
return 1; /* connected */
case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
lws_callback_on_writable(wsi);
/* fallthru */
case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
lwsi_set_state(wsi, LRS_WAITING_SSL);
break;
case LWS_SSL_CAPABLE_MORE_SERVICE:
break;
}
return 0; /* retry */
}
int
lws_ssl_client_connect2(struct lws *wsi, char *errbuf, int len)
{
int n = 0;
if (lwsi_state(wsi) == LRS_WAITING_SSL) {
lws_latency_pre(wsi->context, wsi);
n = lws_tls_client_connect(wsi);
lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
lws_latency(wsi->context, wsi,
"SSL_connect LRS_WAITING_SSL", n, n > 0);
switch (n) {
case LWS_SSL_CAPABLE_ERROR:
lws_snprintf(errbuf, len, "client connect failed");
return -1;
case LWS_SSL_CAPABLE_DONE:
break; /* connected */
case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
lws_callback_on_writable(wsi);
/* fallthru */
case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
lwsi_set_state(wsi, LRS_WAITING_SSL);
/* fallthru */
case LWS_SSL_CAPABLE_MORE_SERVICE:
return 0;
}
}
if (lws_tls_client_confirm_peer_cert(wsi, errbuf, len))
return -1;
return 1;
}
int lws_context_init_client_ssl(const struct lws_context_creation_info *info,
struct lws_vhost *vhost)
{
const char *ca_filepath = info->ssl_ca_filepath;
const char *cipher_list = info->ssl_cipher_list;
const char *private_key_filepath = info->ssl_private_key_filepath;
const char *cert_filepath = info->ssl_cert_filepath;
struct lws wsi;
if (vhost->options & LWS_SERVER_OPTION_ONLY_RAW)
return 0;
/*
* for backwards-compatibility default to using ssl_... members, but
* if the newer client-specific ones are given, use those
*/
if (info->client_ssl_cipher_list)
cipher_list = info->client_ssl_cipher_list;
if (info->client_ssl_cert_filepath)
cert_filepath = info->client_ssl_cert_filepath;
if (info->client_ssl_private_key_filepath)
private_key_filepath = info->client_ssl_private_key_filepath;
if (info->client_ssl_ca_filepath)
ca_filepath = info->client_ssl_ca_filepath;
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
return 0;
if (vhost->tls.ssl_client_ctx)
return 0;
if (info->provided_client_ssl_ctx) {
/* use the provided OpenSSL context if given one */
vhost->tls.ssl_client_ctx = info->provided_client_ssl_ctx;
/* nothing for lib to delete */
vhost->tls.user_supplied_ssl_ctx = 1;
return 0;
}
if (lws_tls_client_create_vhost_context(vhost, info, cipher_list,
ca_filepath, cert_filepath,
private_key_filepath))
return 1;
lwsl_notice("created client ssl context for %s\n", vhost->name);
/*
* give him a fake wsi with context set, so he can use
* lws_get_context() in the callback
*/
memset(&wsi, 0, sizeof(wsi));
wsi.vhost = vhost;
wsi.context = vhost->context;
vhost->protocols[0].callback(&wsi,
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
vhost->tls.ssl_client_ctx, NULL, 0);
return 0;
}

View File

@ -0,0 +1,382 @@
/*
* 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"
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10002000L)
static int
alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg)
{
#if !defined(LWS_WITH_MBEDTLS)
struct alpn_ctx *alpn_ctx = (struct alpn_ctx *)arg;
if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_ctx->data,
alpn_ctx->len, in, inlen) !=
OPENSSL_NPN_NEGOTIATED)
return SSL_TLSEXT_ERR_NOACK;
#endif
return SSL_TLSEXT_ERR_OK;
}
#endif
void
lws_context_init_alpn(struct lws_vhost *vhost)
{
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10002000L)
const char *alpn_comma = vhost->context->tls.alpn_default;
if (vhost->tls.alpn)
alpn_comma = vhost->tls.alpn;
lwsl_info(" Server '%s' advertising ALPN: %s\n",
vhost->name, alpn_comma);
vhost->tls.alpn_ctx.len = lws_alpn_comma_to_openssl(alpn_comma,
vhost->tls.alpn_ctx.data,
sizeof(vhost->tls.alpn_ctx.data) - 1);
SSL_CTX_set_alpn_select_cb(vhost->tls.ssl_ctx, alpn_cb, &vhost->tls.alpn_ctx);
#else
lwsl_err(
" HTTP2 / ALPN configured but not supported by OpenSSL 0x%lx\n",
OPENSSL_VERSION_NUMBER);
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
}
int
lws_tls_server_conn_alpn(struct lws *wsi)
{
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10002000L)
const unsigned char *name = NULL;
char cstr[10];
unsigned len;
SSL_get0_alpn_selected(wsi->tls.ssl, &name, &len);
if (!len) {
lwsl_info("no ALPN upgrade\n");
return 0;
}
if (len > sizeof(cstr) - 1)
len = sizeof(cstr) - 1;
memcpy(cstr, name, len);
cstr[len] = '\0';
lwsl_info("negotiated '%s' using ALPN\n", cstr);
wsi->tls.use_ssl |= LCCSCF_USE_SSL;
return lws_role_call_alpn_negotiated(wsi, (const char *)cstr);
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
return 0;
}
LWS_VISIBLE int
lws_context_init_server_ssl(const struct lws_context_creation_info *info,
struct lws_vhost *vhost)
{
struct lws_context *context = vhost->context;
struct lws wsi;
if (!lws_check_opt(info->options,
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
vhost->tls.use_ssl = 0;
return 0;
}
/*
* If he is giving a cert filepath, take it as a sign he wants to use
* it on this vhost. User code can leave the cert filepath NULL and
* set the LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX option itself, in
* which case he's expected to set up the cert himself at
* LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
* provides the vhost SSL_CTX * in the user parameter.
*/
if (info->ssl_cert_filepath)
vhost->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX;
if (info->port != CONTEXT_PORT_NO_LISTEN) {
vhost->tls.use_ssl = lws_check_opt(vhost->options,
LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX);
if (vhost->tls.use_ssl && info->ssl_cipher_list)
lwsl_notice(" SSL ciphers: '%s'\n",
info->ssl_cipher_list);
if (vhost->tls.use_ssl)
lwsl_notice(" Using SSL mode\n");
else
lwsl_notice(" Using non-SSL mode\n");
}
/*
* give him a fake wsi with context + vhost set, so he can use
* lws_get_context() in the callback
*/
memset(&wsi, 0, sizeof(wsi));
wsi.vhost = vhost;
wsi.context = context;
/*
* as a server, if we are requiring clients to identify themselves
* then set the backend up for it
*/
if (lws_check_opt(info->options,
LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT))
/* Normally SSL listener rejects non-ssl, optionally allow */
vhost->tls.allow_non_ssl_on_ssl_port = 1;
/*
* give user code a chance to load certs into the server
* allowing it to verify incoming client certs
*/
if (vhost->tls.use_ssl) {
if (lws_tls_server_vhost_backend_init(info, vhost, &wsi))
return -1;
lws_tls_server_client_cert_verify_config(vhost);
if (vhost->protocols[0].callback(&wsi,
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS,
vhost->tls.ssl_ctx, vhost, 0))
return -1;
}
if (vhost->tls.use_ssl)
lws_context_init_alpn(vhost);
return 0;
}
LWS_VISIBLE int
lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
{
struct lws_context *context = wsi->context;
struct lws_vhost *vh;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n;
char buf[256];
(void)buf;
if (!LWS_SSL_ENABLED(wsi->vhost))
return 0;
switch (lwsi_state(wsi)) {
case LRS_SSL_INIT:
if (wsi->tls.ssl)
lwsl_err("%s: leaking ssl\n", __func__);
if (accept_fd == LWS_SOCK_INVALID)
assert(0);
if (context->simultaneous_ssl_restriction &&
context->simultaneous_ssl >=
context->simultaneous_ssl_restriction) {
lwsl_notice("unable to deal with SSL connection\n");
return 1;
}
if (lws_tls_server_new_nonblocking(wsi, accept_fd)) {
if (accept_fd != LWS_SOCK_INVALID)
compatible_close(accept_fd);
goto fail;
}
if (context->simultaneous_ssl_restriction &&
++context->simultaneous_ssl ==
context->simultaneous_ssl_restriction)
/* that was the last allowed SSL connection */
lws_gate_accepts(context, 0);
#if defined(LWS_WITH_STATS)
context->updated = 1;
#endif
/*
* we are not accepted yet, but we need to enter ourselves
* as a live connection. That way we can retry when more
* pieces come if we're not sorted yet
*/
lwsi_set_state(wsi, LRS_SSL_ACK_PENDING);
lws_pt_lock(pt, __func__);
if (__insert_wsi_socket_into_fds(context, wsi)) {
lwsl_err("%s: failed to insert into fds\n", __func__);
goto fail;
}
lws_pt_unlock(pt);
lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
context->timeout_secs);
lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n");
/* fallthru */
case LRS_SSL_ACK_PENDING:
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_err("%s: lws_change_pollfd failed\n", __func__);
goto fail;
}
lws_latency_pre(context, wsi);
if (wsi->vhost->tls.allow_non_ssl_on_ssl_port) {
n = recv(wsi->desc.sockfd, (char *)pt->serv_buf,
context->pt_serv_buf_size, MSG_PEEK);
/*
* optionally allow non-SSL connect on SSL listening socket
* This is disabled by default, if enabled it goes around any
* SSL-level access control (eg, client-side certs) so leave
* it disabled unless you know it's not a problem for you
*/
if (n >= 1 && pt->serv_buf[0] >= ' ') {
/*
* TLS content-type for Handshake is 0x16, and
* for ChangeCipherSpec Record, it's 0x14
*
* A non-ssl session will start with the HTTP
* method in ASCII. If we see it's not a legit
* SSL handshake kill the SSL for this
* connection and try to handle as a HTTP
* connection upgrade directly.
*/
wsi->tls.use_ssl = 0;
lws_tls_server_abort_connection(wsi);
/*
* care... this creates wsi with no ssl
* when ssl is enabled and normally
* mandatory
*/
wsi->tls.ssl = NULL;
if (lws_check_opt(context->options,
LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS))
wsi->tls.redirect_to_https = 1;
lwsl_debug("accepted as non-ssl\n");
goto accepted;
}
if (!n) {
/*
* connection is gone, fail out
*/
lwsl_debug("PEEKed 0\n");
goto fail;
}
if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK)) {
/*
* well, we get no way to know ssl or not
* so go around again waiting for something
* to come and give us a hint, or timeout the
* connection.
*/
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
lwsl_info("%s: change_pollfd failed\n",
__func__);
return -1;
}
lwsl_info("SSL_ERROR_WANT_READ\n");
return 0;
}
}
/* normal SSL connection processing path */
#if defined(LWS_WITH_STATS)
if (!wsi->accept_start_us)
wsi->accept_start_us = time_in_microseconds();
#endif
errno = 0;
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1);
n = lws_tls_server_accept(wsi);
lws_latency(context, wsi,
"SSL_accept LRS_SSL_ACK_PENDING\n", n, n == 1);
lwsl_info("SSL_accept says %d\n", n);
switch (n) {
case LWS_SSL_CAPABLE_DONE:
break;
case LWS_SSL_CAPABLE_ERROR:
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
lwsl_info("SSL_accept failed socket %u: %d\n",
wsi->desc.sockfd, n);
wsi->socket_is_permanently_unusable = 1;
goto fail;
default: /* MORE_SERVICE */
return 0;
}
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1);
#if defined(LWS_WITH_STATS)
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY,
time_in_microseconds() - wsi->accept_start_us);
wsi->accept_start_us = time_in_microseconds();
#endif
accepted:
/* adapt our vhost to match the SNI SSL_CTX that was chosen */
vh = context->vhost_list;
while (vh) {
if (!vh->being_destroyed && wsi->tls.ssl &&
vh->tls.ssl_ctx == lws_tls_ctx_from_wsi(wsi)) {
lwsl_info("setting wsi to vh %s\n", vh->name);
wsi->vhost = vh;
break;
}
vh = vh->vhost_next;
}
/* OK, we are accepted... give him some time to negotiate */
lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
context->timeout_secs);
lwsi_set_state(wsi, LRS_ESTABLISHED);
if (lws_tls_server_conn_alpn(wsi))
goto fail;
lwsl_debug("accepted new SSL conn\n");
break;
default:
break;
}
return 0;
fail:
return 1;
}

522
thirdparty/libwebsockets/tls/tls.c vendored Normal file
View File

@ -0,0 +1,522 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 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)
{
struct lws *wsi, *wsi_next;
int ret = 0;
wsi = pt->tls.pending_read_list;
while (wsi && wsi->position_in_fds_table != LWS_NO_FDS_POS) {
wsi_next = wsi->tls.pending_read_list_next;
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;
wsi = wsi_next;
}
return !!ret;
}
void
__lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
if (!wsi->tls.pending_read_list_prev &&
!wsi->tls.pending_read_list_next &&
pt->tls.pending_read_list != wsi)
/* we are not on the list */
return;
/* point previous guy's next to our next */
if (!wsi->tls.pending_read_list_prev)
pt->tls.pending_read_list = wsi->tls.pending_read_list_next;
else
wsi->tls.pending_read_list_prev->tls.pending_read_list_next =
wsi->tls.pending_read_list_next;
/* point next guy's previous to our previous */
if (wsi->tls.pending_read_list_next)
wsi->tls.pending_read_list_next->tls.pending_read_list_prev =
wsi->tls.pending_read_list_prev;
wsi->tls.pending_read_list_prev = NULL;
wsi->tls.pending_read_list_next = NULL;
}
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);
}
#if defined(LWS_WITH_ESP32)
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
nvs_handle nvh;
size_t s;
int n = 0;
ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
n = 1;
goto bail;
}
*buf = lws_malloc(s + 1, "alloc_file");
if (!*buf) {
n = 2;
goto bail;
}
if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
lws_free(*buf);
n = 1;
goto bail;
}
*amount = s;
(*buf)[s] = '\0';
lwsl_notice("%s: nvs: read %s, %d bytes\n", __func__, filename, (int)s);
bail:
nvs_close(nvh);
return n;
}
#else
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
{
FILE *f;
size_t s;
int n = 0;
f = fopen(filename, "rb");
if (f == NULL) {
n = 1;
goto bail;
}
if (fseek(f, 0, SEEK_END) != 0) {
n = 1;
goto bail;
}
s = ftell(f);
if (s == (size_t)-1) {
n = 1;
goto bail;
}
if (fseek(f, 0, SEEK_SET) != 0) {
n = 1;
goto bail;
}
*buf = lws_malloc(s, "alloc_file");
if (!*buf) {
n = 2;
goto bail;
}
if (fread(*buf, s, 1, f) != 1) {
lws_free(*buf);
n = 1;
goto bail;
}
*amount = s;
bail:
if (f)
fclose(f);
return n;
}
#endif
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)
{
const uint8_t *pem, *p, *end;
uint8_t *q;
lws_filepos_t len;
int n;
if (filename) {
n = alloc_file(context, filename, (uint8_t **)&pem, &len);
if (n)
return n;
} else {
pem = (const uint8_t *)inbuf;
len = inlen;
}
/* trim the first line */
p = pem;
end = p + len;
if (strncmp((char *)p, "-----", 5))
goto bail;
p += 5;
while (p < end && *p != '\n' && *p != '-')
p++;
if (*p != '-')
goto bail;
while (p < end && *p != '\n')
p++;
if (p >= end)
goto bail;
p++;
/* trim the last line */
q = (uint8_t *)end - 2;
while (q > pem && *q != '\n')
q--;
if (*q != '\n')
goto bail;
*q = '\0';
*amount = lws_b64_decode_string((char *)p, (char *)pem,
(int)(long long)len);
*buf = (uint8_t *)pem;
return 0;
bail:
lws_free((uint8_t *)pem);
return 4;
}
int
lws_tls_check_cert_lifetime(struct lws_vhost *v)
{
union lws_tls_cert_info_results ir;
time_t now = (time_t)lws_now_secs(), life = 0;
struct lws_acme_cert_aging_args caa;
int n;
if (v->tls.ssl_ctx && !v->tls.skipped_certs) {
if (now < 1464083026) /* May 2016 */
/* 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)
{
/* it exists if we can open it... */
int fd = open(name, O_RDONLY), n;
char buf[1];
if (fd < 0)
return 1;
/* and we can read at least one byte out of it */
n = read(fd, buf, 1);
close(fd);
return n != 1;
}
#endif
/*
* Returns 0 if the filepath "name" exists and can be read from.
*
* In addition, if "name".upd exists, backup "name" to "name.old.1"
* and rename "name".upd to "name" before reporting its existence.
*
* There are four situations and three results possible:
*
* 1) LWS_TLS_EXTANT_NO: There are no certs at all (we are waiting for them to
* be provisioned). We also feel like this if we need privs we don't have
* any more to look in the directory.
*
* 2) There are provisioned certs written (xxx.upd) and we still have root
* privs... in this case we rename any existing cert to have a backup name
* and move the upd cert into place with the correct name. This then becomes
* situation 4 for the caller.
*
* 3) LWS_TLS_EXTANT_ALTERNATIVE: There are provisioned certs written (xxx.upd)
* but we no longer have the privs needed to read or rename them. In this
* case, indicate that the caller should use temp copies if any we do have
* rights to access. This is normal after we have updated the cert.
*
* But if we dropped privs, we can't detect the provisioned xxx.upd cert +
* key, because we can't see in the dir. So we have to upgrade NO to
* ALTERNATIVE when we actually have the in-memory alternative.
*
* 4) LWS_TLS_EXTANT_YES: The certs are present with the correct name and we
* have the rights to read them.
*/
enum lws_tls_extant
lws_tls_use_any_upgrade_check_extant(const char *name)
{
#if !defined(LWS_PLAT_OPTEE)
int n;
#if !defined(LWS_WITH_ESP32)
char buf[256];
lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
if (!lws_tls_extant(buf)) {
/* ah there is an updated file... how about the desired file? */
if (!lws_tls_extant(name)) {
/* rename the desired file */
for (n = 0; n < 50; n++) {
lws_snprintf(buf, sizeof(buf) - 1,
"%s.old.%d", name, n);
if (!rename(name, buf))
break;
}
if (n == 50) {
lwsl_notice("unable to rename %s\n", name);
return LWS_TLS_EXTANT_ALTERNATIVE;
}
lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
}
/* desired file is out of the way, rename the updated file */
if (rename(buf, name)) {
lwsl_notice("unable to rename %s to %s\n", buf, name);
return LWS_TLS_EXTANT_ALTERNATIVE;
}
}
if (lws_tls_extant(name))
return LWS_TLS_EXTANT_NO;
#else
nvs_handle nvh;
size_t s = 8192;
if (nvs_open("lws-station", NVS_READWRITE, &nvh)) {
lwsl_notice("%s: can't open nvs\n", __func__);
return LWS_TLS_EXTANT_NO;
}
n = nvs_get_blob(nvh, name, NULL, &s);
nvs_close(nvh);
if (n)
return LWS_TLS_EXTANT_NO;
#endif
#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;
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

@ -38,6 +38,9 @@
#include <string.h> #include <string.h>
#include "getopt.h" #include "getopt.h"
#define lws_ptr_diff(head, tail) \
((int)((char *)(head) - (char *)(tail)))
extern int opterr; /* if error message should be printed */ extern int opterr; /* if error message should be printed */
extern int optind; /* index into parent argv vector */ extern int optind; /* index into parent argv vector */
extern int optopt; /* character checked for validity */ extern int optopt; /* character checked for validity */
@ -183,10 +186,10 @@ getopt_long(nargc, nargv, options, long_options, index)
return(-1); return(-1);
} }
if ((has_equal = strchr(current_argv, '=')) != NULL) { if ((has_equal = strchr(current_argv, '=')) != NULL) {
current_argv_len = has_equal - current_argv; current_argv_len = lws_ptr_diff(has_equal, current_argv);
has_equal++; has_equal++;
} else } else
current_argv_len = strlen(current_argv); current_argv_len = (int)strlen(current_argv);
for (i = 0; long_options[i].name; i++) { for (i = 0; long_options[i].name; i++) {
if (strncmp(current_argv, long_options[i].name, current_argv_len)) if (strncmp(current_argv, long_options[i].name, current_argv_len))