Bump libwebsockets to version 3.0.0
This commit is contained in:
parent
9d23f1bf1a
commit
e56a3c1dc4
@ -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
|
||||||
|
@ -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']:
|
||||||
|
@ -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
22
thirdparty/README.md
vendored
@ -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.
|
||||||
|
@ -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!
|
||||||
|
|
1304
thirdparty/libwebsockets/client/client.c
vendored
1304
thirdparty/libwebsockets/client/client.c
vendored
File diff suppressed because it is too large
Load Diff
625
thirdparty/libwebsockets/client/ssl-client.c
vendored
625
thirdparty/libwebsockets/client/ssl-client.c
vendored
@ -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;
|
|
||||||
}
|
|
@ -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);
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
308
thirdparty/libwebsockets/core/output.c
vendored
Normal file
308
thirdparty/libwebsockets/core/output.c
vendored
Normal 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
|
||||||
|
}
|
@ -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
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
987
thirdparty/libwebsockets/core/service.c
vendored
Normal 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;
|
||||||
|
}
|
43
thirdparty/libwebsockets/event-libs/poll/poll.c
vendored
Normal file
43
thirdparty/libwebsockets/event-libs/poll/poll.c
vendored
Normal 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,
|
||||||
|
};
|
23
thirdparty/libwebsockets/event-libs/poll/private.h
vendored
Normal file
23
thirdparty/libwebsockets/event-libs/poll/private.h
vendored
Normal 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;
|
74
thirdparty/libwebsockets/event-libs/private.h
vendored
Normal file
74
thirdparty/libwebsockets/event-libs/private.h
vendored
Normal 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
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
344
thirdparty/libwebsockets/ext/extension.c
vendored
344
thirdparty/libwebsockets/ext/extension.c
vendored
@ -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);
|
|
||||||
}
|
|
280
thirdparty/libwebsockets/handshake.c
vendored
280
thirdparty/libwebsockets/handshake.c
vendored
@ -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;
|
|
||||||
}
|
|
805
thirdparty/libwebsockets/lextable.h
vendored
805
thirdparty/libwebsockets/lextable.h
vendored
@ -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 */
|
|
2818
thirdparty/libwebsockets/libwebsockets.h
vendored
2818
thirdparty/libwebsockets/libwebsockets.h
vendored
File diff suppressed because it is too large
Load Diff
50
thirdparty/libwebsockets/lws_config.h
vendored
50
thirdparty/libwebsockets/lws_config.h
vendored
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
74
thirdparty/libwebsockets/mbedtls_verify.diff
vendored
74
thirdparty/libwebsockets/mbedtls_verify.diff
vendored
@ -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;
|
|
||||||
}
|
|
56
thirdparty/libwebsockets/misc/base64-decode.c
vendored
56
thirdparty/libwebsockets/misc/base64-decode.c
vendored
@ -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)
|
||||||
|
2
thirdparty/libwebsockets/misc/getifaddrs.c
vendored
2
thirdparty/libwebsockets/misc/getifaddrs.c
vendored
@ -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>
|
||||||
|
59
thirdparty/libwebsockets/misc/lejp.c
vendored
59
thirdparty/libwebsockets/misc/lejp.c
vendored
@ -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 */
|
||||||
|
232
thirdparty/libwebsockets/misc/lejp.h
vendored
232
thirdparty/libwebsockets/misc/lejp.h
vendored
@ -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);
|
|
2
thirdparty/libwebsockets/misc/sha-1.c
vendored
2
thirdparty/libwebsockets/misc/sha-1.c
vendored
@ -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>
|
||||||
|
883
thirdparty/libwebsockets/output.c
vendored
883
thirdparty/libwebsockets/output.c
vendored
@ -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
|
|
||||||
}
|
|
305
thirdparty/libwebsockets/plat/lws-plat-unix.c
vendored
305
thirdparty/libwebsockets/plat/lws-plat-unix.c
vendored
@ -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;
|
||||||
|
}
|
||||||
|
210
thirdparty/libwebsockets/plat/lws-plat-win.c
vendored
210
thirdparty/libwebsockets/plat/lws-plat-win.c
vendored
@ -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;
|
||||||
|
}
|
||||||
|
2615
thirdparty/libwebsockets/private-libwebsockets.h
vendored
2615
thirdparty/libwebsockets/private-libwebsockets.h
vendored
File diff suppressed because it is too large
Load Diff
687
thirdparty/libwebsockets/roles/h1/ops-h1.c
vendored
Normal file
687
thirdparty/libwebsockets/roles/h1/ops-h1.c
vendored
Normal 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,
|
||||||
|
};
|
27
thirdparty/libwebsockets/roles/h1/private.h
vendored
Normal file
27
thirdparty/libwebsockets/roles/h1/private.h
vendored
Normal 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)
|
@ -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:
|
1231
thirdparty/libwebsockets/roles/http/client/client.c
vendored
Normal file
1231
thirdparty/libwebsockets/roles/http/client/client.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
|
||||||
}
|
}
|
@ -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 */
|
||||||
|
|
838
thirdparty/libwebsockets/roles/http/lextable.h
vendored
Normal file
838
thirdparty/libwebsockets/roles/http/lextable.h
vendored
Normal 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 */
|
257
thirdparty/libwebsockets/roles/http/private.h
vendored
Normal file
257
thirdparty/libwebsockets/roles/http/private.h
vendored
Normal 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);
|
182
thirdparty/libwebsockets/roles/http/server/access-log.c
vendored
Normal file
182
thirdparty/libwebsockets/roles/http/server/access-log.c
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -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:
|
||||||
|
|
@ -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);
|
1139
thirdparty/libwebsockets/roles/http/server/parsers.c
vendored
Normal file
1139
thirdparty/libwebsockets/roles/http/server/parsers.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
177
thirdparty/libwebsockets/roles/listen/ops-listen.c
vendored
Normal file
177
thirdparty/libwebsockets/roles/listen/ops-listen.c
vendored
Normal 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,
|
||||||
|
};
|
81
thirdparty/libwebsockets/roles/pipe/ops-pipe.c
vendored
Normal file
81
thirdparty/libwebsockets/roles/pipe/ops-pipe.c
vendored
Normal 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
282
thirdparty/libwebsockets/roles/private.h
vendored
Normal 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
|
||||||
|
};
|
223
thirdparty/libwebsockets/roles/raw/ops-raw.c
vendored
Normal file
223
thirdparty/libwebsockets/roles/raw/ops-raw.c
vendored
Normal 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,
|
||||||
|
};
|
@ -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");
|
629
thirdparty/libwebsockets/roles/ws/client-ws.c
vendored
Normal file
629
thirdparty/libwebsockets/roles/ws/client-ws.c
vendored
Normal 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
1992
thirdparty/libwebsockets/roles/ws/ops-ws.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
164
thirdparty/libwebsockets/roles/ws/private.h
vendored
Normal file
164
thirdparty/libwebsockets/roles/ws/private.h
vendored
Normal 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);
|
836
thirdparty/libwebsockets/roles/ws/server-ws.c
vendored
Normal file
836
thirdparty/libwebsockets/roles/ws/server-ws.c
vendored
Normal 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;
|
||||||
|
}
|
1783
thirdparty/libwebsockets/server/parsers.c
vendored
1783
thirdparty/libwebsockets/server/parsers.c
vendored
File diff suppressed because it is too large
Load Diff
214
thirdparty/libwebsockets/server/ranges.c
vendored
214
thirdparty/libwebsockets/server/ranges.c
vendored
@ -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;
|
|
||||||
}
|
|
360
thirdparty/libwebsockets/server/server-handshake.c
vendored
360
thirdparty/libwebsockets/server/server-handshake.c
vendored
@ -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;
|
|
||||||
}
|
|
||||||
|
|
477
thirdparty/libwebsockets/server/ssl-server.c
vendored
477
thirdparty/libwebsockets/server/ssl-server.c
vendored
@ -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;
|
|
||||||
}
|
|
||||||
|
|
1714
thirdparty/libwebsockets/service.c
vendored
1714
thirdparty/libwebsockets/service.c
vendored
File diff suppressed because it is too large
Load Diff
973
thirdparty/libwebsockets/ssl.c
vendored
973
thirdparty/libwebsockets/ssl.c
vendored
@ -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
|
|
||||||
}
|
|
202
thirdparty/libwebsockets/tls/mbedtls/lws-genhash.c
vendored
Normal file
202
thirdparty/libwebsockets/tls/mbedtls/lws-genhash.c
vendored
Normal 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;
|
||||||
|
}
|
329
thirdparty/libwebsockets/tls/mbedtls/lws-genrsa.c
vendored
Normal file
329
thirdparty/libwebsockets/tls/mbedtls/lws-genrsa.c
vendored
Normal 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;
|
||||||
|
}
|
240
thirdparty/libwebsockets/tls/mbedtls/mbedtls-client.c
vendored
Normal file
240
thirdparty/libwebsockets/tls/mbedtls/mbedtls-client.c
vendored
Normal 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;
|
||||||
|
}
|
694
thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c
vendored
Normal file
694
thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c
vendored
Normal 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
|
520
thirdparty/libwebsockets/tls/mbedtls/ssl.c
vendored
Normal file
520
thirdparty/libwebsockets/tls/mbedtls/ssl.c
vendored
Normal 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,
|
||||||
|
};
|
@ -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 */
|
17
thirdparty/libwebsockets/mbedtls_wrapper/include/openssl/ssl.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/openssl/ssl.h
vendored
Normal file → Executable file
17
thirdparty/libwebsockets/mbedtls_wrapper/include/openssl/ssl.h → thirdparty/libwebsockets/tls/mbedtls/wrapper/include/openssl/ssl.h
vendored
Normal file → Executable 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
|
@ -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);
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
@ -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
|
||||||
*/
|
*/
|
222
thirdparty/libwebsockets/mbedtls_wrapper/platform/ssl_pm.c → thirdparty/libwebsockets/tls/mbedtls/wrapper/platform/ssl_pm.c
vendored
Normal file → Executable file
222
thirdparty/libwebsockets/mbedtls_wrapper/platform/ssl_pm.c → thirdparty/libwebsockets/tls/mbedtls/wrapper/platform/ssl_pm.c
vendored
Normal file → Executable 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
281
thirdparty/libwebsockets/tls/private.h
vendored
Normal 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
|
150
thirdparty/libwebsockets/tls/tls-client.c
vendored
Normal file
150
thirdparty/libwebsockets/tls/tls-client.c
vendored
Normal 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;
|
||||||
|
}
|
382
thirdparty/libwebsockets/tls/tls-server.c
vendored
Normal file
382
thirdparty/libwebsockets/tls/tls-server.c
vendored
Normal 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
522
thirdparty/libwebsockets/tls/tls.c
vendored
Normal 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);
|
||||||
|
}
|
||||||
|
|
306
thirdparty/libwebsockets/win32helpers/getopt.c
vendored
306
thirdparty/libwebsockets/win32helpers/getopt.c
vendored
@ -1,153 +1,153 @@
|
|||||||
/* $NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $ */
|
/* $NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1987, 1993, 1994
|
* Copyright (c) 1987, 1993, 1994
|
||||||
* The Regents of the University of California. All rights reserved.
|
* The Regents of the University of California. All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
* are met:
|
* are met:
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
* notice, this list of conditions and the following disclaimer.
|
* notice, this list of conditions and the following disclaimer.
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
* documentation and/or other materials provided with the distribution.
|
* documentation and/or other materials provided with the distribution.
|
||||||
* 3. All advertising materials mentioning features or use of this software
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
* must display the following acknowledgement:
|
* must display the following acknowledgement:
|
||||||
* This product includes software developed by the University of
|
* This product includes software developed by the University of
|
||||||
* California, Berkeley and its contributors.
|
* California, Berkeley and its contributors.
|
||||||
* 4. Neither the name of the University nor the names of its contributors
|
* 4. Neither the name of the University nor the names of its contributors
|
||||||
* may be used to endorse or promote products derived from this software
|
* may be used to endorse or promote products derived from this software
|
||||||
* without specific prior written permission.
|
* without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
|
static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define __P(x) x
|
#define __P(x) x
|
||||||
#define _DIAGASSERT(x) assert(x)
|
#define _DIAGASSERT(x) assert(x)
|
||||||
|
|
||||||
#ifdef __weak_alias
|
#ifdef __weak_alias
|
||||||
__weak_alias(getopt,_getopt);
|
__weak_alias(getopt,_getopt);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
int opterr = 1, /* if error message should be printed */
|
int opterr = 1, /* if error message should be printed */
|
||||||
optind = 1, /* index into parent argv vector */
|
optind = 1, /* index into parent argv vector */
|
||||||
optopt, /* character checked for validity */
|
optopt, /* character checked for validity */
|
||||||
optreset; /* reset getopt */
|
optreset; /* reset getopt */
|
||||||
char *optarg; /* argument associated with option */
|
char *optarg; /* argument associated with option */
|
||||||
|
|
||||||
static char * _progname __P((char *));
|
static char * _progname __P((char *));
|
||||||
int getopt_internal __P((int, char * const *, const char *));
|
int getopt_internal __P((int, char * const *, const char *));
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
_progname(nargv0)
|
_progname(nargv0)
|
||||||
char * nargv0;
|
char * nargv0;
|
||||||
{
|
{
|
||||||
char * tmp;
|
char * tmp;
|
||||||
|
|
||||||
_DIAGASSERT(nargv0 != NULL);
|
_DIAGASSERT(nargv0 != NULL);
|
||||||
|
|
||||||
tmp = strrchr(nargv0, '/');
|
tmp = strrchr(nargv0, '/');
|
||||||
if (tmp)
|
if (tmp)
|
||||||
tmp++;
|
tmp++;
|
||||||
else
|
else
|
||||||
tmp = nargv0;
|
tmp = nargv0;
|
||||||
return(tmp);
|
return(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BADCH (int)'?'
|
#define BADCH (int)'?'
|
||||||
#define BADARG (int)':'
|
#define BADARG (int)':'
|
||||||
#define EMSG ""
|
#define EMSG ""
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* getopt --
|
* getopt --
|
||||||
* Parse argc/argv argument vector.
|
* Parse argc/argv argument vector.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
getopt(nargc, nargv, ostr)
|
getopt(nargc, nargv, ostr)
|
||||||
int nargc;
|
int nargc;
|
||||||
char * const nargv[];
|
char * const nargv[];
|
||||||
const char *ostr;
|
const char *ostr;
|
||||||
{
|
{
|
||||||
static char *__progname = 0;
|
static char *__progname = 0;
|
||||||
static char *place = EMSG; /* option letter processing */
|
static char *place = EMSG; /* option letter processing */
|
||||||
char *oli; /* option letter list index */
|
char *oli; /* option letter list index */
|
||||||
__progname = __progname?__progname:_progname(*nargv);
|
__progname = __progname?__progname:_progname(*nargv);
|
||||||
|
|
||||||
_DIAGASSERT(nargv != NULL);
|
_DIAGASSERT(nargv != NULL);
|
||||||
_DIAGASSERT(ostr != NULL);
|
_DIAGASSERT(ostr != NULL);
|
||||||
|
|
||||||
if (optreset || !*place) { /* update scanning pointer */
|
if (optreset || !*place) { /* update scanning pointer */
|
||||||
optreset = 0;
|
optreset = 0;
|
||||||
if (optind >= nargc || *(place = nargv[optind]) != '-') {
|
if (optind >= nargc || *(place = nargv[optind]) != '-') {
|
||||||
place = EMSG;
|
place = EMSG;
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (place[1] && *++place == '-' /* found "--" */
|
if (place[1] && *++place == '-' /* found "--" */
|
||||||
&& place[1] == '\0') {
|
&& place[1] == '\0') {
|
||||||
++optind;
|
++optind;
|
||||||
place = EMSG;
|
place = EMSG;
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
} /* option letter okay? */
|
} /* option letter okay? */
|
||||||
if ((optopt = (int)*place++) == (int)':' ||
|
if ((optopt = (int)*place++) == (int)':' ||
|
||||||
!(oli = strchr(ostr, optopt))) {
|
!(oli = strchr(ostr, optopt))) {
|
||||||
/*
|
/*
|
||||||
* if the user didn't specify '-' as an option,
|
* if the user didn't specify '-' as an option,
|
||||||
* assume it means -1.
|
* assume it means -1.
|
||||||
*/
|
*/
|
||||||
if (optopt == (int)'-')
|
if (optopt == (int)'-')
|
||||||
return (-1);
|
return (-1);
|
||||||
if (!*place)
|
if (!*place)
|
||||||
++optind;
|
++optind;
|
||||||
if (opterr && *ostr != ':')
|
if (opterr && *ostr != ':')
|
||||||
(void)fprintf(stderr,
|
(void)fprintf(stderr,
|
||||||
"%s: illegal option -- %c\n", __progname, optopt);
|
"%s: illegal option -- %c\n", __progname, optopt);
|
||||||
return (BADCH);
|
return (BADCH);
|
||||||
}
|
}
|
||||||
if (*++oli != ':') { /* don't need argument */
|
if (*++oli != ':') { /* don't need argument */
|
||||||
optarg = NULL;
|
optarg = NULL;
|
||||||
if (!*place)
|
if (!*place)
|
||||||
++optind;
|
++optind;
|
||||||
}
|
}
|
||||||
else { /* need an argument */
|
else { /* need an argument */
|
||||||
if (*place) /* no white space */
|
if (*place) /* no white space */
|
||||||
optarg = place;
|
optarg = place;
|
||||||
else if (nargc <= ++optind) { /* no arg */
|
else if (nargc <= ++optind) { /* no arg */
|
||||||
place = EMSG;
|
place = EMSG;
|
||||||
if (*ostr == ':')
|
if (*ostr == ':')
|
||||||
return (BADARG);
|
return (BADARG);
|
||||||
if (opterr)
|
if (opterr)
|
||||||
(void)fprintf(stderr,
|
(void)fprintf(stderr,
|
||||||
"%s: option requires an argument -- %c\n",
|
"%s: option requires an argument -- %c\n",
|
||||||
__progname, optopt);
|
__progname, optopt);
|
||||||
return (BADCH);
|
return (BADCH);
|
||||||
}
|
}
|
||||||
else /* white space */
|
else /* white space */
|
||||||
optarg = nargv[optind];
|
optarg = nargv[optind];
|
||||||
place = EMSG;
|
place = EMSG;
|
||||||
++optind;
|
++optind;
|
||||||
}
|
}
|
||||||
return (optopt); /* dump back option letter */
|
return (optopt); /* dump back option letter */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
477
thirdparty/libwebsockets/win32helpers/getopt_long.c
vendored
477
thirdparty/libwebsockets/win32helpers/getopt_long.c
vendored
@ -1,237 +1,240 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1987, 1993, 1994, 1996
|
* Copyright (c) 1987, 1993, 1994, 1996
|
||||||
* The Regents of the University of California. All rights reserved.
|
* The Regents of the University of California. All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
* are met:
|
* are met:
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
* notice, this list of conditions and the following disclaimer.
|
* notice, this list of conditions and the following disclaimer.
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
* documentation and/or other materials provided with the distribution.
|
* documentation and/or other materials provided with the distribution.
|
||||||
* 3. All advertising materials mentioning features or use of this software
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
* must display the following acknowledgement:
|
* must display the following acknowledgement:
|
||||||
* This product includes software developed by the University of
|
* This product includes software developed by the University of
|
||||||
* California, Berkeley and its contributors.
|
* California, Berkeley and its contributors.
|
||||||
* 4. Neither the name of the University nor the names of its contributors
|
* 4. Neither the name of the University nor the names of its contributors
|
||||||
* may be used to endorse or promote products derived from this software
|
* may be used to endorse or promote products derived from this software
|
||||||
* without specific prior written permission.
|
* without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "getopt.h"
|
#include "getopt.h"
|
||||||
|
|
||||||
extern int opterr; /* if error message should be printed */
|
#define lws_ptr_diff(head, tail) \
|
||||||
extern int optind; /* index into parent argv vector */
|
((int)((char *)(head) - (char *)(tail)))
|
||||||
extern int optopt; /* character checked for validity */
|
|
||||||
extern int optreset; /* reset getopt */
|
extern int opterr; /* if error message should be printed */
|
||||||
extern char *optarg; /* argument associated with option */
|
extern int optind; /* index into parent argv vector */
|
||||||
|
extern int optopt; /* character checked for validity */
|
||||||
#define __P(x) x
|
extern int optreset; /* reset getopt */
|
||||||
#define _DIAGASSERT(x) assert(x)
|
extern char *optarg; /* argument associated with option */
|
||||||
|
|
||||||
static char * __progname __P((char *));
|
#define __P(x) x
|
||||||
int getopt_internal __P((int, char * const *, const char *));
|
#define _DIAGASSERT(x) assert(x)
|
||||||
|
|
||||||
static char *
|
static char * __progname __P((char *));
|
||||||
__progname(nargv0)
|
int getopt_internal __P((int, char * const *, const char *));
|
||||||
char * nargv0;
|
|
||||||
{
|
static char *
|
||||||
char * tmp;
|
__progname(nargv0)
|
||||||
|
char * nargv0;
|
||||||
_DIAGASSERT(nargv0 != NULL);
|
{
|
||||||
|
char * tmp;
|
||||||
tmp = strrchr(nargv0, '/');
|
|
||||||
if (tmp)
|
_DIAGASSERT(nargv0 != NULL);
|
||||||
tmp++;
|
|
||||||
else
|
tmp = strrchr(nargv0, '/');
|
||||||
tmp = nargv0;
|
if (tmp)
|
||||||
return(tmp);
|
tmp++;
|
||||||
}
|
else
|
||||||
|
tmp = nargv0;
|
||||||
#define BADCH (int)'?'
|
return(tmp);
|
||||||
#define BADARG (int)':'
|
}
|
||||||
#define EMSG ""
|
|
||||||
|
#define BADCH (int)'?'
|
||||||
/*
|
#define BADARG (int)':'
|
||||||
* getopt --
|
#define EMSG ""
|
||||||
* Parse argc/argv argument vector.
|
|
||||||
*/
|
/*
|
||||||
int
|
* getopt --
|
||||||
getopt_internal(nargc, nargv, ostr)
|
* Parse argc/argv argument vector.
|
||||||
int nargc;
|
*/
|
||||||
char * const *nargv;
|
int
|
||||||
const char *ostr;
|
getopt_internal(nargc, nargv, ostr)
|
||||||
{
|
int nargc;
|
||||||
static char *place = EMSG; /* option letter processing */
|
char * const *nargv;
|
||||||
char *oli; /* option letter list index */
|
const char *ostr;
|
||||||
|
{
|
||||||
_DIAGASSERT(nargv != NULL);
|
static char *place = EMSG; /* option letter processing */
|
||||||
_DIAGASSERT(ostr != NULL);
|
char *oli; /* option letter list index */
|
||||||
|
|
||||||
if (optreset || !*place) { /* update scanning pointer */
|
_DIAGASSERT(nargv != NULL);
|
||||||
optreset = 0;
|
_DIAGASSERT(ostr != NULL);
|
||||||
if (optind >= nargc || *(place = nargv[optind]) != '-') {
|
|
||||||
place = EMSG;
|
if (optreset || !*place) { /* update scanning pointer */
|
||||||
return (-1);
|
optreset = 0;
|
||||||
}
|
if (optind >= nargc || *(place = nargv[optind]) != '-') {
|
||||||
if (place[1] && *++place == '-') { /* found "--" */
|
place = EMSG;
|
||||||
/* ++optind; */
|
return (-1);
|
||||||
place = EMSG;
|
}
|
||||||
return (-2);
|
if (place[1] && *++place == '-') { /* found "--" */
|
||||||
}
|
/* ++optind; */
|
||||||
} /* option letter okay? */
|
place = EMSG;
|
||||||
if ((optopt = (int)*place++) == (int)':' ||
|
return (-2);
|
||||||
!(oli = strchr(ostr, optopt))) {
|
}
|
||||||
/*
|
} /* option letter okay? */
|
||||||
* if the user didn't specify '-' as an option,
|
if ((optopt = (int)*place++) == (int)':' ||
|
||||||
* assume it means -1.
|
!(oli = strchr(ostr, optopt))) {
|
||||||
*/
|
/*
|
||||||
if (optopt == (int)'-')
|
* if the user didn't specify '-' as an option,
|
||||||
return (-1);
|
* assume it means -1.
|
||||||
if (!*place)
|
*/
|
||||||
++optind;
|
if (optopt == (int)'-')
|
||||||
if (opterr && *ostr != ':')
|
return (-1);
|
||||||
(void)fprintf(stderr,
|
if (!*place)
|
||||||
"%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
|
++optind;
|
||||||
return (BADCH);
|
if (opterr && *ostr != ':')
|
||||||
}
|
(void)fprintf(stderr,
|
||||||
if (*++oli != ':') { /* don't need argument */
|
"%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
|
||||||
optarg = NULL;
|
return (BADCH);
|
||||||
if (!*place)
|
}
|
||||||
++optind;
|
if (*++oli != ':') { /* don't need argument */
|
||||||
} else { /* need an argument */
|
optarg = NULL;
|
||||||
if (*place) /* no white space */
|
if (!*place)
|
||||||
optarg = place;
|
++optind;
|
||||||
else if (nargc <= ++optind) { /* no arg */
|
} else { /* need an argument */
|
||||||
place = EMSG;
|
if (*place) /* no white space */
|
||||||
if ((opterr) && (*ostr != ':'))
|
optarg = place;
|
||||||
(void)fprintf(stderr,
|
else if (nargc <= ++optind) { /* no arg */
|
||||||
"%s: option requires an argument -- %c\n",
|
place = EMSG;
|
||||||
__progname(nargv[0]), optopt);
|
if ((opterr) && (*ostr != ':'))
|
||||||
return (BADARG);
|
(void)fprintf(stderr,
|
||||||
} else /* white space */
|
"%s: option requires an argument -- %c\n",
|
||||||
optarg = nargv[optind];
|
__progname(nargv[0]), optopt);
|
||||||
place = EMSG;
|
return (BADARG);
|
||||||
++optind;
|
} else /* white space */
|
||||||
}
|
optarg = nargv[optind];
|
||||||
return (optopt); /* dump back option letter */
|
place = EMSG;
|
||||||
}
|
++optind;
|
||||||
|
}
|
||||||
#if 0
|
return (optopt); /* dump back option letter */
|
||||||
/*
|
}
|
||||||
* getopt --
|
|
||||||
* Parse argc/argv argument vector.
|
#if 0
|
||||||
*/
|
/*
|
||||||
int
|
* getopt --
|
||||||
getopt2(nargc, nargv, ostr)
|
* Parse argc/argv argument vector.
|
||||||
int nargc;
|
*/
|
||||||
char * const *nargv;
|
int
|
||||||
const char *ostr;
|
getopt2(nargc, nargv, ostr)
|
||||||
{
|
int nargc;
|
||||||
int retval;
|
char * const *nargv;
|
||||||
|
const char *ostr;
|
||||||
if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
|
{
|
||||||
retval = -1;
|
int retval;
|
||||||
++optind;
|
|
||||||
}
|
if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
|
||||||
return(retval);
|
retval = -1;
|
||||||
}
|
++optind;
|
||||||
#endif
|
}
|
||||||
|
return(retval);
|
||||||
/*
|
}
|
||||||
* getopt_long --
|
#endif
|
||||||
* Parse argc/argv argument vector.
|
|
||||||
*/
|
/*
|
||||||
int
|
* getopt_long --
|
||||||
getopt_long(nargc, nargv, options, long_options, index)
|
* Parse argc/argv argument vector.
|
||||||
int nargc;
|
*/
|
||||||
char ** nargv;
|
int
|
||||||
char * options;
|
getopt_long(nargc, nargv, options, long_options, index)
|
||||||
struct option * long_options;
|
int nargc;
|
||||||
int * index;
|
char ** nargv;
|
||||||
{
|
char * options;
|
||||||
int retval;
|
struct option * long_options;
|
||||||
|
int * index;
|
||||||
_DIAGASSERT(nargv != NULL);
|
{
|
||||||
_DIAGASSERT(options != NULL);
|
int retval;
|
||||||
_DIAGASSERT(long_options != NULL);
|
|
||||||
/* index may be NULL */
|
_DIAGASSERT(nargv != NULL);
|
||||||
|
_DIAGASSERT(options != NULL);
|
||||||
if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
|
_DIAGASSERT(long_options != NULL);
|
||||||
char *current_argv = nargv[optind++] + 2, *has_equal;
|
/* index may be NULL */
|
||||||
int i, current_argv_len, match = -1;
|
|
||||||
|
if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
|
||||||
if (*current_argv == '\0') {
|
char *current_argv = nargv[optind++] + 2, *has_equal;
|
||||||
return(-1);
|
int i, current_argv_len, match = -1;
|
||||||
}
|
|
||||||
if ((has_equal = strchr(current_argv, '=')) != NULL) {
|
if (*current_argv == '\0') {
|
||||||
current_argv_len = has_equal - current_argv;
|
return(-1);
|
||||||
has_equal++;
|
}
|
||||||
} else
|
if ((has_equal = strchr(current_argv, '=')) != NULL) {
|
||||||
current_argv_len = strlen(current_argv);
|
current_argv_len = lws_ptr_diff(has_equal, current_argv);
|
||||||
|
has_equal++;
|
||||||
for (i = 0; long_options[i].name; i++) {
|
} else
|
||||||
if (strncmp(current_argv, long_options[i].name, current_argv_len))
|
current_argv_len = (int)strlen(current_argv);
|
||||||
continue;
|
|
||||||
|
for (i = 0; long_options[i].name; i++) {
|
||||||
if (strlen(long_options[i].name) == (unsigned)current_argv_len) {
|
if (strncmp(current_argv, long_options[i].name, current_argv_len))
|
||||||
match = i;
|
continue;
|
||||||
break;
|
|
||||||
}
|
if (strlen(long_options[i].name) == (unsigned)current_argv_len) {
|
||||||
if (match == -1)
|
match = i;
|
||||||
match = i;
|
break;
|
||||||
}
|
}
|
||||||
if (match != -1) {
|
if (match == -1)
|
||||||
if (long_options[match].has_arg == required_argument ||
|
match = i;
|
||||||
long_options[match].has_arg == optional_argument) {
|
}
|
||||||
if (has_equal)
|
if (match != -1) {
|
||||||
optarg = has_equal;
|
if (long_options[match].has_arg == required_argument ||
|
||||||
else
|
long_options[match].has_arg == optional_argument) {
|
||||||
optarg = nargv[optind++];
|
if (has_equal)
|
||||||
}
|
optarg = has_equal;
|
||||||
if ((long_options[match].has_arg == required_argument)
|
else
|
||||||
&& (optarg == NULL)) {
|
optarg = nargv[optind++];
|
||||||
/*
|
}
|
||||||
* Missing argument, leading :
|
if ((long_options[match].has_arg == required_argument)
|
||||||
* indicates no error should be generated
|
&& (optarg == NULL)) {
|
||||||
*/
|
/*
|
||||||
if ((opterr) && (*options != ':'))
|
* Missing argument, leading :
|
||||||
(void)fprintf(stderr,
|
* indicates no error should be generated
|
||||||
"%s: option requires an argument -- %s\n",
|
*/
|
||||||
__progname(nargv[0]), current_argv);
|
if ((opterr) && (*options != ':'))
|
||||||
return (BADARG);
|
(void)fprintf(stderr,
|
||||||
}
|
"%s: option requires an argument -- %s\n",
|
||||||
} else { /* No matching argument */
|
__progname(nargv[0]), current_argv);
|
||||||
if ((opterr) && (*options != ':'))
|
return (BADARG);
|
||||||
(void)fprintf(stderr,
|
}
|
||||||
"%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
|
} else { /* No matching argument */
|
||||||
return (BADCH);
|
if ((opterr) && (*options != ':'))
|
||||||
}
|
(void)fprintf(stderr,
|
||||||
if (long_options[match].flag) {
|
"%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
|
||||||
*long_options[match].flag = long_options[match].val;
|
return (BADCH);
|
||||||
retval = 0;
|
}
|
||||||
} else
|
if (long_options[match].flag) {
|
||||||
retval = long_options[match].val;
|
*long_options[match].flag = long_options[match].val;
|
||||||
if (index)
|
retval = 0;
|
||||||
*index = match;
|
} else
|
||||||
}
|
retval = long_options[match].val;
|
||||||
return(retval);
|
if (index)
|
||||||
}
|
*index = match;
|
||||||
|
}
|
||||||
|
return(retval);
|
||||||
|
}
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <windows.h> //I've omitted context line
|
#include <windows.h> //I've omitted context line
|
||||||
|
|
||||||
#include "gettimeofday.h"
|
#include "gettimeofday.h"
|
||||||
|
|
||||||
int gettimeofday(struct timeval *tv, struct timezone *tz)
|
int gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||||
{
|
{
|
||||||
FILETIME ft;
|
FILETIME ft;
|
||||||
unsigned __int64 tmpres = 0;
|
unsigned __int64 tmpres = 0;
|
||||||
static int tzflag;
|
static int tzflag;
|
||||||
|
|
||||||
if (NULL != tv) {
|
if (NULL != tv) {
|
||||||
GetSystemTimeAsFileTime(&ft);
|
GetSystemTimeAsFileTime(&ft);
|
||||||
|
|
||||||
tmpres |= ft.dwHighDateTime;
|
tmpres |= ft.dwHighDateTime;
|
||||||
tmpres <<= 32;
|
tmpres <<= 32;
|
||||||
tmpres |= ft.dwLowDateTime;
|
tmpres |= ft.dwLowDateTime;
|
||||||
|
|
||||||
/*converting file time to unix epoch*/
|
/*converting file time to unix epoch*/
|
||||||
tmpres /= 10; /*convert into microseconds*/
|
tmpres /= 10; /*convert into microseconds*/
|
||||||
tmpres -= DELTA_EPOCH_IN_MICROSECS;
|
tmpres -= DELTA_EPOCH_IN_MICROSECS;
|
||||||
tv->tv_sec = (long)(tmpres / 1000000UL);
|
tv->tv_sec = (long)(tmpres / 1000000UL);
|
||||||
tv->tv_usec = (long)(tmpres % 1000000UL);
|
tv->tv_usec = (long)(tmpres % 1000000UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NULL != tz) {
|
if (NULL != tz) {
|
||||||
if (!tzflag) {
|
if (!tzflag) {
|
||||||
_tzset();
|
_tzset();
|
||||||
tzflag++;
|
tzflag++;
|
||||||
}
|
}
|
||||||
tz->tz_minuteswest = _timezone / 60;
|
tz->tz_minuteswest = _timezone / 60;
|
||||||
tz->tz_dsttime = _daylight;
|
tz->tz_dsttime = _daylight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user