Update Files

This commit is contained in:
2025-01-22 17:22:38 +01:00
parent 89b9349629
commit 4c5e729485
5132 changed files with 1195369 additions and 0 deletions

View File

@ -0,0 +1,93 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
if (LWS_ROLE_MQTT)
add_subdir_include_directories(mqtt)
endif()
if (LWS_ROLE_DBUS AND NOT LWS_PLAT_FREERTOS)
add_subdir_include_directories(dbus)
endif()
if (LWS_ROLE_H1 OR LWS_ROLE_H2)
add_subdir_include_directories(http)
endif()
if (LWS_ROLE_H1)
add_subdir_include_directories(h1)
endif()
if (LWS_ROLE_H2)
add_subdir_include_directories(h2)
endif()
if (LWS_ROLE_WS)
add_subdir_include_directories(ws)
endif()
if (LWS_ROLE_RAW)
add_subdir_include_directories(raw-skt)
endif()
if (LWS_ROLE_RAW_FILE)
add_subdir_include_directories(raw-file)
endif()
if (LWS_WITH_CGI)
add_subdir_include_directories(cgi)
endif()
if (LWS_ROLE_RAW_PROXY)
add_subdir_include_directories(raw-proxy)
endif()
if (NOT LWS_WITHOUT_SERVER OR LWS_WITH_SECURE_STREAMS_PROCESS_API)
add_subdir_include_directories(listen)
endif()
if (LWS_WITH_CLIENT AND (LWS_ROLE_H1 OR LWS_ROLE_H2))
list(APPEND SOURCES
roles/http/client/client-http.c)
endif()
if (LWS_WITH_NETLINK)
list(APPEND SOURCES roles/netlink/ops-netlink.c)
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()
set(LWS_DEPS_LIB_PATHS ${LWS_DEPS_LIB_PATHS} PARENT_SCOPE)

View File

@ -0,0 +1,170 @@
## Information for new role implementers
### Introduction
In lws the "role" is the job the wsi is doing in the system, eg,
being an http1 or h2, or ws connection, or being a listen socket, etc.
This is different than, eg, a new ws protocol or a different callback
for an existing role. A new role is needed when you want to add support for
something completely new, like a completely new wire protocol that
doesn't use http or ws.
So... what's the point of implementing the protocol inside the lws role framework?
You inherit all the well-maintained lws core functionality around:
- connection lifecycle sequencing in a valgrind-clean way
- client connection proxy support, for HTTP and Socks5
- tls support working equally on mbedTLS and OpenSSL and derivatives without any code in the role
- apis for cert lifecycle management and parsing
- event loop support working on all the lws event loops (poll, libuv , ev, and event)
- clean connection tracking and closing even on advanced event loops
- user code follows the same simple callbacks on wsi
- multi-vhost support
- core multithreaded service support with usually no locking requirement on the role code
- direct compatibility with all other lws roles + protocols in the same event loop
- compatibility with higher-level stuff like lwsws as the server application
### Code placement
The code specific to that role should live in `./lib/roles/**role name**`
If a role is asymmetic between a client and server side, like http is, it
should generally be implemented as a single role.
### Allowing control over enabling roles
All roles should add a cmake define `LWS_ROLE_**role name**` and make its build
dependent on it in CMakeLists.txt. Export the cmakedefine in `./cmake/lws_config.h.in`
as well so user builds can understand if the role is available in the lws build it is
trying to bind to.
If the role is disabled in cmake, nothing in its directory is built.
### Role ops struct
The role is defined by `struct lws_role_ops` in `lib/roles/private-lib-roles.h`,
each role instantiates one of these and fills in the appropriate ops
callbacks to perform its job. By convention that lives in
`./lib/roles/**role name**/ops-**role_name**.c`.
### Private role declarations
Truly private declarations for the role can go in the role directory as you like.
However when the declarations must be accessible to other things in lws build, eg,
the role adds members to `struct lws` when enabled, they should be in the role
directory in a file `private-lib-roles-myrole.h`.
Search for "bring in role private declarations" in `./lib/roles/private-lib-roles.h
and add your private role file there following the style used for the other roles,
eg,
```
#if defined(LWS_ROLE_WS)
#include "roles/ws/private-lib-roles-ws.h"
#else
#define lwsi_role_ws(wsi) (0)
#endif
```
If the role is disabled at cmake, nothing from its private.h should be used anywhere.
### Integrating role assets to lws
If your role needs special storage in lws objects, that's no problem. But to keep
things sane, there are some rules.
- declare a "container struct" in your private.h for everything, eg, the ws role wants
to add storage in lws_vhost for enabled extensions, it declares in its private.h
```
struct lws_vhost_role_ws {
#if !defined(LWS_WITHOUT_EXTENSIONS)
const struct lws_extension *extensions;
#endif
};
```
- add your role content in one place in the lws struct, protected by `#if defined(LWS_ROLE_**role name**)`,
eg, again for LWS_ROLE_WS
```
struct lws_vhost {
...
#if defined(LWS_ROLE_WS)
struct lws_vhost_role_ws ws;
#endif
...
```
### Adding to lws available roles list
Edit the NULL-terminated array `available_roles` at the top of `./lib/core/context.c` to include
a pointer to your new role's ops struct, following the style already there.
```
const struct lws_role_ops * available_roles[] = {
#if defined(LWS_ROLE_H2)
&role_ops_h2,
#endif
...
```
This makes lws aware that your role exists, and it can auto-generate some things like
ALPN lists, and call your role ops callbacks for things like hooking vhost creation.
### Enabling role adoption
The primary way wsi get bound to a specific role is via the lws adoption api
`lws_adopt_descriptor_vhost()`. Add flags as necessary in `./include/libwebsockets/lws-adopt.h`
`enum lws_adoption_type` and follow the existing code in `lws_adopt_descriptor_vhost()`
to bind a wsi with suitable flags to your role ops.
### Implementation of the role
After that plumbing-in is completed, the role ops you declare are "live" on a wsi
bound to them via the adoption api.
The core support for wsis in lws has some generic concepts
- the wsi holds a pointer member `role_ops` that indicates which role ops the
wsi is bound to
- the wsi holds a generic uint32 `wsistate` that contains role flags and wsi state
- role flags are provided (LWSIFR_CLIENT, LWSIFR_SERVER) to differentiate between
client and server connections inside a wsi, along with helpers `lwsi_role_client(wsi)`
and `lwsi_role_server(wsi)`.
- lws provides around 30 generic states for the wsi starting from 'unconnected' through
various proxy or tunnel states, to 'established', and then various states shutting
down until 'dead socket'. The states have testable flags and helpers to discover if
the wsi state is before establishment `lwsi_state_est(wsi)` and if in the state it is
in, it can handle pollout `lwsi_state_can_handle_POLLOUT(wsi)`.
- You set the initial binding, role flags and state using `lws_role_transition()`. Afterwards
you can adjust the state using `lwsi_set_state()`.
### Role ops compression
Since the role ops struct is typically only sparsely filled, rather than have 20 function
pointers most of which may be NULL, there is a separate array of a union of function
pointers that is just long enough for functions that exist in the role, and a nybble index
table with a nybble for each possible op, either 0 indicating that the operation is not
provided in this role, or 1 - 15 indicating the position of the function pointer in the
array.

View File

@ -0,0 +1,42 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
list(APPEND SOURCES
roles/cgi/cgi-server.c
roles/cgi/ops-cgi.c)
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <private-lib-core.h>
static int
rops_handle_POLLIN_cgi(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
struct lws_cgi_args args;
assert(wsi->role_ops == &role_ops_cgi);
if (wsi->lsp_channel >= LWS_STDOUT &&
!(pollfd->revents & pollfd->events & LWS_POLLIN))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
if (wsi->lsp_channel == LWS_STDIN &&
!(pollfd->revents & pollfd->events & LWS_POLLOUT))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
if (wsi->lsp_channel == LWS_STDIN &&
lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_wsi_info(wsi, "failed at set pollfd");
return LWS_HPI_RET_WSI_ALREADY_DIED;
}
if (!wsi->parent) {
lwsl_wsi_debug(wsi, "stdwsi content with parent");
return LWS_HPI_RET_HANDLED;
}
if (!wsi->parent->http.cgi) {
lwsl_wsi_notice(wsi, "stdwsi content with deleted cgi object");
return LWS_HPI_RET_HANDLED;
}
if (!wsi->parent->http.cgi->lsp) {
lwsl_wsi_notice(wsi, "stdwsi content with reaped lsp");
return LWS_HPI_RET_HANDLED;
}
args.ch = wsi->lsp_channel;
args.stdwsi = &wsi->parent->http.cgi->lsp->stdwsi[0];
args.hdr_state = (enum lws_cgi_hdr_state)wsi->hdr_state;
lwsl_wsi_debug(wsi, "CGI LWS_STDOUT %p wsistate 0x%x",
wsi->parent, wsi->wsistate);
if (user_callback_handle_rxflow(wsi->parent->a.protocol->callback,
wsi->parent, LWS_CALLBACK_CGI,
wsi->parent->user_space,
(void *)&args, 0))
return 1;
return LWS_HPI_RET_HANDLED;
}
static int
rops_handle_POLLOUT_cgi(struct lws *wsi)
{
return LWS_HP_RET_USER_SERVICE;
}
static int
rops_destroy_role_cgi(struct lws *wsi)
{
#if defined(LWS_WITH_ZLIB)
if (!wsi->http.cgi)
return 0;
if (!wsi->http.cgi->gzip_init)
return 0;
inflateEnd(&wsi->http.cgi->inflate);
wsi->http.cgi->gzip_init = 0;
#endif
return 0;
}
void
lws_cgi_sul_cb(lws_sorted_usec_list_t *sul)
{
struct lws_context_per_thread *pt = lws_container_of(sul,
struct lws_context_per_thread, sul_cgi);
lws_cgi_kill_terminated(pt);
if (pt->http.cgi_list)
lws_sul_schedule(pt->context, (int)(pt - pt->context->pt),
&pt->sul_cgi, lws_cgi_sul_cb, 3 * LWS_US_PER_SEC);
}
static int
rops_pt_init_destroy_cgi(struct lws_context *context,
const struct lws_context_creation_info *info,
struct lws_context_per_thread *pt, int destroy)
{
lws_sul_cancel(&pt->sul_cgi);
return 0;
}
static int
rops_close_role_cgi(struct lws_context_per_thread *pt, struct lws *wsi)
{
if (wsi->parent && wsi->parent->http.cgi && wsi->parent->http.cgi->lsp)
lws_spawn_stdwsi_closed(wsi->parent->http.cgi->lsp, wsi);
return 0;
}
static const lws_rops_t rops_table_cgi[] = {
/* 1 */ { .pt_init_destroy = rops_pt_init_destroy_cgi },
/* 2 */ { .handle_POLLIN = rops_handle_POLLIN_cgi },
/* 3 */ { .handle_POLLOUT = rops_handle_POLLOUT_cgi },
/* 4 */ { .close_role = rops_close_role_cgi },
/* 5 */ { .destroy_role = rops_destroy_role_cgi },
};
const struct lws_role_ops role_ops_cgi = {
/* role name */ "cgi",
/* alpn id */ NULL,
/* rops_table */ rops_table_cgi,
/* rops_idx */ {
/* LWS_ROPS_check_upgrades */
/* LWS_ROPS_pt_init_destroy */ 0x01,
/* LWS_ROPS_init_vhost */
/* LWS_ROPS_destroy_vhost */ 0x00,
/* LWS_ROPS_service_flag_pending */
/* LWS_ROPS_handle_POLLIN */ 0x02,
/* LWS_ROPS_handle_POLLOUT */
/* LWS_ROPS_perform_user_POLLOUT */ 0x30,
/* LWS_ROPS_callback_on_writable */
/* LWS_ROPS_tx_credit */ 0x00,
/* LWS_ROPS_write_role_protocol */
/* LWS_ROPS_encapsulation_parent */ 0x00,
/* LWS_ROPS_alpn_negotiated */
/* LWS_ROPS_close_via_role_protocol */ 0x00,
/* LWS_ROPS_close_role */
/* LWS_ROPS_close_kill_connection */ 0x40,
/* LWS_ROPS_destroy_role */
/* LWS_ROPS_adoption_bind */ 0x50,
/* LWS_ROPS_client_bind */
/* LWS_ROPS_issue_keepalive */ 0x00,
},
/* adoption_cb clnt, srv */ { 0, 0 },
/* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },
/* close cb clnt, srv */ { 0, 0 },
/* protocol_bind_cb c,s */ { 0, 0 },
/* protocol_unbind_cb c,s */ { 0, 0 },
/* file_handle */ 0,
};

View File

@ -0,0 +1,93 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is included from private-lib-core.h if LWS_ROLE_WS
*/
#if defined(LWS_WITH_ZLIB)
#if defined(LWS_WITH_MINIZ)
#include <miniz.h>
#else
#include <zlib.h>
#endif
#endif
extern const struct lws_role_ops role_ops_cgi;
#define lwsi_role_cgi(wsi) (wsi->role_ops == &role_ops_cgi)
#define LWS_HTTP_CHUNK_HDR_SIZE 16
enum {
SIGNIFICANT_HDR_CONTENT_LENGTH, /* numeric */
SIGNIFICANT_HDR_LOCATION,
SIGNIFICANT_HDR_STATUS, /* numeric */
SIGNIFICANT_HDR_TRANSFER_ENCODING,
SIGNIFICANT_HDR_CONTENT_ENCODING_GZIP,
SIGNIFICANT_HDR_COUNT
};
struct lws;
/* wsi who is owns the cgi points to an lws_cgi */
struct lws_cgi {
struct lws_cgi *cgi_list;
struct lws_spawn_piped *lsp;
lws_sorted_usec_list_t sul_grace;
struct lws *wsi; /* owner */
unsigned char *headers_buf;
unsigned char *headers_start;
unsigned char *headers_pos;
unsigned char *headers_dumped;
unsigned char *headers_end;
char summary[128];
#if defined(LWS_WITH_ZLIB)
z_stream inflate;
uint8_t inflate_buf[1024];
#endif
lws_filepos_t post_in_expected;
lws_filepos_t content_length;
lws_filepos_t content_length_seen;
pid_t pi;
int match[SIGNIFICANT_HDR_COUNT];
char l[12];
int response_code;
int lp;
unsigned char being_closed:1;
unsigned char explicitly_chunked:1;
unsigned char cgi_transaction_over:1;
unsigned char implied_chunked:1;
unsigned char gzip_inflate:1;
unsigned char gzip_init:1;
unsigned char chunked_grace;
};

View File

@ -0,0 +1,67 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
list(APPEND SOURCES
roles/dbus/dbus.c)
if (NOT LWS_DBUS_LIB)
set(LWS_DBUS_LIB "dbus-1")
endif()
find_package(PkgConfig QUIET)
pkg_check_modules(PC_DBUS1 dbus-1 QUIET)
list(APPEND LWS_DBUS_INCLUDE1 ${PC_DBUS1_INCLUDE_DIRS})
list(APPEND LWS_DBUS_LIB ${PC_DBUS1_LIBRARIES})
list(APPEND LWS_DEPS_LIB_PATHS ${PC_DBUS1_LIBRARY_DIRS})
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES};${LWS_DBUS_INCLUDE1})
CHECK_C_SOURCE_COMPILES("#include <dbus/dbus.h>
int main(void) {
return 0;
}" LWS_DBUS_CHECK_OK)
message("dbus include dir 1: ${LWS_DBUS_INCLUDE1}")
if (LWS_DBUS_INCLUDE1)
include_directories("${LWS_DBUS_INCLUDE1}")
endif()
list(APPEND LIB_LIST ${LWS_DBUS_LIB})
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()
set(LWS_DBUS_CHECK_OK ${LWS_DBUS_CHECK_OK} PARENT_SCOPE)
set(LWS_DEPS_LIB_PATHS ${LWS_DEPS_LIB_PATHS} PARENT_SCOPE)

View File

@ -0,0 +1,83 @@
# DBUS Role Support
## DBUS-related distro packages
Fedora: dbus-devel
Debian / Ubuntu: libdbus-1-dev
## Enabling for build at cmake
Fedora example:
```
$ cmake .. -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2="/usr/lib64/dbus-1.0/include"
```
Ubuntu example:
```
$ cmake .. -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2="/usr/lib/x86_64-linux-gnu/dbus-1.0/include"
```
Dbus requires two include paths, which you can force by setting `LWS_DBUS_INCLUDE1`
and `LWS_DBUS_INCLUDE2`. Although INCLUDE1 is usually guessable, both can be
forced to allow cross-build.
If these are not forced, then lws cmake will try to check some popular places,
for `LWS_DBUS_INCLUDE1`, on both Fedora and Debian / Ubuntu, this is
`/usr/include/dbus-1.0`... if the directory exists, it is used.
For `LWS_DBUS_INCLUDE2`, it is the arch-specific dbus header which may be
packaged separately than the main dbus headers. On Fedora, this is in
`/usr/lib[64]/dbus-1.0/include`... if not given externally, lws cmake will
try `/usr/lib64/dbus-1.0/include`. On Debian / Ubuntu, the package installs
it in an arch-specific dir like `/usr/lib/x86_64-linux-gnu/dbus-1.0/include`,
you should force the path.
The library path is usually \[lib\] "dbus-1", but this can also be forced if
you want to build cross or use a special build, via `LWS_DBUS_LIB`.
## Building against local dbus build
If you built your own local dbus and installed it in /usr/local, then
this is the incantation to direct lws to use the local version of dbus:
```
cmake .. -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE1="/usr/local/include/dbus-1.0" -DLWS_DBUS_INCLUDE2="/usr/local/lib/dbus-1.0/include" -DLWS_DBUS_LIB="/usr/local/lib/libdbus-1.so"
```
You'll also need to give the loader a helping hand to do what you want if
there's a perfectly good dbus lib already in `/usr/lib[64]` using `LD_PRELOAD`
like this
```
LD_PRELOAD=/usr/local/lib/libdbus-1.so.3.24.0 myapp
```
## Lws dbus api exports
Because of the irregular situation with libdbus includes, if lws exports the
dbus helpers, which use dbus types, as usual from `#include <libwebsockets.h>`
then if lws was compiled with dbus role support it forces all users to take
care about the dbus include path mess whether they use dbus themselves or not.
For that reason, if you need access to the lws dbus apis, you must explicitly
include them by
```
#include <libwebsockets/lws-dbus.h>
```
This includes `<dbus/dbus.h>` and so requires the include paths set up. But
otherwise non-dbus users that don't include `libwebsockets/lws-dbus.h` don't
have to care about it.
## DBUS and valgrind
https://cgit.freedesktop.org/dbus/dbus/tree/README.valgrind
1) One-time 6KiB "Still reachable" caused by abstract unix domain socket + libc
`getgrouplist()` via nss... bug since 2004(!)
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=273051

View File

@ -0,0 +1,570 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This role for wrapping dbus fds in a wsi + role is unusual in that the
* wsi it creates and binds to the role do not have control over the related fd
* lifecycle. In fact dbus doesn't inform us directly about the lifecycle of
* the fds it wants to be managed by the lws event loop.
*
* What it does tell us is when it wants to wait on POLLOUT and / or POLLIN,
* and since it should stop any watchers before close, we take the approach to
* create a lightweight "shadow" wsi for any fd from dbus that has a POLLIN or
* POLLOUT wait active. When the dbus fd asks to have no wait active, we
* destroy the wsi, since this is indistinguishable from dbus close path
* behaviour. If it actually stays alive and later asks to wait again, well no
* worries we create a new shadow wsi until it looks like it is closing again.
*/
#include <private-lib-core.h>
#include <libwebsockets/lws-dbus.h>
/*
* retreives existing or creates new shadow wsi for fd owned by dbus stuff.
*
* Requires context + vhost lock
*/
static struct lws *
__lws_shadow_wsi(struct lws_dbus_ctx *ctx, DBusWatch *w, int fd, int create_ok)
{
struct lws *wsi;
if (fd < 0 || fd >= (int)ctx->vh->context->fd_limit_per_thread) {
lwsl_err("%s: fd %d vs fds_count %d\n", __func__, fd,
(int)ctx->vh->context->fd_limit_per_thread);
assert(0);
return NULL;
}
wsi = wsi_from_fd(ctx->vh->context, fd);
if (wsi) {
assert(wsi->opaque_parent_data == ctx);
return wsi;
}
if (!create_ok)
return NULL;
lws_context_assert_lock_held(ctx->vh->context);
lws_vhost_assert_lock_held(ctx->vh);
/* requires context lock */
wsi = __lws_wsi_create_with_role(ctx->vh->context, ctx->tsi, NULL,
ctx->vh->lc.log_cx);
if (wsi == NULL) {
lwsl_err("Out of mem\n");
return NULL;
}
lwsl_info("%s: creating shadow wsi\n", __func__);
wsi->desc.sockfd = fd;
lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_dbus);
wsi->a.protocol = ctx->vh->protocols;
wsi->shadow = 1;
wsi->opaque_parent_data = ctx;
ctx->w[0] = w;
__lws_lc_tag(ctx->vh->context, &ctx->vh->context->lcg[LWSLCG_WSI],
&wsi->lc, "dbus|%s", ctx->vh->name);
lws_vhost_bind_wsi(ctx->vh, wsi);
if (__insert_wsi_socket_into_fds(ctx->vh->context, wsi)) {
lwsl_err("inserting wsi socket into fds failed\n");
__lws_vhost_unbind_wsi(wsi); /* cx + vh lock */
lws_free(wsi);
return NULL;
}
return wsi;
}
/*
* Requires cx + vhost lock
*/
static int
__lws_shadow_wsi_destroy(struct lws_dbus_ctx *ctx, struct lws *wsi)
{
lwsl_info("%s: destroying shadow wsi\n", __func__);
lws_context_assert_lock_held(wsi->a.context);
lws_vhost_assert_lock_held(wsi->a.vhost);
if (__remove_wsi_socket_from_fds(wsi)) {
lwsl_err("%s: unable to remove %d from fds\n", __func__,
wsi->desc.sockfd);
return 1;
}
__lws_vhost_unbind_wsi(wsi);
lws_free(wsi);
return 0;
}
static void
handle_dispatch_status(DBusConnection *c, DBusDispatchStatus s, void *data)
{
lwsl_info("%s: new dbus dispatch status: %d\n", __func__, s);
}
/*
* These are complicated by the fact libdbus can have two separate DBusWatch
* objects for the same fd, to control watching POLLIN and POLLOUT individually.
*
* However we will actually watch using poll(), where the unit is the fd, and
* it has a unified events field with just POLLIN / POLLOUT flags.
*
* So we have to be prepared for one or two watchers coming in any order.
*/
static dbus_bool_t
lws_dbus_add_watch(DBusWatch *w, void *data)
{
struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
struct lws_context_per_thread *pt = &ctx->vh->context->pt[ctx->tsi];
unsigned int flags = 0, lws_flags = 0;
struct lws *wsi;
int n;
lws_context_lock(pt->context, __func__);
lws_vhost_lock(ctx->vh);
wsi = __lws_shadow_wsi(ctx, w, dbus_watch_get_unix_fd(w), 1);
lws_vhost_unlock(ctx->vh);
if (!wsi) {
lws_context_unlock(pt->context);
lwsl_err("%s: unable to get wsi\n", __func__);
return FALSE;
}
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
if (w == ctx->w[n])
break;
if (n == (int)LWS_ARRAY_SIZE(ctx->w))
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
if (!ctx->w[n]) {
ctx->w[n] = w;
break;
}
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
if (ctx->w[n] && dbus_watch_get_enabled(ctx->w[n]))
flags |= dbus_watch_get_flags(ctx->w[n]);
if (flags & DBUS_WATCH_READABLE)
lws_flags |= LWS_POLLIN;
if (flags & DBUS_WATCH_WRITABLE)
lws_flags |= LWS_POLLOUT;
lwsl_info("%s: %s: %p, fd %d, data %p, fl %d\n", __func__,
lws_wsi_tag(wsi), w, dbus_watch_get_unix_fd(w),
data, lws_flags);
if (lws_flags)
/* does its own pt lock */
__lws_change_pollfd(wsi, 0, (int)lws_flags);
lws_context_unlock(pt->context);
return TRUE;
}
/* cx + vh lock */
static int
__check_destroy_shadow_wsi(struct lws_dbus_ctx *ctx, struct lws *wsi)
{
int n;
if (!wsi)
return 0;
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
if (ctx->w[n])
return 0;
__lws_shadow_wsi_destroy(ctx, wsi);
if (!ctx->conn || !ctx->hup || ctx->timeouts)
return 0;
if (dbus_connection_get_dispatch_status(ctx->conn) ==
DBUS_DISPATCH_DATA_REMAINS)
return 0;
if (ctx->cb_closing)
ctx->cb_closing(ctx);
return 1;
}
static void
lws_dbus_remove_watch(DBusWatch *w, void *data)
{
struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
struct lws_context_per_thread *pt = &ctx->vh->context->pt[ctx->tsi];
unsigned int flags = 0, lws_flags = 0;
struct lws *wsi;
int n;
lws_context_lock(pt->context, __func__);
lws_vhost_lock(ctx->vh);
wsi = __lws_shadow_wsi(ctx, w, dbus_watch_get_unix_fd(w), 0);
lws_vhost_unlock(ctx->vh);
if (!wsi)
goto bail;
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
if (w == ctx->w[n]) {
ctx->w[n] = NULL;
break;
}
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
if (ctx->w[n])
flags |= dbus_watch_get_flags(ctx->w[n]);
if ((~flags) & DBUS_WATCH_READABLE)
lws_flags |= LWS_POLLIN;
if ((~flags) & DBUS_WATCH_WRITABLE)
lws_flags |= LWS_POLLOUT;
lwsl_info("%s: %p, fd %d, data %p, clearing lws flags %d\n",
__func__, w, dbus_watch_get_unix_fd(w),
data, lws_flags);
/* does its own pt lock */
__lws_change_pollfd(wsi, (int)lws_flags, 0);
bail:
lws_context_unlock(pt->context);
}
static void
lws_dbus_toggle_watch(DBusWatch *w, void *data)
{
if (dbus_watch_get_enabled(w))
lws_dbus_add_watch(w, data);
else
lws_dbus_remove_watch(w, data);
}
static void
lws_dbus_sul_cb(lws_sorted_usec_list_t *sul)
{
struct lws_context_per_thread *pt = lws_container_of(sul,
struct lws_context_per_thread, dbus.sul);
lws_start_foreach_dll_safe(struct lws_dll2 *, rdt, nx,
lws_dll2_get_head(&pt->dbus.timer_list_owner)) {
struct lws_role_dbus_timer *r = lws_container_of(rdt,
struct lws_role_dbus_timer, timer_list);
if (time(NULL) > r->fire) {
lwsl_notice("%s: firing timer\n", __func__);
dbus_timeout_handle(r->data);
}
} lws_end_foreach_dll_safe(rdt, nx);
if (pt->dbus.timer_list_owner.count)
lws_sul_schedule(pt->context, pt->tid, &pt->dbus.sul,
lws_dbus_sul_cb, 3 * LWS_US_PER_SEC);
}
static dbus_bool_t
lws_dbus_add_timeout(DBusTimeout *t, void *data)
{
struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
struct lws_context_per_thread *pt = &ctx->vh->context->pt[ctx->tsi];
int ms = dbus_timeout_get_interval(t);
struct lws_role_dbus_timer *dbt;
time_t ti = time(NULL);
if (!dbus_timeout_get_enabled(t))
return TRUE;
if (ms < 1000)
ms = 1000;
dbt = lws_malloc(sizeof(*dbt), "dbus timer");
if (!dbt)
return FALSE;
lwsl_info("%s: adding timeout %dms\n", __func__,
dbus_timeout_get_interval(t));
dbt->data = t;
dbt->fire = ti + (ms < 1000);
dbt->timer_list.prev = NULL;
dbt->timer_list.next = NULL;
dbt->timer_list.owner = NULL;
lws_dll2_add_head(&dbt->timer_list, &pt->dbus.timer_list_owner);
if (!pt->dbus.sul.list.owner)
lws_sul_schedule(pt->context, pt->tid, &pt->dbus.sul,
lws_dbus_sul_cb, 3 * LWS_US_PER_SEC);
ctx->timeouts++;
return TRUE;
}
static void
lws_dbus_remove_timeout(DBusTimeout *t, void *data)
{
struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
struct lws_context_per_thread *pt = &ctx->vh->context->pt[ctx->tsi];
lwsl_info("%s: t %p, data %p\n", __func__, t, data);
lws_start_foreach_dll_safe(struct lws_dll2 *, rdt, nx,
lws_dll2_get_head(&pt->dbus.timer_list_owner)) {
struct lws_role_dbus_timer *r = lws_container_of(rdt,
struct lws_role_dbus_timer, timer_list);
if (t == r->data) {
lws_dll2_remove(rdt);
lws_free(rdt);
ctx->timeouts--;
break;
}
} lws_end_foreach_dll_safe(rdt, nx);
if (!pt->dbus.timer_list_owner.count)
lws_sul_cancel(&pt->dbus.sul);
}
static void
lws_dbus_toggle_timeout(DBusTimeout *t, void *data)
{
if (dbus_timeout_get_enabled(t))
lws_dbus_add_timeout(t, data);
else
lws_dbus_remove_timeout(t, data);
}
/*
* This sets up a connection along the same lines as
* dbus_connection_setup_with_g_main(), but for using the lws event loop.
*/
int
lws_dbus_connection_setup(struct lws_dbus_ctx *ctx, DBusConnection *conn,
lws_dbus_closing_t cb_closing)
{
int n;
ctx->conn = conn;
ctx->cb_closing = cb_closing;
ctx->hup = 0;
ctx->timeouts = 0;
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
ctx->w[n] = NULL;
if (!dbus_connection_set_watch_functions(conn, lws_dbus_add_watch,
lws_dbus_remove_watch,
lws_dbus_toggle_watch,
ctx, NULL)) {
lwsl_err("%s: dbus_connection_set_watch_functions fail\n",
__func__);
return 1;
}
if (!dbus_connection_set_timeout_functions(conn,
lws_dbus_add_timeout,
lws_dbus_remove_timeout,
lws_dbus_toggle_timeout,
ctx, NULL)) {
lwsl_err("%s: dbus_connection_set_timeout_functions fail\n",
__func__);
return 1;
}
dbus_connection_set_dispatch_status_function(conn,
handle_dispatch_status,
ctx, NULL);
return 0;
}
/*
* This wraps dbus_server_listen(), additionally taking care of the event loop
* -related setups.
*/
DBusServer *
lws_dbus_server_listen(struct lws_dbus_ctx *ctx, const char *ads, DBusError *e,
DBusNewConnectionFunction new_conn)
{
ctx->cb_closing = NULL;
ctx->hup = 0;
ctx->timeouts = 0;
ctx->dbs = dbus_server_listen(ads, e);
if (!ctx->dbs)
return NULL;
dbus_server_set_new_connection_function(ctx->dbs, new_conn, ctx, NULL);
if (!dbus_server_set_watch_functions(ctx->dbs, lws_dbus_add_watch,
lws_dbus_remove_watch,
lws_dbus_toggle_watch,
ctx, NULL)) {
lwsl_err("%s: dbus_connection_set_watch_functions fail\n",
__func__);
goto bail;
}
if (!dbus_server_set_timeout_functions(ctx->dbs, lws_dbus_add_timeout,
lws_dbus_remove_timeout,
lws_dbus_toggle_timeout,
ctx, NULL)) {
lwsl_err("%s: dbus_connection_set_timeout_functions fail\n",
__func__);
goto bail;
}
return ctx->dbs;
bail:
dbus_server_disconnect(ctx->dbs);
dbus_server_unref(ctx->dbs);
return NULL;
}
/*
* There shouldn't be a race here with watcher removal and poll wait, because
* everything including the dbus activity is serialized in one event loop.
*
* If it removes the watcher and we remove the wsi and fd entry before this,
* actually we can no longer map the fd to this invalidated wsi pointer to call
* this.
*/
static int
rops_handle_POLLIN_dbus(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
struct lws_dbus_ctx *ctx =
(struct lws_dbus_ctx *)wsi->opaque_parent_data;
unsigned int flags = 0;
int n;
if (pollfd->revents & LWS_POLLIN)
flags |= DBUS_WATCH_READABLE;
if (pollfd->revents & LWS_POLLOUT)
flags |= DBUS_WATCH_WRITABLE;
if (pollfd->revents & (LWS_POLLHUP))
ctx->hup = 1;
/*
* POLLIN + POLLOUT gets us called here on the corresponding shadow
* wsi. wsi->opaque_parent_data is the watcher handle bound to the wsi
*/
for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
if (ctx->w[n] && !dbus_watch_handle(ctx->w[n], flags))
lwsl_err("%s: dbus_watch_handle failed\n", __func__);
if (ctx->conn) {
lwsl_info("%s: conn: flags %d\n", __func__, flags);
while (dbus_connection_get_dispatch_status(ctx->conn) ==
DBUS_DISPATCH_DATA_REMAINS)
dbus_connection_dispatch(ctx->conn);
handle_dispatch_status(NULL, DBUS_DISPATCH_DATA_REMAINS, NULL);
__check_destroy_shadow_wsi(ctx, wsi);
} else
if (ctx->dbs)
/* ??? */
lwsl_debug("%s: dbs: %d\n", __func__, flags);
return LWS_HPI_RET_HANDLED;
}
static int
rops_pt_init_destroy_dbus(struct lws_context *context,
const struct lws_context_creation_info *info,
struct lws_context_per_thread *pt, int destroy)
{
if (destroy)
lws_sul_cancel(&pt->dbus.sul);
return 0;
}
static const lws_rops_t rops_table_dbus[] = {
/* 1 */ { .pt_init_destroy = rops_pt_init_destroy_dbus },
/* 2 */ { .handle_POLLIN = rops_handle_POLLIN_dbus },
};
const struct lws_role_ops role_ops_dbus = {
/* role name */ "dbus",
/* alpn id */ NULL,
/* rops_table */ rops_table_dbus,
/* rops_idx */ {
/* LWS_ROPS_check_upgrades */
/* LWS_ROPS_pt_init_destroy */ 0x01,
/* LWS_ROPS_init_vhost */
/* LWS_ROPS_destroy_vhost */ 0x00,
/* LWS_ROPS_service_flag_pending */
/* LWS_ROPS_handle_POLLIN */ 0x02,
/* LWS_ROPS_handle_POLLOUT */
/* LWS_ROPS_perform_user_POLLOUT */ 0x00,
/* LWS_ROPS_callback_on_writable */
/* LWS_ROPS_tx_credit */ 0x00,
/* LWS_ROPS_write_role_protocol */
/* LWS_ROPS_encapsulation_parent */ 0x00,
/* LWS_ROPS_alpn_negotiated */
/* LWS_ROPS_close_via_role_protocol */ 0x00,
/* LWS_ROPS_close_role */
/* LWS_ROPS_close_kill_connection */ 0x00,
/* LWS_ROPS_destroy_role */
/* LWS_ROPS_adoption_bind */ 0x00,
/* LWS_ROPS_client_bind */
/* LWS_ROPS_issue_keepalive */ 0x00,
},
/* adoption_cb clnt, srv */ { 0, 0 },
/* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },
/* close cb clnt, srv */ { 0, 0 },
/* protocol_bind_cb c,s */ { 0, 0 },
/* protocol_unbind_cb c,s */ { 0, 0 },
/* file_handle */ 0,
};

View File

@ -0,0 +1,46 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is included from private-lib-core.h if LWS_ROLE_DBUS
*/
#include <dbus/dbus.h>
extern const struct lws_role_ops role_ops_dbus;
#define lwsi_role_dbus(wsi) (wsi->role_ops == &role_ops_dbus)
struct lws_role_dbus_timer {
struct lws_dll2 timer_list;
void *data;
time_t fire;
};
struct lws_pt_role_dbus {
struct lws_dll2_owner timer_list_owner;
lws_sorted_usec_list_t sul;
};
struct _lws_dbus_mode_related {
DBusConnection *conn;
};

View File

@ -0,0 +1,41 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
list(APPEND SOURCES
roles/h1/ops-h1.c)
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is included from private-lib-core.h if LWS_ROLE_H1
*
* Most of the h1 business is defined in the h1 / h2 common roles/http dir
*/
int
_lws_is_http_method(const char *method);
extern const struct lws_role_ops role_ops_h1;
#define lwsi_role_h1(wsi) (wsi->role_ops == &role_ops_h1)

View File

@ -0,0 +1,44 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
list(APPEND SOURCES
roles/h2/http2.c
roles/h2/hpack.c
roles/h2/ops-h2.c)
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,530 @@
static unsigned char lextable[] = {
/* pos 0000: 0 */ /* 0 */ 0x42 /* (to 0x0084 state 98) */,
/* 1 */ 0x01 /* (to 0x0002 state 1) */,
/* pos 0002: 1 */ /* 0 */ 0x5C /* (to 0x00BA state 151) */,
/* 1 */ 0x01 /* (to 0x0004 state 2) */,
/* pos 0004: 2 */ /* 0 */ 0x66 /* (to 0x00D0 state 173) */,
/* 1 */ 0x01 /* (to 0x0006 state 3) */,
/* pos 0006: 3 */ /* 0 */ 0x74 /* (to 0x00EE state 204) */,
/* 1 */ 0x01 /* (to 0x0008 state 4) */,
/* pos 0008: 4 */ /* 0 */ 0x8C /* (to 0x0120 state 263) */,
/* 1 */ 0x01 /* (to 0x000A state 5) */,
/* pos 000a: 5 */ /* 0 */ 0x46 /* (to 0x0096 state 113) */,
/* 1 */ 0x01 /* (to 0x000C state 6) */,
/* pos 000c: 6 */ /* 0 */ 0x75 /* (to 0x00F6 state 211) */,
/* 1 */ 0x01 /* (to 0x000E state 7) */,
/* pos 000e: 7 */ /* 0 */ 0x40 /* (to 0x008E state 104) */,
/* 1 */ 0x01 /* (to 0x0010 state 8) */,
/* pos 0010: 8 */ /* 0 */ 0x45 /* (to 0x009A state 116) */,
/* 1 */ 0x01 /* (to 0x0012 state 9) */,
/* pos 0012: 9 */ /* 0 */ 0x40 /* (to 0x0092 state 108) */,
/* 1 */ 0x01 /* (to 0x0014 state 10) */,
/* pos 0014: 10 */ /* 0 */ 0x01 /* (to 0x0016 state 11) */,
/* 1 */ 0x03 /* (to 0x001A state 14) */,
/* pos 0016: 11 */ /* 0 */ 0x01 /* (to 0x0018 state 12) */,
/* 1 */ 0x5B /* (to 0x00CC state 166) */,
/* pos 0018: 12 */ /* terminal 0 */ 0x00,
/* terminal 36 */ 0x24,
/* pos 001a: 14 */ /* 0 */ 0x72 /* (to 0x00FE state 220) */,
/* 1 */ 0x01 /* (to 0x001C state 15) */,
/* pos 001c: 15 */ /* 0 */ 0x72 /* (to 0x0100 state 222) */,
/* 1 */ 0x01 /* (to 0x001E state 16) */,
/* pos 001e: 16 */ /* 0 */ 0x53 /* (to 0x00C4 state 158) */,
/* 1 */ 0x01 /* (to 0x0020 state 17) */,
/* pos 0020: 17 */ /* terminal 123 */ 0x7B,
/* 1 */ 0x01 /* (to 0x0022 state 18) */,
/* pos 0022: 18 */ /* 0 */ 0x6B /* (to 0x00F8 state 216) */,
/* 1 */ 0x01 /* (to 0x0024 state 19) */,
/* pos 0024: 19 */ /* 0 */ 0x84 /* (to 0x012C state 279) */,
/* 1 */ 0x01 /* (to 0x0026 state 20) */,
/* pos 0026: 20 */ /* 0 */ 0x01 /* (to 0x0028 state 21) */,
/* 1 */ 0x06 /* (to 0x0032 state 27) */,
/* pos 0028: 21 */ /* 0 */ 0xB3 /* (to 0x018E state 377) */,
/* 1 */ 0x01 /* (to 0x002A state 22) */,
/* pos 002a: 22 */ /* 0 */ 0xC3 /* (to 0x01B0 state 414) */,
/* 1 */ 0x01 /* (to 0x002C state 23) */,
/* pos 002c: 23 */ /* 0 */ 0x01 /* (to 0x002E state 24) */,
/* 1 */ 0x8C /* (to 0x0144 state 301) */,
/* pos 002e: 24 */ /* 0 */ 0x01 /* (to 0x0030 state 25) */,
/* 1 */ 0x8A /* (to 0x0142 state 298) */,
/* pos 0030: 25 */ /* terminal 1 */ 0x01,
/* terminal 135 */ 0x87,
/* pos 0032: 27 */ /* 0 */ 0x8E /* (to 0x014E state 314) */,
/* 1 */ 0x01 /* (to 0x0034 state 28) */,
/* pos 0034: 28 */ /* 0 */ 0x0F /* (to 0x0052 state 50) */,
/* 1 */ 0x01 /* (to 0x0036 state 29) */,
/* pos 0036: 29 */ /* 0 */ 0xA4 /* (to 0x017E state 362) */,
/* 1 */ 0x01 /* (to 0x0038 state 30) */,
/* pos 0038: 30 */ /* 0 */ 0xB7 /* (to 0x01A6 state 403) */,
/* 1 */ 0x01 /* (to 0x003A state 31) */,
/* pos 003a: 31 */ /* 0 */ 0xC8 /* (to 0x01CA state 440) */,
/* 1 */ 0x01 /* (to 0x003C state 32) */,
/* pos 003c: 32 */ /* 0 */ 0x01 /* (to 0x003E state 33) */,
/* 1 */ 0x0F /* (to 0x005A state 55) */,
/* pos 003e: 33 */ /* 0 */ 0x01 /* (to 0x0040 state 34) */,
/* 1 */ 0x07 /* (to 0x004C state 46) */,
/* pos 0040: 34 */ /* 0 */ 0x01 /* (to 0x0042 state 35) */,
/* 1 */ 0x03 /* (to 0x0046 state 39) */,
/* pos 0042: 35 */ /* terminal 254 */ 0xFE,
/* 1 */ 0x01 /* (to 0x0044 state 36) */,
/* pos 0044: 36 */ /* terminal 2 */ 0x02,
/* terminal 3 */ 0x03,
/* pos 0046: 39 */ /* 0 */ 0x01 /* (to 0x0048 state 40) */,
/* 1 */ 0x02 /* (to 0x004A state 43) */,
/* pos 0048: 40 */ /* terminal 4 */ 0x04,
/* terminal 5 */ 0x05,
/* pos 004a: 43 */ /* terminal 6 */ 0x06,
/* terminal 7 */ 0x07,
/* pos 004c: 46 */ /* 0 */ 0x01 /* (to 0x004E state 47) */,
/* 1 */ 0x0E /* (to 0x0068 state 67) */,
/* pos 004e: 47 */ /* 0 */ 0x01 /* (to 0x0050 state 48) */,
/* 1 */ 0x0C /* (to 0x0066 state 63) */,
/* pos 0050: 48 */ /* terminal 8 */ 0x08,
/* terminal 11 */ 0x0B,
/* pos 0052: 50 */ /* 0 */ 0xA7 /* (to 0x01A0 state 396) */,
/* 1 */ 0x01 /* (to 0x0054 state 51) */,
/* pos 0054: 51 */ /* 0 */ 0x01 /* (to 0x0056 state 52) */,
/* 1 */ 0x7B /* (to 0x014A state 309) */,
/* pos 0056: 52 */ /* terminal 239 */ 0xEF,
/* 1 */ 0x01 /* (to 0x0058 state 53) */,
/* pos 0058: 53 */ /* terminal 9 */ 0x09,
/* terminal 142 */ 0x8E,
/* pos 005a: 55 */ /* 0 */ 0x0A /* (to 0x006E state 74) */,
/* 1 */ 0x01 /* (to 0x005C state 56) */,
/* pos 005c: 56 */ /* 0 */ 0x11 /* (to 0x007E state 91) */,
/* 1 */ 0x01 /* (to 0x005E state 57) */,
/* pos 005e: 57 */ /* 0 */ 0x64 /* (to 0x0126 state 274) */,
/* 1 */ 0x01 /* (to 0x0060 state 58) */,
/* pos 0060: 58 */ /* terminal 249 */ 0xF9,
/* 1 */ 0x01 /* (to 0x0062 state 59) */,
/* pos 0062: 59 */ /* 0 */ 0x01 /* (to 0x0064 state 60) */,
/* 1 */ 0x0A /* (to 0x0076 state 81) */,
/* pos 0064: 60 */ /* terminal 10 */ 0x0A,
/* terminal 13 */ 0x0D,
/* pos 0066: 63 */ /* terminal 12 */ 0x0C,
/* terminal 14 */ 0x0E,
/* pos 0068: 67 */ /* 0 */ 0x01 /* (to 0x006A state 68) */,
/* 1 */ 0x02 /* (to 0x006C state 71) */,
/* pos 006a: 68 */ /* terminal 15 */ 0x0F,
/* terminal 16 */ 0x10,
/* pos 006c: 71 */ /* terminal 17 */ 0x11,
/* terminal 18 */ 0x12,
/* pos 006e: 74 */ /* 0 */ 0x01 /* (to 0x0070 state 75) */,
/* 1 */ 0x05 /* (to 0x0078 state 84) */,
/* pos 0070: 75 */ /* 0 */ 0x01 /* (to 0x0072 state 76) */,
/* 1 */ 0x02 /* (to 0x0074 state 79) */,
/* pos 0072: 76 */ /* terminal 19 */ 0x13,
/* terminal 20 */ 0x14,
/* pos 0074: 79 */ /* terminal 21 */ 0x15,
/* terminal 23 */ 0x17,
/* pos 0076: 81 */ /* terminal 22 */ 0x16,
/* terminal 256 */ 0x00,
/* pos 0078: 84 */ /* 0 */ 0x01 /* (to 0x007A state 85) */,
/* 1 */ 0x02 /* (to 0x007C state 88) */,
/* pos 007a: 85 */ /* terminal 24 */ 0x18,
/* terminal 25 */ 0x19,
/* pos 007c: 88 */ /* terminal 26 */ 0x1A,
/* terminal 27 */ 0x1B,
/* pos 007e: 91 */ /* 0 */ 0x01 /* (to 0x0080 state 92) */,
/* 1 */ 0x02 /* (to 0x0082 state 95) */,
/* pos 0080: 92 */ /* terminal 28 */ 0x1C,
/* terminal 29 */ 0x1D,
/* pos 0082: 95 */ /* terminal 30 */ 0x1E,
/* terminal 31 */ 0x1F,
/* pos 0084: 98 */ /* 0 */ 0x13 /* (to 0x00AA state 133) */,
/* 1 */ 0x01 /* (to 0x0086 state 99) */,
/* pos 0086: 99 */ /* 0 */ 0x01 /* (to 0x0088 state 100) */,
/* 1 */ 0x0F /* (to 0x00A4 state 129) */,
/* pos 0088: 100 */ /* 0 */ 0x4B /* (to 0x011E state 258) */,
/* 1 */ 0x01 /* (to 0x008A state 101) */,
/* pos 008a: 101 */ /* 0 */ 0x01 /* (to 0x008C state 102) */,
/* 1 */ 0x0C /* (to 0x00A2 state 126) */,
/* pos 008c: 102 */ /* terminal 32 */ 0x20,
/* terminal 37 */ 0x25,
/* pos 008e: 104 */ /* 0 */ 0x01 /* (to 0x0090 state 105) */,
/* 1 */ 0x08 /* (to 0x009E state 119) */,
/* pos 0090: 105 */ /* terminal 33 */ 0x21,
/* terminal 34 */ 0x22,
/* pos 0092: 108 */ /* terminal 124 */ 0x7C,
/* 1 */ 0x01 /* (to 0x0094 state 109) */,
/* pos 0094: 109 */ /* terminal 35 */ 0x23,
/* terminal 62 */ 0x3E,
/* pos 0096: 113 */ /* 0 */ 0x01 /* (to 0x0098 state 114) */,
/* 1 */ 0x05 /* (to 0x00A0 state 124) */,
/* pos 0098: 114 */ /* terminal 38 */ 0x26,
/* terminal 42 */ 0x2A,
/* pos 009a: 116 */ /* terminal 63 */ 0x3F,
/* 1 */ 0x01 /* (to 0x009C state 117) */,
/* pos 009c: 117 */ /* terminal 39 */ 0x27,
/* terminal 43 */ 0x2B,
/* pos 009e: 119 */ /* terminal 40 */ 0x28,
/* terminal 41 */ 0x29,
/* pos 00a0: 124 */ /* terminal 44 */ 0x2C,
/* terminal 59 */ 0x3B,
/* pos 00a2: 126 */ /* terminal 45 */ 0x2D,
/* terminal 46 */ 0x2E,
/* pos 00a4: 129 */ /* 0 */ 0x01 /* (to 0x00A6 state 130) */,
/* 1 */ 0x08 /* (to 0x00B4 state 144) */,
/* pos 00a6: 130 */ /* 0 */ 0x01 /* (to 0x00A8 state 131) */,
/* 1 */ 0x06 /* (to 0x00B2 state 141) */,
/* pos 00a8: 131 */ /* terminal 47 */ 0x2F,
/* terminal 51 */ 0x33,
/* pos 00aa: 133 */ /* 0 */ 0x01 /* (to 0x00AC state 134) */,
/* 1 */ 0x2D /* (to 0x0104 state 229) */,
/* pos 00ac: 134 */ /* 0 */ 0x01 /* (to 0x00AE state 135) */,
/* 1 */ 0x02 /* (to 0x00B0 state 138) */,
/* pos 00ae: 135 */ /* terminal 48 */ 0x30,
/* terminal 49 */ 0x31,
/* pos 00b0: 138 */ /* terminal 50 */ 0x32,
/* terminal 97 */ 0x61,
/* pos 00b2: 141 */ /* terminal 52 */ 0x34,
/* terminal 53 */ 0x35,
/* pos 00b4: 144 */ /* 0 */ 0x01 /* (to 0x00B6 state 145) */,
/* 1 */ 0x02 /* (to 0x00B8 state 148) */,
/* pos 00b6: 145 */ /* terminal 54 */ 0x36,
/* terminal 55 */ 0x37,
/* pos 00b8: 148 */ /* terminal 56 */ 0x38,
/* terminal 57 */ 0x39,
/* pos 00ba: 151 */ /* 0 */ 0x06 /* (to 0x00C6 state 160) */,
/* 1 */ 0x01 /* (to 0x00BC state 152) */,
/* pos 00bc: 152 */ /* 0 */ 0x2C /* (to 0x0114 state 246) */,
/* 1 */ 0x01 /* (to 0x00BE state 153) */,
/* pos 00be: 153 */ /* 0 */ 0x2F /* (to 0x011C state 256) */,
/* 1 */ 0x01 /* (to 0x00C0 state 154) */,
/* pos 00c0: 154 */ /* 0 */ 0x01 /* (to 0x00C2 state 155) */,
/* 1 */ 0x07 /* (to 0x00CE state 170) */,
/* pos 00c2: 155 */ /* terminal 58 */ 0x3A,
/* terminal 66 */ 0x42,
/* pos 00c4: 158 */ /* terminal 60 */ 0x3C,
/* terminal 96 */ 0x60,
/* pos 00c6: 160 */ /* 0 */ 0x01 /* (to 0x00C8 state 161) */,
/* 1 */ 0x21 /* (to 0x0108 state 232) */,
/* pos 00c8: 161 */ /* 0 */ 0x01 /* (to 0x00CA state 162) */,
/* 1 */ 0x1D /* (to 0x0102 state 224) */,
/* pos 00ca: 162 */ /* terminal 61 */ 0x3D,
/* terminal 65 */ 0x41,
/* pos 00cc: 166 */ /* terminal 64 */ 0x40,
/* terminal 91 */ 0x5B,
/* pos 00ce: 170 */ /* terminal 67 */ 0x43,
/* terminal 68 */ 0x44,
/* pos 00d0: 173 */ /* 0 */ 0x01 /* (to 0x00D2 state 174) */,
/* 1 */ 0x08 /* (to 0x00E0 state 189) */,
/* pos 00d2: 174 */ /* 0 */ 0x01 /* (to 0x00D4 state 175) */,
/* 1 */ 0x04 /* (to 0x00DA state 182) */,
/* pos 00d4: 175 */ /* 0 */ 0x01 /* (to 0x00D6 state 176) */,
/* 1 */ 0x02 /* (to 0x00D8 state 179) */,
/* pos 00d6: 176 */ /* terminal 69 */ 0x45,
/* terminal 70 */ 0x46,
/* pos 00d8: 179 */ /* terminal 71 */ 0x47,
/* terminal 72 */ 0x48,
/* pos 00da: 182 */ /* 0 */ 0x01 /* (to 0x00DC state 183) */,
/* 1 */ 0x02 /* (to 0x00DE state 186) */,
/* pos 00dc: 183 */ /* terminal 73 */ 0x49,
/* terminal 74 */ 0x4A,
/* pos 00de: 186 */ /* terminal 75 */ 0x4B,
/* terminal 76 */ 0x4C,
/* pos 00e0: 189 */ /* 0 */ 0x01 /* (to 0x00E2 state 190) */,
/* 1 */ 0x04 /* (to 0x00E8 state 197) */,
/* pos 00e2: 190 */ /* 0 */ 0x01 /* (to 0x00E4 state 191) */,
/* 1 */ 0x02 /* (to 0x00E6 state 194) */,
/* pos 00e4: 191 */ /* terminal 77 */ 0x4D,
/* terminal 78 */ 0x4E,
/* pos 00e6: 194 */ /* terminal 79 */ 0x4F,
/* terminal 80 */ 0x50,
/* pos 00e8: 197 */ /* 0 */ 0x01 /* (to 0x00EA state 198) */,
/* 1 */ 0x02 /* (to 0x00EC state 201) */,
/* pos 00ea: 198 */ /* terminal 81 */ 0x51,
/* terminal 82 */ 0x52,
/* pos 00ec: 201 */ /* terminal 83 */ 0x53,
/* terminal 84 */ 0x54,
/* pos 00ee: 204 */ /* 0 */ 0x01 /* (to 0x00F0 state 205) */,
/* 1 */ 0x11 /* (to 0x0110 state 242) */,
/* pos 00f0: 205 */ /* 0 */ 0x01 /* (to 0x00F2 state 206) */,
/* 1 */ 0x02 /* (to 0x00F4 state 209) */,
/* pos 00f2: 206 */ /* terminal 85 */ 0x55,
/* terminal 86 */ 0x56,
/* pos 00f4: 209 */ /* terminal 87 */ 0x57,
/* terminal 89 */ 0x59,
/* pos 00f6: 211 */ /* terminal 88 */ 0x58,
/* terminal 90 */ 0x5A,
/* pos 00f8: 216 */ /* 0 */ 0x01 /* (to 0x00FA state 217) */,
/* 1 */ 0x1F /* (to 0x0136 state 286) */,
/* pos 00fa: 217 */ /* 0 */ 0x01 /* (to 0x00FC state 218) */,
/* 1 */ 0x17 /* (to 0x0128 state 276) */,
/* pos 00fc: 218 */ /* terminal 92 */ 0x5C,
/* terminal 195 */ 0xC3,
/* pos 00fe: 220 */ /* terminal 93 */ 0x5D,
/* terminal 126 */ 0x7E,
/* pos 0100: 222 */ /* terminal 94 */ 0x5E,
/* terminal 125 */ 0x7D,
/* pos 0102: 224 */ /* terminal 95 */ 0x5F,
/* terminal 98 */ 0x62,
/* pos 0104: 229 */ /* 0 */ 0x01 /* (to 0x0106 state 230) */,
/* 1 */ 0x05 /* (to 0x010E state 240) */,
/* pos 0106: 230 */ /* terminal 99 */ 0x63,
/* terminal 101 */ 0x65,
/* pos 0108: 232 */ /* 0 */ 0x01 /* (to 0x010A state 233) */,
/* 1 */ 0x02 /* (to 0x010C state 237) */,
/* pos 010a: 233 */ /* terminal 100 */ 0x64,
/* terminal 102 */ 0x66,
/* pos 010c: 237 */ /* terminal 103 */ 0x67,
/* terminal 104 */ 0x68,
/* pos 010e: 240 */ /* terminal 105 */ 0x69,
/* terminal 111 */ 0x6F,
/* pos 0110: 242 */ /* 0 */ 0x01 /* (to 0x0112 state 243) */,
/* 1 */ 0x05 /* (to 0x011A state 254) */,
/* pos 0112: 243 */ /* terminal 106 */ 0x6A,
/* terminal 107 */ 0x6B,
/* pos 0114: 246 */ /* 0 */ 0x01 /* (to 0x0116 state 247) */,
/* 1 */ 0x02 /* (to 0x0118 state 250) */,
/* pos 0116: 247 */ /* terminal 108 */ 0x6C,
/* terminal 109 */ 0x6D,
/* pos 0118: 250 */ /* terminal 110 */ 0x6E,
/* terminal 112 */ 0x70,
/* pos 011a: 254 */ /* terminal 113 */ 0x71,
/* terminal 118 */ 0x76,
/* pos 011c: 256 */ /* terminal 114 */ 0x72,
/* terminal 117 */ 0x75,
/* pos 011e: 258 */ /* terminal 115 */ 0x73,
/* terminal 116 */ 0x74,
/* pos 0120: 263 */ /* 0 */ 0x01 /* (to 0x0122 state 264) */,
/* 1 */ 0x02 /* (to 0x0124 state 267) */,
/* pos 0122: 264 */ /* terminal 119 */ 0x77,
/* terminal 120 */ 0x78,
/* pos 0124: 267 */ /* terminal 121 */ 0x79,
/* terminal 122 */ 0x7A,
/* pos 0126: 274 */ /* terminal 127 */ 0x7F,
/* terminal 220 */ 0xDC,
/* pos 0128: 276 */ /* terminal 208 */ 0xD0,
/* 1 */ 0x01 /* (to 0x012A state 277) */,
/* pos 012a: 277 */ /* terminal 128 */ 0x80,
/* terminal 130 */ 0x82,
/* pos 012c: 279 */ /* 0 */ 0x2E /* (to 0x0188 state 372) */,
/* 1 */ 0x01 /* (to 0x012E state 280) */,
/* pos 012e: 280 */ /* 0 */ 0x01 /* (to 0x0130 state 281) */,
/* 1 */ 0x1B /* (to 0x0164 state 332) */,
/* pos 0130: 281 */ /* 0 */ 0x01 /* (to 0x0132 state 282) */,
/* 1 */ 0x06 /* (to 0x013C state 291) */,
/* pos 0132: 282 */ /* terminal 230 */ 0xE6,
/* 1 */ 0x01 /* (to 0x0134 state 283) */,
/* pos 0134: 283 */ /* terminal 129 */ 0x81,
/* terminal 132 */ 0x84,
/* pos 0136: 286 */ /* 0 */ 0x01 /* (to 0x0138 state 287) */,
/* 1 */ 0x14 /* (to 0x015E state 328) */,
/* pos 0138: 287 */ /* 0 */ 0x01 /* (to 0x013A state 288) */,
/* 1 */ 0x30 /* (to 0x0198 state 388) */,
/* pos 013a: 288 */ /* terminal 131 */ 0x83,
/* terminal 162 */ 0xA2,
/* pos 013c: 291 */ /* 0 */ 0x01 /* (to 0x013E state 292) */,
/* 1 */ 0x02 /* (to 0x0140 state 296) */,
/* pos 013e: 292 */ /* terminal 133 */ 0x85,
/* terminal 134 */ 0x86,
/* pos 0140: 296 */ /* terminal 136 */ 0x88,
/* terminal 146 */ 0x92,
/* pos 0142: 298 */ /* terminal 137 */ 0x89,
/* terminal 138 */ 0x8A,
/* pos 0144: 301 */ /* 0 */ 0x01 /* (to 0x0146 state 302) */,
/* 1 */ 0x02 /* (to 0x0148 state 305) */,
/* pos 0146: 302 */ /* terminal 139 */ 0x8B,
/* terminal 140 */ 0x8C,
/* pos 0148: 305 */ /* terminal 141 */ 0x8D,
/* terminal 143 */ 0x8F,
/* pos 014a: 309 */ /* 0 */ 0x01 /* (to 0x014C state 310) */,
/* 1 */ 0x06 /* (to 0x0156 state 319) */,
/* pos 014c: 310 */ /* terminal 144 */ 0x90,
/* terminal 145 */ 0x91,
/* pos 014e: 314 */ /* 0 */ 0x01 /* (to 0x0150 state 315) */,
/* 1 */ 0x12 /* (to 0x0172 state 350) */,
/* pos 0150: 315 */ /* 0 */ 0x01 /* (to 0x0152 state 316) */,
/* 1 */ 0x05 /* (to 0x015A state 325) */,
/* pos 0152: 316 */ /* 0 */ 0x01 /* (to 0x0154 state 317) */,
/* 1 */ 0x03 /* (to 0x0158 state 322) */,
/* pos 0154: 317 */ /* terminal 147 */ 0x93,
/* terminal 149 */ 0x95,
/* pos 0156: 319 */ /* terminal 148 */ 0x94,
/* terminal 159 */ 0x9F,
/* pos 0158: 322 */ /* terminal 150 */ 0x96,
/* terminal 151 */ 0x97,
/* pos 015a: 325 */ /* 0 */ 0x01 /* (to 0x015C state 326) */,
/* 1 */ 0x08 /* (to 0x016A state 338) */,
/* pos 015c: 326 */ /* terminal 152 */ 0x98,
/* terminal 155 */ 0x9B,
/* pos 015e: 328 */ /* 0 */ 0x42 /* (to 0x01E2 state 465) */,
/* 1 */ 0x01 /* (to 0x0160 state 329) */,
/* pos 0160: 329 */ /* 0 */ 0x01 /* (to 0x0162 state 330) */,
/* 1 */ 0x0C /* (to 0x0178 state 355) */,
/* pos 0162: 330 */ /* terminal 153 */ 0x99,
/* terminal 161 */ 0xA1,
/* pos 0164: 332 */ /* 0 */ 0x01 /* (to 0x0166 state 333) */,
/* 1 */ 0x05 /* (to 0x016E state 347) */,
/* pos 0166: 333 */ /* 0 */ 0x01 /* (to 0x0168 state 334) */,
/* 1 */ 0x03 /* (to 0x016C state 342) */,
/* pos 0168: 334 */ /* terminal 154 */ 0x9A,
/* terminal 156 */ 0x9C,
/* pos 016a: 338 */ /* terminal 157 */ 0x9D,
/* terminal 158 */ 0x9E,
/* pos 016c: 342 */ /* terminal 160 */ 0xA0,
/* terminal 163 */ 0xA3,
/* pos 016e: 347 */ /* 0 */ 0x01 /* (to 0x0170 state 348) */,
/* 1 */ 0x07 /* (to 0x017C state 360) */,
/* pos 0170: 348 */ /* terminal 164 */ 0xA4,
/* terminal 169 */ 0xA9,
/* pos 0172: 350 */ /* 0 */ 0x01 /* (to 0x0174 state 351) */,
/* 1 */ 0x09 /* (to 0x0184 state 369) */,
/* pos 0174: 351 */ /* 0 */ 0x01 /* (to 0x0176 state 352) */,
/* 1 */ 0x03 /* (to 0x017A state 357) */,
/* pos 0176: 352 */ /* terminal 165 */ 0xA5,
/* terminal 166 */ 0xA6,
/* pos 0178: 355 */ /* terminal 167 */ 0xA7,
/* terminal 172 */ 0xAC,
/* pos 017a: 357 */ /* terminal 168 */ 0xA8,
/* terminal 174 */ 0xAE,
/* pos 017c: 360 */ /* terminal 170 */ 0xAA,
/* terminal 173 */ 0xAD,
/* pos 017e: 362 */ /* 0 */ 0x01 /* (to 0x0180 state 363) */,
/* 1 */ 0x1B /* (to 0x01B4 state 417) */,
/* pos 0180: 363 */ /* 0 */ 0x01 /* (to 0x0182 state 364) */,
/* 1 */ 0x2A /* (to 0x01D4 state 449) */,
/* pos 0182: 364 */ /* terminal 171 */ 0xAB,
/* terminal 206 */ 0xCE,
/* pos 0184: 369 */ /* 0 */ 0x01 /* (to 0x0186 state 370) */,
/* 1 */ 0x09 /* (to 0x0196 state 385) */,
/* pos 0186: 370 */ /* terminal 175 */ 0xAF,
/* terminal 180 */ 0xB4,
/* pos 0188: 372 */ /* 0 */ 0x01 /* (to 0x018A state 373) */,
/* 1 */ 0x27 /* (to 0x01D6 state 451) */,
/* pos 018a: 373 */ /* 0 */ 0x01 /* (to 0x018C state 374) */,
/* 1 */ 0x05 /* (to 0x0194 state 381) */,
/* pos 018c: 374 */ /* terminal 176 */ 0xB0,
/* terminal 177 */ 0xB1,
/* pos 018e: 377 */ /* 0 */ 0x01 /* (to 0x0190 state 378) */,
/* 1 */ 0x07 /* (to 0x019C state 393) */,
/* pos 0190: 378 */ /* 0 */ 0x01 /* (to 0x0192 state 379) */,
/* 1 */ 0x05 /* (to 0x019A state 390) */,
/* pos 0192: 379 */ /* terminal 178 */ 0xB2,
/* terminal 181 */ 0xB5,
/* pos 0194: 381 */ /* terminal 179 */ 0xB3,
/* terminal 209 */ 0xD1,
/* pos 0196: 385 */ /* terminal 182 */ 0xB6,
/* terminal 183 */ 0xB7,
/* pos 0198: 388 */ /* terminal 184 */ 0xB8,
/* terminal 194 */ 0xC2,
/* pos 019a: 390 */ /* terminal 185 */ 0xB9,
/* terminal 186 */ 0xBA,
/* pos 019c: 393 */ /* 0 */ 0x01 /* (to 0x019E state 394) */,
/* 1 */ 0x04 /* (to 0x01A4 state 400) */,
/* pos 019e: 394 */ /* terminal 187 */ 0xBB,
/* terminal 189 */ 0xBD,
/* pos 01a0: 396 */ /* 0 */ 0x01 /* (to 0x01A2 state 397) */,
/* 1 */ 0x07 /* (to 0x01AE state 412) */,
/* pos 01a2: 397 */ /* terminal 188 */ 0xBC,
/* terminal 191 */ 0xBF,
/* pos 01a4: 400 */ /* terminal 190 */ 0xBE,
/* terminal 196 */ 0xC4,
/* pos 01a6: 403 */ /* 0 */ 0x01 /* (to 0x01A8 state 404) */,
/* 1 */ 0x0D /* (to 0x01C0 state 427) */,
/* pos 01a8: 404 */ /* 0 */ 0x01 /* (to 0x01AA state 405) */,
/* 1 */ 0x0A /* (to 0x01BC state 424) */,
/* pos 01aa: 405 */ /* 0 */ 0x01 /* (to 0x01AC state 406) */,
/* 1 */ 0x08 /* (to 0x01BA state 421) */,
/* pos 01ac: 406 */ /* terminal 192 */ 0xC0,
/* terminal 193 */ 0xC1,
/* pos 01ae: 412 */ /* terminal 197 */ 0xC5,
/* terminal 231 */ 0xE7,
/* pos 01b0: 414 */ /* 0 */ 0x01 /* (to 0x01B2 state 415) */,
/* 1 */ 0x1B /* (to 0x01E6 state 475) */,
/* pos 01b2: 415 */ /* terminal 198 */ 0xC6,
/* terminal 228 */ 0xE4,
/* pos 01b4: 417 */ /* 0 */ 0x1B /* (to 0x01EA state 481) */,
/* 1 */ 0x01 /* (to 0x01B6 state 418) */,
/* pos 01b6: 418 */ /* 0 */ 0x01 /* (to 0x01B8 state 419) */,
/* 1 */ 0x19 /* (to 0x01E8 state 478) */,
/* pos 01b8: 419 */ /* terminal 199 */ 0xC7,
/* terminal 207 */ 0xCF,
/* pos 01ba: 421 */ /* terminal 200 */ 0xC8,
/* terminal 201 */ 0xC9,
/* pos 01bc: 424 */ /* 0 */ 0x01 /* (to 0x01BE state 425) */,
/* 1 */ 0x06 /* (to 0x01C8 state 438) */,
/* pos 01be: 425 */ /* terminal 202 */ 0xCA,
/* terminal 205 */ 0xCD,
/* pos 01c0: 427 */ /* 0 */ 0x0D /* (to 0x01DA state 455) */,
/* 1 */ 0x01 /* (to 0x01C2 state 428) */,
/* pos 01c2: 428 */ /* 0 */ 0x17 /* (to 0x01F0 state 490) */,
/* 1 */ 0x01 /* (to 0x01C4 state 429) */,
/* pos 01c4: 429 */ /* terminal 255 */ 0xFF,
/* 1 */ 0x01 /* (to 0x01C6 state 430) */,
/* pos 01c6: 430 */ /* terminal 203 */ 0xCB,
/* terminal 204 */ 0xCC,
/* pos 01c8: 438 */ /* terminal 210 */ 0xD2,
/* terminal 213 */ 0xD5,
/* pos 01ca: 440 */ /* 0 */ 0x01 /* (to 0x01CC state 441) */,
/* 1 */ 0x14 /* (to 0x01F2 state 494) */,
/* pos 01cc: 441 */ /* 0 */ 0x01 /* (to 0x01CE state 442) */,
/* 1 */ 0x09 /* (to 0x01DE state 461) */,
/* pos 01ce: 442 */ /* 0 */ 0x01 /* (to 0x01D0 state 443) */,
/* 1 */ 0x02 /* (to 0x01D2 state 447) */,
/* pos 01d0: 443 */ /* terminal 211 */ 0xD3,
/* terminal 212 */ 0xD4,
/* pos 01d2: 447 */ /* terminal 214 */ 0xD6,
/* terminal 221 */ 0xDD,
/* pos 01d4: 449 */ /* terminal 215 */ 0xD7,
/* terminal 225 */ 0xE1,
/* pos 01d6: 451 */ /* 0 */ 0x01 /* (to 0x01D8 state 452) */,
/* 1 */ 0x07 /* (to 0x01E4 state 469) */,
/* pos 01d8: 452 */ /* terminal 216 */ 0xD8,
/* terminal 217 */ 0xD9,
/* pos 01da: 455 */ /* 0 */ 0x01 /* (to 0x01DC state 456) */,
/* 1 */ 0x09 /* (to 0x01EC state 484) */,
/* pos 01dc: 456 */ /* terminal 218 */ 0xDA,
/* terminal 219 */ 0xDB,
/* pos 01de: 461 */ /* 0 */ 0x01 /* (to 0x01E0 state 462) */,
/* 1 */ 0x08 /* (to 0x01EE state 488) */,
/* pos 01e0: 462 */ /* terminal 222 */ 0xDE,
/* terminal 223 */ 0xDF,
/* pos 01e2: 465 */ /* terminal 224 */ 0xE0,
/* terminal 226 */ 0xE2,
/* pos 01e4: 469 */ /* terminal 227 */ 0xE3,
/* terminal 229 */ 0xE5,
/* pos 01e6: 475 */ /* terminal 232 */ 0xE8,
/* terminal 233 */ 0xE9,
/* pos 01e8: 478 */ /* terminal 234 */ 0xEA,
/* terminal 235 */ 0xEB,
/* pos 01ea: 481 */ /* terminal 236 */ 0xEC,
/* terminal 237 */ 0xED,
/* pos 01ec: 484 */ /* terminal 238 */ 0xEE,
/* terminal 240 */ 0xF0,
/* pos 01ee: 488 */ /* terminal 241 */ 0xF1,
/* terminal 244 */ 0xF4,
/* pos 01f0: 490 */ /* terminal 242 */ 0xF2,
/* terminal 243 */ 0xF3,
/* pos 01f2: 494 */ /* 0 */ 0x01 /* (to 0x01F4 state 495) */,
/* 1 */ 0x04 /* (to 0x01FA state 503) */,
/* pos 01f4: 495 */ /* 0 */ 0x01 /* (to 0x01F6 state 496) */,
/* 1 */ 0x02 /* (to 0x01F8 state 499) */,
/* pos 01f6: 496 */ /* terminal 245 */ 0xF5,
/* terminal 246 */ 0xF6,
/* pos 01f8: 499 */ /* terminal 247 */ 0xF7,
/* terminal 248 */ 0xF8,
/* pos 01fa: 503 */ /* 0 */ 0x01 /* (to 0x01FC state 504) */,
/* 1 */ 0x02 /* (to 0x01FE state 507) */,
/* pos 01fc: 504 */ /* terminal 250 */ 0xFA,
/* terminal 251 */ 0xFB,
/* pos 01fe: 507 */ /* terminal 252 */ 0xFC,
/* terminal 253 */ 0xFD,
/* total size 512 bytes, biggest jump 200/256, fails=0 */
};
static unsigned char lextable_terms[] = {
0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x03, 0x00,
0x34, 0x0f, 0x43, 0x03, 0xf1, 0x3c, 0xfc, 0x3c,
0x0f, 0x30, 0x37, 0xf7, 0x0f, 0xc3, 0xcf, 0x03,
0x3c, 0xfc, 0xc0, 0xf3, 0xf0, 0x3c, 0xfc, 0xf0,
0xcf, 0xfc, 0xcc, 0xff, 0xfc, 0x0d, 0x34, 0xcc,
0xcf, 0x33, 0xf0, 0x33, 0x0c, 0x3f, 0xc3, 0x3f,
0xcc, 0x30, 0xfc, 0xcf, 0x3c, 0xf0, 0x0c, 0xcf,
0xd0, 0x03, 0x3f, 0x33, 0xff, 0xff, 0xc3, 0xf3,
};
/* state that points to 0x100 for disambiguation with 0x0 */
#define HUFTABLE_0x100_PREV 118

View File

@ -0,0 +1,516 @@
/*
* minilex.c
*
* High efficiency lexical state parser
*
* Copyright (C)2011-2014 Andy Green <andy@warmcat.com>
*
* Licensed under MIT
*
* Usage: gcc minihuf.c -o minihuf && ./minihuf > huftable.h
*
* Run it twice to test parsing on the generated table on stderr
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LWS_ARRAY_SIZE(n) (sizeof(n) / sizeof(n[0]))
struct huf {
unsigned int code;
unsigned char len;
};
static struct huf huf_literal[] = {
/* 0x00 */ { 0x1ff8, 13 },
/* 0x01 */ { 0x7fffd8, 23 },
/* 0x02 */ { 0xfffffe2, 28 },
/* 0x03 */ { 0xfffffe3, 28 },
/* 0x04 */ { 0xfffffe4, 28 },
/* 0x05 */ { 0xfffffe5, 28 },
/* 0x06 */ { 0xfffffe6, 28 },
/* 0x07 */ { 0xfffffe7, 28 },
/* 0x08 */ { 0xfffffe8, 28 },
/* 0x09 */ { 0xffffea, 24 },
/* 0x0a */ { 0x3ffffffc, 30 },
/* 0x0b */ { 0xfffffe9, 28 },
/* 0x0c */ { 0xfffffea, 28 },
/* 0x0d */ { 0x3ffffffd, 30 },
/* 0x0e */ { 0xfffffeb, 28 },
/* 0x0f */ { 0xfffffec, 28 },
/* 0x10 */ { 0xfffffed, 28 },
/* 0x11 */ { 0xfffffee, 28 },
/* 0x12 */ { 0xfffffef, 28 },
/* 0x13 */ { 0xffffff0, 28 },
/* 0x14 */ { 0xffffff1, 28 },
/* 0x15 */ { 0xffffff2, 28 },
/* 0x16 */ { 0x3ffffffe, 30 },
/* 0x17 */ { 0xffffff3, 28 },
/* 0x18 */ { 0xffffff4, 28 },
/* 0x19 */ { 0xffffff5, 28 },
/* 0x1a */ { 0xffffff6, 28 },
/* 0x1b */ { 0xffffff7, 28 },
/* 0x1c */ { 0xffffff8, 28 },
/* 0x1d */ { 0xffffff9, 28 },
/* 0x1e */ { 0xffffffa, 28 },
/* 0x1f */ { 0xffffffb, 28 },
/* 0x20 */ { 0x14, 6 },
/* 0x21 */ { 0x3f8, 10 },
/* 0x22 */ { 0x3f9, 10 },
/* 0x23 */ { 0xffa, 12 },
/* 0x24 */ { 0x1ff9, 13 },
/* 0x25 */ { 0x15, 6 },
/* 0x26 */ { 0xf8, 8 },
/* 0x27 */ { 0x7fa, 11 },
/* 0x28 */ { 0x3fa, 10 },
/* 0x29 */ { 0x3fb, 10 },
/* 0x2a */ { 0xf9, 8 },
/* 0x2b */ { 0x7fb, 11 },
/* 0x2c */ { 0xfa, 8 },
/* 0x2d */ { 0x16, 6 },
/* 0x2e */ { 0x17, 6 },
/* 0x2f */ { 0x18, 6 },
/* 0x30 */ { 0x0, 5 },
/* 0x31 */ { 0x1, 5 },
/* 0x32 */ { 0x2, 5 },
/* 0x33 */ { 0x19, 6 },
/* 0x34 */ { 0x1a, 6 },
/* 0x35 */ { 0x1b, 6 },
/* 0x36 */ { 0x1c, 6 },
/* 0x37 */ { 0x1d, 6 },
/* 0x38 */ { 0x1e, 6 },
/* 0x39 */ { 0x1f, 6 },
/* 0x3a */ { 0x5c, 7 },
/* 0x3b */ { 0xfb, 8 },
/* 0x3c */ { 0x7ffc, 15 },
/* 0x3d */ { 0x20, 6 },
/* 0x3e */ { 0xffb, 12 },
/* 0x3f */ { 0x3fc, 10 },
/* 0x40 */ { 0x1ffa, 13 },
/* 0x41 */ { 0x21, 6 },
/* 0x42 */ { 0x5d, 7 },
/* 0x43 */ { 0x5e, 7 },
/* 0x44 */ { 0x5f, 7 },
/* 0x45 */ { 0x60, 7 },
/* 0x46 */ { 0x61, 7 },
/* 0x47 */ { 0x62, 7 },
/* 0x48 */ { 0x63, 7 },
/* 0x49 */ { 0x64, 7 },
/* 0x4a */ { 0x65, 7 },
/* 0x4b */ { 0x66, 7 },
/* 0x4c */ { 0x67, 7 },
/* 0x4d */ { 0x68, 7 },
/* 0x4e */ { 0x69, 7 },
/* 0x4f */ { 0x6a, 7 },
/* 0x50 */ { 0x6b, 7 },
/* 0x51 */ { 0x6c, 7 },
/* 0x52 */ { 0x6d, 7 },
/* 0x53 */ { 0x6e, 7 },
/* 0x54 */ { 0x6f, 7 },
/* 0x55 */ { 0x70, 7 },
/* 0x56 */ { 0x71, 7 },
/* 0x57 */ { 0x72, 7 },
/* 0x58 */ { 0xfc, 8 },
/* 0x59 */ { 0x73, 7 },
/* 0x5a */ { 0xfd, 8 },
/* 0x5b */ { 0x1ffb, 13 },
/* 0x5c */ { 0x7fff0, 19 },
/* 0x5d */ { 0x1ffc, 13 },
/* 0x5e */ { 0x3ffc, 14 },
/* 0x5f */ { 0x22, 6 },
/* 0x60 */ { 0x7ffd, 15 },
/* 0x61 */ { 0x3, 5 },
/* 0x62 */ { 0x23, 6 },
/* 0x63 */ { 0x4, 5 },
/* 0x64 */ { 0x24, 6 },
/* 0x65 */ { 0x5, 5 },
/* 0x66 */ { 0x25, 6 },
/* 0x67 */ { 0x26, 6 },
/* 0x68 */ { 0x27, 6 },
/* 0x69 */ { 0x6, 5 },
/* 0x6a */ { 0x74, 7 },
/* 0x6b */ { 0x75, 7 },
/* 0x6c */ { 0x28, 6 },
/* 0x6d */ { 0x29, 6 },
/* 0x6e */ { 0x2a, 6 },
/* 0x6f */ { 0x7, 5 },
/* 0x70 */ { 0x2b, 6 },
/* 0x71 */ { 0x76, 7 },
/* 0x72 */ { 0x2c, 6 },
/* 0x73 */ { 0x8, 5 },
/* 0x74 */ { 0x9, 5 },
/* 0x75 */ { 0x2d, 6 },
/* 0x76 */ { 0x77, 7 },
/* 0x77 */ { 0x78, 7 },
/* 0x78 */ { 0x79, 7 },
/* 0x79 */ { 0x7a, 7 },
/* 0x7a */ { 0x7b, 7 },
/* 0x7b */ { 0x7ffe, 15 },
/* 0x7c */ { 0x7fc, 11 },
/* 0x7d */ { 0x3ffd, 14 },
/* 0x7e */ { 0x1ffd, 13 },
/* 0x7f */ { 0xffffffc, 28 },
/* 0x80 */ { 0xfffe6, 20 },
/* 0x81 */ { 0x3fffd2, 22 },
/* 0x82 */ { 0xfffe7, 20 },
/* 0x83 */ { 0xfffe8, 20 },
/* 0x84 */ { 0x3fffd3, 22 },
/* 0x85 */ { 0x3fffd4, 22 },
/* 0x86 */ { 0x3fffd5, 22 },
/* 0x87 */ { 0x7fffd9, 23 },
/* 0x88 */ { 0x3fffd6, 22 },
/* 0x89 */ { 0x7fffda, 23 },
/* 0x8a */ { 0x7fffdb, 23 },
/* 0x8b */ { 0x7fffdc, 23 },
/* 0x8c */ { 0x7fffdd, 23 },
/* 0x8d */ { 0x7fffde, 23 },
/* 0x8e */ { 0xffffeb, 24 },
/* 0x8f */ { 0x7fffdf, 23 },
/* 0x90 */ { 0xffffec, 24 },
/* 0x91 */ { 0xffffed, 24 },
/* 0x92 */ { 0x3fffd7, 22 },
/* 0x93 */ { 0x7fffe0, 23 },
/* 0x94 */ { 0xffffee, 24 },
/* 0x95 */ { 0x7fffe1, 23 },
/* 0x96 */ { 0x7fffe2, 23 },
/* 0x97 */ { 0x7fffe3, 23 },
/* 0x98 */ { 0x7fffe4, 23 },
/* 0x99 */ { 0x1fffdc, 21 },
/* 0x9a */ { 0x3fffd8, 22 },
/* 0x9b */ { 0x7fffe5, 23 },
/* 0x9c */ { 0x3fffd9, 22 },
/* 0x9d */ { 0x7fffe6, 23 },
/* 0x9e */ { 0x7fffe7, 23 },
/* 0x9f */ { 0xffffef, 24 },
/* 0xa0 */ { 0x3fffda, 22 },
/* 0xa1 */ { 0x1fffdd, 21 },
/* 0xa2 */ { 0xfffe9, 20 },
/* 0xa3 */ { 0x3fffdb, 22 },
/* 0xa4 */ { 0x3fffdc, 22 },
/* 0xa5 */ { 0x7fffe8, 23 },
/* 0xa6 */ { 0x7fffe9, 23 },
/* 0xa7 */ { 0x1fffde, 21 },
/* 0xa8 */ { 0x7fffea, 23 },
/* 0xa9 */ { 0x3fffdd, 22 },
/* 0xaa */ { 0x3fffde, 22 },
/* 0xab */ { 0xfffff0, 24 },
/* 0xac */ { 0x1fffdf, 21 },
/* 0xad */ { 0x3fffdf, 22 },
/* 0xae */ { 0x7fffeb, 23 },
/* 0xaf */ { 0x7fffec, 23 },
/* 0xb0 */ { 0x1fffe0, 21 },
/* 0xb1 */ { 0x1fffe1, 21 },
/* 0xb2 */ { 0x3fffe0, 22 },
/* 0xb3 */ { 0x1fffe2, 21 },
/* 0xb4 */ { 0x7fffed, 23 },
/* 0xb5 */ { 0x3fffe1, 22 },
/* 0xb6 */ { 0x7fffee, 23 },
/* 0xb7 */ { 0x7fffef, 23 },
/* 0xb8 */ { 0xfffea, 20 },
/* 0xb9 */ { 0x3fffe2, 22 },
/* 0xba */ { 0x3fffe3, 22 },
/* 0xbb */ { 0x3fffe4, 22 },
/* 0xbc */ { 0x7ffff0, 23 },
/* 0xbd */ { 0x3fffe5, 22 },
/* 0xbe */ { 0x3fffe6, 22 },
/* 0xbf */ { 0x7ffff1, 23 },
/* 0xc0 */ { 0x3ffffe0, 26 },
/* 0xc1 */ { 0x3ffffe1, 26 },
/* 0xc2 */ { 0xfffeb, 20 },
/* 0xc3 */ { 0x7fff1, 19 },
/* 0xc4 */ { 0x3fffe7, 22 },
/* 0xc5 */ { 0x7ffff2, 23 },
/* 0xc6 */ { 0x3fffe8, 22 },
/* 0xc7 */ { 0x1ffffec, 25 },
/* 0xc8 */ { 0x3ffffe2, 26 },
/* 0xc9 */ { 0x3ffffe3, 26 },
/* 0xca */ { 0x3ffffe4, 26 },
/* 0xcb */ { 0x7ffffde, 27 },
/* 0xcc */ { 0x7ffffdf, 27 },
/* 0xcd */ { 0x3ffffe5, 26 },
/* 0xce */ { 0xfffff1, 24 },
/* 0xcf */ { 0x1ffffed, 25 },
/* 0xd0 */ { 0x7fff2, 19 },
/* 0xd1 */ { 0x1fffe3, 21 },
/* 0xd2 */ { 0x3ffffe6, 26 },
/* 0xd3 */ { 0x7ffffe0, 27 },
/* 0xd4 */ { 0x7ffffe1, 27 },
/* 0xd5 */ { 0x3ffffe7, 26 },
/* 0xd6 */ { 0x7ffffe2, 27 },
/* 0xd7 */ { 0xfffff2, 24 },
/* 0xd8 */ { 0x1fffe4, 21 },
/* 0xd9 */ { 0x1fffe5, 21 },
/* 0xda */ { 0x3ffffe8, 26 },
/* 0xdb */ { 0x3ffffe9, 26 },
/* 0xdc */ { 0xffffffd, 28 },
/* 0xdd */ { 0x7ffffe3, 27 },
/* 0xde */ { 0x7ffffe4, 27 },
/* 0xdf */ { 0x7ffffe5, 27 },
/* 0xe0 */ { 0xfffec, 20 },
/* 0xe1 */ { 0xfffff3, 24 },
/* 0xe2 */ { 0xfffed, 20 },
/* 0xe3 */ { 0x1fffe6, 21 },
/* 0xe4 */ { 0x3fffe9, 22 },
/* 0xe5 */ { 0x1fffe7, 21 },
/* 0xe6 */ { 0x1fffe8, 21 },
/* 0xe7 */ { 0x7ffff3, 23 },
/* 0xe8 */ { 0x3fffea, 22 },
/* 0xe9 */ { 0x3fffeb, 22 },
/* 0xea */ { 0x1ffffee, 25 },
/* 0xeb */ { 0x1ffffef, 25 },
/* 0xec */ { 0xfffff4, 24 },
/* 0xed */ { 0xfffff5, 24 },
/* 0xee */ { 0x3ffffea, 26 },
/* 0xef */ { 0x7ffff4, 23 },
/* 0xf0 */ { 0x3ffffeb, 26 },
/* 0xf1 */ { 0x7ffffe6, 27 },
/* 0xf2 */ { 0x3ffffec, 26 },
/* 0xf3 */ { 0x3ffffed, 26 },
/* 0xf4 */ { 0x7ffffe7, 27 },
/* 0xf5 */ { 0x7ffffe8, 27 },
/* 0xf6 */ { 0x7ffffe9, 27 },
/* 0xf7 */ { 0x7ffffea, 27 },
/* 0xf8 */ { 0x7ffffeb, 27 },
/* 0xf9 */ { 0xffffffe, 28 },
/* 0xfa */ { 0x7ffffec, 27 },
/* 0xfb */ { 0x7ffffed, 27 },
/* 0xfc */ { 0x7ffffee, 27 },
/* 0xfd */ { 0x7ffffef, 27 },
/* 0xfe */ { 0x7fffff0, 27 },
/* 0xff */ { 0x3ffffee, 26 },
/* 0x100 */ { 0x3fffffff, 30 },
};
int code_bit(int idx, int bit)
{
if (bit < huf_literal[idx].len)
return !!(huf_literal[idx].code & (1 << (huf_literal[idx].len - 1 - bit)));
return -1;
}
#include "huftable.h"
#define PARALLEL 2
struct state {
int terminal;
int state[PARALLEL];
int bytepos;
int real_pos;
};
struct state state[2000];
unsigned char terms[2000];
int next = 1;
int lextable_decode(int pos, char c)
{
int q = pos + !!c;
if (lextable_terms[q >> 3] & (1 << (q & 7))) /* terminal */
return lextable[q] | 0x8000;
return pos + (lextable[q] << 1);
}
int main(void)
{
int n = 0;
int m = 0;
int prev;
char c;
int walk;
int saw;
int y;
int j;
int pos = 0;
int biggest = 0;
int fails = 0;
m = 0;
while (m < LWS_ARRAY_SIZE(state)) {
for (j = 0; j < PARALLEL; j++) {
state[m].state[j] = 0xffff;
state[m].terminal = 0;
}
m++;
}
while (n < LWS_ARRAY_SIZE(huf_literal)) {
m = 0;
walk = 0;
prev = 0;
while (m < huf_literal[n].len) {
saw = 0;
if (state[walk].state[code_bit(n, m)] != 0xffff) {
/* exists -- go forward */
walk = state[walk].state[code_bit(n, m)];
goto again;
}
/* something we didn't see before */
state[walk].state[code_bit(n, m)] = next;
walk = next++;
again:
m++;
}
state[walk].terminal = n++;
state[walk].state[0] = 0; /* terminal marker */
}
walk = 0;
for (n = 0; n < next; n++) {
state[n].bytepos = walk;
walk += (2 * 2);
}
/* compute everyone's position first */
pos = 0;
walk = 0;
for (n = 0; n < next; n++) {
state[n].real_pos = pos;
if (state[n].state[0]) /* nonterminal */
pos += 2;
walk ++;
}
fprintf(stdout, "static unsigned char lextable[] = {\n");
#define TERMINAL_MASK 0x8000
walk = 0;
pos = 0;
for (n = 0; n < next; n++) {
for (m = 0; m < 2; m++) {
saw = state[n].state[m];
if (saw == 0) { // c is a terminal then
m = 2;
continue;
}
if (!m)
fprintf(stdout, "/* pos %04x: %3d */ ",
state[n].real_pos, n);
else
fprintf(stdout, " ");
if (saw == 0xffff) {
fprintf(stdout,
" 0xff, 0xff, /* 0 = fail */\n ");
pos ++; /* fail */
fails++;
continue;
}
if (state[saw].state[0] == 0) { /* points to terminal */
fprintf(stdout, " /* terminal %d */ 0x%02X,\n",
state[saw].terminal,
state[saw].terminal & 0xff);
terms[(state[n].real_pos + m) >> 3] |=
1 << ((state[n].real_pos + m) & 7);
pos++;
walk++;
continue;
}
j = (state[saw].real_pos - q) >> 1;
if (j > biggest)
biggest = j;
if (j > 0xffff) {
fprintf(stderr,
"Jump > 64K bytes ahead (%d to %d)\n",
state[n].real_pos, state[saw].real_pos);
return 1;
}
fprintf(stdout, " /* %d */ 0x%02X "
"/* (to 0x%04X state %3d) */,\n",
m,
j & 0xff,
state[saw].real_pos, saw);
pos++;
walk++;
}
}
fprintf(stdout, "/* total size %d bytes, biggest jump %d/256, fails=%d */\n};\n"
"\n static unsigned char lextable_terms[] = {\n",
pos, biggest, fails);
for (n = 0; n < (walk + 7) / 8; n++) {
if (!(n & 7))
fprintf(stdout, "\n\t");
fprintf(stdout, "0x%02x, ", terms[n]);
}
fprintf(stdout, "\n};\n");
/*
* Try to parse every legal input string
*/
for (n = 0; n < LWS_ARRAY_SIZE(huf_literal); n++) {
walk = 0;
m = 0;
y = -1;
fprintf(stderr, " trying %d\n", n);
while (m < huf_literal[n].len) {
prev = walk;
walk = lextable_decode(walk, code_bit(n, m));
if (walk == 0xffff) {
fprintf(stderr, "failed\n");
return 3;
}
if (walk & 0x8000) {
y = walk & 0x7fff;
if (y == 0 && m == 29) {
y |= 0x100;
fprintf(stdout,
"\n/* state that points to "
"0x100 for disambiguation with "
"0x0 */\n"
"#define HUFTABLE_0x100_PREV "
"%d\n", prev);
}
break;
}
m++;
}
if (y != n) {
fprintf(stderr, "decode failed %d got %d (0x%x)\n", n, y, y);
return 4;
}
}
fprintf(stderr, "All decode OK\n");
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,384 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
extern const struct lws_role_ops role_ops_h2;
#define lwsi_role_h2(wsi) (wsi->role_ops == &role_ops_h2)
struct http2_settings {
uint32_t s[H2SET_COUNT];
};
struct lws_vhost_role_h2 {
struct http2_settings set;
};
enum lws_h2_wellknown_frame_types {
LWS_H2_FRAME_TYPE_DATA,
LWS_H2_FRAME_TYPE_HEADERS,
LWS_H2_FRAME_TYPE_PRIORITY,
LWS_H2_FRAME_TYPE_RST_STREAM,
LWS_H2_FRAME_TYPE_SETTINGS,
LWS_H2_FRAME_TYPE_PUSH_PROMISE,
LWS_H2_FRAME_TYPE_PING,
LWS_H2_FRAME_TYPE_GOAWAY,
LWS_H2_FRAME_TYPE_WINDOW_UPDATE,
LWS_H2_FRAME_TYPE_CONTINUATION,
LWS_H2_FRAME_TYPE_COUNT /* always last */
};
enum lws_h2_flags {
LWS_H2_FLAG_END_STREAM = 1,
LWS_H2_FLAG_END_HEADERS = 4,
LWS_H2_FLAG_PADDED = 8,
LWS_H2_FLAG_PRIORITY = 0x20,
LWS_H2_FLAG_SETTINGS_ACK = 1,
};
enum lws_h2_errors {
H2_ERR_NO_ERROR, /* Graceful shutdown */
H2_ERR_PROTOCOL_ERROR, /* Protocol error detected */
H2_ERR_INTERNAL_ERROR, /* Implementation fault */
H2_ERR_FLOW_CONTROL_ERROR, /* Flow-control limits exceeded */
H2_ERR_SETTINGS_TIMEOUT, /* Settings not acknowledged */
H2_ERR_STREAM_CLOSED, /* Frame received for closed stream */
H2_ERR_FRAME_SIZE_ERROR, /* Frame size incorrect */
H2_ERR_REFUSED_STREAM, /* Stream not processed */
H2_ERR_CANCEL, /* Stream cancelled */
H2_ERR_COMPRESSION_ERROR, /* Compression state not updated */
H2_ERR_CONNECT_ERROR, /* TCP connection error for CONNECT method */
H2_ERR_ENHANCE_YOUR_CALM, /* Processing capacity exceeded */
H2_ERR_INADEQUATE_SECURITY, /* Negotiated TLS parameters not acceptable */
H2_ERR_HTTP_1_1_REQUIRED, /* Use HTTP/1.1 for the request */
};
enum lws_h2_states {
LWS_H2_STATE_IDLE,
/*
* Send PUSH_PROMISE -> LWS_H2_STATE_RESERVED_LOCAL
* Recv PUSH_PROMISE -> LWS_H2_STATE_RESERVED_REMOTE
* Send HEADERS -> LWS_H2_STATE_OPEN
* Recv HEADERS -> LWS_H2_STATE_OPEN
*
* - Only PUSH_PROMISE + HEADERS valid to send
* - Only HEADERS or PRIORITY valid to receive
*/
LWS_H2_STATE_RESERVED_LOCAL,
/*
* Send RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv RST_STREAM -> LWS_H2_STATE_CLOSED
* Send HEADERS -> LWS_H2_STATE_HALF_CLOSED_REMOTE
*
* - Only HEADERS, RST_STREAM, or PRIORITY valid to send
* - Only RST_STREAM, PRIORITY, or WINDOW_UPDATE valid to receive
*/
LWS_H2_STATE_RESERVED_REMOTE,
/*
* Send RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv HEADERS -> LWS_H2_STATE_HALF_CLOSED_LOCAL
*
* - Only RST_STREAM, WINDOW_UPDATE, or PRIORITY valid to send
* - Only HEADERS, RST_STREAM, or PRIORITY valid to receive
*/
LWS_H2_STATE_OPEN,
/*
* Send RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv RST_STREAM -> LWS_H2_STATE_CLOSED
* Send END_STREAM flag -> LWS_H2_STATE_HALF_CLOSED_LOCAL
* Recv END_STREAM flag -> LWS_H2_STATE_HALF_CLOSED_REMOTE
*/
LWS_H2_STATE_HALF_CLOSED_REMOTE,
/*
* Send RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv RST_STREAM -> LWS_H2_STATE_CLOSED
* Send END_STREAM flag -> LWS_H2_STATE_CLOSED
*
* - Any frame valid to send
* - Only WINDOW_UPDATE, PRIORITY, or RST_STREAM valid to receive
*/
LWS_H2_STATE_HALF_CLOSED_LOCAL,
/*
* Send RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv RST_STREAM -> LWS_H2_STATE_CLOSED
* Recv END_STREAM flag -> LWS_H2_STATE_CLOSED
*
* - Only WINDOW_UPDATE, PRIORITY, and RST_STREAM valid to send
* - Any frame valid to receive
*/
LWS_H2_STATE_CLOSED,
/*
* - Only PRIORITY, WINDOW_UPDATE (IGNORE) and RST_STREAM (IGNORE)
* may be received
*
* - Only PRIORITY valid to send
*/
};
void
lws_h2_state(struct lws *wsi, enum lws_h2_states s);
#define LWS_H2_STREAM_ID_MASTER 0
#define LWS_H2_SETTINGS_LEN 6
#define LWS_H2_FLAG_SETTINGS_ACK 1
enum http2_hpack_state {
HPKS_TYPE,
HPKS_IDX_EXT,
HPKS_HLEN,
HPKS_HLEN_EXT,
HPKS_DATA,
};
/*
* lws general parsimonious header strategy is only store values from known
* headers, and refer to them by index.
*
* That means if we can't map the peer header name to one that lws knows, we
* will drop the content but track the indexing with associated_lws_hdr_idx =
* LWS_HPACK_IGNORE_ENTRY.
*/
enum http2_hpack_type {
HPKT_INDEXED_HDR_7, /* 1xxxxxxx: just "header field" */
HPKT_INDEXED_HDR_6_VALUE_INCR, /* 01xxxxxx: NEW indexed hdr with value */
HPKT_LITERAL_HDR_VALUE_INCR, /* 01000000: NEW literal hdr with value */
HPKT_INDEXED_HDR_4_VALUE, /* 0000xxxx: indexed hdr with value */
HPKT_INDEXED_HDR_4_VALUE_NEVER, /* 0001xxxx: indexed hdr with value NEVER NEW */
HPKT_LITERAL_HDR_VALUE, /* 00000000: literal hdr with value */
HPKT_LITERAL_HDR_VALUE_NEVER, /* 00010000: literal hdr with value NEVER NEW */
HPKT_SIZE_5
};
#define LWS_HPACK_IGNORE_ENTRY 0xffff
struct hpack_dt_entry {
char *value; /* malloc'd */
uint16_t value_len;
uint16_t hdr_len; /* virtual, for accounting */
uint16_t lws_hdr_idx; /* LWS_HPACK_IGNORE_ENTRY = IGNORE */
};
struct hpack_dynamic_table {
struct hpack_dt_entry *entries; /* malloc'd */
uint32_t virtual_payload_usage;
uint32_t virtual_payload_max;
uint16_t pos;
uint16_t used_entries;
uint16_t num_entries;
};
enum lws_h2_protocol_send_type {
LWS_PPS_NONE,
LWS_H2_PPS_MY_SETTINGS,
LWS_H2_PPS_ACK_SETTINGS,
LWS_H2_PPS_PING,
LWS_H2_PPS_PONG,
LWS_H2_PPS_GOAWAY,
LWS_H2_PPS_RST_STREAM,
LWS_H2_PPS_UPDATE_WINDOW,
LWS_H2_PPS_SETTINGS_INITIAL_UPDATE_WINDOW
};
struct lws_h2_protocol_send {
struct lws_h2_protocol_send *next; /* linked list */
enum lws_h2_protocol_send_type type;
union uu {
struct {
char str[32];
uint32_t highest_sid;
uint32_t err;
} ga;
struct {
uint32_t sid;
uint32_t err;
} rs;
struct {
uint8_t ping_payload[8];
} ping;
struct {
uint32_t sid;
uint32_t credit;
} update_window;
} u;
};
struct lws_h2_ghost_sid {
struct lws_h2_ghost_sid *next;
uint32_t sid;
};
/*
* http/2 connection info that is only used by the root connection that has
* the network connection.
*
* h2 tends to spawn many child connections from one network connection, so
* it's necessary to make members only needed by the network connection
* distinct and only malloc'd on network connections.
*
* There's only one HPACK parser per network connection.
*
* But there is an ah per logical child connection... the network connection
* fills it but it belongs to the logical child.
*/
struct lws_h2_netconn {
struct http2_settings our_set;
struct http2_settings peer_set;
struct hpack_dynamic_table hpack_dyn_table;
uint8_t ping_payload[8];
uint8_t one_setting[LWS_H2_SETTINGS_LEN];
char goaway_str[32]; /* for rx */
struct lws *swsi;
struct lws_h2_protocol_send *pps; /* linked list */
enum http2_hpack_state hpack;
enum http2_hpack_type hpack_type;
unsigned int huff:1;
unsigned int value:1;
unsigned int unknown_header:1;
unsigned int cont_exp:1;
unsigned int cont_exp_headers:1;
unsigned int we_told_goaway:1;
unsigned int pad_length:1;
unsigned int collected_priority:1;
unsigned int is_first_header_char:1;
unsigned int zero_huff_padding:1;
unsigned int last_action_dyntable_resize:1;
unsigned int sent_preface:1;
uint32_t hdr_idx;
uint32_t hpack_len;
uint32_t hpack_e_dep;
uint32_t count;
uint32_t preamble;
uint32_t length;
uint32_t sid;
uint32_t inside;
uint32_t highest_sid;
uint32_t highest_sid_opened;
uint32_t cont_exp_sid;
uint32_t dep;
uint32_t goaway_last_sid;
uint32_t goaway_err;
uint32_t hpack_hdr_len;
uint16_t hpack_pos;
uint8_t frame_state;
uint8_t type;
uint8_t flags;
uint8_t padding;
uint8_t weight_temp;
uint8_t huff_pad;
char first_hdr_char;
uint8_t hpack_m;
uint8_t ext_count;
};
struct _lws_h2_related {
struct lws_h2_netconn *h2n; /* malloc'd for root net conn */
char *pending_status_body;
uint8_t h2_state; /* RFC7540 state of the connection */
uint8_t END_STREAM:1;
uint8_t END_HEADERS:1;
uint8_t send_END_STREAM:1;
uint8_t long_poll:1;
uint8_t initialized:1;
};
#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->mux.parent_wsi)
int
lws_h2_rst_stream(struct lws *wsi, uint32_t err, const char *reason);
struct lws * lws_h2_get_nth_child(struct lws *wsi, int n);
void lws_h2_init(struct lws *wsi);
int
lws_h2_settings(struct lws *nwsi, struct http2_settings *settings,
unsigned char *buf, int len);
int
lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
lws_filepos_t *inused);
int
lws_h2_do_pps_send(struct lws *wsi);
int
lws_h2_frame_write(struct lws *wsi, int type, int flags, unsigned int sid,
unsigned int len, unsigned char *buf);
struct lws *
lws_wsi_mux_from_id(struct lws *wsi, unsigned int sid);
int
lws_hpack_interpret(struct lws *wsi, unsigned char c);
int
lws_add_http2_header_by_name(struct lws *wsi,
const unsigned char *name,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end);
int
lws_add_http2_header_by_token(struct lws *wsi,
enum lws_token_indexes token,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end);
int
lws_add_http2_header_status(struct lws *wsi,
unsigned int code, unsigned char **p,
unsigned char *end);
void
lws_hpack_destroy_dynamic_header(struct lws *wsi);
int
lws_hpack_dynamic_size(struct lws *wsi, int size);
int
lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason);
int
lws_h2_tx_cr_get(struct lws *wsi);
void
lws_h2_tx_cr_consume(struct lws *wsi, int consumed);
int
lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h);
void
lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pss);
extern const struct http2_settings lws_h2_defaults;
int
lws_h2_ws_handshake(struct lws *wsi);
int lws_h2_issue_preface(struct lws *wsi);
int
lws_h2_client_handshake(struct lws *wsi);
struct lws *
lws_wsi_h2_adopt(struct lws *parent_wsi, struct lws *wsi);
int
lws_handle_POLLOUT_event_h2(struct lws *wsi);
int
lws_read_h2(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
struct lws_h2_protocol_send *
lws_h2_new_pps(enum lws_h2_protocol_send_type type);

View File

@ -0,0 +1,96 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(. ./compression)
list(APPEND SOURCES
roles/http/header.c
roles/http/date.c
roles/http/parsers.c)
if (NOT LWS_WITHOUT_SERVER)
list(APPEND SOURCES
roles/http/server/server.c
roles/http/server/lws-spa.c)
endif()
if (LWS_WITH_CACHE_NSCOOKIEJAR AND LWS_WITH_CLIENT)
list(APPEND SOURCES
roles/http/cookie.c)
endif()
if (LWS_WITH_HTTP_PROXY AND LWS_WITH_HUBBUB)
list(APPEND SOURCES
roles/http/server/rewrite.c)
endif()
if (LWS_WITH_ACCESS_LOG)
list(APPEND SOURCES
roles/http/server/access-log.c)
endif()
if (LWS_WITH_HTTP_STREAM_COMPRESSION)
list(APPEND SOURCES
roles/http/compression/stream.c
roles/http/compression/deflate/deflate.c)
if (LWS_WITH_HTTP_BROTLI)
list(APPEND SOURCES
roles/http/compression/brotli/brotli.c)
list(APPEND LIB_LIST brotlienc brotlidec brotlidec)
endif()
endif()
if (LWS_WITH_LEJP_CONF AND LWS_WITH_NETWORK AND NOT LWS_PLAT_OPTEE)
list(APPEND SOURCES
roles/http/server/lejp-conf.c
)
endif()
if (LWS_WITH_RANGES)
list(APPEND SOURCES
roles/http/server/ranges.c)
endif()
if (LWS_WITH_ZIP_FOPS)
if (LWS_WITH_ZLIB)
list(APPEND SOURCES
roles/http/server/fops-zip.c)
else()
message(FATAL_ERROR "Pre-zipped file support (LWS_WITH_ZIP_FOPS) requires ZLIB (LWS_WITH_ZLIB)")
endif()
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
HTTP compression
----------------
This directory contains generic compression transforms that can be applied to
specifically HTTP content streams, after the header, be it h1 or h2.
The compression transforms expose an "ops" type struct and a compressor name
as used by `content-encoding`... the ops struct definition can be found in
./private-lib-roles-http-compression.h.
Because the compression transform depends on being able to send on its output
before it can process new input, the transform adds a new kind of buflist
`wsi->buflist_comp` that represents pre-compression transform data
("input data" from the perspective of the compression transform) that was
delivered to be processed but couldn't be accepted.
Currently, zlib 'deflate' and brotli 'br' are supported on the server side.

View File

@ -0,0 +1,124 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
static int
lcs_init_compression_brotli(lws_comp_ctx_t *ctx, int decomp)
{
ctx->is_decompression = (unsigned char)!!decomp;
if (!decomp) {
ctx->u.br_en = BrotliEncoderCreateInstance(NULL, NULL, NULL);
if (ctx->u.br_en) {
BrotliEncoderSetParameter(ctx->u.br_en,
BROTLI_PARAM_MODE, BROTLI_MODE_TEXT);
BrotliEncoderSetParameter(ctx->u.br_en,
BROTLI_PARAM_QUALITY, BROTLI_MIN_QUALITY);
}
}
else
ctx->u.br_de = BrotliDecoderCreateInstance(NULL, NULL, NULL);
return !ctx->u.br_de;
}
static int
lcs_process_brotli(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused,
void *out, size_t *olen_oused)
{
size_t a_in, a_out, t_out;
const uint8_t *n_in;
uint8_t *n_out;
int n;
n_in = (void *)in;
a_in = *ilen_iused;
a_out = *olen_oused;
n_out = out;
t_out = 0;
if (!ctx->is_decompression) {
if (!a_in && !BrotliEncoderHasMoreOutput(ctx->u.br_en)) {
*olen_oused = 0;
goto bail;
}
n = BROTLI_OPERATION_PROCESS;
if (!ctx->buflist_comp && ctx->final_on_input_side)
n = BROTLI_OPERATION_FINISH;
if (BrotliEncoderCompressStream(ctx->u.br_en, n, &a_in, &n_in,
&a_out, &n_out, &t_out) ==
BROTLI_FALSE) {
lwsl_err("brotli encode failed\n");
return -1;
}
ctx->may_have_more = !a_out;
} else {
n = BrotliDecoderDecompressStream(ctx->u.br_de, &a_in, &n_in,
&a_out, &n_out, &t_out);
switch (n) {
case BROTLI_DECODER_RESULT_ERROR:
lwsl_err("brotli decoder error\n");
return -1;
}
}
*ilen_iused -= a_in;
*olen_oused -= a_out;
bail:
if (!ctx->is_decompression)
return BrotliEncoderIsFinished(ctx->u.br_en);
else
return BrotliDecoderIsFinished(ctx->u.br_de);
}
static void
lcs_destroy_brotli(lws_comp_ctx_t *ctx)
{
if (!ctx)
return;
if (!(*ctx).is_decompression)
BrotliEncoderDestroyInstance((*ctx).u.br_en);
else
BrotliDecoderDestroyInstance((*ctx).u.br_de);
(*ctx).u.generic_ctx_ptr = NULL;
}
struct lws_compression_support lcs_brotli = {
/* .encoding_name */ "br",
/* .init_compression */ lcs_init_compression_brotli,
/* .process */ lcs_process_brotli,
/* .destroy */ lcs_destroy_brotli,
};

View File

@ -0,0 +1,113 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
static int
lcs_init_compression_deflate(lws_comp_ctx_t *ctx, int decomp)
{
int n;
ctx->is_decompression = !!decomp;
ctx->u.deflate = lws_malloc(sizeof(*ctx->u.deflate), __func__);
if (!ctx->u.deflate)
return 2;
memset(ctx->u.deflate, 0, sizeof(*ctx->u.deflate));
if (!decomp &&
(n = deflateInit2(ctx->u.deflate, 1, Z_DEFLATED, -15, 8,
Z_DEFAULT_STRATEGY)) != Z_OK) {
lwsl_err("deflate init failed: %d\n", n);
lws_free_set_NULL(ctx->u.deflate);
return 1;
}
if (decomp &&
inflateInit2(ctx->u.deflate, 16 + 15) != Z_OK) {
lws_free_set_NULL(ctx->u.deflate);
return 1;
}
return 0;
}
static int
lcs_process_deflate(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused,
void *out, size_t *olen_oused)
{
size_t olen_oused_in = *olen_oused;
int n;
ctx->u.deflate->next_in = (void *)in;
ctx->u.deflate->avail_in = (unsigned int)*ilen_iused;
ctx->u.deflate->next_out = out;
ctx->u.deflate->avail_out = (unsigned int)*olen_oused;
if (!ctx->is_decompression)
n = deflate(ctx->u.deflate, Z_SYNC_FLUSH);
else
n = inflate(ctx->u.deflate, Z_SYNC_FLUSH);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
lwsl_err("zlib error inflate %d\n", n);
return -1;
}
*ilen_iused -= ctx->u.deflate->avail_in;
*olen_oused -= ctx->u.deflate->avail_out;
/* it's ambiguous with zlib... */
ctx->may_have_more = (*olen_oused == olen_oused_in);
return n == Z_STREAM_END;
}
static void
lcs_destroy_deflate(lws_comp_ctx_t *ctx)
{
if (!ctx)
return;
if (!(*ctx).is_decompression)
deflateEnd((*ctx).u.deflate);
else
inflateEnd((*ctx).u.deflate);
lws_free_set_NULL(ctx->u.deflate);
}
struct lws_compression_support lcs_deflate = {
/* .encoding_name */ "deflate",
/* .init_compression */ lcs_init_compression_deflate,
/* .process */ lcs_process_deflate,
/* .destroy */ lcs_destroy_deflate,
};

View File

@ -0,0 +1,87 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is included from private-lib-core.h if LWS_WITH_HTTP_STREAM_COMPRESSION
*/
#if defined(LWS_WITH_MINIZ)
#include <miniz.h>
#else
#include <zlib.h>
#endif
#if defined(LWS_WITH_HTTP_BROTLI)
#include <brotli/encode.h>
#include <brotli/decode.h>
#endif
/*
* struct holding union of all the available compression methods' context data,
* and state if it's compressing or decompressing
*/
typedef struct lws_compression_ctx {
union {
#if defined(LWS_WITH_HTTP_BROTLI)
BrotliEncoderState *br_en;
BrotliDecoderState *br_de;
#endif
z_stream *deflate;
void *generic_ctx_ptr;
} u;
struct lws_buflist *buflist_comp;
unsigned int is_decompression:1;
unsigned int final_on_input_side:1;
unsigned int may_have_more:1;
unsigned int chunking:1;
} lws_comp_ctx_t;
/* generic structure defining the interface to a compression method */
struct lws_compression_support {
/** compression name as used by, eg, content-ecoding */
const char *encoding_name;
/** create a compression context for the compression method, or NULL */
int (*init_compression)(lws_comp_ctx_t *ctx, int decomp);
/** pass data into the context to be processed */
int (*process)(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused,
void *out, size_t *olen_oused);
/** destroy the de/compression context */
void (*destroy)(lws_comp_ctx_t *ctx);
};
extern struct lws_compression_support lcs_deflate;
extern struct lws_compression_support lcs_brotli;
int
lws_http_compression_validate(struct lws *wsi);
int
lws_http_compression_transform(struct lws *wsi, unsigned char *buf,
size_t len, enum lws_write_protocol *wp,
unsigned char **outbuf, size_t *olen_oused);
void
lws_http_compression_destroy(struct lws *wsi);

View File

@ -0,0 +1,227 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
/* compression methods listed in order of preference */
struct lws_compression_support *lcs_available[] = {
#if defined(LWS_WITH_HTTP_BROTLI)
&lcs_brotli,
#endif
&lcs_deflate,
};
/* compute acceptable compression encodings while we still have an ah */
int
lws_http_compression_validate(struct lws *wsi)
{
const char *a;
size_t n;
wsi->http.comp_accept_mask = 0;
if (!wsi->http.ah || !lwsi_role_server(wsi))
return 0;
a = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING);
if (!a)
return 0;
for (n = 0; n < LWS_ARRAY_SIZE(lcs_available); n++)
if (strstr(a, lcs_available[n]->encoding_name))
wsi->http.comp_accept_mask = (uint8_t)(wsi->http.comp_accept_mask | (1 << n));
return 0;
}
int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp)
{
size_t n;
for (n = 0; n < LWS_ARRAY_SIZE(lcs_available); n++) {
/* if name is non-NULL, choose only that compression method */
if (name && strcmp(lcs_available[n]->encoding_name, name))
continue;
/*
* If we're the server, confirm that the client told us he could
* handle this kind of compression transform...
*/
if (!decomp && !(wsi->http.comp_accept_mask & (1 << n)))
continue;
/* let's go with this one then... */
break;
}
if (n == LWS_ARRAY_SIZE(lcs_available))
return 1;
lcs_available[n]->init_compression(&wsi->http.comp_ctx, decomp);
if (!wsi->http.comp_ctx.u.generic_ctx_ptr) {
lwsl_err("%s: init_compression %d failed\n", __func__, (int)n);
return 1;
}
wsi->http.lcs = lcs_available[n];
wsi->http.comp_ctx.may_have_more = 0;
wsi->http.comp_ctx.final_on_input_side = 0;
wsi->http.comp_ctx.chunking = 0;
wsi->http.comp_ctx.is_decompression = !!decomp;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING,
(unsigned char *)lcs_available[n]->encoding_name,
(int)strlen(lcs_available[n]->encoding_name), p, end))
return -1;
lwsl_info("%s: %s: applied %s content-encoding\n", __func__,
lws_wsi_tag(wsi), lcs_available[n]->encoding_name);
return 0;
}
void
lws_http_compression_destroy(struct lws *wsi)
{
if (!wsi->http.lcs || !wsi->http.comp_ctx.u.generic_ctx_ptr)
return;
wsi->http.lcs->destroy(&wsi->http.comp_ctx);
wsi->http.lcs = NULL;
}
/*
* This manages the compression transform independent of h1 or h2.
*
* wsi->buflist_comp stashes pre-transform input that was not yet compressed
*/
int
lws_http_compression_transform(struct lws *wsi, unsigned char *buf,
size_t len, enum lws_write_protocol *wp,
unsigned char **outbuf, size_t *olen_oused)
{
size_t ilen_iused = len;
int n, use = 0, wp1f = (*wp) & 0x1f;
lws_comp_ctx_t *ctx = &wsi->http.comp_ctx;
ctx->may_have_more = 0;
if (!wsi->http.lcs ||
(wp1f != LWS_WRITE_HTTP && wp1f != LWS_WRITE_HTTP_FINAL)) {
*outbuf = buf;
*olen_oused = len;
return 0;
}
if (wp1f == LWS_WRITE_HTTP_FINAL) {
/*
* ...we may get a large buffer that represents the final input
* buffer, but it may form multiple frames after being
* tranformed by compression; only the last of those is actually
* the final frame on the output stream.
*
* Note that we have received the FINAL input, and downgrade it
* to a non-final for now.
*/
ctx->final_on_input_side = 1;
*wp = (unsigned int)(LWS_WRITE_HTTP | ((*wp) & ~0x1fu));
}
if (ctx->buflist_comp) {
/*
* we can't send this new stuff when we have old stuff
* buffered and not compressed yet. Add it to the tail
* and switch to trying to process the head.
*/
if (buf && len) {
if (lws_buflist_append_segment(
&ctx->buflist_comp, buf, len) < 0)
return -1;
lwsl_debug("%s: %s: adding %d to comp buflist\n",
__func__, lws_wsi_tag(wsi), (int)len);
}
len = lws_buflist_next_segment_len(&ctx->buflist_comp, &buf);
ilen_iused = len;
use = 1;
lwsl_debug("%s: %s: trying comp buflist %d\n", __func__,
lws_wsi_tag(wsi), (int)len);
}
if (!buf && ilen_iused)
return 0;
lwsl_debug("%s: %s: pre-process: ilen_iused %d, olen_oused %d\n",
__func__, lws_wsi_tag(wsi), (int)ilen_iused, (int)*olen_oused);
n = wsi->http.lcs->process(ctx, buf, &ilen_iused, *outbuf, olen_oused);
if (n && n != 1) {
lwsl_err("%s: problem with compression\n", __func__);
return -1;
}
if (!ctx->may_have_more && ctx->final_on_input_side)
*wp = (unsigned int)(LWS_WRITE_HTTP_FINAL | ((*wp) & ~0x1fu));
lwsl_debug("%s: %s: more %d, ilen_iused %d\n", __func__, lws_wsi_tag(wsi),
ctx->may_have_more, (int)ilen_iused);
if (use && ilen_iused) {
/*
* we were flushing stuff from the buflist head... account for
* however much actually got processed by the compression
* transform
*/
lws_buflist_use_segment(&ctx->buflist_comp, ilen_iused);
lwsl_debug("%s: %s: marking %d of comp buflist as used "
"(ctx->buflist_comp %p)\n", __func__,
lws_wsi_tag(wsi), (int)len, ctx->buflist_comp);
}
if (!use && ilen_iused != len) {
/*
* ...we were sending stuff from the caller directly and not
* all of it got processed... stash on the buflist tail
*/
if (lws_buflist_append_segment(&ctx->buflist_comp,
buf + ilen_iused, len - ilen_iused) < 0)
return -1;
lwsl_debug("%s: buffering %d unused comp input\n", __func__,
(int)(len - ilen_iused));
}
if (ctx->buflist_comp || ctx->may_have_more)
lws_callback_on_writable(wsi);
return 0;
}

View File

@ -0,0 +1,735 @@
#include <libwebsockets.h>
#include "private-lib-core.h"
//#define LWS_COOKIE_DEBUG
#if defined(LWS_COOKIE_DEBUG)
#define lwsl_cookie lwsl_notice
#else
#define lwsl_cookie lwsl_debug
#endif
#define LWS_COOKIE_MAX_CACHE_NAME_LEN 128
#define lws_tolower(_c) (((_c) >= 'A' && (_c) <= 'Z') ? \
(char)((_c) + 'a' - 'A') : \
(char)(_c))
#define LWS_COOKIE_NSC_FORMAT "%.*s\t"\
"%s\t"\
"%.*s\t"\
"%s\t"\
"%llu\t"\
"%.*s\t"\
"%.*s"
static const char *const mon = "janfebmaraprnayjunjulaugsepoctnovdec";
enum lws_cookie_nsc_f {
LWSC_NSC_DOMAIN,
LWSC_NSC_HOSTONLY,
LWSC_NSC_PATH,
LWSC_NSC_SECURE,
LWSC_NSC_EXPIRES,
LWSC_NSC_NAME,
LWSC_NSC_VALUE,
LWSC_NSC_COUNT,
};
enum lws_cookie_elements {
CE_DOMAIN,
CE_PATH,
CE_EXPIRES,
CE_MAXAGE,
CE_NAME,
CE_VALUE,
CE_HOSTONLY, /* these are bool, NULL = 0, non-NULL = 1 */
CE_SECURE,
CE_COUNT
};
struct lws_cookie {
const char *f[CE_COUNT];
size_t l[CE_COUNT];
unsigned int httponly:1;
};
static int
lws_cookie_parse_date(const char *d, size_t len, time_t *t)
{
struct tm date;
int offset = 0, i;
memset(&date, 0, sizeof(date));
while (len) {
if (isalnum((int)*d)) {
offset++;
goto next;
}
switch (offset) {
case 2:
if (*d == ':' && len >= 6) {
date.tm_hour = atoi(d - 2);
if (date.tm_hour < 0 || date.tm_hour > 23)
return -1;
date.tm_min = atoi(d + 1);
if (date.tm_min < 0 || date.tm_min > 60)
return -1;
date.tm_sec = atoi(d + 4);
if (date.tm_sec < 0 || date.tm_sec > 61)
/* leap second */
return -1;
d += 6;
len -= 6;
offset = 0;
continue;
}
if (!date.tm_mday) {
date.tm_mday = atoi(d - 2);
if (date.tm_mday < 1 || date.tm_mday > 31)
return -1;
goto next2;
}
if (!date.tm_year) {
date.tm_year = atoi(d - 2);
if (date.tm_year < 0 || date.tm_year > 99)
return -1;
if (date.tm_year < 70)
date.tm_year += 100;
}
goto next2;
case 3:
for (i = 0; i < 36; i += 3) {
if (lws_tolower(*(d - 3)) == mon[i] &&
lws_tolower(*(d - 2)) == mon[i + 1] &&
lws_tolower(*(d - 1)) == mon[i + 2]) {
date.tm_mon = i / 3;
break;
}
}
goto next2;
case 4:
if (!date.tm_year) {
date.tm_year = atoi(d - 4);
if (date.tm_year < 1601)
return -1;
date.tm_year -= 1900;
}
goto next2;
default:
goto next2;
}
next2:
offset = 0;
next:
d++;
len--;
}
*t = mktime(&date);
if (*t < 0)
return -1;
return 0;
}
static void
lws_cookie_rm_sws(const char **buf_p, size_t *len_p)
{
const char *buf;
size_t len;
if (!buf_p || !*buf_p || !len_p || !*len_p) {
lwsl_err("%s: false parameter\n", __func__);
return;
}
buf = *buf_p;
len = *len_p;
while (buf[0] == ' ' && len > 0) {
buf++;
len--;
}
while (buf[len - 1] == ' ' && len > 0)
len--;
*buf_p = buf;
*len_p = len;
}
static int
is_iprefix(const char *h, size_t hl, const char *n, size_t nl)
{
if (!h || !n || nl > hl)
return 0;
while (nl) {
nl--;
if (lws_tolower(h[nl]) != lws_tolower(n[nl]))
return 0;
}
return 1;
}
static int
lws_cookie_compile_cache_name(char *buf, size_t buf_len, struct lws_cookie *c)
{
if (!buf || !c->f[CE_DOMAIN] || !c->f[CE_PATH] || !c->f[CE_NAME] ||
c->l[CE_DOMAIN] + c->l[CE_PATH] + c->l[CE_NAME] + 6 > buf_len)
return -1;
memcpy(buf, c->f[CE_DOMAIN], c->l[CE_DOMAIN]);
buf += c->l[CE_DOMAIN];
*buf++ = '|';
memcpy(buf, c->f[CE_PATH], c->l[CE_PATH]);
buf += c->l[CE_PATH];
*buf++ = '|';
memcpy(buf, c->f[CE_NAME], c->l[CE_NAME]);
buf += c->l[CE_NAME];
*buf = '\0';
return 0;
}
static int
lws_cookie_parse_nsc(struct lws_cookie *c, const char *b, size_t l)
{
enum lws_cookie_nsc_f state = LWSC_NSC_DOMAIN;
size_t n = 0;
if (!c || !b || l < 13)
return -1;
memset(c, 0, sizeof(*c));
lwsl_cookie("%s: parsing (%.*s) \n", __func__, (int)l, b);
while (l) {
l--;
if (b[n] != '\t' && l) {
n++;
continue;
}
switch (state) {
case LWSC_NSC_DOMAIN:
c->f[CE_DOMAIN] = b;
c->l[CE_DOMAIN] = n;
break;
case LWSC_NSC_PATH:
c->f[CE_PATH] = b;
c->l[CE_PATH] = n;
break;
case LWSC_NSC_EXPIRES:
c->f[CE_EXPIRES] = b;
c->l[CE_EXPIRES] = n;
break;
case LWSC_NSC_NAME:
c->f[CE_NAME] = b;
c->l[CE_NAME] = n;
break;
case LWSC_NSC_HOSTONLY:
if (b[0] == 'T') {
c->f[CE_HOSTONLY] = b;
c->l[CE_HOSTONLY] = 1;
}
break;
case LWSC_NSC_SECURE:
if (b[0] == 'T') {
c->f[CE_SECURE] = b;
c->l[CE_SECURE] = 1;
}
break;
case LWSC_NSC_VALUE:
c->f[CE_VALUE] = b;
c->l[CE_VALUE] = n + 1;
for (n = 0; n < LWS_ARRAY_SIZE(c->f); n++)
lwsl_cookie("%s: %d: %.*s\n", __func__,
(int)n, (int)c->l[n], c->f[n]);
return 0;
default:
return -1;
}
b += n + 1;
n = 0;
state++;
}
return -1;
}
static int
lws_cookie_write_nsc(struct lws *wsi, struct lws_cookie *c)
{
char cache_name[LWS_COOKIE_MAX_CACHE_NAME_LEN];
const char *ads, *path;
struct lws_cache_ttl_lru *l1;
struct client_info_stash *stash;
char *cookie_string = NULL, *dl;
/* 6 tabs + 20 for max time_t + 2 * TRUE/FALSE + null */
size_t size = 6 + 20 + 10 + 1;
time_t expires = 0;
int ret = 0;
if (!wsi || !c)
return -1;
l1 = wsi->a.context->l1;
if (!l1 || !wsi->a.context->nsc)
return -1;
stash = wsi->stash ? wsi->stash : lws_get_network_wsi(wsi)->stash;
if (stash) {
ads = stash->cis[CIS_ADDRESS];
path = stash->cis[CIS_PATH];
} else {
ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
path = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI);
}
if (!ads || !path)
return -1;
if (!c->f[CE_NAME] || !c->f[CE_VALUE]) {
lwsl_err("%s: malformed c\n", __func__);
return -1;
}
if (!c->f[CE_EXPIRES]) {
/*
* Currently we just take the approach to reject session cookies
*/
lwsl_warn("%s: reject session cookies\n", __func__);
return 0;
}
if (!c->f[CE_DOMAIN]) {
c->f[CE_HOSTONLY] = "T";
c->l[CE_HOSTONLY] = 1;
c->f[CE_DOMAIN] = ads;
c->l[CE_DOMAIN] = strlen(ads);
}
if (!c->f[CE_PATH]) {
c->f[CE_PATH] = path;
c->l[CE_PATH] = strlen(path);
dl = memchr(c->f[CE_PATH], '?', c->l[CE_PATH]);
if (dl)
c->l[CE_PATH] = (size_t)(dl - c->f[CE_PATH]);
}
if (lws_cookie_compile_cache_name(cache_name, sizeof(cache_name), c))
return -1;
if (c->f[CE_EXPIRES] &&
lws_cookie_parse_date(c->f[CE_EXPIRES], c->l[CE_EXPIRES], &expires)) {
lwsl_err("%s: can't parse date %.*s\n", __func__,
(int)c->l[CE_EXPIRES], c->f[CE_EXPIRES]);
return -1;
}
size += c->l[CE_NAME] + c->l[CE_VALUE] + c->l[CE_DOMAIN] + c->l[CE_PATH];
cookie_string = (char *)lws_malloc(size, __func__);
if (!cookie_string) {
lwsl_err("%s: OOM\n",__func__);
return -1;
}
lws_snprintf(cookie_string, size, LWS_COOKIE_NSC_FORMAT,
(int)c->l[CE_DOMAIN], c->f[CE_DOMAIN],
c->f[CE_HOSTONLY] ? "TRUE" : "FALSE",
(int)c->l[CE_PATH], c->f[CE_PATH],
c->f[CE_SECURE] ? "TRUE" : "FALSE",
(unsigned long long)expires,
(int)c->l[CE_NAME], c->f[CE_NAME],
(int)c->l[CE_VALUE], c->f[CE_VALUE]);
lwsl_cookie("%s: name %s\n", __func__, cache_name);
lwsl_cookie("%s: c %s\n", __func__, cookie_string);
if (lws_cache_write_through(l1, cache_name,
(const uint8_t *)cookie_string,
strlen(cookie_string),
(lws_usec_t)((unsigned long long)expires *
(lws_usec_t)LWS_US_PER_SEC), NULL)) {
ret = -1;
goto exit;
}
#if defined(LWS_COOKIE_DEBUG)
char *po;
if (lws_cache_item_get(l1, cache_name, (const void **)&po, &size) ||
size != strlen(cookie_string) || memcmp(po, cookie_string, size)) {
lwsl_err("%s: L1 '%s' missing\n", __func__, cache_name);
}
if (lws_cache_item_get(wsi->a.context->nsc, cache_name,
(const void **)&po, &size) ||
size != strlen(cookie_string) ||
memcmp(po, cookie_string, size)) {
lwsl_err("%s: NSC '%s' missing, size %llu, po %s\n", __func__,
cache_name, (unsigned long long)size, po);
}
#endif
exit:
lws_free(cookie_string);
return ret;
}
static int
lws_cookie_attach_cookies(struct lws *wsi, char *buf, char *end)
{
const char *domain, *path, *dl_domain, *dl_path, *po;
char cache_name[LWS_COOKIE_MAX_CACHE_NAME_LEN];
size_t domain_len, path_len, size, ret = 0;
struct lws_cache_ttl_lru *l1;
struct client_info_stash *stash;
lws_cache_results_t cr;
struct lws_cookie c;
int hostdomain = 1;
char *p, *p1;
if (!wsi)
return -1;
stash = wsi->stash ? wsi->stash : lws_get_network_wsi(wsi)->stash;
if (!stash || !stash->cis[CIS_ADDRESS] ||
!stash->cis[CIS_PATH])
return -1;
l1 = wsi->a.context->l1;
if (!l1 || !wsi->a.context->nsc){
lwsl_err("%s:no cookiejar\n", __func__);
return -1;
}
memset(&c, 0, sizeof(c));
domain = stash->cis[CIS_ADDRESS];
path = stash->cis[CIS_PATH];
if (!domain || !path)
return -1;
path_len = strlen(path);
/* remove query string if exist */
dl_path = memchr(path, '?', path_len);
if (dl_path)
path_len = lws_ptr_diff_size_t(dl_path, path);
/* remove last slash if exist */
if (path_len != 1 && path[path_len - 1] == '/')
path_len--;
if (!path_len)
return -1;
lwsl_cookie("%s: path %.*s len %d\n", __func__, (int)path_len, path, (int)path_len);
/* when dest buf is not provided, we only return size of cookie string */
if (!buf || !end)
p = NULL;
else
p = buf;
/* iterate through domain and path levels to find matching cookies */
dl_domain = domain;
while (dl_domain) {
domain_len = strlen(domain);
dl_domain = memchr(domain, '.', domain_len);
/* don't match top level domain */
if (!dl_domain)
break;
if (domain_len + path_len + 6 > sizeof(cache_name))
return -1;
/* compile key string "[domain]|[path]|*"" */
p1 = cache_name;
memcpy(p1, domain, domain_len);
p1 += domain_len;
*p1 = '|';
p1++;
memcpy(p1, path, path_len);
p1 += path_len;
*p1 = '|';
p1++;
*p1 = '*';
p1++;
*p1 = '\0';
lwsl_cookie("%s: looking for %s\n", __func__, cache_name);
if (!lws_cache_lookup(l1, cache_name,
(const void **)&cr.ptr, &cr.size)) {
while (!lws_cache_results_walk(&cr)) {
lwsl_cookie(" %s (%d)\n", (const char *)cr.tag,
(int)cr.payload_len);
if (lws_cache_item_get(l1, (const char *)cr.tag,
(const void **)&po, &size) ||
lws_cookie_parse_nsc(&c, po, size)) {
lwsl_err("%s: failed to get c '%s'\n",
__func__, cr.tag);
break;
}
if (c.f[CE_HOSTONLY] && !hostdomain){
lwsl_cookie("%s: not sending this\n",
__func__);
continue;
}
if (p) {
if (ret) {
*p = ';';
p++;
*p = ' ';
p++;
}
memcpy(p, c.f[CE_NAME], c.l[CE_NAME]);
p += c.l[CE_NAME];
*p = '=';
p++;
memcpy(p, c.f[CE_VALUE], c.l[CE_VALUE]);
p += c.l[CE_VALUE];
}
if (ret)
ret += 2;
ret += c.l[CE_NAME] + 1 + c.l[CE_VALUE];
}
}
domain = dl_domain + 1;
hostdomain = 0;
}
lwsl_notice("%s: c len (%d)\n", __func__, (int)ret);
return (int)ret;
}
static struct {
const char *const name;
uint8_t len;
} cft[] = {
{ "domain=", 7 },
{ "path=", 5 },
{ "expires=", 8 },
{ "max-age=", 8 },
{ "httponly", 8 },
{ "secure", 6 }
};
int
lws_parse_set_cookie(struct lws *wsi)
{
char *tk_head, *tk_end, *buf_head, *buf_end, *cookiep, *dl;
struct lws_cache_ttl_lru *l1;
struct lws_cookie c;
size_t fl;
int f, n;
if (!wsi)
return -1;
l1 = wsi->a.context->l1;
if (!l1)
return -1;
f = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_SET_COOKIE];
while (f) {
cookiep = wsi->http.ah->data + wsi->http.ah->frags[f].offset;
fl = wsi->http.ah->frags[f].len;
f = wsi->http.ah->frags[f].nfrag;
if (!cookiep || !fl)
continue;
#if defined(LWS_COOKIE_DEBUG)
lwsl_notice("%s:parsing: %.*s\n", __func__, (int)fl, cookiep);
#endif
buf_head = cookiep;
buf_end = cookiep + fl - 1;
memset(&c, 0, sizeof(struct lws_cookie));
do {
tk_head = buf_head;
tk_end = memchr(buf_head, ';',
(size_t)(buf_end - buf_head + 1));
if (!tk_end) {
tk_end = buf_end;
buf_head = buf_end;
} else {
buf_head = tk_end + 1;
tk_end--;
}
if (c.f[CE_NAME])
goto parse_av;
/*
* find name value, remove leading trailing
* WS and DQ for value
*/
dl = memchr(tk_head, '=', lws_ptr_diff_size_t(tk_end,
tk_head + 1));
if (!dl || dl == tk_head)
return -1;
c.f[CE_NAME] = tk_head;
c.l[CE_NAME] = lws_ptr_diff_size_t(dl, tk_head);
lws_cookie_rm_sws(&c.f[CE_NAME], &c.l[CE_NAME]);
if (!c.l[CE_NAME])
return -1;
lwsl_cookie("%s: c name l %d v:%.*s\n", __func__,
(int)c.l[CE_NAME],
(int)c.l[CE_NAME], c.f[CE_NAME]);
c.f[CE_VALUE] = dl + 1;
c.l[CE_VALUE] = lws_ptr_diff_size_t(tk_end,
c.f[CE_VALUE]) + 1;
lws_cookie_rm_sws(&c.f[CE_VALUE], &c.l[CE_VALUE]);
if (c.l[CE_VALUE] >= 2 && c.f[CE_VALUE][0] == '\"') {
c.f[CE_VALUE]++;
c.l[CE_VALUE] -= 2;
}
lwsl_cookie("%s: c value l %d v:%.*s\n", __func__,
(int)c.l[CE_VALUE], (int)c.l[CE_VALUE],
c.f[CE_VALUE]);
continue;
parse_av:
while (*tk_head == ' ') {
if (tk_head == tk_end)
return -1;
tk_head++;
}
for (n = 0; n < (int)LWS_ARRAY_SIZE(cft); n++) {
if (lws_tolower(*tk_head) != cft[n].name[0])
continue;
if (!is_iprefix(tk_head,
lws_ptr_diff_size_t(tk_end,
tk_head) + 1,
cft[n].name, cft[n].len))
continue;
if (n == 4 || n == 5) {
c.f[n] = "T";
c.l[n] = 1;
break;
}
c.f[n] = tk_head + cft[n].len;
c.l[n] = lws_ptr_diff_size_t(tk_end, c.f[n]) + 1;
lws_cookie_rm_sws(&c.f[n], &c.l[n]);
if (n == CE_DOMAIN && c.l[0] &&
c.f[n][0] == '.'){
c.f[n]++;
c.l[n]--;
}
lwsl_cookie("%s: %s l %d v:%.*s\n", __func__,
cft[n].name, (int)c.l[n],
(int)c.l[n], c.f[n]);
break;
}
} while (tk_end != buf_end);
if (lws_cookie_write_nsc(wsi, &c))
lwsl_err("%s:failed to write nsc\n", __func__);
}
return 0;
}
int
lws_cookie_send_cookies(struct lws *wsi, char **pp, char *end)
{
char *p;
int size;
if (!wsi || !pp || !(*pp) || !end)
return -1;
size = lws_cookie_attach_cookies(wsi, NULL, NULL);
if (!size)
return 0;
if (size < 0) {
lwsl_err("%s:failed to get cookie string size\n", __func__);
return -1;
}
lwsl_notice("%s: size %d\n", __func__, size);
#if defined(LWS_COOKIE_DEBUG)
char *p_dbg = *pp;
#endif
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COOKIE, NULL, size,
(unsigned char **)pp, (unsigned char *)end))
return -1;
#if defined(LWS_COOKIE_DEBUG)
lwsl_notice("%s: dummy copy (%.*s) \n", __func__, (int)(*pp - p_dbg), p_dbg);
#endif
#ifdef LWS_WITH_HTTP2
if (lws_wsi_is_h2(wsi))
p = *pp - size;
else
#endif
p = *pp - size - 2;
if (lws_cookie_attach_cookies(wsi, p, p + size) <= 0) {
lwsl_err("%s:failed to attach cookies\n", __func__);
return -1;
}
#if defined(LWS_COOKIE_DEBUG)
lwsl_notice("%s: real copy (%.*s) total len %d\n", __func__, (int)(*pp - p_dbg), p_dbg, (int)(*pp - p_dbg));
lwsl_hexdump_notice(p_dbg, (size_t)(*pp - p_dbg));
#endif
return 0;
}

View File

@ -0,0 +1,225 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* RFC7231 date string generation and parsing
*/
#include "private-lib-core.h"
/*
* To avoid needless pointers, we encode these in one string using the fact
* they're 3 chars each to index it
*/
static const char *const s =
"JanFebMarAprMayJunJulAugSepOctNovDecMonTueWedThuFriSatSun";
static int
lws_http_date_render(char *buf, size_t len, const struct tm *tm)
{
const char *w = s + 36 + (3 * tm->tm_wday), *m = s + (3 * tm->tm_mon);
if (len < 29)
return -1;
lws_snprintf(buf, len, "%c%c%c, %02d %c%c%c %d %02d:%02d:%02d GMT",
w[0], w[1], w[2], tm->tm_mday, m[0], m[1], m[2],
1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
return 0;
}
int
lws_http_date_render_from_unix(char *buf, size_t len, const time_t *t)
{
#if defined(LWS_HAVE_GMTIME_R)
struct tm tmp;
struct tm *tm = gmtime_r(t, &tmp);
#else
struct tm *tm = gmtime(t);
#endif
if (!tm)
return -1;
if (lws_http_date_render(buf, len, tm))
return -1;
return 0;
}
static int
lws_http_date_parse(const char *b, size_t len, struct tm *tm)
{
int n;
if (len < 29)
return -1;
/*
* We reject anything that isn't a properly-formatted RFC7231 date, eg
*
* Tue, 15 Nov 1994 08:12:31 GMT
*/
if (b[3] != ',' || b[4] != ' ' || b[7] != ' ' || b[11] != ' ' ||
b[16] != ' ' || b[19] != ':' || b[22] != ':' || b[25] != ' ' ||
b[26] != 'G' || b[27] != 'M' || b[28] != 'T')
return -1;
memset(tm, 0, sizeof(*tm));
for (n = 36; n < 57; n += 3)
if (b[0] == s[n] && b[1] == s[n + 1] && b[2] == s[n + 2])
break;
else
tm->tm_wday++;
if (n == 57)
return -1;
for (n = 0; n < 36; n += 3)
if (b[8] == s[n] && b[9] == s[n + 1] && b[10] == s[n + 2])
break;
else
tm->tm_mon++;
if (n == 36)
return -1;
tm->tm_mday = atoi(b + 5);
n = atoi(b + 12);
if (n < 1900)
return -1;
tm->tm_year = n - 1900;
n = atoi(b + 17);
if (n < 0 || n > 23)
return -1;
tm->tm_hour = n;
n = atoi(b + 20);
if (n < 0 || n > 60)
return -1;
tm->tm_min = n;
n = atoi(b + 23);
if (n < 0 || n > 61) /* leap second */
return -1;
tm->tm_sec = n;
return 0;
}
int
lws_http_date_parse_unix(const char *b, size_t len, time_t *t)
{
struct tm tm;
if (lws_http_date_parse(b, len, &tm))
return -1;
#if defined(WIN32)
*t = _mkgmtime(&tm);
#else
#if defined(LWS_HAVE_TIMEGM)
*t = timegm(&tm);
#else
/* this is a poor fallback since it uses localtime zone */
*t = mktime(&tm);
#endif
#endif
return (int)(*t == -1 ? -1 : 0);
}
#if defined(LWS_WITH_CLIENT)
int
lws_http_check_retry_after(struct lws *wsi, lws_usec_t *us_interval_in_out)
{
size_t len = (unsigned int)lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_RETRY_AFTER);
char *p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_RETRY_AFTER);
lws_usec_t u;
time_t t, td;
if (!p)
return 1;
/*
* There are two arg styles for RETRY_AFTER specified in RFC7231 7.1.3,
* either a full absolute second-resolution date/time, or an integer
* interval
*
* Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
* Retry-After: 120
*/
if (len < 9)
u = ((lws_usec_t)(time_t)atoi(p)) * LWS_USEC_PER_SEC;
else {
if (lws_http_date_parse_unix(p, len, &t))
return 1;
/*
* If possible, look for DATE from the server as well, so we
* can calculate the interval it thinks it is giving us,
* eliminating problems from server - client clock skew
*/
time(&td);
len = (unsigned int)lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_DATE);
if (len) {
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_DATE);
/* if this fails, it leaves td as client time */
(void)lws_http_date_parse_unix(p, len, &td);
}
if (td >= t)
/*
* if he's effectively giving us a 0 or negative
* interval, just ignore the whole thing and keep the
* incoming interval
*/
return 1;
u = ((lws_usec_t)(t - td)) * LWS_USEC_PER_SEC;
}
/*
* We are only willing to increase the incoming interval, not
* decrease it
*/
if (u < *us_interval_in_out)
/* keep the incoming interval */
return 1;
/* use the computed interval */
*us_interval_in_out = u;
return 0;
}
#endif

View File

@ -0,0 +1,666 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "lextable-strings.h"
const unsigned char *
lws_token_to_string(enum lws_token_indexes token)
{
if ((unsigned int)token >= LWS_ARRAY_SIZE(set))
return NULL;
return (unsigned char *)set[token];
}
/*
* Return http header index if one matches slen chars of s, or -1
*/
int
lws_http_string_to_known_header(const char *s, size_t slen)
{
int n;
for (n = 0; n < (int)LWS_ARRAY_SIZE(set); n++)
if (!strncmp(set[n], s, slen))
return n;
return LWS_HTTP_NO_KNOWN_HEADER;
}
#ifdef LWS_WITH_HTTP2
int
lws_wsi_is_h2(struct lws *wsi)
{
return wsi->upgraded_to_http2 ||
wsi->mux_substream ||
#if defined(LWS_WITH_CLIENT)
wsi->client_mux_substream ||
#endif
lwsi_role_h2(wsi) ||
lwsi_role_h2_ENCAPSULATION(wsi);
}
#endif
int
lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end)
{
#ifdef LWS_WITH_HTTP2
if (lws_wsi_is_h2(wsi))
return lws_add_http2_header_by_name(wsi, name,
value, length, p, end);
#else
(void)wsi;
#endif
if (name) {
char has_colon = 0;
while (*p < end && *name) {
has_colon = has_colon || *name == ':';
*((*p)++) = *name++;
}
if (*p + (has_colon ? 1 : 2) >= end)
return 1;
if (!has_colon)
*((*p)++) = ':';
*((*p)++) = ' ';
}
if (*p + length + 3 >= end)
return 1;
if (value)
memcpy(*p, value, (unsigned int)length);
*p += length;
*((*p)++) = '\x0d';
*((*p)++) = '\x0a';
return 0;
}
int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
unsigned char *end)
{
#ifdef LWS_WITH_HTTP2
if (lws_wsi_is_h2(wsi))
return 0;
#else
(void)wsi;
#endif
if ((lws_intptr_t)(end - *p) < 3)
return 1;
*((*p)++) = '\x0d';
*((*p)++) = '\x0a';
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, (unsigned int)len, LWS_WRITE_HTTP_HEADERS) != len)
return 1;
return 0;
}
int
lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end)
{
const unsigned char *name;
#ifdef LWS_WITH_HTTP2
if (lws_wsi_is_h2(wsi))
return lws_add_http2_header_by_token(wsi, token, value,
length, p, end);
#endif
name = lws_token_to_string(token);
if (!name)
return 1;
return lws_add_http_header_by_name(wsi, name, value, length, p, end);
}
int
lws_add_http_header_content_length(struct lws *wsi,
lws_filepos_t content_length,
unsigned char **p, unsigned char *end)
{
char b[24];
int n;
n = lws_snprintf(b, sizeof(b) - 1, "%llu", (unsigned long long)content_length);
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)b, n, p, end))
return 1;
wsi->http.tx_content_length = content_length;
wsi->http.tx_content_remain = content_length;
lwsl_info("%s: %s: tx_content_length/remain %llu\n", __func__,
lws_wsi_tag(wsi), (unsigned long long)content_length);
return 0;
}
#if defined(LWS_WITH_SERVER)
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)
{
const char *ka[] = { "close", "keep-alive" };
int types[] = { HTTP_CONNECTION_CLOSE, HTTP_CONNECTION_KEEP_ALIVE },
t = 0;
if (lws_add_http_header_status(wsi, code, p, end))
return 1;
if (content_type &&
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 defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
if (!wsi->http.lcs && content_type &&
(!strncmp(content_type, "text/", 5) ||
!strcmp(content_type, "application/javascript") ||
!strcmp(content_type, "image/svg+xml")))
lws_http_compression_apply(wsi, NULL, p, end, 0);
#endif
/*
* if we decided to compress it, we don't know the content length...
* the compressed data will go out chunked on h1
*/
if (
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
!wsi->http.lcs &&
#endif
content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN) {
if (lws_add_http_header_content_length(wsi, content_len,
p, end))
return 1;
} else {
/* there was no length... it normally means CONNECTION_CLOSE */
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
if (!wsi->mux_substream && wsi->http.lcs) {
/* so...
* - h1 connection
* - http compression transform active
* - did not send content length
*
* then mark as chunked...
*/
wsi->http.comp_ctx.chunking = 1;
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
(unsigned char *)"chunked", 7, p, end))
return -1;
/* ... but h1 compression is chunked, if active we can
* still pipeline
*/
if (wsi->http.lcs &&
wsi->http.conn_type == HTTP_CONNECTION_KEEP_ALIVE)
t = 1;
}
#endif
if (!wsi->mux_substream) {
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_CONNECTION,
(unsigned char *)ka[t],
(int)strlen(ka[t]), p, end))
return 1;
wsi->http.conn_type = (enum http_conn_type)types[t];
}
}
return 0;
}
static const char * const err400[] = {
"Bad Request",
"Unauthorized",
"Payment Required",
"Forbidden",
"Not Found",
"Method Not Allowed",
"Not Acceptable",
"Proxy Auth Required",
"Request Timeout",
"Conflict",
"Gone",
"Length Required",
"Precondition Failed",
"Request Entity Too Large",
"Request URI too Long",
"Unsupported Media Type",
"Requested Range Not Satisfiable",
"Expectation Failed"
};
static const char * const err500[] = {
"Internal Server Error",
"Not Implemented",
"Bad Gateway",
"Service Unavailable",
"Gateway Timeout",
"HTTP Version Not Supported"
};
/* security best practices from Mozilla Observatory */
static const
struct lws_protocol_vhost_options pvo_hsbph[] = {{
NULL, NULL, "referrer-policy:", "no-referrer"
}, {
&pvo_hsbph[0], NULL, "x-frame-options:", "deny"
}, {
&pvo_hsbph[1], NULL, "x-xss-protection:", "1; mode=block"
}, {
&pvo_hsbph[2], NULL, "x-content-type-options:", "nosniff"
}, {
&pvo_hsbph[3], NULL, "content-security-policy:",
"default-src 'none'; img-src 'self' data: ; "
"script-src 'self'; font-src 'self'; "
"style-src 'self'; connect-src 'self' ws: wss:; "
"frame-ancestors 'none'; base-uri 'none';"
"form-action 'self';"
}};
int
lws_add_http_header_status(struct lws *wsi, unsigned int _code,
unsigned char **p, unsigned char *end)
{
static const char * const hver[] = {
"HTTP/1.0", "HTTP/1.1", "HTTP/2"
};
const struct lws_protocol_vhost_options *headers;
unsigned int code = _code & LWSAHH_CODE_MASK;
const char *description = "", *p1;
unsigned char code_and_desc[60];
int n;
wsi->http.response_code = code;
#ifdef LWS_WITH_ACCESS_LOG
wsi->http.access_log.response = (int)code;
#endif
#ifdef LWS_WITH_HTTP2
if (lws_wsi_is_h2(wsi)) {
n = lws_add_http2_header_status(wsi, code, p, end);
if (n)
return n;
} else
#endif
{
if (code >= 400 && code < (400 + LWS_ARRAY_SIZE(err400)))
description = err400[code - 400];
if (code >= 500 && code < (500 + LWS_ARRAY_SIZE(err500)))
description = err500[code - 500];
if (code == 100)
description = "Continue";
if (code == 200)
description = "OK";
if (code == 304)
description = "Not Modified";
else
if (code >= 300 && code < 400)
description = "Redirect";
if (wsi->http.request_version < LWS_ARRAY_SIZE(hver))
p1 = hver[wsi->http.request_version];
else
p1 = hver[0];
n = lws_snprintf((char *)code_and_desc,
sizeof(code_and_desc) - 1, "%s %u %s",
p1, code, description);
if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p,
end))
return 1;
}
headers = wsi->a.vhost->headers;
while (headers) {
if (lws_add_http_header_by_name(wsi,
(const unsigned char *)headers->name,
(unsigned char *)headers->value,
(int)strlen(headers->value), p, end))
return 1;
headers = headers->next;
}
if (wsi->a.vhost->options &
LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE) {
headers = &pvo_hsbph[LWS_ARRAY_SIZE(pvo_hsbph) - 1];
while (headers) {
if (lws_add_http_header_by_name(wsi,
(const unsigned char *)headers->name,
(unsigned char *)headers->value,
(int)strlen(headers->value), p, end))
return 1;
headers = headers->next;
}
}
if (wsi->a.context->server_string &&
!(_code & LWSAHH_FLAG_NO_SERVER_NAME)) {
assert(wsi->a.context->server_string_len > 0);
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER,
(unsigned char *)wsi->a.context->server_string,
wsi->a.context->server_string_len, p, end))
return 1;
}
if (wsi->a.vhost->options & LWS_SERVER_OPTION_STS)
if (lws_add_http_header_by_name(wsi, (unsigned char *)
"Strict-Transport-Security:",
(unsigned char *)"max-age=15768000 ; "
"includeSubDomains", 36, p, end))
return 1;
if (*p >= (end - 2)) {
lwsl_err("%s: reached end of buffer\n", __func__);
return 1;
}
return 0;
}
int
lws_return_http_status(struct lws *wsi, unsigned int code,
const char *html_body)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
unsigned char *p = pt->serv_buf + LWS_PRE;
unsigned char *start = p;
unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
char *body = (char *)start + context->pt_serv_buf_size - 512;
int n = 0, m = 0, len;
char slen[20];
if (!wsi->a.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->a.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->a.vhost->http.error_document_404,
(int)strlen(wsi->a.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)
html_body = "";
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 *)"text/html", 9,
&p, end))
return 1;
len = lws_snprintf(body, 510, "<html><head>"
"<meta charset=utf-8 http-equiv=\"Content-Language\" "
"content=\"en\"/>"
"<link rel=\"stylesheet\" type=\"text/css\" "
"href=\"/error.css\"/>"
"</head><body><h1>%u</h1>%s</body></html>", code, html_body);
n = lws_snprintf(slen, 12, "%d", len);
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)slen, n, &p, end))
return 1;
if (lws_finalize_http_header(wsi, &p, end))
return 1;
#if defined(LWS_WITH_HTTP2)
if (wsi->mux_substream) {
/*
* for HTTP/2, the headers must be sent separately, since they
* go out in their own frame. That puts us in a bind that
* we won't always be able to get away with two lws_write()s in
* sequence, since the first may use up the writability due to
* the pipe being choked or SSL_WANT_.
*
* However we do need to send the human-readable body, and the
* END_STREAM.
*
* Solve it by writing the headers now...
*/
m = lws_write(wsi, start, lws_ptr_diff_size_t(p, start),
LWS_WRITE_HTTP_HEADERS);
if (m != lws_ptr_diff(p, start))
return 1;
/*
* ... but stash the body and send it as a priority next
* handle_POLLOUT
*/
wsi->http.tx_content_length = (unsigned int)len;
wsi->http.tx_content_remain = (unsigned int)len;
wsi->h2.pending_status_body = lws_malloc((unsigned int)len + LWS_PRE + 1,
"pending status body");
if (!wsi->h2.pending_status_body)
return -1;
strcpy(wsi->h2.pending_status_body + LWS_PRE, body);
lws_callback_on_writable(wsi);
return 0;
} else
#endif
{
/*
* for http/1, we can just append the body after the finalized
* headers and send it all in one go.
*/
n = lws_ptr_diff(p, start) + len;
memcpy(p, body, (unsigned int)len);
m = lws_write(wsi, start, (unsigned int)n, LWS_WRITE_HTTP);
if (m != n)
return 1;
}
return m != n;
}
int
lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
unsigned char **p, unsigned char *end)
{
unsigned char *start = *p;
if (lws_add_http_header_status(wsi, (unsigned int)code, p, end))
return -1;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION, loc, len,
p, end))
return -1;
/*
* 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
* it can do the redirect...
*/
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)"text/html", 9, p,
end))
return -1;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)"0", 1, p, end))
return -1;
if (lws_finalize_http_header(wsi, p, end))
return -1;
return lws_write(wsi, start, lws_ptr_diff_size_t(*p, start),
LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
}
#endif
#if !defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
int
lws_http_compression_apply(struct lws *wsi, const char *name,
unsigned char **p, unsigned char *end, char decomp)
{
(void)wsi;
(void)name;
(void)p;
(void)end;
(void)decomp;
return 0;
}
#endif
int
lws_http_headers_detach(struct lws *wsi)
{
return lws_header_table_detach(wsi, 0);
}
#if defined(LWS_WITH_SERVER)
void
lws_sul_http_ah_lifecheck(lws_sorted_usec_list_t *sul)
{
struct allocated_headers *ah;
struct lws_context_per_thread *pt = lws_container_of(sul,
struct lws_context_per_thread, sul_ah_lifecheck);
struct lws *wsi;
time_t now;
int m;
now = time(NULL);
lws_pt_lock(pt, __func__);
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->a.vhost &&
(now - ah->assigned) <
ah->wsi->a.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("%s: ah excessive hold: wsi %p\n"
" peer address: %s\n"
" ah pos %lu\n", __func__, lws_wsi_tag(wsi),
buf, (unsigned long)ah->pos);
buf[0] = '\0';
m = 0;
do {
c = lws_token_to_string((enum lws_token_indexes)m);
if (!c)
break;
if (!(*c))
break;
len = lws_hdr_total_length(wsi, (enum lws_token_indexes)m);
if (!len || len > (int)sizeof(buf) - 1) {
m++;
continue;
}
if (lws_hdr_copy(wsi, buf, sizeof buf, (enum lws_token_indexes)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 */
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"excessive ah");
ah = pt->http.ah_list;
}
lws_pt_unlock(pt);
}
#endif

View File

@ -0,0 +1,136 @@
/* set of parsable strings -- ALL LOWER CASE */
static const char * const set[] = {
"get ",
"post ",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
"options ",
#endif
"host:",
"connection:",
"upgrade:",
"origin:",
#if defined(LWS_ROLE_WS) || defined(LWS_HTTP_HEADERS_ALL)
"sec-websocket-draft:",
#endif
"\x0d\x0a",
#if defined(LWS_ROLE_WS) || defined(LWS_HTTP_HEADERS_ALL)
"sec-websocket-extensions:",
"sec-websocket-key1:",
"sec-websocket-key2:",
"sec-websocket-protocol:",
"sec-websocket-accept:",
"sec-websocket-nonce:",
#endif
"http/1.1 ",
#if defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"http2-settings:",
#endif
"accept:",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
"access-control-request-headers:",
#endif
"if-modified-since:",
"if-none-match:",
"accept-encoding:",
"accept-language:",
"pragma:",
"cache-control:",
"authorization:",
"cookie:",
"content-length:",
"content-type:",
"date:",
"range:",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"referer:",
#endif
#if defined(LWS_ROLE_WS) || defined(LWS_HTTP_HEADERS_ALL)
"sec-websocket-key:",
"sec-websocket-version:",
"sec-websocket-origin:",
#endif
#if defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
":authority",
":method",
":path",
":scheme",
":status",
#endif
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"accept-charset:",
#endif
"accept-ranges:",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"access-control-allow-origin:",
#endif
"age:",
"allow:",
"content-disposition:",
"content-encoding:",
"content-language:",
"content-location:",
"content-range:",
"etag:",
"expect:",
"expires:",
"from:",
"if-match:",
"if-range:",
"if-unmodified-since:",
"last-modified:",
"link:",
"location:",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"max-forwards:",
"proxy-authenticate:",
"proxy-authorization:",
#endif
"refresh:",
"retry-after:",
"server:",
"set-cookie:",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"strict-transport-security:",
#endif
"transfer-encoding:",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"user-agent:",
"vary:",
"via:",
"www-authenticate:",
#endif
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
"patch",
"put",
"delete",
#endif
"uri-args", /* fake header used for uri-only storage */
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
"proxy ",
"x-real-ip:",
#endif
"http/1.0 ",
"x-forwarded-for:",
"connect ",
"head ",
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
"te:", /* http/2 wants it to reject it */
"replay-nonce:", /* ACME */
#endif
#if defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL)
":protocol", /* defined in mcmanus-httpbis-h2-ws-02 */
#endif
"x-auth-token:",
"x-amzn-dss-signature:",
"", /* not matchable */
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,500 @@
/*
* minilex.c
*
* High efficiency lexical state parser
*
* Copyright (C)2011-2020 Andy Green <andy@warmcat.com>
*
* Licensed under MIT
*
* Usage: gcc minilex.c -o minilex && ./minilex > lextable.h
*
* Run it twice to test parsing on the generated table on stderr
*
* Whoo this got a bit complicated by lws-buildtime deselection of some
* headers optionally. There are 3 x vars, UNCOMMON, WS, H2 so we make
* eight copies of the lextable selected by the appropriate #if defined()
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* get all the strings */
#define LWS_ROLE_WS 1
#define LWS_WITH_HTTP_UNCOMMON_HEADERS 1
#define LWS_ROLE_H2 1
#include "lextable-strings.h"
#undef LWS_ROLE_WS
#undef LWS_WITH_HTTP_UNCOMMON_HEADERS
#undef LWS_ROLE_H2
/* bitfield for the 8 versions as to which strings exist... index layout
*
* b0 b1 b2
* 0 =
* 1 = uncommon
* 2 = ws
* 3 = uncommon ws
* 4 = h2
* 5 = uncommon h2
* 6 = ws h2
* 7 = uncommon ws h2
*/
unsigned char filter_array[] = {
0xff, /* get */
0xff, /* post */
0xaa, /* options */
0xff, /* host */
0xff, /* connection */
0xff, /* upgrade */
0xff, /* origin */
0xcc, /* sec-ws-draft */
0xff, /* crlf */
0xcc, /* sec-ws-ext */
0xcc, /* sec-ws-key1 */
0xcc, /* sec-ws-key2 */
0xcc, /* sec-ws-protocol */
0xcc, /* sec-ws-accept */
0xcc, /* sec-ws-nonce */
0xff, /* http/1.1 */
0xf0, /* http2-settings */
0xff, /* accept */
0xaa, /* access-control-req-hdrs */
0xff, /* if-modified-since */
0xff, /* if-none-match */
0xff, /* accept-encoding */
0xff, /* accept-language */
0xff, /* pragma */
0xff, /* cache-control */
0xff, /* authorization */
0xff, /* cookie */
0xff, /* content-length */
0xff, /* content-type */
0xff, /* date */
0xff, /* range */
0xfa, /* referer */
0xcc, /* sec-ws-key */
0xcc, /* sec-ws-version */
0xcc, /* sec-sc-origin */
0xf0, /* authority */
0xf0, /* method */
0xf0, /* path */
0xf0, /* scheme */
0xf0, /* status */
0xfa, /* accept-charset */
0xff, /* accept-ranges */
0xfa, /* access-control-allow-origin */
0xff, /* age */
0xff, /* allow */
0xff, /* content-disposition */
0xff, /* content-encoding */
0xff, /* content-language */
0xff, /* content-location */
0xff, /* content-range */
0xff, /* etag */
0xff, /* expect */
0xff, /* expires */
0xff, /* from */
0xff, /* if-match */
0xff, /* if-range */
0xff, /* if-unmodified-since */
0xff, /* last-modified */
0xff, /* link */
0xff, /* location */
0xfa, /* max-forwards */
0xfa, /* proxy-authenticate */
0xfa, /* proxy-authorization */
0xff, /* refresh */
0xff, /* retry-after */
0xff, /* server */
0xff, /* set-cookie */
0xfa, /* strict-transport-security */
0xff, /* transfer-encoding */
0xfa, /* user-agent */
0xfa, /* vary */
0xfa, /* via */
0xfa, /* www-authenticate */
0xaa, /* patch */
0xaa, /* put */
0xaa, /* delete */
0xff, /* uri-args */
0xaa, /* proxy */
0xaa, /* x-real-ip */
0xff, /* http/1.0 */
0xff, /* x-forwarded-for */
0xff, /* connect */
0xff, /* head */
0xfa, /* te */
0xfa, /* replay-nonce */
0xf0, /* protocol */
0xff, /* x-auth-token */
0xff /* not matchable */
};
static unsigned char lws_header_implies_psuedoheader_map[] = {
0x07, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00 /* <-64 */,
0x0e /* <- 72 */, 0x24 /* <- 80 */, 0, 0, 0, 0
};
/*
* b7 = 0 = 1-byte seq
* 0x08 = fail
* 2-byte seq
* 0x00 - 0x07, then terminal as given in 2nd byte
3-byte seq
* no match: go fwd 3 byte, match: jump fwd by amt in +1/+2 bytes
* = 1 = 1-byte seq
* no match: die, match go fwd 1 byte
*/
unsigned char lextable[][2000] = {
{
#include "lextable.h"
},
#define LWS_WITH_HTTP_UNCOMMON_HEADERS
{
#include "lextable.h"
},
#undef LWS_WITH_HTTP_UNCOMMON_HEADERS
#define LWS_ROLE_WS 1
{
#include "lextable.h"
},
#define LWS_WITH_HTTP_UNCOMMON_HEADERS
{
#include "lextable.h"
},
#undef LWS_ROLE_WS
#undef LWS_WITH_HTTP_UNCOMMON_HEADERS
#define LWS_ROLE_H2 1
{
#include "lextable.h"
},
#define LWS_WITH_HTTP_UNCOMMON_HEADERS
{
#include "lextable.h"
},
#undef LWS_WITH_HTTP_UNCOMMON_HEADERS
#define LWS_ROLE_WS 1
{
#include "lextable.h"
},
#define LWS_WITH_HTTP_UNCOMMON_HEADERS 1
{
#include "lextable.h"
},
};
#define PARALLEL 30
struct state {
char c[PARALLEL];
int state[PARALLEL];
int count;
int bytepos;
int real_pos;
};
static unsigned char pseudomap[8][16];
struct state state[1000];
int next = 1;
#define FAIL_CHAR 0x08
int lextable_decode(int version, int pos, char c)
{
while (1) {
if (lextable[version][pos] & (1 << 7)) { /* 1-byte, fail on mismatch */
if ((lextable[version][pos] & 0x7f) != c)
return -1;
/* fall thru */
pos++;
if (lextable[version][pos] == FAIL_CHAR)
return -1;
return pos;
} else { /* b7 = 0, end or 3-byte */
if (lextable[version][pos] < FAIL_CHAR) /* terminal marker */
return pos;
if (lextable[version][pos] == c) /* goto */
return pos + (lextable[version][pos + 1]) +
(lextable[version][pos + 2] << 8);
/* fall thru goto */
pos += 3;
/* continue */
}
}
}
int issue(int version)
{
const char *rset[200];
int n = 0;
int m;
int prev;
int walk;
int saw;
int y;
int j;
int pos = 0;
int setmembers = 0;
memset(rset, 0, sizeof(rset));
if (version == 7)
printf("#if defined(LWS_HTTP_HEADERS_ALL) || (%cdefined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && "
"%cdefined(LWS_ROLE_WS) && "
"%cdefined(LWS_ROLE_H2))\n", version & 1 ? ' ' : '!',
version & 2 ? ' ' : '!', version & 4 ? ' ' : '!');
else
printf("#if !defined(LWS_HTTP_HEADERS_ALL) && %cdefined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && "
"%cdefined(LWS_ROLE_WS) && "
"%cdefined(LWS_ROLE_H2)\n", version & 1 ? ' ' : '!',
version & 2 ? ' ' : '!', version & 4 ? ' ' : '!');
/*
* let's create version's view of the set of strings
*/
for (n = 0; n < sizeof(set) / sizeof(set[0]); n++)
if (filter_array[n] & (1 << version)) {
printf("\t/* %d: %d: %s */\n", setmembers, n, set[n]);
if (lws_header_implies_psuedoheader_map[n >> 3] & (1 << (n & 7)))
pseudomap[version][(setmembers >> 3)] |= 1 << (setmembers & 7);
rset[setmembers++] = set[n];
}
n = 0;
while (n < setmembers) {
m = 0;
walk = 0;
prev = 0;
if (rset[n][0] == '\0') {
n++;
continue;
}
while (rset[n][m]) {
saw = 0;
for (y = 0; y < state[walk].count; y++)
if (state[walk].c[y] == rset[n][m]) {
/* exists -- go forward */
walk = state[walk].state[y];
saw = 1;
break;
}
if (saw)
goto again;
/* something we didn't see before */
state[walk].c[state[walk].count] = rset[n][m];
state[walk].state[state[walk].count] = next;
state[walk].count++;
walk = next++;
again:
m++;
}
state[walk].c[0] = n++;
state[walk].state[0] = 0; /* terminal marker */
state[walk].count = 1;
}
walk = 0;
for (n = 0; n < next; n++) {
state[n].bytepos = walk;
walk += (2 * state[n].count);
}
/* compute everyone's position first */
pos = 0;
walk = 0;
for (n = 0; n < next; n++) {
state[n].real_pos = pos;
for (m = 0; m < state[n].count; m++) {
if (state[n].state[m] == 0)
pos += 2; /* terminal marker */
else { /* c is a character */
if ((state[state[n].state[m]].bytepos -
walk) == 2)
pos++;
else {
pos += 3;
if (m == state[n].count - 1)
pos++; /* fail */
}
}
walk += 2;
}
}
walk = 0;
pos = 0;
for (n = 0; n < next; n++) {
for (m = 0; m < state[n].count; m++) {
if (!m)
fprintf(stdout, "/* pos %04x: %3d */ ",
state[n].real_pos, n);
else
fprintf(stdout, " ");
y = state[n].c[m];
saw = state[n].state[m];
if (saw == 0) { // c is a terminal then
if (y > 0x7ff) {
fprintf(stderr, "terminal too big\n");
return 2;
}
fprintf(stdout, " 0x%02X, 0x%02X "
" "
"/* - terminal marker %2d - */,\n",
y >> 8, y & 0xff, y & 0x7f);
pos += 2;
walk += 2;
continue;
}
/* c is a character */
prev = y &0x7f;
if (prev < 32 || prev > 126)
prev = '.';
if ((state[saw].bytepos - walk) == 2) {
fprintf(stdout, " 0x%02X /* '%c' -> */,\n",
y | 0x80, prev);
pos++;
walk += 2;
continue;
}
j = state[saw].real_pos - pos;
if (j > 0xffff) {
fprintf(stderr,
"Jump > 64K bytes ahead (%d to %d)\n",
state[n].real_pos, state[saw].real_pos);
return 1;
}
fprintf(stdout, " 0x%02X /* '%c' */, 0x%02X, 0x%02X "
"/* (to 0x%04X state %3d) */,\n",
y, prev,
j & 0xff, j >> 8,
state[saw].real_pos, saw);
pos += 3;
if (m == state[n].count - 1) {
fprintf(stdout,
" 0x%02X, /* fail */\n",
FAIL_CHAR);
pos++; /* fail */
}
walk += 2;
}
}
fprintf(stdout, "/* total size %d bytes */\n", pos);
printf("#endif\n\n");
/*
* Try to parse every legal input string
*/
for (n = 0; n < setmembers; n++) {
walk = 0;
m = 0;
y = -1;
if (rset[n][0] == '\0')
continue;
fprintf(stderr, " trying %d '%s'\n", n, rset[n]);
while (rset[n][m]) {
walk = lextable_decode(version, walk, rset[n][m]);
if (walk < 0) {
fprintf(stderr, "failed\n");
return 3;
}
if (lextable[version][walk] < FAIL_CHAR) {
y = (lextable[version][walk] << 8) +
lextable[version][walk + 1];
break;
}
m++;
}
if (y != n) {
fprintf(stderr, "decode failed %d\n", y);
return 4;
}
}
fprintf(stderr, "All decode OK\n");
return 0;
}
int main(void)
{
int m, n;
for (n = 0; n < 8; n++) {
issue(n);
}
printf("\n/*\n");
for (n = 0; n < 8; n++) {
if (n == 7)
printf("#if defined(LWS_HTTP_HEADERS_ALL) || (%cdefined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && "
"%cdefined(LWS_ROLE_WS) && "
"%cdefined(LWS_ROLE_H2))\n", n & 1 ? ' ' : '!',
n & 2 ? ' ' : '!', n & 4 ? ' ' : '!');
else
printf("#if !defined(LWS_HTTP_HEADERS_ALL) && %cdefined(LWS_WITH_HTTP_UNCOMMON_HEADERS) && "
"%cdefined(LWS_ROLE_WS) && "
"%cdefined(LWS_ROLE_H2)\n", n & 1 ? ' ' : '!',
n & 2 ? ' ' : '!', n & 4 ? ' ' : '!');
printf("static uint8_t lws_header_implies_psuedoheader_map[] = {\n\t");
for (m = 0; m < sizeof(pseudomap[n]); m++)
printf("0x%02x,", pseudomap[n][m]);
printf("\n};\n");
printf("#endif\n");
}
printf("*/\n");
fprintf(stderr, "did all the variants\n");
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,355 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is included from private-lib-core.h if either H1 or H2 roles are
* enabled
*/
#if defined(LWS_WITH_HUBBUB)
#include <hubbub/hubbub.h>
#include <hubbub/parser.h>
#endif
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
#include "private-lib-roles-http-compression.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_conn_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_PLAT_FREERTOS)
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
#define LWS_HTTP_NO_KNOWN_HEADER 0xff
/*
* 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];
#if defined(LWS_WITH_CLIENT)
char initial_handshake_hash_base64[30];
#endif
int hdr_token_idx;
ah_data_idx_t pos;
ah_data_idx_t http_response;
ah_data_idx_t current_token_limit;
ah_data_idx_t unk_pos; /* to undo speculative unknown header */
#if defined(LWS_WITH_CUSTOM_HEADERS)
ah_data_idx_t unk_value_pos;
ah_data_idx_t unk_ll_head;
ah_data_idx_t unk_ll_tail;
#endif
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_HUBBUB)
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 {
#if defined(LWS_CLIENT_HTTP_PROXYING)
char http_proxy_address[128];
#endif
const struct lws_http_mount *mount_list;
const char *error_document_404;
#if defined(LWS_CLIENT_HTTP_PROXYING)
unsigned int http_proxy_port;
#endif
};
#ifdef LWS_WITH_ACCESS_LOG
struct lws_access_log {
char *header_log;
char *user_agent;
char *referrer;
unsigned long sent;
int response;
};
#endif
#define LWS_HTTP_CHUNK_HDR_MAX_SIZE (6 + 2) /* 6 hex digits and then CRLF */
#define LWS_HTTP_CHUNK_TRL_MAX_SIZE (2 + 5) /* CRLF, then maybe 0 CRLF CRLF */
struct _lws_http_mode_related {
struct lws *new_wsi_list;
unsigned char *pending_return_headers;
size_t pending_return_headers_len;
size_t prh_content_length;
#if defined(LWS_WITH_HTTP_PROXY)
struct lws_rewrite *rw;
struct lws_buflist *buflist_post_body;
#endif
struct allocated_headers *ah;
struct lws *ah_wait_list;
unsigned long writeable_len;
#if defined(LWS_WITH_FILE_OPS)
lws_filepos_t filepos;
lws_filepos_t filelen;
lws_fop_fd_t fop_fd;
#endif
#if defined(LWS_WITH_CLIENT)
char multipart_boundary[16];
#endif
#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
#if defined(LWS_WITH_SERVER)
unsigned int response_code;
#endif
#ifdef LWS_WITH_CGI
struct lws_cgi *cgi; /* wsi being cgi stream have one of these */
#endif
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
struct lws_compression_support *lcs;
lws_comp_ctx_t comp_ctx;
unsigned char comp_accept_mask;
#endif
enum http_version request_version;
enum http_conn_type conn_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;
unsigned int proxy_clientside:1;
unsigned int proxy_parent_chunked:1;
#endif
unsigned int deferred_transaction_completed:1;
unsigned int content_length_explicitly_zero:1;
unsigned int content_length_given:1;
unsigned int did_stream_close:1;
unsigned int multipart:1;
unsigned int cgi_transaction_complete:1;
unsigned int multipart_issue_boundary:1;
char auth_username[64];
char auth_password[64];
char *digest_auth_hdr;
};
#if defined(LWS_WITH_CLIENT)
enum lws_chunk_parser {
ELCP_HEX,
ELCP_CR,
ELCP_CONTENT,
ELCP_POST_CR,
ELCP_POST_LF,
ELCP_TRAILER_CR,
ELCP_TRAILER_LF
};
#endif
enum lws_parse_urldecode_results {
LPUR_CONTINUE,
LPUR_SWALLOW,
LPUR_FORBID,
LPUR_EXCESSIVE,
};
enum lws_check_basic_auth_results {
LCBA_CONTINUE,
LCBA_FAILED_AUTH,
LCBA_END_TRANSACTION,
};
enum lws_check_basic_auth_results
lws_check_basic_auth(struct lws *wsi, const char *basic_auth_login_file, unsigned int auth_mode);
int
lws_unauthorised_basic_auth(struct lws *wsi);
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);
int
lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit,
char *uri_ptr, char ws);
void
lws_sul_http_ah_lifecheck(lws_sorted_usec_list_t *sul);
uint8_t *
lws_http_multipart_headers(struct lws *wsi, uint8_t *p);
int
lws_http_string_to_known_header(const char *s, size_t slen);
int
lws_http_date_render_from_unix(char *buf, size_t len, const time_t *t);
int
lws_http_date_parse_unix(const char *b, size_t len, time_t *t);
enum {
CCTLS_RETURN_ERROR = -1,
CCTLS_RETURN_DONE = 0,
CCTLS_RETURN_RETRY = 1,
};
int
lws_client_create_tls(struct lws *wsi, const char **pcce, int do_c1);

View File

@ -0,0 +1,209 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.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 uri_len, int meth)
{
char da[64], uri[256], ta[64];
time_t t = time(NULL);
struct lws *nwsi;
const char *me;
int l = 256, m;
struct tm *ptm = NULL;
#if defined(LWS_HAVE_LOCALTIME_R)
struct tm tm;
#endif
if (!wsi->a.vhost)
return;
/* only worry about preparing it if we store it */
if (wsi->a.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((unsigned int)l, "access log");
if (!wsi->http.access_log.header_log)
return;
#if defined(LWS_HAVE_LOCALTIME_R)
ptm = localtime_r(&t, &tm);
#else
ptm = localtime(&t);
#endif
if (ptm)
strftime(da, sizeof(da), "%d/%b/%Y:%H:%M:%S %z", ptm);
else
strcpy(da, "01/Jan/1970:00:00:00 +0000");
#if defined(LWS_ROLE_H2)
if (wsi->mux_substream)
me = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
else
#endif
me = method_names[meth];
if (!me)
me = "(null)";
m = uri_len;
if (m > (int)sizeof(uri) - 1)
m = sizeof(uri) - 1;
strncpy(uri, uri_ptr, (unsigned int)m);
uri[m] = '\0';
nwsi = lws_get_network_wsi(wsi);
if (nwsi->sa46_peer.sa4.sin_family)
lws_sa46_write_numeric_address(&nwsi->sa46_peer, ta, sizeof(ta));
else
strncpy(ta, "unknown", sizeof(ta));
lws_snprintf(wsi->http.access_log.header_log, (size_t)l,
"%s - - [%s] \"%s %s %s\"",
ta, da, me, uri, hver[wsi->http.request_version]);
//lwsl_notice("%s\n", wsi->http.access_log.header_log);
l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT);
if (l) {
wsi->http.access_log.user_agent =
lws_malloc((unsigned int)l + 5, "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;
}
wsi->http.access_log.user_agent[0] = '\0';
if (lws_hdr_copy(wsi, wsi->http.access_log.user_agent, l + 4,
WSI_TOKEN_HTTP_USER_AGENT) >= 0)
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((unsigned int)l + 5, "referrer");
if (!wsi->http.access_log.referrer) {
lwsl_err("OOM getting referrer\n");
lws_free_set_NULL(wsi->http.access_log.user_agent);
lws_free_set_NULL(wsi->http.access_log.header_log);
return;
}
wsi->http.access_log.referrer[0] = '\0';
if (lws_hdr_copy(wsi, wsi->http.access_log.referrer,
l + 4, WSI_TOKEN_HTTP_REFERER) >= 0)
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->a.vhost)
return 0;
if (wsi->a.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 - (unsigned int)l) {
p[sizeof(ass) - 6 - (unsigned int)l] = '\0';
l--;
}
l += lws_snprintf(ass + (unsigned int)l, sizeof(ass) - 1 - (unsigned int)l, "\" \"%s\"\n", p);
ass[sizeof(ass) - 1] = '\0';
if ((int)write(wsi->a.vhost->log_fd, ass, (size_t)l) != l)
lwsl_err("Failed to write log\n");
if (wsi->http.access_log.header_log) {
lws_free(wsi->http.access_log.header_log);
wsi->http.access_log.header_log = NULL;
}
if (wsi->http.access_log.user_agent) {
lws_free(wsi->http.access_log.user_agent);
wsi->http.access_log.user_agent = NULL;
}
if (wsi->http.access_log.referrer) {
lws_free(wsi->http.access_log.referrer);
wsi->http.access_log.referrer = NULL;
}
wsi->access_log_pending = 0;
return 0;
}

View File

@ -0,0 +1,657 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Original code used in this source file:
*
* https://github.com/PerBothner/DomTerm.git @912add15f3d0aec
*
* ./lws-term/io.c
* ./lws-term/junzip.c
*
* Copyright (C) 2017 Per Bothner <per@bothner.com>
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* ( copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Somewhat rewritten by AG
*/
#include "private-lib-core.h"
#if defined(LWS_WITH_MINIZ)
#include <miniz.h>
#else
#include <zlib.h>
#endif
/*
* This code works with zip format containers which may have files compressed
* with gzip deflate (type 8) or store uncompressed (type 0).
*
* Linux zip produces such zipfiles by default, eg
*
* $ zip ../myzip.zip file1 file2 file3
*/
#define ZIP_COMPRESSION_METHOD_STORE 0
#define ZIP_COMPRESSION_METHOD_DEFLATE 8
typedef struct {
lws_filepos_t filename_start;
uint32_t crc32;
uint32_t comp_size;
uint32_t uncomp_size;
uint32_t offset;
uint32_t mod_time;
uint16_t filename_len;
uint16_t extra;
uint16_t method;
uint16_t file_com_len;
} lws_fops_zip_hdr_t;
typedef struct {
struct lws_fop_fd fop_fd; /* MUST BE FIRST logical fop_fd into
* file inside zip: fops_zip fops */
lws_fop_fd_t zip_fop_fd; /* logical fop fd on to zip file
* itself: using platform fops */
lws_fops_zip_hdr_t hdr;
z_stream inflate;
lws_filepos_t content_start;
lws_filepos_t exp_uncomp_pos;
union {
uint8_t trailer8[8];
uint32_t trailer32[2];
} u;
uint8_t rbuf[128]; /* decompression chunk size */
int entry_count;
unsigned int decompress:1; /* 0 = direct from file */
unsigned int add_gzip_container:1;
} *lws_fops_zip_t;
struct lws_plat_file_ops fops_zip;
#define fop_fd_to_priv(FD) ((lws_fops_zip_t)(FD))
static const uint8_t hd[] = { 31, 139, 8, 0, 0, 0, 0, 0, 0, 3 };
enum {
ZC_SIGNATURE = 0,
ZC_VERSION_MADE_BY = 4,
ZC_VERSION_NEEDED_TO_EXTRACT = 6,
ZC_GENERAL_PURPOSE_BIT_FLAG = 8,
ZC_COMPRESSION_METHOD = 10,
ZC_LAST_MOD_FILE_TIME = 12,
ZC_LAST_MOD_FILE_DATE = 14,
ZC_CRC32 = 16,
ZC_COMPRESSED_SIZE = 20,
ZC_UNCOMPRESSED_SIZE = 24,
ZC_FILE_NAME_LENGTH = 28,
ZC_EXTRA_FIELD_LENGTH = 30,
ZC_FILE_COMMENT_LENGTH = 32,
ZC_DISK_NUMBER_START = 34,
ZC_INTERNAL_FILE_ATTRIBUTES = 36,
ZC_EXTERNAL_FILE_ATTRIBUTES = 38,
ZC_REL_OFFSET_LOCAL_HEADER = 42,
ZC_DIRECTORY_LENGTH = 46,
ZE_SIGNATURE_OFFSET = 0,
ZE_DESK_NUMBER = 4,
ZE_CENTRAL_DIRECTORY_DISK_NUMBER = 6,
ZE_NUM_ENTRIES_THIS_DISK = 8,
ZE_NUM_ENTRIES = 10,
ZE_CENTRAL_DIRECTORY_SIZE = 12,
ZE_CENTRAL_DIR_OFFSET = 16,
ZE_ZIP_COMMENT_LENGTH = 20,
ZE_DIRECTORY_LENGTH = 22,
ZL_REL_OFFSET_CONTENT = 28,
ZL_HEADER_LENGTH = 30,
LWS_FZ_ERR_SEEK_END_RECORD = 1,
LWS_FZ_ERR_READ_END_RECORD,
LWS_FZ_ERR_END_RECORD_MAGIC,
LWS_FZ_ERR_END_RECORD_SANITY,
LWS_FZ_ERR_CENTRAL_SEEK,
LWS_FZ_ERR_CENTRAL_READ,
LWS_FZ_ERR_CENTRAL_SANITY,
LWS_FZ_ERR_NAME_TOO_LONG,
LWS_FZ_ERR_NAME_SEEK,
LWS_FZ_ERR_NAME_READ,
LWS_FZ_ERR_CONTENT_SANITY,
LWS_FZ_ERR_CONTENT_SEEK,
LWS_FZ_ERR_SCAN_SEEK,
LWS_FZ_ERR_NOT_FOUND,
LWS_FZ_ERR_ZLIB_INIT,
LWS_FZ_ERR_READ_CONTENT,
LWS_FZ_ERR_SEEK_COMPRESSED,
};
#define eff_size(_priv) (_priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE ? \
_priv->hdr.uncomp_size : _priv->hdr.comp_size)
static uint16_t
get_u16(void *p)
{
const uint8_t *c = (const uint8_t *)p;
return (uint16_t)((c[0] | (c[1] << 8)));
}
static uint32_t
get_u32(void *p)
{
const uint8_t *c = (const uint8_t *)p;
return (uint32_t)((c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)));
}
int
lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
{
lws_filepos_t amount;
uint8_t buf[96];
int i;
if (lws_vfs_file_seek_end(priv->zip_fop_fd, -ZE_DIRECTORY_LENGTH) < 0)
return LWS_FZ_ERR_SEEK_END_RECORD;
if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
ZE_DIRECTORY_LENGTH))
return LWS_FZ_ERR_READ_END_RECORD;
if (amount != ZE_DIRECTORY_LENGTH)
return LWS_FZ_ERR_READ_END_RECORD;
/*
* We require the zip to have the last record right at the end
* Linux zip always does this if no zip comment.
*/
if (buf[0] != 'P' || buf[1] != 'K' || buf[2] != 5 || buf[3] != 6)
return LWS_FZ_ERR_END_RECORD_MAGIC;
i = get_u16(buf + ZE_NUM_ENTRIES);
if (get_u16(buf + ZE_DESK_NUMBER) ||
get_u16(buf + ZE_CENTRAL_DIRECTORY_DISK_NUMBER) ||
i != get_u16(buf + ZE_NUM_ENTRIES_THIS_DISK))
return LWS_FZ_ERR_END_RECORD_SANITY;
/* end record is OK... look for our file in the central dir */
if (lws_vfs_file_seek_set(priv->zip_fop_fd,
get_u32(buf + ZE_CENTRAL_DIR_OFFSET)) < 0)
return LWS_FZ_ERR_CENTRAL_SEEK;
while (i--) {
priv->content_start = lws_vfs_tell(priv->zip_fop_fd);
if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
ZC_DIRECTORY_LENGTH))
return LWS_FZ_ERR_CENTRAL_READ;
if (amount != ZC_DIRECTORY_LENGTH)
return LWS_FZ_ERR_CENTRAL_READ;
if (get_u32(buf + ZC_SIGNATURE) != 0x02014B50)
return LWS_FZ_ERR_CENTRAL_SANITY;
lwsl_debug("cstart 0x%lx\n", (unsigned long)priv->content_start);
priv->hdr.filename_len = get_u16(buf + ZC_FILE_NAME_LENGTH);
priv->hdr.extra = get_u16(buf + ZC_EXTRA_FIELD_LENGTH);
priv->hdr.filename_start = lws_vfs_tell(priv->zip_fop_fd);
priv->hdr.method = get_u16(buf + ZC_COMPRESSION_METHOD);
priv->hdr.crc32 = get_u32(buf + ZC_CRC32);
priv->hdr.comp_size = get_u32(buf + ZC_COMPRESSED_SIZE);
priv->hdr.uncomp_size = get_u32(buf + ZC_UNCOMPRESSED_SIZE);
priv->hdr.offset = get_u32(buf + ZC_REL_OFFSET_LOCAL_HEADER);
priv->hdr.mod_time = get_u32(buf + ZC_LAST_MOD_FILE_TIME);
priv->hdr.file_com_len = get_u16(buf + ZC_FILE_COMMENT_LENGTH);
if (priv->hdr.filename_len != len)
goto next;
if (len >= (int)sizeof(buf) - 1)
return LWS_FZ_ERR_NAME_TOO_LONG;
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
&amount, buf, (unsigned int)len))
return LWS_FZ_ERR_NAME_READ;
if ((int)amount != len)
return LWS_FZ_ERR_NAME_READ;
buf[len] = '\0';
lwsl_debug("check %s vs %s\n", buf, name);
if (strcmp((const char *)buf, name))
goto next;
/* we found a match */
if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->hdr.offset) < 0)
return LWS_FZ_ERR_NAME_SEEK;
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
&amount, buf,
ZL_HEADER_LENGTH))
return LWS_FZ_ERR_NAME_READ;
if (amount != ZL_HEADER_LENGTH)
return LWS_FZ_ERR_NAME_READ;
priv->content_start = priv->hdr.offset +
ZL_HEADER_LENGTH +
priv->hdr.filename_len +
get_u16(buf + ZL_REL_OFFSET_CONTENT);
lwsl_debug("content supposed to start at 0x%lx\n",
(unsigned long)priv->content_start);
if (priv->content_start > priv->zip_fop_fd->len)
return LWS_FZ_ERR_CONTENT_SANITY;
if (lws_vfs_file_seek_set(priv->zip_fop_fd,
(lws_fileofs_t)priv->content_start) < 0)
return LWS_FZ_ERR_CONTENT_SEEK;
/* we are aligned at the start of the content */
priv->exp_uncomp_pos = 0;
return 0;
next:
if (i && lws_vfs_file_seek_set(priv->zip_fop_fd,
(lws_fileofs_t)priv->content_start +
(ZC_DIRECTORY_LENGTH +
priv->hdr.filename_len +
priv->hdr.extra +
priv->hdr.file_com_len)) < 0)
return LWS_FZ_ERR_SCAN_SEEK;
}
return LWS_FZ_ERR_NOT_FOUND;
}
static int
lws_fops_zip_reset_inflate(lws_fops_zip_t priv)
{
if (priv->decompress)
inflateEnd(&priv->inflate);
priv->inflate.zalloc = Z_NULL;
priv->inflate.zfree = Z_NULL;
priv->inflate.opaque = Z_NULL;
priv->inflate.avail_in = 0;
priv->inflate.next_in = Z_NULL;
if (inflateInit2(&priv->inflate, -MAX_WBITS) != Z_OK) {
lwsl_err("inflate init failed\n");
return LWS_FZ_ERR_ZLIB_INIT;
}
if (lws_vfs_file_seek_set(priv->zip_fop_fd, (lws_fileofs_t)priv->content_start) < 0)
return LWS_FZ_ERR_CONTENT_SEEK;
priv->exp_uncomp_pos = 0;
return 0;
}
static lws_fop_fd_t
lws_fops_zip_open(const struct lws_plat_file_ops *fops_own,
const struct lws_plat_file_ops *fops, const char *vfs_path,
const char *vpath, lws_fop_flags_t *flags)
{
lws_fop_flags_t local_flags = 0;
lws_fops_zip_t priv;
char rp[192];
int m;
/*
* vpath points at the / after the fops signature in vfs_path, eg
* with a vfs_path "/var/www/docs/manual.zip/index.html", vpath
* will come pointing at "/index.html"
*/
priv = lws_zalloc(sizeof(*priv), "fops_zip priv");
if (!priv)
return NULL;
priv->fop_fd.fops = &fops_zip;
m = sizeof(rp) - 1;
if ((vpath - vfs_path - 1) < m)
m = lws_ptr_diff(vpath, vfs_path) - 1;
lws_strncpy(rp, vfs_path, (unsigned int)m + 1);
/* open the zip file itself using the incoming fops, not fops_zip */
priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops_own, fops, rp, NULL, &local_flags);
if (!priv->zip_fop_fd) {
lwsl_err("%s: unable to open zip %s\n", __func__, rp);
goto bail1;
}
if (*vpath == '/')
vpath++;
m = lws_fops_zip_scan(priv, vpath, (int)strlen(vpath));
if (m) {
lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
goto bail2;
}
/* the directory metadata tells us modification time, so pass it on */
priv->fop_fd.mod_time = priv->hdr.mod_time;
*flags |= LWS_FOP_FLAG_MOD_TIME_VALID | LWS_FOP_FLAG_VIRTUAL;
priv->fop_fd.flags = *flags;
/* The zip fop_fd is left pointing at the start of the content.
*
* 1) Content could be uncompressed (STORE), and we can always serve
* that directly
*
* 2) Content could be compressed (GZIP), and the client can handle
* receiving GZIP... we can wrap it in a GZIP header and trailer
* and serve the content part directly. The flag indicating we
* are providing GZIP directly is set so lws will send the right
* headers.
*
* 3) Content could be compressed (GZIP) but the client can't handle
* receiving GZIP... we can decompress it and serve as it is
* inflated piecemeal.
*
* 4) Content may be compressed some unknown way... fail
*
*/
if (priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE) {
/*
* it is stored uncompressed, leave it indicated as
* uncompressed, and just serve it from inside the
* zip with no gzip container;
*/
lwsl_info("direct zip serving (stored)\n");
priv->fop_fd.len = priv->hdr.uncomp_size;
return &priv->fop_fd;
}
if ((*flags & LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP) &&
priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
/*
* We can serve the gzipped file contents directly as gzip
* from inside the zip container; client says it is OK.
*
* To convert to standalone gzip, we have to add a 10-byte
* constant header and a variable 8-byte trailer around the
* content.
*
* The 8-byte trailer is prepared now and held in the priv.
*/
lwsl_info("direct zip serving (gzipped)\n");
priv->fop_fd.len = sizeof(hd) + priv->hdr.comp_size +
sizeof(priv->u);
if (lws_is_be()) {
uint8_t *p = priv->u.trailer8;
*p++ = (uint8_t)priv->hdr.crc32;
*p++ = (uint8_t)(priv->hdr.crc32 >> 8);
*p++ = (uint8_t)(priv->hdr.crc32 >> 16);
*p++ = (uint8_t)(priv->hdr.crc32 >> 24);
*p++ = (uint8_t)priv->hdr.uncomp_size;
*p++ = (uint8_t)(priv->hdr.uncomp_size >> 8);
*p++ = (uint8_t)(priv->hdr.uncomp_size >> 16);
*p = (uint8_t)(priv->hdr.uncomp_size >> 24);
} else {
priv->u.trailer32[0] = priv->hdr.crc32;
priv->u.trailer32[1] = priv->hdr.uncomp_size;
}
*flags |= LWS_FOP_FLAG_COMPR_IS_GZIP;
priv->fop_fd.flags = *flags;
priv->add_gzip_container = 1;
return &priv->fop_fd;
}
if (priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
/* we must decompress it to serve it */
lwsl_info("decompressed zip serving\n");
priv->fop_fd.len = priv->hdr.uncomp_size;
if (lws_fops_zip_reset_inflate(priv)) {
lwsl_err("inflate init failed\n");
goto bail2;
}
priv->decompress = 1;
return &priv->fop_fd;
}
/* we can't handle it ... */
lwsl_err("zipped file %s compressed in unknown way (%d)\n", vfs_path,
priv->hdr.method);
bail2:
lws_vfs_file_close(&priv->zip_fop_fd);
bail1:
free(priv);
return NULL;
}
/* ie, we are closing the fop_fd for the file inside the gzip */
static int
lws_fops_zip_close(lws_fop_fd_t *fd)
{
lws_fops_zip_t priv = fop_fd_to_priv(*fd);
if (priv->decompress)
inflateEnd(&priv->inflate);
lws_vfs_file_close(&priv->zip_fop_fd); /* close the gzip fop_fd */
free(priv);
*fd = NULL;
return 0;
}
static lws_fileofs_t
lws_fops_zip_seek_cur(lws_fop_fd_t fd, lws_fileofs_t offset_from_cur_pos)
{
fd->pos = (lws_filepos_t)((lws_fileofs_t)fd->pos + offset_from_cur_pos);
return (lws_fileofs_t)fd->pos;
}
static int
lws_fops_zip_read(lws_fop_fd_t fd, lws_filepos_t *amount, uint8_t *buf,
lws_filepos_t len)
{
lws_fops_zip_t priv = fop_fd_to_priv(fd);
lws_filepos_t ramount, rlen, cur = lws_vfs_tell(fd);
int ret;
if (priv->decompress) {
if (priv->exp_uncomp_pos != fd->pos) {
/*
* there has been a seek in the uncompressed fop_fd
* we have to restart the decompression and loop eating
* the decompressed data up to the seek point
*/
lwsl_info("seek in decompressed\n");
lws_fops_zip_reset_inflate(priv);
while (priv->exp_uncomp_pos != fd->pos) {
rlen = len;
if (rlen > fd->pos - priv->exp_uncomp_pos)
rlen = fd->pos - priv->exp_uncomp_pos;
if (lws_fops_zip_read(fd, amount, buf, rlen))
return LWS_FZ_ERR_SEEK_COMPRESSED;
}
*amount = 0;
}
priv->inflate.avail_out = (unsigned int)len;
priv->inflate.next_out = buf;
spin:
if (!priv->inflate.avail_in) {
rlen = sizeof(priv->rbuf);
if (rlen > eff_size(priv) - (cur - priv->content_start))
rlen = eff_size(priv) - (cur - priv->content_start);
if (priv->zip_fop_fd->fops->LWS_FOP_READ(
priv->zip_fop_fd, &ramount, priv->rbuf,
rlen))
return LWS_FZ_ERR_READ_CONTENT;
cur += ramount;
priv->inflate.avail_in = (unsigned int)ramount;
priv->inflate.next_in = priv->rbuf;
}
ret = inflate(&priv->inflate, Z_NO_FLUSH);
if (ret == Z_STREAM_ERROR)
return ret;
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR;
/* fallthru */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
return ret;
}
if (!priv->inflate.avail_in && priv->inflate.avail_out &&
cur != priv->content_start + priv->hdr.comp_size)
goto spin;
*amount = len - priv->inflate.avail_out;
priv->exp_uncomp_pos += *amount;
fd->pos += *amount;
return 0;
}
if (priv->add_gzip_container) {
lwsl_info("%s: gzip + container\n", __func__);
*amount = 0;
/* place the canned header at the start */
if (len && fd->pos < sizeof(hd)) {
rlen = sizeof(hd) - fd->pos;
if (rlen > len)
rlen = len;
/* provide stuff from canned header */
memcpy(buf, hd + fd->pos, (size_t)rlen);
fd->pos += rlen;
buf += rlen;
len -= rlen;
*amount += rlen;
}
/* serve gzipped data direct from zipfile */
if (len && fd->pos >= sizeof(hd) &&
fd->pos < priv->hdr.comp_size + sizeof(hd)) {
rlen = priv->hdr.comp_size - (priv->zip_fop_fd->pos -
priv->content_start);
if (rlen > len)
rlen = len;
if (rlen &&
priv->zip_fop_fd->pos < (priv->hdr.comp_size +
priv->content_start)) {
if (lws_vfs_file_read(priv->zip_fop_fd,
&ramount, buf, rlen))
return LWS_FZ_ERR_READ_CONTENT;
*amount += ramount;
fd->pos += ramount; // virtual pos
buf += ramount;
len -= ramount;
}
}
/* place the prepared trailer at the end */
if (len && fd->pos >= priv->hdr.comp_size + sizeof(hd) &&
fd->pos < priv->hdr.comp_size + sizeof(hd) +
sizeof(priv->u)) {
cur = fd->pos - priv->hdr.comp_size - sizeof(hd);
rlen = sizeof(priv->u) - cur;
if (rlen > len)
rlen = len;
memcpy(buf, priv->u.trailer8 + cur, (size_t)rlen);
*amount += rlen;
fd->pos += rlen;
}
return 0;
}
lwsl_info("%s: store\n", __func__);
if (len > eff_size(priv) - cur)
len = eff_size(priv) - cur;
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
amount, buf, len))
return LWS_FZ_ERR_READ_CONTENT;
fd->pos += *amount;
return 0;
}
struct lws_plat_file_ops fops_zip = {
lws_fops_zip_open,
lws_fops_zip_close,
lws_fops_zip_seek_cur,
lws_fops_zip_read,
NULL,
{ { ".zip/", 5 }, { ".jar/", 5 }, { ".war/", 5 } },
NULL,
NULL,
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,725 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#define LWS_MAX_ELEM_NAME 32
enum urldecode_stateful {
US_NAME,
US_IDLE,
US_PC1,
US_PC2,
MT_LOOK_BOUND_IN,
MT_HNAME,
MT_DISP,
MT_TYPE,
MT_IGNORE1,
MT_IGNORE2,
MT_IGNORE3,
MT_COMPLETED,
};
static struct mp_hdr {
const char * const hdr;
uint8_t hdr_len;
} mp_hdrs[] = {
{ "content-disposition: ", 21 },
{ "content-type: ", 14 },
{ "\x0d\x0a", 2 }
};
struct lws_spa;
typedef int (*lws_urldecode_stateful_cb)(struct lws_spa *spa,
const char *name, char **buf, int len, int final);
struct lws_urldecode_stateful {
char *out;
struct lws_spa *data;
struct lws *wsi;
char name[LWS_MAX_ELEM_NAME];
char temp[LWS_MAX_ELEM_NAME];
char content_type[32];
char content_disp[32];
char content_disp_filename[256];
char mime_boundary[128];
int out_len;
int pos;
int hdr_idx;
int mp;
int sum;
uint8_t matchable;
uint8_t multipart_form_data:1;
uint8_t inside_quote:1;
uint8_t subname:1;
uint8_t boundary_real_crlf:1;
enum urldecode_stateful state;
lws_urldecode_stateful_cb output;
};
struct lws_spa {
struct lws_urldecode_stateful *s;
lws_spa_create_info_t i;
int *param_length;
char finalized;
char **params;
char *storage;
char *end;
};
static struct lws_urldecode_stateful *
lws_urldecode_s_create(struct lws_spa *spa, struct lws *wsi, char *out,
int out_len, lws_urldecode_stateful_cb output)
{
struct lws_urldecode_stateful *s;
char buf[205], *p;
int m = 0;
if (spa->i.ac)
s = lwsac_use_zero(spa->i.ac, sizeof(*s), spa->i.ac_chunk_size);
else
s = lws_zalloc(sizeof(*s), "stateful urldecode");
if (!s)
return NULL;
s->out = out;
s->out_len = out_len;
s->output = output;
s->pos = 0;
s->sum = 0;
s->mp = 0;
s->state = US_NAME;
s->name[0] = '\0';
s->data = spa;
s->wsi = wsi;
if (lws_hdr_copy(wsi, buf, sizeof(buf),
WSI_TOKEN_HTTP_CONTENT_TYPE) > 0) {
/* multipart/form-data;
* boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
if (!strncmp(buf, "multipart/form-data", 19) ||
!strncmp(buf, "multipart/related", 17)) {
s->multipart_form_data = 1;
s->state = MT_LOOK_BOUND_IN;
s->mp = 2;
p = strstr(buf, "boundary=");
if (p) {
p += 9;
s->mime_boundary[m++] = '\x0d';
s->mime_boundary[m++] = '\x0a';
s->mime_boundary[m++] = '-';
s->mime_boundary[m++] = '-';
if (*p == '\"')
p++;
while (m < (int)sizeof(s->mime_boundary) - 1 &&
*p && *p != ' ' && *p != ';' && *p != '\"')
s->mime_boundary[m++] = *p++;
s->mime_boundary[m] = '\0';
// lwsl_notice("boundary '%s'\n", s->mime_boundary);
}
}
}
return s;
}
static int
lws_urldecode_s_process(struct lws_urldecode_stateful *s, const char *in,
int len)
{
int n, hit;
char c;
while (len--) {
if (s->pos == s->out_len - s->mp - 1) {
if (s->output(s->data, s->name, &s->out, s->pos,
LWS_UFS_CONTENT))
return -1;
s->pos = 0;
}
switch (s->state) {
/* states for url arg style */
case US_NAME:
s->inside_quote = 0;
if (*in == '=') {
s->name[s->pos] = '\0';
s->pos = 0;
s->state = US_IDLE;
in++;
continue;
}
if (*in == '&') {
s->name[s->pos] = '\0';
if (s->output(s->data, s->name, &s->out,
s->pos, LWS_UFS_FINAL_CONTENT))
return -1;
s->pos = 0;
s->state = US_IDLE;
in++;
continue;
}
if (s->pos >= (int)sizeof(s->name) - 1) {
lwsl_hexdump_notice(s->name, (size_t)s->pos);
lwsl_notice("Name too long...\n");
return -1;
}
s->name[s->pos++] = *in++;
break;
case US_IDLE:
if (*in == '%') {
s->state++;
in++;
continue;
}
if (*in == '&') {
s->out[s->pos] = '\0';
if (s->output(s->data, s->name, &s->out,
s->pos, LWS_UFS_FINAL_CONTENT))
return -1;
s->pos = 0;
s->state = US_NAME;
in++;
continue;
}
if (*in == '+') {
in++;
s->out[s->pos++] = ' ';
continue;
}
s->out[s->pos++] = *in++;
break;
case US_PC1:
n = char_to_hex(*in);
if (n < 0)
return -1;
in++;
s->sum = n << 4;
s->state++;
break;
case US_PC2:
n = char_to_hex(*in);
if (n < 0)
return -1;
in++;
s->out[s->pos++] = (char)(s->sum | n);
s->state = US_IDLE;
break;
/* states for multipart / mime style */
case MT_LOOK_BOUND_IN:
retry_as_first:
if (*in == s->mime_boundary[s->mp] &&
s->mime_boundary[s->mp]) {
in++;
s->mp++;
if (!s->mime_boundary[s->mp]) {
s->mp = 0;
s->state = MT_IGNORE1;
if (s->output(s->data, s->name,
&s->out, s->pos,
LWS_UFS_FINAL_CONTENT))
return -1;
s->pos = 0;
s->content_disp[0] = '\0';
s->name[0] = '\0';
s->content_disp_filename[0] = '\0';
s->boundary_real_crlf = 1;
}
continue;
}
if (s->mp) {
n = 0;
if (!s->boundary_real_crlf)
n = 2;
if (s->mp >= n) {
memcpy(s->out + s->pos,
s->mime_boundary + n,
(unsigned int)(s->mp - n));
s->pos += s->mp;
s->mp = 0;
goto retry_as_first;
}
}
s->out[s->pos++] = *in;
in++;
s->mp = 0;
break;
case MT_HNAME:
c =*in;
if (c >= 'A' && c <= 'Z')
c = (char)(c + 'a' - 'A');
if (!s->mp)
/* initially, any of them might match */
s->matchable = (1 << LWS_ARRAY_SIZE(mp_hdrs)) - 1;
hit = -1;
for (n = 0; n < (int)LWS_ARRAY_SIZE(mp_hdrs); n++) {
if (!(s->matchable & (1 << n)))
continue;
/* this guy is still in contention... */
if (s->mp >= mp_hdrs[n].hdr_len) {
/* he went past the end of it */
s->matchable &= (uint8_t)~(1 << n);
continue;
}
if (c != mp_hdrs[n].hdr[s->mp]) {
/* mismatched a char */
s->matchable &= (uint8_t)~(1 << n);
continue;
}
if (s->mp + 1 == mp_hdrs[n].hdr_len) {
/* we have a winner... */
hit = n;
break;
}
}
in++;
if (hit == -1 && !s->matchable) {
/* We ruled them all out */
s->state = MT_IGNORE1;
s->mp = 0;
continue;
}
s->mp++;
if (hit < 0)
continue;
/* we matched the one in hit */
s->mp = 0;
s->temp[0] = '\0';
s->subname = 0;
if (hit == 2)
s->state = MT_LOOK_BOUND_IN;
else
s->state += (unsigned int)hit + 1u;
break;
case MT_DISP:
/* form-data; name="file"; filename="t.txt" */
if (*in == '\x0d') {
if (s->content_disp_filename[0])
if (s->output(s->data, s->name,
&s->out, s->pos,
LWS_UFS_OPEN))
return -1;
s->state = MT_IGNORE2;
goto done;
}
if (*in == ';') {
s->subname = 1;
s->temp[0] = '\0';
s->mp = 0;
goto done;
}
if (*in == '\"') {
s->inside_quote = !!((s->inside_quote ^ 1) & 1);
goto done;
}
if (s->subname) {
if (*in == '=') {
s->temp[s->mp] = '\0';
s->subname = 0;
s->mp = 0;
goto done;
}
if (s->mp < (int)sizeof(s->temp) - 1 &&
(*in != ' ' || s->inside_quote))
s->temp[s->mp++] = *in;
goto done;
}
if (!s->temp[0]) {
if (s->mp < (int)sizeof(s->content_disp) - 1)
s->content_disp[s->mp++] = *in;
if (s->mp < (int)sizeof(s->content_disp))
s->content_disp[s->mp] = '\0';
goto done;
}
if (!strcmp(s->temp, "name")) {
if (s->mp < (int)sizeof(s->name) - 1)
s->name[s->mp++] = *in;
else
s->mp = (int)sizeof(s->name) - 1;
s->name[s->mp] = '\0';
goto done;
}
if (!strcmp(s->temp, "filename")) {
if (s->mp < (int)sizeof(s->content_disp_filename) - 1)
s->content_disp_filename[s->mp++] = *in;
s->content_disp_filename[s->mp] = '\0';
goto done;
}
done:
in++;
break;
case MT_TYPE:
if (*in == '\x0d')
s->state = MT_IGNORE2;
else {
if (s->mp < (int)sizeof(s->content_type) - 1)
s->content_type[s->mp++] = *in;
s->content_type[s->mp] = '\0';
}
in++;
break;
case MT_IGNORE1:
if (*in == '\x0d')
s->state = MT_IGNORE2;
if (*in == '-')
s->state = MT_IGNORE3;
in++;
break;
case MT_IGNORE2:
s->mp = 0;
if (*in == '\x0a')
s->state = MT_HNAME;
in++;
break;
case MT_IGNORE3:
if (*in == '\x0d')
s->state = MT_IGNORE2;
if (*in == '-') {
s->state = MT_COMPLETED;
s->wsi->http.rx_content_remain = 0;
}
in++;
break;
case MT_COMPLETED:
break;
}
}
return 0;
}
static int
lws_urldecode_s_destroy(struct lws_spa *spa, struct lws_urldecode_stateful *s)
{
int ret = 0;
if (s->state != US_IDLE)
ret = -1;
if (!ret)
if (s->output(s->data, s->name, &s->out, s->pos,
LWS_UFS_FINAL_CONTENT))
ret = -1;
if (s->output(s->data, s->name, NULL, 0, LWS_UFS_CLOSE))
return -1;
if (!spa->i.ac)
lws_free(s);
return ret;
}
static int
lws_urldecode_spa_lookup(struct lws_spa *spa, const char *name)
{
const char * const *pp = spa->i.param_names;
int n;
for (n = 0; n < spa->i.count_params; n++) {
if (!*pp && !spa->i.param_names_stride && spa->i.ac) {
unsigned int len = (unsigned int)strlen(name);
char **ptr = (char**)spa->i.param_names;
/* Use NULLs at end of list to dynamically create
* unknown entries */
ptr[n] = lwsac_use(spa->i.ac, len + 1, spa->i.ac_chunk_size);
if (!ptr[n])
return -1;
memcpy(ptr[n], name, len);
ptr[n][len] = '\0';
return n;
}
if (*pp && !strcmp(*pp, name))
return n;
if (spa->i.param_names_stride)
pp = (const char * const *)(((char *)pp) + spa->i.param_names_stride);
else
pp++;
}
return -1;
}
static int
lws_urldecode_spa_cb(struct lws_spa *spa, const char *name, char **buf, int len,
int final)
{
int n;
if (final == LWS_UFS_CLOSE || spa->s->content_disp_filename[0]) {
if (spa->i.opt_cb) {
n = spa->i.opt_cb(spa->i.opt_data, name,
spa->s->content_disp_filename,
buf ? *buf : NULL, len, (enum lws_spa_fileupload_states)final);
if (n < 0)
return -1;
}
return 0;
}
n = lws_urldecode_spa_lookup(spa, name);
if (n == -1 || !len) /* unrecognized */
return 0;
if (!spa->i.ac) {
if (!spa->params[n])
spa->params[n] = *buf;
if ((*buf) + len >= spa->end) {
lwsl_info("%s: exceeded storage\n", __func__);
return -1;
}
/* move it on inside storage */
(*buf) += len;
*((*buf)++) = '\0';
spa->s->out_len -= len + 1;
} else {
spa->params[n] = lwsac_use(spa->i.ac, (unsigned int)len + 1,
spa->i.ac_chunk_size);
if (!spa->params[n])
return -1;
memcpy(spa->params[n], *buf, (unsigned int)len);
spa->params[n][len] = '\0';
}
spa->param_length[n] += len;
return 0;
}
struct lws_spa *
lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *i)
{
struct lws_spa *spa;
if (i->ac)
spa = lwsac_use_zero(i->ac, sizeof(*spa), i->ac_chunk_size);
else
spa = lws_zalloc(sizeof(*spa), "spa");
if (!spa)
return NULL;
spa->i = *i;
if (!spa->i.max_storage)
spa->i.max_storage = 512;
if (i->ac)
spa->storage = lwsac_use(i->ac, (unsigned int)spa->i.max_storage,
i->ac_chunk_size);
else
spa->storage = lws_malloc((unsigned int)spa->i.max_storage, "spa");
if (!spa->storage)
goto bail2;
spa->end = spa->storage + spa->i.max_storage - 1;
if (i->count_params) {
if (i->ac)
spa->params = lwsac_use_zero(i->ac,
sizeof(char *) * (unsigned int)i->count_params, i->ac_chunk_size);
else
spa->params = lws_zalloc(sizeof(char *) * (unsigned int)i->count_params,
"spa params");
if (!spa->params)
goto bail3;
}
spa->s = lws_urldecode_s_create(spa, wsi, spa->storage, spa->i.max_storage,
lws_urldecode_spa_cb);
if (!spa->s)
goto bail4;
if (i->count_params) {
if (i->ac)
spa->param_length = lwsac_use_zero(i->ac,
sizeof(int) * (unsigned int)i->count_params, i->ac_chunk_size);
else
spa->param_length = lws_zalloc(sizeof(int) * (unsigned int)i->count_params,
"spa param len");
if (!spa->param_length)
goto bail5;
}
// lwsl_notice("%s: Created SPA %p\n", __func__, spa);
return spa;
bail5:
lws_urldecode_s_destroy(spa, spa->s);
bail4:
if (!i->ac)
lws_free(spa->params);
bail3:
if (!i->ac)
lws_free(spa->storage);
bail2:
if (!i->ac)
lws_free(spa);
if (i->ac)
lwsac_free(i->ac);
return NULL;
}
struct lws_spa *
lws_spa_create(struct lws *wsi, const char * const *param_names,
int count_params, int max_storage,
lws_spa_fileupload_cb opt_cb, void *opt_data)
{
lws_spa_create_info_t i;
memset(&i, 0, sizeof(i));
i.count_params = count_params;
i.max_storage = max_storage;
i.opt_cb = opt_cb;
i.opt_data = opt_data;
i.param_names = param_names;
return lws_spa_create_via_info(wsi, &i);
}
int
lws_spa_process(struct lws_spa *spa, const char *in, int len)
{
if (!spa) {
lwsl_err("%s: NULL spa\n", __func__);
return -1;
}
/* we reject any junk after the last part arrived and we finalized */
if (spa->finalized)
return 0;
return lws_urldecode_s_process(spa->s, in, len);
}
int
lws_spa_get_length(struct lws_spa *spa, int n)
{
if (n >= spa->i.count_params)
return 0;
return spa->param_length[n];
}
const char *
lws_spa_get_string(struct lws_spa *spa, int n)
{
if (n >= spa->i.count_params)
return NULL;
return spa->params[n];
}
int
lws_spa_finalize(struct lws_spa *spa)
{
if (!spa)
return 0;
if (spa->s) {
lws_urldecode_s_destroy(spa, spa->s);
spa->s = NULL;
}
spa->finalized = 1;
return 0;
}
int
lws_spa_destroy(struct lws_spa *spa)
{
int n = 0;
lwsl_info("%s: destroy spa %p\n", __func__, spa);
if (spa->s)
lws_urldecode_s_destroy(spa, spa->s);
if (spa->i.ac)
lwsac_free(spa->i.ac);
else {
lws_free(spa->param_length);
lws_free(spa->params);
lws_free(spa->storage);
lws_free(spa);
}
return n;
}

View File

@ -0,0 +1,214 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.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=";
while (1) {
char 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 = (unsigned long long)(((unsigned long long)rp->start * 10) + (unsigned long long)(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 = (unsigned long long)(((unsigned long long)rp->end * 10) + (unsigned long long)(c - '0'));
rp->end_valid = 1;
break;
}
rp->pos++;
}
}
void
lws_ranges_reset(struct lws_range_parsing *rp)
{
rp->pos = 0;
rp->ctr = 0;
rp->start = 0;
rp->end = 0;
rp->start_valid = 0;
rp->end_valid = 0;
rp->state = LWSRS_BYTES_EQ;
}
/*
* returns count of valid ranges
*/
int
lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp,
unsigned long long extent)
{
rp->agg = 0;
rp->send_ctr = 0;
rp->inside = 0;
rp->count_ranges = 0;
rp->did_try = 0;
lws_ranges_reset(rp);
rp->state = LWSRS_COMPLETED;
rp->extent = extent;
if (lws_hdr_copy(wsi, (char *)rp->buf, sizeof(rp->buf),
WSI_TOKEN_HTTP_RANGE) <= 0)
return 0;
rp->state = LWSRS_BYTES_EQ;
while (lws_ranges_next(rp)) {
rp->count_ranges++;
rp->agg += rp->end - rp->start + 1;
}
lwsl_debug("%s: count %d\n", __func__, rp->count_ranges);
lws_ranges_reset(rp);
if (rp->did_try && !rp->count_ranges)
return -1; /* "not satisfiable */
lws_ranges_next(rp);
return rp->count_ranges;
}

View File

@ -0,0 +1,55 @@
#include "private-lib-core.h"
#if defined(LWS_WITH_HUBBUB)
struct lws_rewrite *
lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from,
const char *to)
{
struct lws_rewrite *r = lws_malloc(sizeof(*r), "rewrite");
if (!r) {
lwsl_err("OOM\n");
return NULL;
}
if (hubbub_parser_create("UTF-8", false, &r->parser) != HUBBUB_OK) {
lws_free(r);
return NULL;
}
r->from = from;
r->from_len = strlen(from);
r->to = to;
r->to_len = strlen(to);
r->params.token_handler.handler = cb;
r->wsi = wsi;
r->params.token_handler.pw = (void *)r;
if (hubbub_parser_setopt(r->parser, HUBBUB_PARSER_TOKEN_HANDLER,
&r->params) != HUBBUB_OK) {
lws_free(r);
return NULL;
}
return r;
}
int
lws_rewrite_parse(struct lws_rewrite *r,
const unsigned char *in, int in_len)
{
if (r && hubbub_parser_parse_chunk(r->parser, in, in_len) != HUBBUB_OK)
return -1;
return 0;
}
void
lws_rewrite_destroy(struct lws_rewrite *r)
{
hubbub_parser_destroy(r->parser);
lws_free(r);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
list(APPEND SOURCES
roles/listen/ops-listen.c)
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View File

@ -0,0 +1,220 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <private-lib-core.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->a.context;
struct lws_filter_network_conn_args filt;
lws_sock_file_fd_type fd;
memset(&filt, 0, sizeof(filt));
/* if our vhost is going down, ignore it */
if (wsi->a.vhost->being_destroyed)
return LWS_HPI_RET_HANDLED;
/* 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;
int opts = LWS_ADOPT_SOCKET | LWS_ADOPT_ALLOW_SSL;
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->a.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... */
filt.clilen = sizeof(filt.cli_addr);
/*
* 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.
*/
filt.accept_fd = accept((int)pollfd->fd,
(struct sockaddr *)&filt.cli_addr,
&filt.clilen);
if (filt.accept_fd == LWS_SOCK_INVALID) {
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK) {
break;
}
lwsl_err("accept: errno %d\n", LWS_ERRNO);
return LWS_HPI_RET_HANDLED;
}
if (context->being_destroyed) {
compatible_close(filt.accept_fd);
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
lws_plat_set_socket_options(wsi->a.vhost, filt.accept_fd, 0);
#if defined(LWS_WITH_IPV6)
lwsl_debug("accepted new conn port %u on fd=%d\n",
((filt.cli_addr.ss_family == AF_INET6) ?
ntohs(((struct sockaddr_in6 *) &filt.cli_addr)->sin6_port) :
ntohs(((struct sockaddr_in *) &filt.cli_addr)->sin_port)),
filt.accept_fd);
#else
{
struct sockaddr_in sain;
memcpy(&sain, &filt.cli_addr, sizeof(sain));
lwsl_debug("accepted new conn port %u on fd=%d\n",
ntohs(sain.sin_port),
filt.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->a.vhost->protocols[0].callback)(wsi,
LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
(void *)&filt,
(void *)(lws_intptr_t)filt.accept_fd, 0)) {
lwsl_debug("Callback denied net connection\n");
compatible_close(filt.accept_fd);
return LWS_HPI_RET_HANDLED;
}
if (!(wsi->a.vhost->options &
LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG))
opts |= LWS_ADOPT_HTTP;
#if defined(LWS_WITH_TLS)
if (!wsi->a.vhost->tls.use_ssl)
#endif
opts &= ~LWS_ADOPT_ALLOW_SSL;
fd.sockfd = filt.accept_fd;
cwsi = lws_adopt_descriptor_vhost(wsi->a.vhost, (lws_adoption_type)opts, fd,
wsi->a.vhost->listen_accept_protocol, NULL);
if (!cwsi) {
lwsl_info("%s: vh %s: adopt failed\n", __func__,
wsi->a.vhost->name);
/* already closed cleanly as necessary */
return LWS_HPI_RET_WSI_ALREADY_DIED;
}
/*
if (lws_server_socket_service_ssl(cwsi, accept_fd, 1)) {
lws_close_free_wsi(cwsi, LWS_CLOSE_STATUS_NOSTATUS,
"listen svc fail");
return LWS_HPI_RET_WSI_ALREADY_DIED;
}
lwsl_info("%s: new %s: wsistate 0x%lx, role_ops %s\n",
__func__, lws_wsi_tag(cwsi), (unsigned long)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;
}
static const lws_rops_t rops_table_listen[] = {
/* 1 */ { .handle_POLLIN = rops_handle_POLLIN_listen },
/* 2 */ { .handle_POLLOUT = rops_handle_POLLOUT_listen },
};
const struct lws_role_ops role_ops_listen = {
/* role name */ "listen",
/* alpn id */ NULL,
/* rops_table */ rops_table_listen,
/* rops_idx */ {
/* LWS_ROPS_check_upgrades */
/* LWS_ROPS_pt_init_destroy */ 0x00,
/* LWS_ROPS_init_vhost */
/* LWS_ROPS_destroy_vhost */ 0x00,
/* LWS_ROPS_service_flag_pending */
/* LWS_ROPS_handle_POLLIN */ 0x01,
/* LWS_ROPS_handle_POLLOUT */
/* LWS_ROPS_perform_user_POLLOUT */ 0x20,
/* LWS_ROPS_callback_on_writable */
/* LWS_ROPS_tx_credit */ 0x00,
/* LWS_ROPS_write_role_protocol */
/* LWS_ROPS_encapsulation_parent */ 0x00,
/* LWS_ROPS_alpn_negotiated */
/* LWS_ROPS_close_via_role_protocol */ 0x00,
/* LWS_ROPS_close_role */
/* LWS_ROPS_close_kill_connection */ 0x00,
/* LWS_ROPS_destroy_role */
/* LWS_ROPS_adoption_bind */ 0x00,
/* LWS_ROPS_client_bind */
/* LWS_ROPS_issue_keepalive */ 0x00,
},
/* adoption_cb clnt, srv */ { 0, 0 },
/* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },
/* close cb clnt, srv */ { 0, 0 },
/* protocol_bind_cb c,s */ { 0, 0 },
/* protocol_unbind_cb c,s */ { 0, 0 },
/* file_handle */ 0,
};

View File

@ -0,0 +1,48 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
if (LWS_WITH_CLIENT)
list(APPEND SOURCES
roles/mqtt/mqtt.c
roles/mqtt/ops-mqtt.c
roles/mqtt/primitives.c
roles/mqtt/client/client-mqtt.c
roles/mqtt/client/client-mqtt-handshake.c
)
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View File

@ -0,0 +1,200 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
* Sakthi Kannan <saktr@amazon.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <private-lib-core.h>
#define MQTT_CONNECT_MSG_BASE_LEN (12)
struct lws *
lws_mqtt_client_send_connect(struct lws *wsi)
{
/* static int */
/* lws_mqttc_abs_writeable(lws_abs_protocol_inst_t *api, size_t budget) */
const lws_mqttc_t *c = &wsi->mqtt->client;
uint8_t b[256 + LWS_PRE], *start = b + LWS_PRE, *p = start;
unsigned int len = MQTT_CONNECT_MSG_BASE_LEN;
switch (lwsi_state(wsi)) {
case LRS_MQTTC_IDLE:
/*
* Transport connected - this is our chance to do the
* protocol connect action.
*/
/* 1. Fixed Headers */
if (lws_mqtt_fill_fixed_header(p++, LMQCP_CTOS_CONNECT, 0, 0, 0)) {
lwsl_err("%s: Failled to fill fixed header\n", __func__);
return NULL;
}
/*
* 2. Remaining length - Add the lengths of client ID,
* username and password and their length fields if
* the respective flags are set.
*/
len += c->id->len;
if (c->conn_flags & LMQCFT_USERNAME && c->username) {
len = len + (unsigned int)c->username->len + 2;
if (c->conn_flags & LMQCFT_PASSWORD)
len += (unsigned int)(c->password ? c->password->len : 0) + 2u;
}
if (c->conn_flags & LMQCFT_WILL_FLAG && c->will.topic) {
len = len + (unsigned int)c->will.topic->len + 2;
len += (c->will.message ? c->will.message->len : 0) + 2u;
}
p += lws_mqtt_vbi_encode(len, p);
/*
* 3. Variable Header - Protocol name & level, Connect
* flags and keep alive time (in secs).
*/
lws_ser_wu16be(p, 4); /* Length of protocol name */
p += 2;
*p++ = 'M';
*p++ = 'Q';
*p++ = 'T';
*p++ = 'T';
*p++ = MQTT_VER_3_1_1;
*p++ = (uint8_t)c->conn_flags;
lws_ser_wu16be(p, c->keep_alive_secs);
p += 2;
/*
* 4. Payload - Client ID, Will topic & message,
* Username & password.
*/
if (lws_mqtt_str_is_not_empty(c->id)) {
lws_ser_wu16be(p, c->id->len);
p += 2;
memcpy(p, c->id->buf, c->id->len);
p += c->id->len;
} else {
/*
* If the Client supplies a zero-byte
* ClientId, the Client MUST also set
* CleanSession to 1 [MQTT-3.1.3-7].
*/
if (!(c->conn_flags & LMQCFT_CLEAN_START)) {
lwsl_err("%s: Empty client ID needs a clean start\n",
__func__);
return NULL;
}
*p++ = 0;
}
if (c->conn_flags & LMQCFT_WILL_FLAG) {
if (lws_mqtt_str_is_not_empty(c->will.topic)) {
lws_ser_wu16be(p, c->will.topic->len);
p += 2;
memcpy(p, c->will.topic->buf, c->will.topic->len);
p += c->will.topic->len;
if (lws_mqtt_str_is_not_empty(c->will.message)) {
lws_ser_wu16be(p, c->will.message->len);
p += 2;
memcpy(p, c->will.message->buf,
c->will.message->len);
p += c->will.message->len;
} else {
lws_ser_wu16be(p, 0);
p += 2;
}
} else {
lwsl_err("%s: Missing Will Topic\n", __func__);
return NULL;
}
}
if (c->conn_flags & LMQCFT_USERNAME) {
/*
* Detailed sanity check on the username and
* password strings.
*/
if (lws_mqtt_str_is_not_empty(c->username)) {
lws_ser_wu16be(p, c->username->len);
p += 2;
memcpy(p, c->username->buf, c->username->len);
p += c->username->len;
} else {
lwsl_err("%s: Empty / missing Username!\n",
__func__);
return NULL;
}
if (c->conn_flags & LMQCFT_PASSWORD) {
if (lws_mqtt_str_is_not_empty(c->password)) {
lws_ser_wu16be(p, c->password->len);
p += 2;
memcpy(p, c->password->buf,
c->password->len);
p += c->password->len;
} else {
lws_ser_wu16be(p, 0);
p += 2;
}
}
} else if (c->conn_flags & LMQCFT_PASSWORD) {
lwsl_err("%s: Unsupported - Password without username\n",
__func__);
return NULL;
}
break;
default:
lwsl_err("%s: unexpected state %d\n", __func__, lwsi_state(wsi));
return NULL;
}
/*
* Perform the actual write
*/
if (lws_write(wsi, (unsigned char *)&b[LWS_PRE], lws_ptr_diff_size_t(p, start),
LWS_WRITE_BINARY) != lws_ptr_diff(p, start)) {
lwsl_notice("%s: write failed\n", __func__);
return NULL;
}
return wsi;
}
struct lws *
lws_mqtt_client_send_disconnect(struct lws *wsi)
{
uint8_t b[256 + LWS_PRE], *start = b + LWS_PRE, *p = start;
/* 1. Fixed Headers */
if (lws_mqtt_fill_fixed_header(p++, LMQCP_DISCONNECT, 0, 0, 0))
{
lwsl_err("%s: Failled to fill fixed header\n", __func__);
return NULL;
}
*p++ = 0;
if (lws_write(wsi, (unsigned char *)&b[LWS_PRE], lws_ptr_diff_size_t(p, start),
LWS_WRITE_BINARY) != lws_ptr_diff(p, start)) {
lwsl_err("%s: write failed\n", __func__);
return NULL;
}
return wsi;
}

View File

@ -0,0 +1,375 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*
* You can leave buf NULL, if so it will be allocated on the heap once the
* actual length is known. nf should be 0, it will be set at allocation time.
*
* Or you can ensure no allocation and use an external buffer by setting buf
* and lim. But buf must be in the ep context somehow, since it may have to
* survive returns to the event loop unchanged. Set nf to 0 in this case.
*
* Or you can set buf to an externally allocated buffer, in which case you may
* set nf so it will be freed when the string is "freed".
*/
#include "private-lib-core.h"
/* #include "lws-mqtt.h" */
/* 3.1.3.1-5: MUST allow... that contain only the characters... */
static const uint8_t *code = (const uint8_t *)
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int
lws_mqtt_generate_id(struct lws* wsi, lws_mqtt_str_t **ms, const char *client_id)
{
struct lws_context *context = wsi->a.context;
uint16_t ran[24]; /* 16-bit so wrap bias from %62 diluted by ~1000 */
size_t n, len;
uint8_t *buf;
if (client_id)
len = strlen(client_id);
else
len = LWS_MQTT_RANDOM_CIDLEN;
*ms = lws_mqtt_str_create((uint16_t)(len + 1));
if (!*ms)
return 1;
buf = lws_mqtt_str_next(*ms, NULL);
if (client_id) {
lws_strnncpy((char *)buf, client_id, len, len + 1);
lwsl_notice("%s: User space provided a client ID '%s'\n",
__func__, (const char *)buf);
} else {
lwsl_notice("%s: generating random client id\n", __func__);
n = len * sizeof(ran[0]);
if (lws_get_random(context, ran, n) != n) {
lws_mqtt_str_free(ms);
return 1;
}
for (n = 0; n < len; n++)
buf[n] = code[ran[n] % 62];
buf[len] = '\0';
}
if (lws_mqtt_str_advance(*ms, (uint16_t)len)) {
lws_mqtt_str_free(ms);
return 1;
}
return 0;
}
int
lws_read_mqtt(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
{
lws_mqttc_t *c = &wsi->mqtt->client;
return _lws_mqtt_rx_parser(wsi, &c->par, buf, (size_t)len);
}
int
lws_create_client_mqtt_object(const struct lws_client_connect_info *i,
struct lws *wsi)
{
lws_mqttc_t *c;
const lws_mqtt_client_connect_param_t *cp = i->mqtt_cp;
/* allocate the ws struct for the wsi */
wsi->mqtt = lws_zalloc(sizeof(*wsi->mqtt), "client mqtt struct");
if (!wsi->mqtt)
goto oom;
wsi->mqtt->wsi = wsi;
c = &wsi->mqtt->client;
if (lws_mqtt_generate_id(wsi, &c->id, cp->client_id)) {
lwsl_err("%s: Error generating client ID\n", __func__);
return 1;
}
lwsl_info("%s: using client id '%.*s'\n", __func__, c->id->len,
(const char *)c->id->buf);
if (cp->clean_start || !(cp->client_id &&
cp->client_id[0]))
c->conn_flags = LMQCFT_CLEAN_START;
if (cp->client_id_nofree)
c->conn_flags |= LMQCFT_CLIENT_ID_NOFREE;
if (cp->username_nofree)
c->conn_flags |= LMQCFT_USERNAME_NOFREE;
if (cp->password_nofree)
c->conn_flags |= LMQCFT_PASSWORD_NOFREE;
if (!(c->conn_flags & LMQCFT_CLIENT_ID_NOFREE))
lws_free((void *)cp->client_id);
c->keep_alive_secs = cp->keep_alive;
c->aws_iot = cp->aws_iot;
if (cp->will_param.topic &&
*cp->will_param.topic) {
c->will.topic = lws_mqtt_str_create_cstr_dup(
cp->will_param.topic, 0);
if (!c->will.topic)
goto oom1;
c->conn_flags |= LMQCFT_WILL_FLAG;
if (cp->will_param.message) {
c->will.message = lws_mqtt_str_create_cstr_dup(
cp->will_param.message, 0);
if (!c->will.message)
goto oom2;
}
c->conn_flags = (uint16_t)(unsigned int)(c->conn_flags | ((cp->will_param.qos << 3) & LMQCFT_WILL_QOS_MASK));
c->conn_flags |= (uint16_t)((!!cp->will_param.retain) * LMQCFT_WILL_RETAIN);
}
if (cp->username &&
*cp->username) {
c->username = lws_mqtt_str_create_cstr_dup(cp->username, 0);
if (!c->username)
goto oom3;
c->conn_flags |= LMQCFT_USERNAME;
if (!(c->conn_flags & LMQCFT_USERNAME_NOFREE))
lws_free((void *)cp->username);
if (cp->password) {
c->password =
lws_mqtt_str_create_cstr_dup(cp->password, 0);
if (!c->password)
goto oom4;
c->conn_flags |= LMQCFT_PASSWORD;
if (!(c->conn_flags & LMQCFT_PASSWORD_NOFREE))
lws_free((void *)cp->password);
}
}
return 0;
oom4:
lws_mqtt_str_free(&c->username);
oom3:
lws_mqtt_str_free(&c->will.message);
oom2:
lws_mqtt_str_free(&c->will.topic);
oom1:
lws_mqtt_str_free(&c->id);
oom:
lwsl_err("%s: OOM!\n", __func__);
return 1;
}
int
lws_mqtt_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
struct lws *wsi_conn)
{
struct lws_context *context = wsi->a.context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n = 0, m = 0;
struct lws_tokens ebuf;
int buffered = 0;
int pending = 0;
#if defined(LWS_WITH_TLS)
char erbuf[128];
#endif
const char *cce = NULL;
switch (lwsi_state(wsi)) {
#if defined(LWS_WITH_SOCKS5)
/* SOCKS Greeting Reply */
case LRS_WAITING_SOCKS_GREETING_REPLY:
case LRS_WAITING_SOCKS_AUTH_REPLY:
case LRS_WAITING_SOCKS_CONNECT_REPLY:
switch (lws_socks5c_handle_state(wsi, pollfd, &cce)) {
case LW5CHS_RET_RET0:
return 0;
case LW5CHS_RET_BAIL3:
goto bail3;
case LW5CHS_RET_STARTHS:
/*
* Now we got the socks5 connection, we need to go down
* the tls path on it if that's what we want
*/
if (!(wsi->tls.use_ssl & LCCSCF_USE_SSL))
goto start_ws_handshake;
switch (lws_client_create_tls(wsi, &cce, 0)) {
case 0:
break;
case 1:
return 0;
default:
goto bail3;
}
break;
default:
break;
}
break;
#endif
case LRS_WAITING_DNS:
/*
* we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
* timeout protection set in client-handshake.c
*/
if (!lws_client_connect_2_dnsreq(wsi)) {
/* closed */
lwsl_client("closed\n");
return -1;
}
/* either still pending connection, or changed mode */
return 0;
case LRS_WAITING_CONNECT:
/*
* we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
* timeout protection set in client-handshake.c
*/
if (pollfd->revents & LWS_POLLOUT)
lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL);
break;
#if defined(LWS_WITH_TLS)
case LRS_WAITING_SSL:
if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
n = lws_ssl_client_connect2(wsi, erbuf, sizeof(erbuf));
if (!n)
return 0;
if (n < 0) {
cce = erbuf;
goto bail3;
}
} else
wsi->tls.ssl = NULL;
#endif /* LWS_WITH_TLS */
/* fallthru */
#if defined(LWS_WITH_SOCKS5)
start_ws_handshake:
#endif
lwsi_set_state(wsi, LRS_MQTTC_IDLE);
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
(int)context->timeout_secs);
/* fallthru */
case LRS_MQTTC_IDLE:
/*
* we should be ready to send out MQTT CONNECT
*/
lwsl_info("%s: %s: Transport established, send out CONNECT\n",
__func__, lws_wsi_tag(wsi));
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
return -1;
if (!lws_mqtt_client_send_connect(wsi)) {
lwsl_err("%s: Unable to send MQTT CONNECT\n", __func__);
return -1;
}
if (lws_change_pollfd(wsi, 0, LWS_POLLIN))
return -1;
lwsi_set_state(wsi, LRS_MQTTC_AWAIT_CONNACK);
return 0;
case LRS_ESTABLISHED:
case LRS_MQTTC_AWAIT_CONNACK:
buffered = 0;
ebuf.token = pt->serv_buf;
ebuf.len = (int)wsi->a.context->pt_serv_buf_size;
if ((unsigned int)ebuf.len > wsi->a.context->pt_serv_buf_size)
ebuf.len = (int)wsi->a.context->pt_serv_buf_size;
if ((int)pending > ebuf.len)
pending = (char)ebuf.len;
ebuf.len = lws_ssl_capable_read(wsi, ebuf.token,
(unsigned int)(pending ? pending :
ebuf.len));
switch (ebuf.len) {
case 0:
lwsl_info("%s: zero length read\n",
__func__);
goto fail;
case LWS_SSL_CAPABLE_MORE_SERVICE:
lwsl_info("SSL Capable more service\n");
return 0;
case LWS_SSL_CAPABLE_ERROR:
lwsl_info("%s: LWS_SSL_CAPABLE_ERROR\n",
__func__);
goto fail;
}
if (ebuf.len < 0)
n = -1;
else
n = lws_read_mqtt(wsi, ebuf.token, (unsigned int)ebuf.len);
if (n < 0) {
lwsl_err("%s: Parsing packet failed\n", __func__);
goto fail;
}
m = ebuf.len - n;
// lws_buflist_describe(&wsi->buflist, wsi, __func__);
lwsl_debug("%s: consuming %d / %d\n", __func__, n, ebuf.len);
if (lws_buflist_aware_finished_consuming(wsi, &ebuf, m,
buffered,
__func__))
return -1;
return 0;
#if defined(LWS_WITH_TLS) || defined(LWS_WITH_SOCKS5)
bail3:
#endif
lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n");
if (cce)
lwsl_info("reason: %s\n", cce);
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cbail3");
return -1;
default:
break;
}
return 0;
fail:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "mqtt svc fail");
return LWS_HPI_RET_WSI_ALREADY_DIED;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,654 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
static int
rops_handle_POLLIN_mqtt(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
unsigned int pending = 0;
struct lws_tokens ebuf;
int n = 0;
char buffered = 0;
lwsl_debug("%s: wsistate 0x%x, %s pollout %d\n", __func__,
(unsigned int)wsi->wsistate, wsi->a.protocol->name,
pollfd->revents);
/*
* After the CONNACK and nwsi establishment, the first logical
* stream is migrated out of the nwsi to be child sid 1, and the
* nwsi no longer has a wsi->mqtt of its own.
*
* RX events on the nwsi must be converted to events seen or not
* seen by one or more child streams.
*
* SUBACK - reflected to child stream that asked for it
* PUBACK - routed to child that did the related publish
*/
ebuf.token = NULL;
ebuf.len = 0;
if (lwsi_state(wsi) != LRS_ESTABLISHED) {
#if defined(LWS_WITH_CLIENT)
if (lwsi_state(wsi) == LRS_WAITING_SSL &&
((pollfd->revents & LWS_POLLOUT)) &&
lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_info("failed at set pollfd\n");
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
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;
}
n = lws_mqtt_client_socket_service(wsi, pollfd, NULL);
if (n)
return LWS_HPI_RET_WSI_ALREADY_DIED;
#endif
return LWS_HPI_RET_HANDLED;
}
/* 1: something requested a callback when it was OK to write */
if ((pollfd->revents & LWS_POLLOUT) &&
lwsi_state_can_handle_POLLOUT(wsi) &&
lws_handle_POLLOUT_event(wsi, pollfd)) {
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
/* 3: buflist needs to be drained
*/
read:
// lws_buflist_describe(&wsi->buflist, wsi, __func__);
ebuf.len = (int)lws_buflist_next_segment_len(&wsi->buflist, &ebuf.token);
if (ebuf.len) {
lwsl_info("draining buflist (len %d)\n", ebuf.len);
buffered = 1;
goto drain;
}
if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
return LWS_HPI_RET_HANDLED;
/* if (lws_is_flowcontrolled(wsi)) { */
/* lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n", */
/* __func__, wsi, wsi->rxflow_bitmap); */
/* return LWS_HPI_RET_HANDLED; */
/* } */
if (!(lwsi_role_client(wsi) && lwsi_state(wsi) != LRS_ESTABLISHED)) {
/*
* In case we are going to react to this rx by scheduling
* writes, we need to restrict the amount of rx to the size
* the protocol reported for rx buffer.
*
* Otherwise we get a situation we have to absorb possibly a
* lot of reads before we get a chance to drain them by writing
* them, eg, with echo type tests in autobahn.
*/
buffered = 0;
ebuf.token = pt->serv_buf;
ebuf.len = (int)wsi->a.context->pt_serv_buf_size;
if ((unsigned int)ebuf.len > wsi->a.context->pt_serv_buf_size)
ebuf.len = (int)wsi->a.context->pt_serv_buf_size;
if ((int)pending > ebuf.len)
pending = (unsigned int)ebuf.len;
ebuf.len = lws_ssl_capable_read(wsi, ebuf.token,
pending ? pending :
(unsigned int)ebuf.len);
switch (ebuf.len) {
case 0:
lwsl_info("%s: zero length read\n",
__func__);
return LWS_HPI_RET_PLEASE_CLOSE_ME;
case LWS_SSL_CAPABLE_MORE_SERVICE:
lwsl_info("SSL Capable more service\n");
return LWS_HPI_RET_HANDLED;
case LWS_SSL_CAPABLE_ERROR:
lwsl_info("%s: LWS_SSL_CAPABLE_ERROR\n",
__func__);
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
/*
* coverity thinks ssl_capable_read() may read over
* 2GB. Dissuade it...
*/
ebuf.len &= 0x7fffffff;
}
drain:
/* service incoming data */
//lws_buflist_describe(&wsi->buflist, wsi, __func__);
if (ebuf.len) {
n = lws_read_mqtt(wsi, ebuf.token, (unsigned int)ebuf.len);
if (n < 0) {
lwsl_notice("%s: lws_read_mqtt returned %d\n",
__func__, n);
/* we closed wsi */
goto fail;
}
// lws_buflist_describe(&wsi->buflist, wsi, __func__);
lwsl_debug("%s: consuming %d / %d\n", __func__, n, ebuf.len);
if (lws_buflist_aware_finished_consuming(wsi, &ebuf, ebuf.len,
buffered, __func__))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
ebuf.token = NULL;
ebuf.len = 0;
pending = (unsigned int)lws_ssl_pending(wsi);
if (pending) {
pending = pending > wsi->a.context->pt_serv_buf_size ?
wsi->a.context->pt_serv_buf_size : pending;
goto read;
}
if (buffered && /* were draining, now nothing left */
!lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
lwsl_info("%s: %s flow buf: drained\n", __func__, lws_wsi_tag(wsi));
/* having drained the rxflow buffer, can rearm POLLIN */
#if !defined(LWS_WITH_SERVER)
n =
#endif
__lws_rx_flow_control(wsi);
/* n ignored, needed for NO_SERVER case */
}
/* n = 0 */
return LWS_HPI_RET_HANDLED;
fail:
lwsl_err("%s: Failed, bailing\n", __func__);
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "mqtt svc fail");
return LWS_HPI_RET_WSI_ALREADY_DIED;
}
#if 0 /* defined(LWS_WITH_SERVER) */
static int
rops_adoption_bind_mqtt(struct lws *wsi, int type, const char *vh_prot_name)
{
/* no http but socket... must be mqtt */
if ((type & LWS_ADOPT_HTTP) || !(type & LWS_ADOPT_SOCKET) ||
(type & _LWS_ADOPT_FINISH))
return 0; /* no match */
lws_role_transition(wsi, 0, (type & LWS_ADOPT_ALLOW_SSL) ? LRS_SSL_INIT :
LRS_ESTABLISHED, &role_ops_mqtt);
if (vh_prot_name)
lws_bind_protocol(wsi, wsi->a.protocol, __func__);
else
/* this is the only time he will transition */
lws_bind_protocol(wsi,
&wsi->a.vhost->protocols[wsi->a.vhost->mqtt_protocol_index],
__func__);
return 1; /* bound */
}
#endif
static int
rops_client_bind_mqtt(struct lws *wsi, const struct lws_client_connect_info *i)
{
lwsl_debug("%s: i = %p\n", __func__, i);
if (!i) {
/* finalize */
if (!wsi->user_space && wsi->stash->cis[CIS_METHOD])
if (lws_ensure_user_space(wsi))
return 1;
if (!wsi->stash->cis[CIS_METHOD] && !wsi->stash->cis[CIS_ALPN])
wsi->stash->cis[CIS_ALPN] = "x-amzn-mqtt-ca";
/* if we went on the ah waiting list, it's ok, we can
* wait.
*
* When we do get the ah, now or later, he will end up
* at lws_http_client_connect_via_info2().
*/
#if defined(LWS_WITH_CLIENT)
if (lws_header_table_attach(wsi, 0) < 0)
/*
* if we failed here, the connection is already closed
* and freed.
*/
return -1;
#else
if (lws_header_table_attach(wsi, 0))
return 0;
#endif
return 0;
}
/* if a recognized mqtt method, bind to it */
if (strcmp(i->method, "MQTT"))
return 0; /* no match */
if (lws_create_client_mqtt_object(i, wsi))
return 1;
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
&role_ops_mqtt);
return 1; /* matched */
}
static int
rops_handle_POLLOUT_mqtt(struct lws *wsi)
{
struct lws **wsi2;
lwsl_debug("%s\n", __func__);
#if defined(LWS_WITH_CLIENT)
if (wsi->mqtt && wsi->mqtt->send_pingreq && !wsi->mqtt->inside_payload) {
uint8_t buf[LWS_PRE + 2];
/*
* We are swallowing this POLLOUT in order to send a PINGREQ
* autonomously
*/
wsi->mqtt->send_pingreq = 0;
lwsl_notice("%s: issuing PINGREQ\n", __func__);
buf[LWS_PRE] = LMQCP_CTOS_PINGREQ << 4;
buf[LWS_PRE + 1] = 0;
if (lws_write(wsi, (uint8_t *)&buf[LWS_PRE], 2,
LWS_WRITE_BINARY) != 2)
return LWS_HP_RET_BAIL_DIE;
return LWS_HP_RET_BAIL_OK;
}
#endif
if (wsi->mqtt && !wsi->mqtt->inside_payload &&
(wsi->mqtt->send_pubrec || wsi->mqtt->send_pubrel ||
wsi->mqtt->send_pubcomp)) {
uint8_t buf[LWS_PRE + 4];
/* Remaining len = 2 */
buf[LWS_PRE + 1] = 2;
if (wsi->mqtt->send_pubrec) {
lwsl_notice("%s: issuing PUBREC for pkt id: %d\n",
__func__, wsi->mqtt->peer_ack_pkt_id);
buf[LWS_PRE] = LMQCP_PUBREC << 4 | 0x2;
/* Packet ID */
lws_ser_wu16be(&buf[LWS_PRE + 2],
wsi->mqtt->peer_ack_pkt_id);
wsi->mqtt->send_pubrec = 0;
} else if (wsi->mqtt->send_pubrel) {
lwsl_notice("%s: issuing PUBREL for pkt id: %d\n",
__func__, wsi->mqtt->ack_pkt_id);
buf[LWS_PRE] = LMQCP_PUBREL << 4 | 0x2;
lws_ser_wu16be(&buf[LWS_PRE + 2],
wsi->mqtt->ack_pkt_id);
wsi->mqtt->send_pubrel = 0;
} else {
lwsl_notice("%s: issuing PUBCOMP for pkt id: %d\n",
__func__, wsi->mqtt->peer_ack_pkt_id);
buf[LWS_PRE] = LMQCP_PUBCOMP << 4 | 0x2;
lws_ser_wu16be(&buf[LWS_PRE + 2],
wsi->mqtt->peer_ack_pkt_id);
wsi->mqtt->send_pubcomp = 0;
}
if (lws_write(wsi, (uint8_t *)&buf[LWS_PRE], 4,
LWS_WRITE_BINARY) != 4)
return LWS_HP_RET_BAIL_DIE;
return LWS_HP_RET_BAIL_OK;
}
wsi = lws_get_network_wsi(wsi);
wsi->mux.requested_POLLOUT = 0;
wsi2 = &wsi->mux.child_list;
if (!*wsi2) {
lwsl_debug("%s: no children\n", __func__);
return LWS_HP_RET_DROP_POLLOUT;
}
if (!wsi->mqtt)
return LWS_HP_RET_BAIL_DIE;
lws_wsi_mux_dump_waiting_children(wsi);
do {
struct lws *w, **wa;
wa = &(*wsi2)->mux.sibling_list;
if (!(*wsi2)->mux.requested_POLLOUT)
goto next_child;
if (!lwsi_state_can_handle_POLLOUT(wsi))
goto next_child;
/*
* If the nwsi is in the middle of a frame, we can only
* continue to send that
*/
if (wsi->mqtt->inside_payload && !(*wsi2)->mqtt->inside_payload)
goto next_child;
/*
* we're going to do writable callback for this child.
* move him to be the last child
*/
w = lws_wsi_mux_move_child_to_tail(wsi2);
if (!w) {
wa = &wsi->mux.child_list;
goto next_child;
}
lwsl_debug("%s: child %s (wsistate 0x%x)\n", __func__,
lws_wsi_tag(w), (unsigned int)w->wsistate);
if (lwsi_state(wsi) == LRS_ESTABLISHED &&
!wsi->mqtt->inside_payload &&
wsi->mqtt->send_puback) {
uint8_t buf[LWS_PRE + 4];
lwsl_notice("%s: issuing PUBACK for pkt id: %d\n",
__func__, wsi->mqtt->ack_pkt_id);
/* Fixed header */
buf[LWS_PRE] = LMQCP_PUBACK << 4;
/* Remaining len = 2 */
buf[LWS_PRE + 1] = 2;
/* Packet ID */
lws_ser_wu16be(&buf[LWS_PRE + 2], wsi->mqtt->peer_ack_pkt_id);
if (lws_write(wsi, (uint8_t *)&buf[LWS_PRE], 4,
LWS_WRITE_BINARY) != 4)
return LWS_HP_RET_BAIL_DIE;
wsi->mqtt->send_puback = 0;
w->mux.requested_POLLOUT = 1;
wa = &wsi->mux.child_list;
goto next_child;
}
if (lws_callback_as_writeable(w)) {
lwsl_notice("%s: Closing child %s\n", __func__, lws_wsi_tag(w));
lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,
"mqtt pollout handle");
wa = &wsi->mux.child_list;
}
next_child:
wsi2 = wa;
} while (wsi2 && *wsi2 && !lws_send_pipe_choked(wsi));
// lws_wsi_mux_dump_waiting_children(wsi);
if (lws_wsi_mux_action_pending_writeable_reqs(wsi))
return LWS_HP_RET_BAIL_DIE;
return LWS_HP_RET_BAIL_OK;
}
#if defined(LWS_WITH_CLIENT)
static int
rops_issue_keepalive_mqtt(struct lws *wsi, int isvalid)
{
struct lws *nwsi = lws_get_network_wsi(wsi);
if (isvalid) {
_lws_validity_confirmed_role(nwsi);
return 0;
}
nwsi->mqtt->send_pingreq = 1;
lws_callback_on_writable(nwsi);
return 0;
}
#endif
static int
rops_close_role_mqtt(struct lws_context_per_thread *pt, struct lws *wsi)
{
struct lws *nwsi = lws_get_network_wsi(wsi);
lws_mqtt_subs_t *s, *s1, *mysub;
lws_mqttc_t *c;
if (!wsi->mqtt)
return 0;
c = &wsi->mqtt->client;
lws_sul_cancel(&wsi->mqtt->sul_qos_puback_pubrec_wait);
lws_mqtt_str_free(&c->username);
lws_mqtt_str_free(&c->password);
lws_mqtt_str_free(&c->will.message);
lws_mqtt_str_free(&c->will.topic);
lws_mqtt_str_free(&c->id);
/* clean up any subscription allocations */
s = wsi->mqtt->subs_head;
wsi->mqtt->subs_head = NULL;
while (s) {
s1 = s->next;
/*
* Account for children no longer using nwsi subscription
*/
mysub = lws_mqtt_find_sub(nwsi->mqtt, (const char *)&s[1]);
// assert(mysub); /* if child subscribed, nwsi must feel the same */
if (mysub) {
assert(mysub->ref_count);
mysub->ref_count--;
}
lws_free(s);
s = s1;
}
lws_mqtt_publish_param_t *pub =
(lws_mqtt_publish_param_t *)
wsi->mqtt->rx_cpkt_param;
if (pub)
lws_free_set_NULL(pub->topic);
lws_free_set_NULL(wsi->mqtt->rx_cpkt_param);
lws_free_set_NULL(wsi->mqtt);
return 0;
}
static int
rops_callback_on_writable_mqtt(struct lws *wsi)
{
#if defined(LWS_WITH_CLIENT)
struct lws *network_wsi;
#endif
int already;
lwsl_debug("%s: %s (wsistate 0x%x)\n", __func__, lws_wsi_tag(wsi),
(unsigned int)wsi->wsistate);
if (wsi->mux.requested_POLLOUT
#if defined(LWS_WITH_CLIENT)
&& !wsi->client_h2_alpn
#endif
) {
lwsl_debug("already pending writable\n");
return 1;
}
#if 0
/* is this for DATA or for control messages? */
if (wsi->upgraded_to_http2 && !wsi->h2.h2n->pps &&
!lws_h2_tx_cr_get(wsi)) {
/*
* 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->h2.tx_cr);
wsi->h2.skint = 1;
return 0;
}
wsi->h2.skint = 0;
#endif
#if defined(LWS_WITH_CLIENT)
network_wsi = lws_get_network_wsi(wsi);
#endif
already = lws_wsi_mux_mark_parents_needing_writeable(wsi);
/* for network action, act only on the network wsi */
if (already
#if defined(LWS_WITH_CLIENT)
&& !network_wsi->client_mux_substream
#endif
)
return 1;
return 0;
}
static int
rops_close_kill_connection_mqtt(struct lws *wsi, enum lws_close_status reason)
{
lwsl_info(" %s, his parent %s: child list %p, siblings:\n",
lws_wsi_tag(wsi),
lws_wsi_tag(wsi->mux.parent_wsi), wsi->mux.child_list);
//lws_wsi_mux_dump_children(wsi);
if (wsi->mux_substream
#if defined(LWS_WITH_CLIENT)
|| wsi->client_mux_substream
#endif
) {
lwsl_info("closing %s: parent %s: first child %p\n",
lws_wsi_tag(wsi),
lws_wsi_tag(wsi->mux.parent_wsi),
wsi->mux.child_list);
if (wsi->mux.child_list && lwsl_visible(LLL_INFO)) {
lwsl_info(" parent %s: closing children: list:\n", lws_wsi_tag(wsi));
lws_wsi_mux_dump_children(wsi);
}
lws_wsi_mux_close_children(wsi, (int)reason);
}
if ((
#if defined(LWS_WITH_CLIENT)
wsi->client_mux_substream ||
#endif
wsi->mux_substream) &&
wsi->mux.parent_wsi) {
lws_wsi_mux_sibling_disconnect(wsi);
}
return 0;
}
static const lws_rops_t rops_table_mqtt[] = {
/* 1 */ { .handle_POLLIN = rops_handle_POLLIN_mqtt },
/* 2 */ { .handle_POLLOUT = rops_handle_POLLOUT_mqtt },
/* 3 */ { .callback_on_writable = rops_callback_on_writable_mqtt },
/* 4 */ { .close_role = rops_close_role_mqtt },
/* 5 */ { .close_kill_connection = rops_close_kill_connection_mqtt },
#if defined(LWS_WITH_CLIENT)
/* 6 */ { .client_bind = rops_client_bind_mqtt },
/* 7 */ { .issue_keepalive = rops_issue_keepalive_mqtt },
#endif
};
struct lws_role_ops role_ops_mqtt = {
/* role name */ "mqtt",
/* alpn id */ "x-amzn-mqtt-ca", /* "mqtt/3.1.1" */
/* rops_table */ rops_table_mqtt,
/* rops_idx */ {
/* LWS_ROPS_check_upgrades */
/* LWS_ROPS_pt_init_destroy */ 0x00,
/* LWS_ROPS_init_vhost */
/* LWS_ROPS_destroy_vhost */ 0x00,
/* LWS_ROPS_service_flag_pending */
/* LWS_ROPS_handle_POLLIN */ 0x01,
/* LWS_ROPS_handle_POLLOUT */
/* LWS_ROPS_perform_user_POLLOUT */ 0x20,
/* LWS_ROPS_callback_on_writable */
/* LWS_ROPS_tx_credit */ 0x30,
/* LWS_ROPS_write_role_protocol */
/* LWS_ROPS_encapsulation_parent */ 0x00,
/* LWS_ROPS_alpn_negotiated */
/* LWS_ROPS_close_via_role_protocol */ 0x00,
/* LWS_ROPS_close_role */
/* LWS_ROPS_close_kill_connection */ 0x45,
/* LWS_ROPS_destroy_role */
/* LWS_ROPS_adoption_bind */ 0x00,
/* LWS_ROPS_client_bind */
#if defined(LWS_WITH_CLIENT)
/* LWS_ROPS_issue_keepalive */ 0x67,
#else
/* LWS_ROPS_issue_keepalive */ 0x00,
#endif
},
.adoption_cb = { LWS_CALLBACK_MQTT_NEW_CLIENT_INSTANTIATED,
LWS_CALLBACK_MQTT_NEW_CLIENT_INSTANTIATED },
.rx_cb = { LWS_CALLBACK_MQTT_CLIENT_RX,
LWS_CALLBACK_MQTT_CLIENT_RX },
.writeable_cb = { LWS_CALLBACK_MQTT_CLIENT_WRITEABLE,
LWS_CALLBACK_MQTT_CLIENT_WRITEABLE },
.close_cb = { LWS_CALLBACK_MQTT_CLIENT_CLOSED,
LWS_CALLBACK_MQTT_CLIENT_CLOSED },
.protocol_bind_cb = { LWS_CALLBACK_MQTT_IDLE,
LWS_CALLBACK_MQTT_IDLE },
.protocol_unbind_cb = { LWS_CALLBACK_MQTT_DROP_PROTOCOL,
LWS_CALLBACK_MQTT_DROP_PROTOCOL },
.file_handle = 0,
};

View File

@ -0,0 +1,324 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* MQTT v5
*
* http://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html
*/
#include "private-lib-core.h"
#include <string.h>
#include <sys/types.h>
#include <assert.h>
/*
* Encode is done into a buffer of at least 4 bytes space.
*
* Returns -1 for error, or number of bytes used
*/
int
lws_mqtt_vbi_encode(uint32_t value, void *buf)
{
uint8_t *p = (uint8_t *)buf, b;
if (value > 0xfffffff) {
assert(0);
return -1;
}
do {
b = value & 0x7f;
value >>= 7;
if (value)
*p++ = (0x80 | b);
else
*p++ = b;
} while (value);
return lws_ptr_diff(p, (uint8_t *)buf);
}
void
lws_mqtt_vbi_init(lws_mqtt_vbi *vbi)
{
vbi->value = 0;
vbi->consumed = 0;
vbi->budget = 4;
}
void
lws_mqtt_2byte_init(lws_mqtt_vbi *vbi)
{
vbi->value = 0;
vbi->consumed = 0;
vbi->budget = 2;
}
void
lws_mqtt_4byte_init(lws_mqtt_vbi *vbi)
{
vbi->value = 0;
vbi->consumed = 0;
vbi->budget = 4;
}
lws_mqtt_stateful_primitive_return_t
lws_mqtt_vbi_r(lws_mqtt_vbi *vbi, const uint8_t **in, size_t *len)
{
uint8_t multiplier = 0;
if (!vbi->budget) {
lwsl_info("%s: bad vbi\n", __func__);
return LMSPR_FAILED_ALREADY_COMPLETED;
}
while (*len && vbi->budget--) {
uint8_t u = *((*in)++);
(*len)--;
vbi->consumed++;
vbi->value = vbi->value + (uint32_t)((u & 0x7f) << multiplier);
multiplier = (uint8_t)(multiplier + 7);
if (!(u & 0x80))
return LMSPR_COMPLETED; /* finished */
}
if (!vbi->budget) { /* should have ended on b7 = 0 and exited then... */
lwsl_info("%s: bad vbi\n", __func__);
return LMSPR_FAILED_FORMAT;
}
return LMSPR_NEED_MORE;
}
lws_mqtt_stateful_primitive_return_t
lws_mqtt_mb_parse(lws_mqtt_vbi *vbi, const uint8_t **in, size_t *len)
{
if (!vbi->budget)
return LMSPR_FAILED_ALREADY_COMPLETED;
while (*len && vbi->budget--) {
vbi->value = (vbi->value << 8) | *((*in)++);
(*len)--;
vbi->consumed++;
}
return vbi->budget ? LMSPR_NEED_MORE : LMSPR_COMPLETED;
}
/*
* You can leave buf NULL, if so it will be allocated on the heap once the
* actual length is known. nf should be 0, it will be set at allocation time.
*
* Or you can ensure no allocation and use an external buffer by setting buf
* and lim. But buf must be in the ep context somehow, since it may have to
* survive returns to the event loop unchanged. Set nf to 0 in this case.
*
* Or you can set buf to an externally allocated buffer, in which case you may
* set nf so it will be freed when the string is "freed".
*/
void
lws_mqtt_str_init(lws_mqtt_str_t *s, uint8_t *buf, uint16_t lim, char nf)
{
s->len = 0; /* at COMPLETED, consumed count is s->len + 2 */
s->pos = 0;
s->buf = buf;
s->limit = lim;
s->len_valid = 0;
s->needs_freeing = nf;
}
lws_mqtt_str_t *
lws_mqtt_str_create(uint16_t lim)
{
lws_mqtt_str_t *s = lws_malloc(sizeof(*s) + lim + 1, __func__);
if (!s)
return NULL;
s->len = 0;
s->pos = 0;
s->buf = (uint8_t *)&s[1];
s->limit = lim;
s->len_valid = 0;
s->needs_freeing = 1;
return s;
}
lws_mqtt_str_t *
lws_mqtt_str_create_init(uint8_t *buf, uint16_t len, uint16_t lim)
{
lws_mqtt_str_t *s;
if (!lim)
lim = len;
s = lws_mqtt_str_create(lim);
if (!s)
return NULL;
memcpy(s->buf, buf, len);
s->len = len;
s->len_valid = 1;
s->pos = len;
return s;
}
lws_mqtt_str_t *
lws_mqtt_str_create_cstr_dup(const char *buf, uint16_t lim)
{
size_t len = strlen(buf);
if (!lim)
lim = (uint16_t)len;
return lws_mqtt_str_create_init((uint8_t *)buf, (uint16_t)len, lim);
}
uint8_t *
lws_mqtt_str_next(lws_mqtt_str_t *s, uint16_t *budget)
{
if (budget)
*budget = (uint16_t)(s->limit - s->pos);
return &s->buf[s->pos];
}
int
lws_mqtt_str_advance(lws_mqtt_str_t *s, int n)
{
if (n > s->limit - s->pos) {
lwsl_err("%s: attempted overflow %d vs %d\n", __func__,
n, s->limit - s->pos);
return 1;
}
s->pos = (uint16_t)(s->pos + (uint16_t)n);
s->len = (uint16_t)(s->len + (uint16_t)n);
return 0;
}
void
lws_mqtt_str_free(lws_mqtt_str_t **ps)
{
lws_mqtt_str_t *s = *ps;
if (!s || !s->needs_freeing)
return;
/* buf may be independently allocated or allocated along with the
* lws_mqtt_str_t at the end... if so the whole lws_mqtt_str_t is freed.
*/
if (s->buf != (uint8_t *)&s[1])
lws_free_set_NULL(s->buf);
else
lws_free_set_NULL(*ps);
}
/*
* Parses and allocates for lws_mqtt_str_t in a fragmentation-immune, but
* efficient for bulk data way.
*
* Returns: LMSPR_NEED_MORE if needs more data,
* LMSPR_COMPLETED if complete, <0 for error
*
* *len is reduced by, and *in is advanced by, the amount of data actually used,
* except in error case
*
* lws_mqtt_str_free() must be called after calling this successfully
* or not.
*/
lws_mqtt_stateful_primitive_return_t
lws_mqtt_str_parse(lws_mqtt_str_t *s, const uint8_t **in, size_t *len)
{
const uint8_t *oin = *in;
/* handle the length + allocation if needed */
while (*len && !s->len_valid && s->pos < 2) {
s->len = (uint16_t)((s->len << 8) | *((*in)++));
(*len)--;
oin = *in;
if (++s->pos == 2) {
if (s->len > s->limit)
return LMSPR_FAILED_OVERSIZE;
s->pos = 0;
s->len_valid = 1;
if (!s->len) /* do not need to allocate */
return LMSPR_COMPLETED;
if (!s->buf) {
s->buf = lws_malloc(s->len, __func__);
if (!s->buf)
return LMSPR_FAILED_OOM;
s->needs_freeing = 1;
}
}
}
/* handle copying bulk data into allocation */
if (s->len_valid && *len) {
uint16_t span = (uint16_t)(s->len - s->pos);
if (span > *len)
span = (uint16_t)*len;
memcpy(s->buf + s->pos, *in, span);
*in += span;
s->pos = (uint16_t)(s->pos + (uint16_t)span);
}
*len -= (unsigned long)(*in - oin);
return s->buf && s->pos == s->len ? LMSPR_COMPLETED : LMSPR_NEED_MORE;
}
int
lws_mqtt_bindata_cmp(const lws_mqtt_str_t *bd1,
const lws_mqtt_str_t *bd2)
{
if (bd1->len != bd2->len)
return 1;
if (!!bd1->buf != !!bd2->buf)
return 1;
if (!bd1->buf && !bd2->buf)
return 0;
return memcmp(bd1->buf, bd2->buf, bd1->len);
}

View File

@ -0,0 +1,449 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef _PRIVATE_LIB_ROLES_MQTT
#define _PRIVATE_LIB_ROLES_MQTT 1
extern struct lws_role_ops role_ops_mqtt;
#define lwsi_role_mqtt(wsi) (wsi->role_ops == &role_ops_mqtt)
#define LWS_MQTT_MAX_CHILDREN 8 /* max child streams on same parent */
#define LMQCP_LUT_FLAG_RESERVED_FLAGS 0x10
#define LMQCP_LUT_FLAG_PACKET_ID_NONE 0x00
#define LMQCP_LUT_FLAG_PACKET_ID_HAS 0x20
#define LMQCP_LUT_FLAG_PACKET_ID_QOS12 0x40
#define LMQCP_LUT_FLAG_PACKET_ID_MASK 0x60
#define LMQCP_LUT_FLAG_PAYLOAD 0x80 /* payload req (publish = opt)*/
#define lws_mqtt_str_is_not_empty(s) ( ((s)) && \
((s))->len && \
((s))->buf && \
*((s))->buf )
#define LWS_MQTT_RESPONSE_TIMEOUT (3 * LWS_US_PER_SEC)
#define LWS_MQTT_RETRY_CEILING (60 * LWS_US_PER_SEC)
#define LWS_MQTT_MAX_PUBLISH_RETRY (3)
typedef enum {
LMSPR_COMPLETED = 0,
LMSPR_NEED_MORE = 1,
LMSPR_FAILED_OOM = -1,
LMSPR_FAILED_OVERSIZE = -2,
LMSPR_FAILED_FORMAT = -3,
LMSPR_FAILED_ALREADY_COMPLETED = -4,
} lws_mqtt_stateful_primitive_return_t;
typedef struct {
uint32_t value;
char budget;
char consumed;
} lws_mqtt_vbi;
/* works for vbi, 2-byte and 4-byte fixed length */
static inline int
lws_mqtt_mb_first(lws_mqtt_vbi *vbi) { return !vbi->consumed; }
int
lws_mqtt_vbi_encode(uint32_t value, void *buf);
/*
* Decode is done statefully on an arbitrary amount of input data (which may
* be one byte). It's like this so it can continue seamlessly if a buffer ends
* partway through the primitive, and the api matches the bulk binary data case.
*
* VBI decode:
*
* Initialize the lws_mqtt_vbi state by calling lws_mqtt_vbi_init() on it, then
* feed lws_mqtt_vbi_r() bytes to decode.
*
* Returns <0 for error, LMSPR_COMPLETED if done (vbi->value is valid), or
* LMSPR_NEED_MORE if more calls to lws_mqtt_vbi_r() with subsequent bytes
* needed.
*
* *in and *len are updated accordingly.
*
* 2-byte and 4-byte decode:
*
* Initialize the lws_mqtt_vbi state by calling lws_mqtt_2byte_init() or
* lws_mqtt_4byte_init() on it, then feed lws_mqtt_mb_parse() bytes
* to decode.
*
* Returns <0 for error, LMSPR_COMPLETED if done (vbi->value is valid), or
* LMSPR_NEED_MORE if more calls to lws_mqtt_mb_parse() with subsequent
* bytes needed.
*
* *in and *len are updated accordingly.
*/
void
lws_mqtt_vbi_init(lws_mqtt_vbi *vbi);
void
lws_mqtt_2byte_init(lws_mqtt_vbi *vbi);
void
lws_mqtt_4byte_init(lws_mqtt_vbi *vbi);
lws_mqtt_stateful_primitive_return_t
lws_mqtt_vbi_r(lws_mqtt_vbi *vbi, const uint8_t **in, size_t *len);
lws_mqtt_stateful_primitive_return_t
lws_mqtt_mb_parse(lws_mqtt_vbi *vbi, const uint8_t **in, size_t *len);
struct lws_mqtt_str_st {
uint8_t *buf;
uint16_t len;
uint16_t limit; /* it's cheaper to add the state here than
* the pointer to point to it elsewhere */
uint16_t pos;
char len_valid;
char needs_freeing;
};
static inline int
lws_mqtt_str_first(struct lws_mqtt_str_st *s) { return !s->buf && !s->pos; }
lws_mqtt_stateful_primitive_return_t
lws_mqtt_str_parse(struct lws_mqtt_str_st *bd, const uint8_t **in, size_t *len);
typedef enum {
LMQCPP_IDLE,
/* receive packet type part of fixed header took us out of idle... */
LMQCPP_CONNECT_PACKET = LMQCP_CTOS_CONNECT << 4,
LMQCPP_CONNECT_REMAINING_LEN_VBI,
LMQCPP_CONNECT_VH_PNAME,
LMQCPP_CONNECT_VH_PVERSION,
LMQCPP_CONNECT_VH_FLAGS,
LMQCPP_CONNECT_VH_KEEPALIVE,
LMQCPP_CONNECT_VH_PROPERTIES_VBI_LEN,
LMQCPP_CONNACK_PACKET = LMQCP_STOC_CONNACK << 4,
LMQCPP_CONNACK_VH_FLAGS,
LMQCPP_CONNACK_VH_RETURN_CODE,
LMQCPP_PUBLISH_PACKET = LMQCP_PUBLISH << 4,
LMQCPP_PUBLISH_REMAINING_LEN_VBI,
LMQCPP_PUBLISH_VH_TOPIC,
LMQCPP_PUBLISH_VH_PKT_ID,
LMQCPP_PUBACK_PACKET = LMQCP_PUBACK << 4,
LMQCPP_PUBACK_VH_PKT_ID,
LMQCPP_PUBACK_PROPERTIES_LEN_VBI,
LMQCPP_PUBREC_PACKET = LMQCP_PUBREC << 4,
LMQCPP_PUBREC_VH_PKT_ID,
LMQCPP_PUBREL_PACKET = LMQCP_PUBREL << 4,
LMQCPP_PUBREL_VH_PKT_ID,
LMQCPP_PUBCOMP_PACKET = LMQCP_PUBCOMP << 4,
LMQCPP_PUBCOMP_VH_PKT_ID,
LMQCPP_SUBACK_PACKET = LMQCP_STOC_SUBACK << 4,
LMQCPP_SUBACK_VH_PKT_ID,
LMQCPP_SUBACK_PAYLOAD,
LMQCPP_UNSUBACK_PACKET = LMQCP_STOC_UNSUBACK << 4,
LMQCPP_UNSUBACK_VH_PKT_ID,
LMQCPP_PINGRESP_ZERO = LMQCP_STOC_PINGRESP << 4,
LMQCPP_PAYLOAD,
LMQCPP_EAT_PROPERTIES_AND_COMPLETE,
LMQCPP_PROP_ID_VBI,
/* all possible property payloads */
/* 3.3.2.3.2 */
LMQCPP_PROP_PAYLOAD_FORMAT_INDICATOR_1BYTE = 0x101,
LMQCPP_PROP_MSG_EXPIRY_INTERVAL_4BYTE = 0x102,
LMQCPP_PROP_CONTENT_TYPE_UTF8S = 0x103,
LMQCPP_PROP_RESPONSE_TOPIC_UTF8S = 0x108,
LMQCPP_PROP_CORRELATION_BINDATA = 0x109,
LMQCPP_PROP_SUBSCRIPTION_ID_VBI = 0x10b,
LMQCPP_PROP_SESSION_EXPIRY_INTERVAL_4BYTE = 0x111,
LMQCPP_PROP_ASSIGNED_CLIENTID_UTF8S = 0x112,
LMQCPP_PROP_SERVER_KEEPALIVE_2BYTE = 0x113,
LMQCPP_PROP_AUTH_METHOD_UTF8S = 0x115,
LMQCPP_PROP_AUTH_DATA_BINDATA = 0x116,
LMQCPP_PROP_REQUEST_PROBLEM_INFO_1BYTE = 0x117,
LMQCPP_PROP_WILL_DELAY_INTERVAL_4BYTE = 0x118,
LMQCPP_PROP_REQUEST_REPSONSE_INFO_1BYTE = 0x119,
LMQCPP_PROP_RESPONSE_INFO_UTF8S = 0x11a,
LMQCPP_PROP_SERVER_REFERENCE_UTF8S = 0x11c,
LMQCPP_PROP_REASON_STRING_UTF8S = 0x11f,
LMQCPP_PROP_RECEIVE_MAXIMUM_2BYTE = 0x121,
LMQCPP_PROP_TOPIC_MAXIMUM_2BYTE = 0x122,
LMQCPP_PROP_TOPIC_ALIAS_2BYTE = 0x123,
LMQCPP_PROP_MAXIMUM_QOS_1BYTE = 0x124,
LMQCPP_PROP_RETAIN_AVAILABLE_1BYTE = 0x125,
LMQCPP_PROP_USER_PROPERTY_NAME_UTF8S = 0x126,
LMQCPP_PROP_USER_PROPERTY_VALUE_UTF8S = 0x226,
LMQCPP_PROP_MAXIMUM_PACKET_SIZE_4BYTE = 0x127,
LMQCPP_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE_1BYTE = 0x128,
LMQCPP_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE_1BYTE = 0x129,
LMQCPP_PROP_SHARED_SUBSCRIPTION_AVAILABLE_1BYTE = 0x12a,
} lws_mqtt_packet_parse_state_t;
/*
* the states an MQTT connection can be in
*/
typedef enum {
LGSMQTT_UNKNOWN,
LGSMQTT_IDLE,
LGSMQTT_TRANSPORT_CONNECTED,
LGSMQTT_SENT_CONNECT,
LGSMQTT_ESTABLISHED,
LGSMQTT_SENT_SUBSCRIBE,
LGSMQTT_SUBSCRIBED,
} lwsgs_mqtt_states_t;
typedef struct lws_mqtt_parser_st {
/* struct lws_mqtt_str_st s_content_type; */
lws_mqtt_packet_parse_state_t state;
lws_mqtt_vbi vbit;
lws_mqtt_reason_t reason;
lws_mqtt_str_t s_temp;
uint8_t fixed_seen[4];
uint8_t props_seen[8];
uint8_t cpkt_flags;
uint32_t cpkt_remlen;
uint32_t props_len;
uint32_t consumed;
uint32_t prop_id;
uint32_t props_consumed;
uint32_t payload_consumed;
uint16_t keepalive;
uint16_t cpkt_id;
uint32_t n;
uint8_t temp[32];
uint8_t conn_rc;
uint8_t payload_format;
uint8_t packet_type_flags;
uint8_t conn_protocol_version;
uint8_t fixed;
uint8_t flag_pending_send_connack_close:1;
uint8_t flag_pending_send_reason_close:1;
uint8_t flag_prop_multi:1;
uint8_t flag_server:1;
} lws_mqtt_parser_t;
typedef enum {
LMVTR_VALID = 0,
LMVTR_VALID_WILDCARD = 1,
LMVTR_VALID_SHADOW = 2,
LMVTR_FAILED_OVERSIZE = -1,
LMVTR_FAILED_WILDCARD_FORMAT = -2,
LMVTR_FAILED_SHADOW_FORMAT = -3,
} lws_mqtt_validate_topic_return_t;
typedef enum {
LMMTR_TOPIC_NOMATCH = 0,
LMMTR_TOPIC_MATCH = 1,
LMMTR_TOPIC_MATCH_ERROR = -1
} lws_mqtt_match_topic_return_t;
typedef struct lws_mqtt_subs {
struct lws_mqtt_subs *next;
uint8_t ref_count; /* number of children referencing */
/* Flags */
uint8_t wildcard:1;
uint8_t shadow:1;
/* subscription name + NUL overallocated here */
char topic[];
} lws_mqtt_subs_t;
typedef struct lws_mqtts {
lws_mqtt_parser_t par;
lwsgs_mqtt_states_t estate;
struct lws_dll2 active_session_list_head;
struct lws_dll2 limbo_session_list_head;
} lws_mqtts_t;
typedef struct lws_mqttc {
lws_mqtt_parser_t par;
lwsgs_mqtt_states_t estate;
struct lws_mqtt_str_st *id;
struct lws_mqtt_str_st *username;
struct lws_mqtt_str_st *password;
struct {
struct lws_mqtt_str_st *topic;
struct lws_mqtt_str_st *message;
lws_mqtt_qos_levels_t qos;
uint8_t retain;
} will;
uint16_t keep_alive_secs;
uint16_t conn_flags;
uint8_t aws_iot;
} lws_mqttc_t;
struct _lws_mqtt_related {
lws_mqttc_t client;
lws_sorted_usec_list_t sul_qos_puback_pubrec_wait; /* QoS1 puback or QoS2 pubrec wait TO */
lws_sorted_usec_list_t sul_qos1_puback_wait; /* QoS1 puback wait TO */
lws_sorted_usec_list_t sul_unsuback_wait; /* unsuback wait TO */
lws_sorted_usec_list_t sul_qos2_pubrec_wait; /* QoS2 pubrec wait TO */
lws_sorted_usec_list_t sul_shadow_wait; /* Device Shadow wait TO */
struct lws *wsi; /**< so sul can use lws_container_of */
lws_mqtt_subs_t *subs_head; /**< Linked-list of heap-allocated subscription objects */
void *rx_cpkt_param;
uint16_t pkt_id;
uint16_t ack_pkt_id;
uint16_t peer_ack_pkt_id;
uint16_t sub_size;
#if defined(LWS_WITH_CLIENT)
uint8_t send_pingreq:1;
uint8_t session_resumed:1;
#endif
uint8_t inside_payload:1;
uint8_t inside_subscribe:1;
uint8_t inside_unsubscribe:1;
uint8_t inside_birth:1;
uint8_t inside_resume_session:1;
uint8_t send_puback:1;
uint8_t send_pubrel:1;
uint8_t send_pubrec:1;
uint8_t send_pubcomp:1;
uint8_t unacked_publish:1;
uint8_t unacked_pubrel:1;
uint8_t done_subscribe:1;
uint8_t done_birth:1;
uint8_t inside_shadow:1;
uint8_t done_shadow_subscribe:1;
uint8_t send_shadow_unsubscribe:1;
};
/*
* New sessions are created by starting CONNECT. If the ClientID sent
* by the client matches a different, extant session, then the
* existing one is taken over and the new one created for duration of
* CONNECT processing is destroyed.
*
* On the server side, bearing in mind multiple simultaneous,
* fragmented CONNECTs may be interleaved ongoing, all state and
* parsing temps for a session must live in the session object.
*/
struct lws_mqtt_endpoint_st;
typedef struct lws_mqtts_session_st {
struct lws_dll2 session_list;
} lws_mqtts_session_t;
#define ctl_pkt_type(x) (x->packet_type_flags >> 4)
void
lws_mqttc_state_transition(lws_mqttc_t *ep, lwsgs_mqtt_states_t s);
int
_lws_mqtt_rx_parser(struct lws *wsi, lws_mqtt_parser_t *par,
const uint8_t *buf, size_t len);
int
lws_mqtt_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
struct lws *wsi_conn);
int
lws_create_client_mqtt_object(const struct lws_client_connect_info *i,
struct lws *wsi);
struct lws *
lws_mqtt_client_send_connect(struct lws *wsi);
struct lws *
lws_mqtt_client_send_disconnect(struct lws *wsi);
int
lws_mqtt_fill_fixed_header(uint8_t *p, lws_mqtt_control_packet_t ctrl_pkt_type,
uint8_t dup, lws_mqtt_qos_levels_t qos,
uint8_t retain);
struct lws *
lws_wsi_mqtt_adopt(struct lws *parent_wsi, struct lws *wsi);
lws_mqtt_subs_t *
lws_mqtt_find_sub(struct _lws_mqtt_related *mqtt, const char *topic);
lws_mqtt_match_topic_return_t
lws_mqtt_is_topic_matched(const char* sub, const char* pub);
#endif /* _PRIVATE_LIB_ROLES_MQTT */

View File

@ -0,0 +1,717 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* We mainly focus on the routing table / gateways because those are the
* elements that decide if we can get on to the internet or not.
*
* We also need to understand the source addresses of possible outgoing routes,
* and follow LINK down (ifconfig down) to clean up routes on the interface idx
* going down that are not otherwise cleaned.
*/
#include <private-lib-core.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
//#define lwsl_netlink lwsl_notice
#define lwsl_cx_netlink lwsl_cx_info
#define lwsl_cx_netlink_debug lwsl_cx_debug
static void
lws_netlink_coldplug_done_cb(lws_sorted_usec_list_t *sul)
{
struct lws_context *ctx = lws_container_of(sul, struct lws_context,
sul_nl_coldplug);
ctx->nl_initial_done = 1;
#if defined(LWS_WITH_SYS_STATE)
/* if nothing is there to intercept anything, go all the way */
lws_state_transition_steps(&ctx->mgr_system, LWS_SYSTATE_OPERATIONAL);
#endif
}
static int
rops_handle_POLLIN_netlink(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
struct lws_context *cx = pt->context;
uint8_t s[4096]
#if defined(_DEBUG)
, route_change = 0
#endif
#if defined(LWS_WITH_SYS_SMD)
, gateway_change = 0
#endif
;
struct sockaddr_nl nladdr;
lws_route_t robj, *rou;
struct nlmsghdr *h;
struct msghdr msg;
struct iovec iov;
unsigned int n, removed;
char buf[72];
if (!(pollfd->revents & LWS_POLLIN))
return LWS_HPI_RET_HANDLED;
memset(&msg, 0, sizeof(msg));
iov.iov_base = (void *)s;
iov.iov_len = sizeof(s);
msg.msg_name = (void *)&(nladdr);
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
n = (unsigned int)recvmsg(wsi->desc.sockfd, &msg, 0);
if ((int)n < 0) {
lwsl_cx_notice(cx, "recvmsg failed");
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
// lwsl_hexdump_notice(s, (size_t)n);
h = (struct nlmsghdr *)s;
/* we can get a bunch of messages coalesced in one read*/
for ( ; NLMSG_OK(h, n); h = NLMSG_NEXT(h, n)) {
struct ifaddrmsg *ifam;
struct rtattr *ra;
struct rtmsg *rm;
#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG)
struct ndmsg *nd;
#endif
unsigned int ra_len;
uint8_t *p;
struct ifinfomsg *ifi;
struct rtattr *attribute;
unsigned int len;
lwsl_cx_netlink(cx, "RTM %d", h->nlmsg_type);
memset(&robj, 0, sizeof(robj));
robj.if_idx = -1;
robj.priority = -1;
rm = (struct rtmsg *)NLMSG_DATA(h);
/*
* We have to care about NEWLINK so we can understand when a
* network interface went down, and clear the related routes.
*
* We don't get individual DELROUTEs for these.
*/
switch (h->nlmsg_type) {
case RTM_NEWLINK:
ifi = NLMSG_DATA(h);
len = (unsigned int)(h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)));
/* loop over all attributes for the NEWLINK message */
for (attribute = IFLA_RTA(ifi); RTA_OK(attribute, len);
attribute = RTA_NEXT(attribute, len)) {
lwsl_cx_netlink(cx, "if attr %d",
(int)attribute->rta_type);
switch(attribute->rta_type) {
case IFLA_IFNAME:
lwsl_cx_netlink(cx, "NETLINK ifidx %d : %s",
ifi->ifi_index,
(char *)RTA_DATA(attribute));
break;
default:
break;
} /* switch */
} /* for loop */
lwsl_cx_netlink(cx, "NEWLINK ifi_index %d, flags 0x%x",
ifi->ifi_index, ifi->ifi_flags);
/*
* Despite "New"link this is actually telling us there
* is some change on the network interface IFF_ state
*/
if (!(ifi->ifi_flags & IFF_UP)) {
/*
* Interface is down, so scrub all routes that
* applied to it
*/
lwsl_cx_netlink(cx, "NEWLINK: ifdown %d",
ifi->ifi_index);
lws_pt_lock(pt, __func__);
_lws_route_table_ifdown(pt, ifi->ifi_index);
lws_pt_unlock(pt);
}
continue; /* ie, not break, no second half */
case RTM_NEWADDR:
case RTM_DELADDR:
ifam = (struct ifaddrmsg *)NLMSG_DATA(h);
robj.source_ads = 1;
robj.dest_len = ifam->ifa_prefixlen;
robj.if_idx = (int)ifam->ifa_index;
robj.scope = ifam->ifa_scope;
robj.ifa_flags = ifam->ifa_flags;
robj.dest.sa4.sin_family = ifam->ifa_family;
/* address attributes */
ra = (struct rtattr *)IFA_RTA(ifam);
ra_len = (unsigned int)IFA_PAYLOAD(h);
lwsl_cx_netlink(cx, "%s",
h->nlmsg_type == RTM_NEWADDR ? "NEWADDR" : "DELADDR");
// Parse attributes.
for ( ; RTA_OK(ra, ra_len); ra = RTA_NEXT(ra, ra_len)) {
//lwsl_cx_netlink_debug(cx, "%s: IFA %d\n", __func__, ra->rta_type);
switch (ra->rta_type) {
case IFA_LOCAL:
// Local address
lws_sa46_copy_address(&robj.src, RTA_DATA(ra), rm->rtm_family);
robj.src_len = rm->rtm_src_len;
lws_sa46_write_numeric_address(&robj.src, buf, sizeof(buf));
lwsl_cx_netlink_debug(cx, "IFA_LOCAL: %s/%d", buf, robj.src_len);
break;
case IFA_ADDRESS:
// Prefix address, not local interface.
lws_sa46_copy_address(&robj.dest, RTA_DATA(ra), rm->rtm_family);
robj.dest_len = rm->rtm_dst_len;
lws_sa46_write_numeric_address(&robj.dest, buf, sizeof(buf));
lwsl_cx_netlink_debug(cx, "IFA_ADDRESS: %s/%d", buf, robj.dest_len);
break;
case IFA_FLAGS:
lwsl_cx_netlink_debug(cx, "IFA_FLAGS: 0x%x (not handled)",
*(unsigned int*)RTA_DATA(ra));
break;
case IFA_BROADCAST:
lwsl_cx_netlink_debug(cx, "IFA_BROADCAST (not handled)");
break;
case IFA_ANYCAST:
lwsl_cx_netlink_debug(cx, "IFA_ANYCAST (not handled)");
break;
case IFA_CACHEINFO:
lwsl_cx_netlink_debug(cx, "IFA_CACHEINFO (not handled)");
break;
case IFA_LABEL:
strncpy(buf, RTA_DATA(ra), sizeof(buf));
buf[sizeof(buf)-1] = '\0';
lwsl_cx_netlink_debug(cx, "IFA_LABEL: %s (not used)", buf);
break;
default:
lwsl_cx_netlink_debug(cx, "unknown IFA attr type %d", ra->rta_type);
break;
}
//lwsl_cx_debug(cx, "rta payload length: %ld", RTA_PAYLOAD(ra));
} /* for */
/*
* almost nothing interesting within IFA_* attributes:
* so skip it and goto to the second half
*/
goto second_half;
case RTM_NEWROUTE:
case RTM_DELROUTE:
lwsl_cx_netlink(cx, "%s",
h->nlmsg_type == RTM_NEWROUTE ?
"NEWROUTE" : "DELROUTE");
/* route attributes */
ra = (struct rtattr *)RTM_RTA(rm);
ra_len = (unsigned int)RTM_PAYLOAD(h);
break;
case RTM_DELNEIGH:
case RTM_NEWNEIGH:
lwsl_cx_netlink(cx, "%s", h->nlmsg_type ==
RTM_NEWNEIGH ? "NEWNEIGH" :
"DELNEIGH");
#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG)
nd = (struct ndmsg *)rm;
lwsl_cx_netlink(cx, "fam %u, ifidx %u, flags 0x%x",
nd->ndm_family, nd->ndm_ifindex,
nd->ndm_flags);
#endif
ra = (struct rtattr *)RTM_RTA(rm);
ra_len = (unsigned int)RTM_PAYLOAD(h);
for ( ; RTA_OK(ra, ra_len); ra = RTA_NEXT(ra, ra_len)) {
lwsl_cx_netlink(cx, "atr %d", ra->rta_type);
switch (ra->rta_type) {
case NDA_DST:
lwsl_cx_netlink(cx, "dst len %d",
ra->rta_len);
break;
}
}
lws_pt_lock(pt, __func__);
_lws_route_pt_close_unroutable(pt);
lws_pt_unlock(pt);
continue;
default:
lwsl_cx_netlink(cx, "*** Unknown RTM_%d",
h->nlmsg_type);
continue;
} /* switch */
robj.proto = rm->rtm_protocol;
// iterate over route attributes
for ( ; RTA_OK(ra, ra_len); ra = RTA_NEXT(ra, ra_len)) {
// lwsl_netlink("%s: atr %d\n", __func__, ra->rta_type);
switch (ra->rta_type) {
case RTA_PREFSRC: /* protocol ads: preferred src ads */
case RTA_SRC:
lws_sa46_copy_address(&robj.src, RTA_DATA(ra),
rm->rtm_family);
robj.src_len = rm->rtm_src_len;
lws_sa46_write_numeric_address(&robj.src, buf, sizeof(buf));
if (ra->rta_type == RTA_SRC)
lwsl_cx_netlink_debug(cx, "RTA_SRC: %s/%d", buf, robj.src_len);
else
lwsl_cx_netlink_debug(cx, "RTA_PREFSRC: %s/%d", buf, robj.src_len);
break;
case RTA_DST:
/* check if is local addr -> considering it as src addr too */
if (rm->rtm_type == RTN_LOCAL &&
((rm->rtm_family == AF_INET && rm->rtm_dst_len == 32) ||
(rm->rtm_family == AF_INET6 && rm->rtm_dst_len == 128))) {
lws_sa46_copy_address(&robj.src, RTA_DATA(ra),
rm->rtm_family);
lwsl_cx_netlink_debug(cx, "Local addr: RTA_DST -> added to RTA_SRC");
}
lws_sa46_copy_address(&robj.dest, RTA_DATA(ra),
rm->rtm_family);
robj.dest_len = rm->rtm_dst_len;
lws_sa46_write_numeric_address(&robj.dest, buf, sizeof(buf));
lwsl_cx_netlink_debug(cx, "RTA_DST: %s/%d", buf, robj.dest_len);
break;
case RTA_GATEWAY:
lws_sa46_copy_address(&robj.gateway, RTA_DATA(ra),
rm->rtm_family);
lws_sa46_write_numeric_address(&robj.gateway, buf, sizeof(buf));
lwsl_cx_netlink_debug(cx, "RTA_GATEWAY: %s", buf);
#if defined(LWS_WITH_SYS_SMD)
gateway_change = 1;
#endif
break;
case RTA_IIF: /* int: input interface index */
case RTA_OIF: /* int: output interface index */
robj.if_idx = *(int *)RTA_DATA(ra);
lwsl_cx_netlink_debug(cx, "RTA_IIF/RTA_OIF: %d", robj.if_idx);
break;
case RTA_PRIORITY: /* int: priority of route */
p = RTA_DATA(ra);
robj.priority = p[3] << 24 | p[2] << 16 |
p[1] << 8 | p[0];
lwsl_cx_netlink_debug(cx, "RTA_PRIORITY: %d", robj.priority);
break;
case RTA_CACHEINFO: /* struct rta_cacheinfo */
lwsl_cx_netlink_debug(cx, "RTA_CACHEINFO (not handled)");
break;
#if defined(LWS_HAVE_RTA_PREF)
case RTA_PREF: /* char: RFC4191 v6 router preference */
lwsl_cx_netlink_debug(cx, "RTA_PREF (not handled)");
break;
#endif
case RTA_TABLE: /* int */
lwsl_cx_netlink_debug(cx, "RTA_TABLE (not handled)");
break;
default:
lwsl_cx_netlink_debug(cx, "unknown attr type %d", ra->rta_type);
break;
}
//lwsl_cx_debug(cx, "rta payload length: %ld", RTA_PAYLOAD(ra));
} /* for */
/*
* the second half, once all the attributes were collected
*/
second_half:
switch (h->nlmsg_type) {
case RTM_DELROUTE:
/*
* This will also take down wsi marked as using it
*/
lwsl_cx_netlink(cx, "DELROUTE: if_idx %d",
robj.if_idx);
lws_pt_lock(pt, __func__);
_lws_route_remove(pt, &robj, 0);
lws_pt_unlock(pt);
goto inform;
case RTM_NEWROUTE:
/*
* We don't want any routing debris like /32 or broadcast
* in our routing table... we will collect source addresses
* bound to interfaces via NEWADDR
*/
if (rm->rtm_type != RTN_UNICAST
&& rm->rtm_type != RTN_LOCAL) {
lwsl_cx_netlink(cx, "NEWROUTE: IGNORED (%s)",
rm->rtm_type == RTN_BROADCAST ? "broadcast" :
rm->rtm_type == RTN_ANYCAST ? "anycast" :
rm->rtm_type == RTN_MULTICAST ? "multicast" :
rm->rtm_type == RTN_UNREACHABLE ? "unreachable" :
rm->rtm_type == RTN_NAT ? "nat" :
rm->rtm_type == RTN_UNSPEC ? "unspecified" :
"other");
break;
}
if (rm->rtm_flags & RTM_F_CLONED) {
lwsl_cx_netlink(cx, "NEWROUTE: IGNORED (cloned)");
break;
}
lwsl_cx_netlink(cx, "NEWROUTE: ACCEPTED (if_idx %d)",
robj.if_idx);
#if defined(_DEBUG)
_lws_routing_entry_dump(cx, &robj);
#endif
/*
* 1. Allocate new route for linked-list.
* (robj is on stack, do NOT use)
*/
rou = lws_malloc(sizeof(*rou), __func__);
if (!rou) {
lwsl_cx_err(cx, "oom");
return LWS_HPI_RET_HANDLED;
}
*rou = robj;
// 2. Remove duplicates and add route (both under a lock).
lws_pt_lock(pt, __func__);
/*
* Is robj a dupe in the routing table already?
*
* match on pri ignore == set pri and skip
* no match == add
*
* returns zero ALWAYS
*
* We could be adding a route to the same destination with
* a higher or lower priority from a different source, so why
* all existing routes? Only remove if its the same source and
* destination, effectively a change in priority.
*/
_lws_route_remove(pt, &robj,
LRR_MATCH_DST | LRR_MATCH_SRC | LRR_IGNORE_PRI);
/* add route to linked-list */
rou->uidx = _lws_route_get_uidx(cx);
lws_dll2_add_tail(&rou->list, &cx->routing_table);
lws_pt_unlock(pt);
lwsl_cx_netlink_debug(cx, "route list size %u",
cx->routing_table.count);
/*
* 3. Close anyything we cant reach anymore due to the removal.
* (don't need to or want to do this under lock)
*/
_lws_route_pt_close_unroutable(pt);
inform:
#if defined(_DEBUG)
route_change = 1;
#endif
#if defined(LWS_WITH_SYS_SMD)
/*
* Reflect the route add / del event using SMD.
* Participants interested can refer to the pt
* routing table
*/
(void)lws_smd_msg_printf(cx, LWSSMDCL_NETWORK,
"{\"rt\":\"%s\"}\n",
(h->nlmsg_type == RTM_NEWROUTE) ?
"add" : "del");
#endif
break;
case RTM_DELADDR:
lwsl_cx_notice(cx, "DELADDR");
#if defined(_DEBUG)
_lws_routing_entry_dump(cx, &robj);
#endif
lws_pt_lock(pt, __func__);
removed = cx->routing_table.count;
_lws_route_remove(pt, &robj, LRR_MATCH_SRC | LRR_IGNORE_PRI);
removed -= cx->routing_table.count;
lws_pt_unlock(pt);
_lws_route_pt_close_unroutable(pt);
if (removed > 0)
goto inform;
break;
case RTM_NEWADDR:
lwsl_cx_netlink(cx, "NEWADDR (nothing to do)");
#if defined(_DEBUG)
_lws_routing_entry_dump(cx, &robj);
#endif
/*
* An address alone does not provide new routes.
* NEWADDR will happen when the DHCP lease is being
* renewed, and will likely not change any routes.
*/
break;
default:
// lwsl_info("%s: unknown msg type %d\n", __func__,
// h->nlmsg_type);
break;
}
} /* message iterator */
#if defined(LWS_WITH_SYS_SMD)
if (gateway_change)
/*
* If a route with a gw was added or deleted, retrigger captive
* portal detection if we have that
*/
(void)lws_smd_msg_printf(cx, LWSSMDCL_NETWORK,
"{\"trigger\": \"cpdcheck\", "
"\"src\":\"gw-change\"}");
#endif
#if defined(_DEBUG)
if (route_change) {
lws_context_lock(cx, __func__);
_lws_routing_table_dump(cx);
lws_context_unlock(cx);
}
#endif
if (!cx->nl_initial_done &&
pt == &cx->pt[0] &&
cx->routing_table.count) {
/*
* While netlink info still coming, keep moving the timer for
* calling it "done" to +100ms until after it stops coming
*/
lws_context_lock(cx, __func__);
lws_sul_schedule(cx, 0, &cx->sul_nl_coldplug,
lws_netlink_coldplug_done_cb,
100 * LWS_US_PER_MS);
lws_context_unlock(cx);
}
return LWS_HPI_RET_HANDLED;
}
struct nl_req_s {
struct nlmsghdr hdr;
struct rtmsg gen;
};
int
rops_pt_init_destroy_netlink(struct lws_context *context,
const struct lws_context_creation_info *info,
struct lws_context_per_thread *pt, int destroy)
{
struct sockaddr_nl sanl;
struct nl_req_s req;
struct msghdr msg;
struct iovec iov;
struct lws *wsi;
int n, ret = 1;
if (destroy) {
/*
* pt netlink wsi closed + freed as part of pt's destroy
* wsi mass close, just need to take down the routing table
*/
_lws_route_table_empty(pt);
return 0;
}
if (context->netlink)
return 0;
if (pt > &context->pt[0])
/* we can only have one netlink socket */
return 0;
lwsl_cx_info(context, "creating netlink skt");
/*
* We want a netlink socket per pt as well
*/
lws_context_lock(context, __func__);
wsi = __lws_wsi_create_with_role(context, (int)(pt - &context->pt[0]),
&role_ops_netlink, NULL);
lws_context_unlock(context);
if (!wsi)
goto bail;
wsi->desc.sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (wsi->desc.sockfd == LWS_SOCK_INVALID) {
lwsl_cx_err(context, "unable to open netlink");
goto bail1;
}
lws_plat_set_nonblocking(wsi->desc.sockfd);
__lws_lc_tag(context, &context->lcg[LWSLCG_VHOST], &wsi->lc,
"netlink");
memset(&sanl, 0, sizeof(sanl));
sanl.nl_family = AF_NETLINK;
sanl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR
#if defined(LWS_WITH_IPV6)
| RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR
#endif
;
if (lws_fi(&context->fic, "netlink_bind") ||
bind(wsi->desc.sockfd, (struct sockaddr*)&sanl, sizeof(sanl)) < 0) {
lwsl_cx_warn(context, "netlink bind failed");
ret = 0; /* some systems deny access, just ignore */
goto bail2;
}
context->netlink = wsi;
if (lws_wsi_inject_to_loop(pt, wsi))
goto bail2;
/* if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
lwsl_err("%s: pollfd in fail\n", __func__);
goto bail2;
}
*/
/*
* Since we're starting the PT, ask to be sent all the existing routes.
*
* This requires CAP_ADMIN, or root... we do this early before dropping
* privs
*/
memset(&sanl, 0, sizeof(sanl));
memset(&msg, 0, sizeof(msg));
memset(&req, 0, sizeof(req));
sanl.nl_family = AF_NETLINK;
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.gen));
req.hdr.nlmsg_type = RTM_GETROUTE;
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.hdr.nlmsg_seq = 1;
req.hdr.nlmsg_pid = (uint32_t)getpid();
req.gen.rtm_family = AF_PACKET;
req.gen.rtm_table = RT_TABLE_DEFAULT;
iov.iov_base = &req;
iov.iov_len = req.hdr.nlmsg_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_name = &sanl;
msg.msg_namelen = sizeof(sanl);
n = (int)sendmsg(wsi->desc.sockfd, (struct msghdr *)&msg, 0);
if (n < 0) {
lwsl_cx_notice(context, "rt dump req failed... permissions? errno %d",
LWS_ERRNO);
}
/*
* Responses are going to come asynchronously, let's block moving
* off state IFACE_COLDPLUG until we have had them. This is important
* since if we don't hold there, when we do get the responses we may
* cull any ongoing connections as unroutable otherwise
*/
lwsl_cx_debug(context, "starting netlink coldplug wait");
return 0;
bail2:
__lws_lc_untag(wsi->a.context, &wsi->lc);
compatible_close(wsi->desc.sockfd);
bail1:
lws_free(wsi);
bail:
return ret;
}
static const lws_rops_t rops_table_netlink[] = {
/* 1 */ { .pt_init_destroy = rops_pt_init_destroy_netlink },
/* 2 */ { .handle_POLLIN = rops_handle_POLLIN_netlink },
};
const struct lws_role_ops role_ops_netlink = {
/* role name */ "netlink",
/* alpn id */ NULL,
/* rops_table */ rops_table_netlink,
/* rops_idx */ {
/* LWS_ROPS_check_upgrades */
/* LWS_ROPS_pt_init_destroy */ 0x01,
/* LWS_ROPS_init_vhost */
/* LWS_ROPS_destroy_vhost */ 0x00,
/* LWS_ROPS_service_flag_pending */
/* LWS_ROPS_handle_POLLIN */ 0x02,
/* LWS_ROPS_handle_POLLOUT */
/* LWS_ROPS_perform_user_POLLOUT */ 0x00,
/* LWS_ROPS_callback_on_writable */
/* LWS_ROPS_tx_credit */ 0x00,
/* LWS_ROPS_write_role_protocol */
/* LWS_ROPS_encapsulation_parent */ 0x00,
/* LWS_ROPS_alpn_negotiated */
/* LWS_ROPS_close_via_role_protocol */ 0x00,
/* LWS_ROPS_close_role */
/* LWS_ROPS_close_kill_connection */ 0x00,
/* LWS_ROPS_destroy_role */
/* LWS_ROPS_adoption_bind */ 0x00,
/* LWS_ROPS_client_bind */
/* LWS_ROPS_issue_keepalive */ 0x00,
},
/* adoption_cb clnt, srv */ { 0, 0 },
/* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },
/* close cb clnt, srv */ { 0, 0 },
/* protocol_bind_cb c,s */ { 0, 0 },
/* protocol_unbind_cb c,s */ { 0, 0 },
/* file_handle */ 0,
};

View File

@ -0,0 +1,163 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <private-lib-core.h>
static int
rops_handle_POLLIN_pipe(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
#if defined(LWS_HAVE_EVENTFD)
eventfd_t value;
int n;
n = eventfd_read(wsi->desc.sockfd, &value);
if (n < 0) {
lwsl_notice("%s: eventfd read %d bailed errno %d\n", __func__,
wsi->desc.sockfd, LWS_ERRNO);
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
#elif !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 = (int)read(wsi->desc.sockfd, s, sizeof(s));
(void)n;
if (n < 0)
return LWS_HPI_RET_PLEASE_CLOSE_ME;
#elif defined(WIN32)
char s[100];
int n;
n = recv(wsi->desc.sockfd, s, sizeof(s), 0);
if (n == SOCKET_ERROR)
return LWS_HPI_RET_PLEASE_CLOSE_ME;
#endif
#if defined(LWS_WITH_THREADPOOL) && defined(LWS_HAVE_PTHREAD_H)
/*
* threadpools that need to call for on_writable callbacks do it by
* marking the task as needing one for its wsi, then cancelling service.
*
* Each tsi will call this to perform the actual callback_on_writable
* from the correct service thread context
*/
lws_threadpool_tsi_context(pt->context, pt->tid);
#endif
#if LWS_MAX_SMP > 1
/*
* Other pts need to take care of their own wsi bound to a vhost that
* is going down
*/
if (pt->context->owner_vh_being_destroyed.head) {
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
pt->context->owner_vh_being_destroyed.head) {
struct lws_vhost *v =
lws_container_of(d, struct lws_vhost,
vh_being_destroyed_list);
lws_vhost_lock(v); /* -------------- vh { */
__lws_vhost_destroy_pt_wsi_dieback_start(v);
lws_vhost_unlock(v); /* } vh -------------- */
} lws_end_foreach_dll_safe(d, d1);
}
#endif
#if defined(LWS_WITH_SECURE_STREAMS)
lws_dll2_foreach_safe(&pt->ss_owner, NULL, lws_ss_cancel_notify_dll);
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) && defined(LWS_WITH_CLIENT)
lws_dll2_foreach_safe(&pt->ss_client_owner, NULL, lws_sspc_cancel_notify_dll);
#endif
#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(pt, 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;
}
static const lws_rops_t rops_table_pipe[] = {
/* 1 */ { .handle_POLLIN = rops_handle_POLLIN_pipe },
};
const struct lws_role_ops role_ops_pipe = {
/* role name */ "pipe",
/* alpn id */ NULL,
/* rops_table */ rops_table_pipe,
/* rops_idx */ {
/* LWS_ROPS_check_upgrades */
/* LWS_ROPS_pt_init_destroy */ 0x00,
/* LWS_ROPS_init_vhost */
/* LWS_ROPS_destroy_vhost */ 0x00,
/* LWS_ROPS_service_flag_pending */
/* LWS_ROPS_handle_POLLIN */ 0x01,
/* LWS_ROPS_handle_POLLOUT */
/* LWS_ROPS_perform_user_POLLOUT */ 0x00,
/* LWS_ROPS_callback_on_writable */
/* LWS_ROPS_tx_credit */ 0x00,
/* LWS_ROPS_write_role_protocol */
/* LWS_ROPS_encapsulation_parent */ 0x00,
/* LWS_ROPS_alpn_negotiated */
/* LWS_ROPS_close_via_role_protocol */ 0x00,
/* LWS_ROPS_close_role */
/* LWS_ROPS_close_kill_connection */ 0x00,
/* LWS_ROPS_destroy_role */
/* LWS_ROPS_adoption_bind */ 0x00,
/* LWS_ROPS_client_bind */
/* LWS_ROPS_issue_keepalive */ 0x00,
},
/* adoption_cb clnt, srv */ { 0, 0 },
/* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },
/* close cb clnt, srv */ { 0, 0 },
/* protocol_bind_cb c,s */ { 0, 0 },
/* protocol_unbind_cb c,s */ { 0, 0 },
#if defined(WIN32)
/* file_handle (no, UDP) */ 0,
#else
/* file_handle */ 1,
#endif
};

View File

@ -0,0 +1,438 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
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 & (unsigned int)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_DNS = LWSIFS_NOT_EST | 1,
LRS_WAITING_CONNECT = LWSIFS_NOT_EST | 2,
/* Phase 2: establishing intermediaries on top of transport */
LRS_WAITING_PROXY_REPLY = LWSIFS_NOT_EST | 3,
LRS_WAITING_SSL = LWSIFS_NOT_EST | 4,
LRS_WAITING_SOCKS_GREETING_REPLY = LWSIFS_NOT_EST | 5,
LRS_WAITING_SOCKS_CONNECT_REPLY = LWSIFS_NOT_EST | 6,
LRS_WAITING_SOCKS_AUTH_REPLY = LWSIFS_NOT_EST | 7,
/* Phase 3: establishing tls tunnel */
LRS_SSL_INIT = LWSIFS_NOT_EST | 8,
LRS_SSL_ACK_PENDING = LWSIFS_NOT_EST | 9,
LRS_PRE_WS_SERVING_ACCEPT = LWSIFS_NOT_EST | 10,
/* Phase 4: connected */
LRS_WAITING_SERVER_REPLY = LWSIFS_NOT_EST | 11,
LRS_H2_AWAIT_PREFACE = LWSIFS_NOT_EST | 12,
LRS_H2_AWAIT_SETTINGS = LWSIFS_NOT_EST |
LWSIFS_POCB | 13,
LRS_MQTTC_IDLE = LWSIFS_POCB | 33,
LRS_MQTTC_AWAIT_CONNACK = 34,
/* Phase 5: protocol logically established */
LRS_H2_CLIENT_SEND_SETTINGS = LWSIFS_POCB | 14,
LRS_H2_WAITING_TO_SEND_HEADERS = LWSIFS_POCB | 15,
LRS_DEFERRING_ACTION = LWSIFS_POCB | 16,
LRS_IDLING = 17,
LRS_H1C_ISSUE_HANDSHAKE = 18,
LRS_H1C_ISSUE_HANDSHAKE2 = 19,
LRS_ISSUE_HTTP_BODY = 20,
LRS_ISSUING_FILE = 21,
LRS_HEADERS = 22,
LRS_BODY = 23,
LRS_DISCARD_BODY = 24,
LRS_ESTABLISHED = LWSIFS_POCB | 25,
/* 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 | 26,
/* Phase 6: finishing */
LRS_WAITING_TO_SEND_CLOSE = LWSIFS_POCB | 27,
LRS_RETURNED_CLOSE = LWSIFS_POCB | 28,
LRS_AWAITING_CLOSE_ACK = LWSIFS_POCB | 29,
LRS_FLUSHING_BEFORE_CLOSE = LWSIFS_POCB | 30,
LRS_SHUTDOWN = 31,
/* Phase 7: dead */
LRS_DEAD_SOCKET = 32,
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 & (lws_wsi_state_t)(~LRS_MASK)) | lrs
#else
void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs);
#endif
#define _LWS_ADOPT_FINISH (1 << 24)
/*
* Internal role-specific ops
*
* Many roles are sparsely filled with callbacks, rather than has 20 x
* function pointers in the ops struct, let's have a 20 nybble array telling us
* if the pointer doesn't exist, or its offset in a smaller "just pointers that
* exist" array.
*
* We can support up to 15 valid pointers in the role that way and only have to
* provide pointers that exist for that role, at the cost of a 10-byte nybble
* table.
*
* For x86_64, a set 196 byte allocation becomes 60 + 8 bytes per defined ptr,
* where the ops table is sparse this is a considable .rodata saving, for 32-bit
* 52 + 4 bytes per defined ptr accounting for padding.
*/
/*
* 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.
*/
typedef int (*lws_rops_check_upgrades_t)(struct lws *wsi);
/* role-specific context init during context creation */
typedef int (*lws_rops_pt_init_destroy_t)(struct lws_context *context,
const struct lws_context_creation_info *info,
struct lws_context_per_thread *pt, int destroy);
/* role-specific per-vhost init during vhost creation */
typedef int (*lws_rops_init_vhost_t)(struct lws_vhost *vh,
const struct lws_context_creation_info *info);
/* role-specific per-vhost destructor during vhost destroy */
typedef int (*lws_rops_destroy_vhost_t)(struct lws_vhost *vh);
/* chance for the role to force POLLIN without network activity */
typedef int (*lws_rops_service_flag_pending_t)(struct lws_context *context,
int tsi);
/* an fd using this role has POLLIN signalled */
typedef int (*lws_rops_handle_POLLIN_t)(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 */
typedef int (*lws_rops_handle_POLLOUT_t)(struct lws *wsi);
/* perform user pollout */
typedef int (*lws_rops_perform_user_POLLOUT_t)(struct lws *wsi);
/* do effective callback on writeable */
typedef int (*lws_rops_callback_on_writable_t)(struct lws *wsi);
/* connection-specific tx credit in bytes */
typedef int (*lws_rops_tx_credit_t)(struct lws *wsi, char peer_to_us, int add);
/* role-specific write formatting */
typedef int (*lws_rops_write_role_protocol_t)(struct lws *wsi,
unsigned char *buf, size_t len,
enum lws_write_protocol *wp);
/* get encapsulation parent */
typedef struct lws * (*lws_rops_encapsulation_parent_t)(struct lws *wsi);
/* role-specific destructor */
typedef int (*lws_rops_alpn_negotiated_t)(struct lws *wsi, const char *alpn);
/* chance for the role to handle close in the protocol */
typedef int (*lws_rops_close_via_role_protocol_t)(struct lws *wsi,
enum lws_close_status reason);
/* role-specific close processing */
typedef int (*lws_rops_close_role_t)(struct lws_context_per_thread *pt,
struct lws *wsi);
/* role-specific connection close processing */
typedef int (*lws_rops_close_kill_connection_t)(struct lws *wsi,
enum lws_close_status reason);
/* role-specific destructor */
typedef int (*lws_rops_destroy_role_t)(struct lws *wsi);
/* role-specific socket-adopt */
typedef int (*lws_rops_adoption_bind_t)(struct lws *wsi, int type,
const char *prot);
/* role-specific client-bind:
* ret 1 = bound, 0 = not bound, -1 = fail out
* i may be NULL, indicating client_bind is being called after
* a successful bind earlier, to finalize the binding. In that
* case ret 0 = OK, 1 = fail, wsi needs freeing, -1 = fail, wsi freed */
typedef int (*lws_rops_client_bind_t)(struct lws *wsi,
const struct lws_client_connect_info *i);
/* isvalid = 0: request a role-specific keepalive (PING etc)
* = 1: reset any related validity timer */
typedef int (*lws_rops_issue_keepalive_t)(struct lws *wsi, int isvalid);
#define LWS_COUNT_ROLE_OPS 20
typedef union lws_rops {
lws_rops_check_upgrades_t check_upgrades;
lws_rops_pt_init_destroy_t pt_init_destroy;
lws_rops_init_vhost_t init_vhost;
lws_rops_destroy_vhost_t destroy_vhost;
lws_rops_service_flag_pending_t service_flag_pending;
lws_rops_handle_POLLIN_t handle_POLLIN;
lws_rops_handle_POLLOUT_t handle_POLLOUT;
lws_rops_perform_user_POLLOUT_t perform_user_POLLOUT;
lws_rops_callback_on_writable_t callback_on_writable;
lws_rops_tx_credit_t tx_credit;
lws_rops_write_role_protocol_t write_role_protocol;
lws_rops_encapsulation_parent_t encapsulation_parent;
lws_rops_alpn_negotiated_t alpn_negotiated;
lws_rops_close_via_role_protocol_t close_via_role_protocol;
lws_rops_close_role_t close_role;
lws_rops_close_kill_connection_t close_kill_connection;
lws_rops_destroy_role_t destroy_role;
lws_rops_adoption_bind_t adoption_bind;
lws_rops_client_bind_t client_bind;
lws_rops_issue_keepalive_t issue_keepalive;
} lws_rops_t;
typedef enum {
LWS_ROPS_check_upgrades,
LWS_ROPS_pt_init_destroy,
LWS_ROPS_init_vhost,
LWS_ROPS_destroy_vhost,
LWS_ROPS_service_flag_pending,
LWS_ROPS_handle_POLLIN,
LWS_ROPS_handle_POLLOUT,
LWS_ROPS_perform_user_POLLOUT,
LWS_ROPS_callback_on_writable,
LWS_ROPS_tx_credit,
LWS_ROPS_write_role_protocol,
LWS_ROPS_encapsulation_parent,
LWS_ROPS_alpn_negotiated,
LWS_ROPS_close_via_role_protocol,
LWS_ROPS_close_role,
LWS_ROPS_close_kill_connection,
LWS_ROPS_destroy_role,
LWS_ROPS_adoption_bind,
LWS_ROPS_client_bind,
LWS_ROPS_issue_keepalive,
} lws_rops_func_idx_t;
struct lws_context_per_thread;
struct lws_role_ops {
const char *name;
const char *alpn;
const lws_rops_t *rops_table;
/**< the occupied role ops func ptrs */
uint8_t rops_idx[(LWS_COUNT_ROLE_OPS + 1) / 2];
/**< translates role index into .rops[] offset */
/*
* the callback reasons for adoption for client, server
* (just client applies if no concept of client or server)
*/
uint8_t adoption_cb[2];
/*
* the callback reasons for adoption for client, server
* (just client applies if no concept of client or server)
*/
uint8_t rx_cb[2];
/*
* the callback reasons for WRITEABLE for client, server
* (just client applies if no concept of client or server)
*/
uint8_t writeable_cb[2];
/*
* the callback reasons for CLOSE for client, server
* (just client applies if no concept of client or server)
*/
uint8_t close_cb[2];
/*
* the callback reasons for protocol bind for client, server
* (just client applies if no concept of client or server)
*/
uint8_t protocol_bind_cb[2];
/*
* the callback reasons for protocol unbind for client, server
* (just client applies if no concept of client or server)
*/
uint8_t protocol_unbind_cb[2];
uint8_t file_handle:1;
/* role operates on files not sockets */
};
#define lws_rops_fidx(_rops, fidx) \
((fidx & 1) ? (_rops)->rops_idx[fidx / 2] & 0xf : \
(_rops)->rops_idx[fidx / 2] >> 4)
#define lws_rops_func_fidx(_rops, fidx) \
((_rops)->rops_table[lws_rops_fidx(_rops, fidx) - 1])
/* core roles */
extern const struct lws_role_ops role_ops_raw_skt, role_ops_raw_file,
role_ops_listen, role_ops_pipe,
role_ops_netlink;
/* bring in role private declarations */
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
#include "private-lib-roles-http.h"
#else
#define lwsi_role_http(wsi) (0)
#endif
#if defined(LWS_ROLE_H1)
#include "private-lib-roles-h1.h"
#else
#define lwsi_role_h1(wsi) (0)
#endif
#if defined(LWS_ROLE_H2)
#include "private-lib-roles-h2.h"
#else
#define lwsi_role_h2(wsi) (0)
#endif
#if defined(LWS_ROLE_WS)
#include "private-lib-roles-ws.h"
#else
#define lwsi_role_ws(wsi) (0)
#endif
#if defined(LWS_ROLE_CGI)
#include "private-lib-roles-cgi.h"
#else
#define lwsi_role_cgi(wsi) (0)
#endif
#if defined(LWS_ROLE_DBUS)
#include "private-lib-roles-dbus.h"
#else
#define lwsi_role_dbus(wsi) (0)
#endif
#if defined(LWS_ROLE_RAW_PROXY)
#include "private-lib-roles-raw-proxy.h"
#else
#define lwsi_role_raw_proxy(wsi) (0)
#endif
#if defined(LWS_ROLE_MQTT)
#include "mqtt/private-lib-roles-mqtt.h"
#else
#define lwsi_role_mqtt(wsi) (0)
#endif
enum {
LWS_HP_RET_BAIL_OK,
LWS_HP_RET_BAIL_DIE,
LWS_HP_RET_USER_SERVICE,
LWS_HP_RET_DROP_POLLOUT,
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
};
#define LWS_CONNECT_COMPLETION_GOOD (-99)
int
lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot);
struct lws *
lws_client_connect_4_established(struct lws *wsi, struct lws *wsi_piggyback,
ssize_t plen);
struct lws *
lws_client_connect_3_connect(struct lws *wsi, const char *ads,
const struct addrinfo *result, int n, void *opaque);

View File

@ -0,0 +1,40 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
list(APPEND SOURCES roles/raw-file/ops-raw-file.c)
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View File

@ -0,0 +1,127 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <private-lib-core.h>
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) {
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_wsi_info(wsi, "failed at set pollfd");
return LWS_HPI_RET_WSI_ALREADY_DIED;
}
n = lws_callback_as_writeable(wsi);
if (n)
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
if (pollfd->revents & LWS_POLLIN) {
if (user_callback_handle_rxflow(wsi->a.protocol->callback,
wsi, LWS_CALLBACK_RAW_RX_FILE,
wsi->user_space, NULL, 0)) {
lwsl_wsi_debug(wsi, "raw rx callback closed it");
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
}
if (pollfd->revents & LWS_POLLHUP)
if (!(pollfd->revents & LWS_POLLIN))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
return LWS_HPI_RET_HANDLED;
}
static int
rops_adoption_bind_raw_file(struct lws *wsi, int type, const char *vh_prot_name)
{
/* no socket or http: it can only be a raw file */
if ((type & LWS_ADOPT_HTTP) || (type & LWS_ADOPT_SOCKET) ||
(type & _LWS_ADOPT_FINISH))
return 0; /* no match */
lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_raw_file);
if (!vh_prot_name) {
if (wsi->a.vhost->default_protocol_index >=
wsi->a.vhost->count_protocols)
return 0;
wsi->a.protocol = &wsi->a.vhost->protocols[
wsi->a.vhost->default_protocol_index];
}
return 1; /* bound */
}
static const lws_rops_t rops_table_raw_file[] = {
/* 1 */ { .handle_POLLIN = rops_handle_POLLIN_raw_file },
/* 2 */ { .adoption_bind = rops_adoption_bind_raw_file },
};
const struct lws_role_ops role_ops_raw_file = {
/* role name */ "raw-file",
/* alpn id */ NULL,
/* rops_table */ rops_table_raw_file,
/* rops_idx */ {
/* LWS_ROPS_check_upgrades */
/* LWS_ROPS_pt_init_destroy */ 0x00,
/* LWS_ROPS_init_vhost */
/* LWS_ROPS_destroy_vhost */ 0x00,
/* LWS_ROPS_service_flag_pending */
/* LWS_ROPS_handle_POLLIN */ 0x01,
/* LWS_ROPS_handle_POLLOUT */
/* LWS_ROPS_perform_user_POLLOUT */ 0x00,
/* LWS_ROPS_callback_on_writable */
/* LWS_ROPS_tx_credit */ 0x00,
/* LWS_ROPS_write_role_protocol */
/* LWS_ROPS_encapsulation_parent */ 0x00,
/* LWS_ROPS_alpn_negotiated */
/* LWS_ROPS_close_via_role_protocol */ 0x00,
/* LWS_ROPS_close_role */
/* LWS_ROPS_close_kill_connection */ 0x00,
/* LWS_ROPS_destroy_role */
/* LWS_ROPS_adoption_bind */ 0x02,
/* LWS_ROPS_client_bind */
/* LWS_ROPS_issue_keepalive */ 0x00,
},
/* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_ADOPT_FILE,
LWS_CALLBACK_RAW_ADOPT_FILE },
/* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_RX_FILE,
LWS_CALLBACK_RAW_RX_FILE },
/* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE_FILE,
LWS_CALLBACK_RAW_WRITEABLE_FILE},
/* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE_FILE,
LWS_CALLBACK_RAW_CLOSE_FILE},
/* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL,
LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL },
/* protocol_unbind cb c, srv */ { LWS_CALLBACK_RAW_FILE_DROP_PROTOCOL,
LWS_CALLBACK_RAW_FILE_DROP_PROTOCOL },
/* file_handle */ 1,
};

View File

@ -0,0 +1,42 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
list(APPEND SOURCES
roles/raw-proxy/ops-raw-proxy.c)
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View File

@ -0,0 +1,246 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <private-lib-core.h>
static int
rops_handle_POLLIN_raw_proxy(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 (lws_has_buffered_out(wsi)) {
if (!(pollfd->revents & LWS_POLLOUT))
return LWS_HPI_RET_HANDLED;
/* drain the output buflist */
if (lws_issue_raw(wsi, NULL, 0) < 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 (lwsi_state(wsi) == LRS_WAITING_CONNECT)
goto try_pollout;
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))) {
ebuf.token = NULL;
ebuf.len = 0;
buffered = lws_buflist_aware_read(pt, wsi, &ebuf, 1, __func__);
if (buffered < 0)
goto fail;
switch (ebuf.len) {
case 0:
lwsl_info("%s: read 0 len\n", __func__);
wsi->seen_zero_length_recv = 1;
if (lws_change_pollfd(wsi, LWS_POLLIN, 0))
goto fail;
/*
* 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->a.protocol->callback,
wsi, lwsi_role_client(wsi) ?
LWS_CALLBACK_RAW_PROXY_CLI_RX :
LWS_CALLBACK_RAW_PROXY_SRV_RX,
wsi->user_space, ebuf.token,
(size_t)ebuf.len);
if (n < 0) {
lwsl_info("LWS_CALLBACK_RAW_PROXY_*_RX fail\n");
goto fail;
}
if (lws_buflist_aware_finished_consuming(wsi, &ebuf, ebuf.len,
buffered, __func__))
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:
if (!(pollfd->revents & LWS_POLLOUT))
return LWS_HPI_RET_HANDLED;
if (lws_handle_POLLOUT_event(wsi, pollfd)) {
lwsl_debug("POLLOUT event closed it\n");
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
#if defined(LWS_WITH_CLIENT)
if (lws_http_client_socket_service(wsi, pollfd))
return LWS_HPI_RET_WSI_ALREADY_DIED;
#endif
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_adoption_bind_raw_proxy(struct lws *wsi, int type,
const char *vh_prot_name)
{
/* no http but socket... must be raw skt */
if ((type & LWS_ADOPT_HTTP) || !(type & LWS_ADOPT_SOCKET) ||
(!(type & LWS_ADOPT_FLAG_RAW_PROXY)) || (type & _LWS_ADOPT_FINISH))
return 0; /* no match */
#if defined(LWS_WITH_UDP)
if (type & LWS_ADOPT_FLAG_UDP)
/*
* these can be >128 bytes, so just alloc for UDP
*/
wsi->udp = lws_malloc(sizeof(*wsi->udp), "udp struct");
#endif
lws_role_transition(wsi, LWSIFR_SERVER, (type & LWS_ADOPT_ALLOW_SSL) ?
LRS_SSL_INIT : LRS_ESTABLISHED,
&role_ops_raw_proxy);
if (vh_prot_name)
lws_bind_protocol(wsi, wsi->a.protocol, __func__);
else
/* this is the only time he will transition */
lws_bind_protocol(wsi,
&wsi->a.vhost->protocols[wsi->a.vhost->raw_protocol_index],
__func__);
return 1; /* bound */
}
static int
rops_client_bind_raw_proxy(struct lws *wsi,
const struct lws_client_connect_info *i)
{
if (!i) {
/* finalize */
if (!wsi->user_space && wsi->stash->cis[CIS_METHOD])
if (lws_ensure_user_space(wsi))
return 1;
return 0;
}
/* we are a fallback if nothing else matched */
if (i->local_protocol_name && !strcmp(i->local_protocol_name, "raw-proxy"))
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
&role_ops_raw_proxy);
return 0;
}
static int
rops_handle_POLLOUT_raw_proxy(struct lws *wsi)
{
if (lwsi_state(wsi) == LRS_ESTABLISHED)
return LWS_HP_RET_USER_SERVICE;
if (lwsi_role_client(wsi))
return LWS_HP_RET_USER_SERVICE;
return LWS_HP_RET_BAIL_OK;
}
static const lws_rops_t rops_table_raw_proxy[] = {
/* 1 */ { .handle_POLLIN = rops_handle_POLLIN_raw_proxy },
/* 2 */ { .handle_POLLOUT = rops_handle_POLLOUT_raw_proxy },
/* 3 */ { .adoption_bind = rops_adoption_bind_raw_proxy },
/* 4 */ { .client_bind = rops_client_bind_raw_proxy },
};
const struct lws_role_ops role_ops_raw_proxy = {
/* role name */ "raw-proxy",
/* alpn id */ NULL,
/* rops_table */ rops_table_raw_proxy,
/* rops_idx */ {
/* LWS_ROPS_check_upgrades */
/* LWS_ROPS_pt_init_destroy */ 0x00,
/* LWS_ROPS_init_vhost */
/* LWS_ROPS_destroy_vhost */ 0x00,
/* LWS_ROPS_service_flag_pending */
/* LWS_ROPS_handle_POLLIN */ 0x01,
/* LWS_ROPS_handle_POLLOUT */
/* LWS_ROPS_perform_user_POLLOUT */ 0x20,
/* LWS_ROPS_callback_on_writable */
/* LWS_ROPS_tx_credit */ 0x00,
/* LWS_ROPS_write_role_protocol */
/* LWS_ROPS_encapsulation_parent */ 0x00,
/* LWS_ROPS_alpn_negotiated */
/* LWS_ROPS_close_via_role_protocol */ 0x00,
/* LWS_ROPS_close_role */
/* LWS_ROPS_close_kill_connection */ 0x00,
/* LWS_ROPS_destroy_role */
/* LWS_ROPS_adoption_bind */ 0x03,
/* LWS_ROPS_client_bind */
/* LWS_ROPS_issue_keepalive */ 0x40,
},
/* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_ADOPT,
LWS_CALLBACK_RAW_PROXY_SRV_ADOPT },
/* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_RX,
LWS_CALLBACK_RAW_PROXY_SRV_RX },
/* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE,
LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE, },
/* close cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_CLOSE,
LWS_CALLBACK_RAW_PROXY_SRV_CLOSE },
/* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_BIND_PROTOCOL,
LWS_CALLBACK_RAW_PROXY_SRV_BIND_PROTOCOL },
/* protocol_unbind cb c, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_DROP_PROTOCOL,
LWS_CALLBACK_RAW_PROXY_SRV_DROP_PROTOCOL },
/* file_handle */ 0,
};

View File

@ -0,0 +1,44 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is included from private-lib-core.h if LWS_ROLE_RAW_PROXY
*/
extern const struct lws_role_ops role_ops_raw_proxy;
#define lwsi_role_raw_proxy(wsi) (wsi->role_ops == &role_ops_raw_proxy)
#if 0
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;
};
struct _lws_raw_proxy_related {
struct lws *wsi_onward;
};
#endif

View File

@ -0,0 +1,46 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
list(APPEND SOURCES
roles/raw-skt/ops-raw-skt.c)
if (LWS_WITH_ABSTRACT)
list(APPEND SOURCES
abstract/transports/raw-skt.c)
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View File

@ -0,0 +1,404 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <private-lib-core.h>
#if defined(LWS_WITH_CLIENT)
static int
lws_raw_skt_connect(struct lws *wsi)
{
int n;
#if defined(LWS_WITH_TLS)
const char *cce = NULL;
char ccebuf[128];
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
switch (lws_client_create_tls(wsi, &cce, 1)) {
#else
switch (lws_client_create_tls(wsi, &cce, 0)) {
#endif
case CCTLS_RETURN_ERROR:
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
return -1;
case CCTLS_RETURN_RETRY:
return 0;
case CCTLS_RETURN_DONE:
break;
}
if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
n = lws_ssl_client_connect2(wsi, ccebuf, sizeof(ccebuf));
if (n < 0) {
lws_inform_client_conn_fail(wsi, (void *)ccebuf,
strlen(ccebuf));
return -1;
}
if (n != 1)
return 0; /* wait */
}
#endif
if (!wsi->hdr_parsing_completed) {
n = user_callback_handle_rxflow(wsi->a.protocol->callback,
wsi, wsi->role_ops->adoption_cb[lwsi_role_server(wsi)],
wsi->user_space, NULL, 0);
if (n) {
lws_inform_client_conn_fail(wsi, (void *)"user", 4);
return 1;
}
}
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
lwsi_set_state(wsi, LRS_ESTABLISHED);
return 1; /* success */
}
#endif
static int
rops_handle_POLLIN_raw_skt(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
#if defined(LWS_WITH_SOCKS5)
const char *cce = NULL;
#endif
struct lws_tokens ebuf;
int n = 0, buffered = 0;
/* pending truncated sends have uber priority */
if (lws_has_buffered_out(wsi)) {
if (!(pollfd->revents & LWS_POLLOUT))
return LWS_HPI_RET_HANDLED;
/* drain the output buflist */
if (lws_issue_raw(wsi, NULL, 0) < 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 defined(LWS_WITH_SERVER)
if (!lwsi_role_client(wsi) && lwsi_state(wsi) != LRS_ESTABLISHED) {
lwsl_wsi_debug(wsi, "wsistate 0x%x\n", (int)wsi->wsistate);
if (lwsi_state(wsi) != LRS_SSL_INIT)
if (lws_server_socket_service_ssl(wsi,
LWS_SOCK_INVALID,
!!(pollfd->revents & pollfd->events & LWS_POLLIN)))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
return LWS_HPI_RET_HANDLED;
}
#endif
if ((pollfd->revents & pollfd->events & LWS_POLLIN) &&
!(wsi->favoured_pollin &&
(pollfd->revents & pollfd->events & LWS_POLLOUT))) {
lwsl_wsi_debug(wsi, "POLLIN: state 0x%x", lwsi_state(wsi));
switch (lwsi_state(wsi)) {
/* any tunnel has to have been established... */
case LRS_SSL_ACK_PENDING:
goto nope;
/* we are actually connected */
case LRS_WAITING_CONNECT:
goto nope;
case LRS_WAITING_SSL:
#if defined(LWS_WITH_CLIENT)
n = lws_raw_skt_connect(wsi);
if (n < 0)
goto fail;
#endif
break;
#if defined(LWS_WITH_SOCKS5)
/* SOCKS Greeting Reply */
case LRS_WAITING_SOCKS_GREETING_REPLY:
case LRS_WAITING_SOCKS_AUTH_REPLY:
case LRS_WAITING_SOCKS_CONNECT_REPLY:
switch (lws_socks5c_handle_state(wsi, pollfd, &cce)) {
case LW5CHS_RET_RET0:
goto nope;
case LW5CHS_RET_BAIL3:
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
goto fail;
case LW5CHS_RET_STARTHS:
lwsi_set_state(wsi, LRS_ESTABLISHED);
lws_client_connect_4_established(wsi, NULL, 0);
/*
* Now we got the socks5 connection, we need to
* go down the tls path on it now if that's what
* we want
*/
goto post_rx;
default:
break;
}
goto post_rx;
#endif
default:
ebuf.token = NULL;
ebuf.len = (int) wsi->a.protocol->rx_buffer_size;
buffered = lws_buflist_aware_read(pt, wsi, &ebuf, 1, __func__);
switch (ebuf.len) {
case 0:
if (wsi->unix_skt)
break;
lwsl_wsi_info(wsi, "read 0 len");
wsi->seen_zero_length_recv = 1;
if (lws_change_pollfd(wsi, LWS_POLLIN, 0))
goto fail;
/*
* 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;
}
#if defined(LWS_WITH_UDP)
if (lws_fi(&wsi->fic, "udp_rx_loss")) {
n = ebuf.len;
goto post_rx;
}
#endif
n = user_callback_handle_rxflow(wsi->a.protocol->callback,
wsi, LWS_CALLBACK_RAW_RX,
wsi->user_space, ebuf.token,
(unsigned int)ebuf.len);
#if defined(LWS_WITH_UDP) || defined(LWS_WITH_SOCKS5)
post_rx:
#endif
if (n < 0) {
lwsl_wsi_info(wsi, "LWS_CALLBACK_RAW_RX_fail");
goto fail;
}
if (lws_buflist_aware_finished_consuming(wsi, &ebuf, ebuf.len,
buffered, __func__))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
goto try_pollout;
}
}
nope:
if (wsi->favoured_pollin &&
(pollfd->revents & pollfd->events & LWS_POLLOUT))
/* we balanced the last favouring of pollin */
wsi->favoured_pollin = 0;
try_pollout:
if (!(pollfd->revents & LWS_POLLOUT))
return LWS_HPI_RET_HANDLED;
#if defined(LWS_WITH_CLIENT)
if (lwsi_state(wsi) == LRS_WAITING_CONNECT) {
if (!lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL))
return LWS_HPI_RET_WSI_ALREADY_DIED;
if (lws_raw_skt_connect(wsi) < 0)
goto fail;
}
#endif
if (lwsi_state(wsi) == LRS_WAITING_SSL)
return LWS_HPI_RET_HANDLED;
/* one shot */
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
goto fail;
/* clear back-to-back write detection */
wsi->could_have_pending = 0;
n = user_callback_handle_rxflow(wsi->a.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;
}
#if defined(LWS_WITH_SERVER)
static int
rops_adoption_bind_raw_skt(struct lws *wsi, int type, const char *vh_prot_name)
{
// lwsl_notice("%s: bind type %d\n", __func__, type);
/* no http but socket... must be raw skt */
if ((type & LWS_ADOPT_HTTP) || !(type & LWS_ADOPT_SOCKET) ||
((type & _LWS_ADOPT_FINISH) && (!(type & LWS_ADOPT_FLAG_UDP))))
return 0; /* no match */
#if defined(LWS_WITH_UDP)
if ((type & LWS_ADOPT_FLAG_UDP) && !wsi->udp) {
/*
* these can be >128 bytes, so just alloc for UDP
*/
wsi->udp = lws_malloc(sizeof(*wsi->udp), "udp struct");
if (!wsi->udp)
return 0;
memset(wsi->udp, 0, sizeof(*wsi->udp));
}
#endif
lws_role_transition(wsi, 0, (type & LWS_ADOPT_ALLOW_SSL) ? LRS_SSL_INIT :
LRS_ESTABLISHED, &role_ops_raw_skt);
if (vh_prot_name)
lws_bind_protocol(wsi, wsi->a.protocol, __func__);
else
/* this is the only time he will transition */
lws_bind_protocol(wsi,
&wsi->a.vhost->protocols[wsi->a.vhost->raw_protocol_index],
__func__);
return 1; /* bound */
}
#endif
#if defined(LWS_WITH_CLIENT)
static int
rops_client_bind_raw_skt(struct lws *wsi,
const struct lws_client_connect_info *i)
{
if (!i) {
/* finalize */
if (!wsi->user_space && wsi->stash->cis[CIS_METHOD])
if (lws_ensure_user_space(wsi))
return 1;
return 0;
}
/* we are a fallback if nothing else matched */
if (!i->local_protocol_name ||
strcmp(i->local_protocol_name, "raw-proxy"))
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
&role_ops_raw_skt);
return 1; /* matched */
}
#endif
static const lws_rops_t rops_table_raw_skt[] = {
/* 1 */ { .handle_POLLIN = rops_handle_POLLIN_raw_skt },
#if defined(LWS_WITH_SERVER)
/* 2 */ { .adoption_bind = rops_adoption_bind_raw_skt },
#else
/* 2 */ { .adoption_bind = NULL },
#endif
#if defined(LWS_WITH_CLIENT)
/* 3 */ { .client_bind = rops_client_bind_raw_skt },
#endif
};
const struct lws_role_ops role_ops_raw_skt = {
/* role name */ "raw-skt",
/* alpn id */ NULL,
/* rops_table */ rops_table_raw_skt,
/* rops_idx */ {
/* LWS_ROPS_check_upgrades */
/* LWS_ROPS_pt_init_destroy */ 0x00,
/* LWS_ROPS_init_vhost */
/* LWS_ROPS_destroy_vhost */ 0x00,
/* LWS_ROPS_service_flag_pending */
/* LWS_ROPS_handle_POLLIN */ 0x01,
/* LWS_ROPS_handle_POLLOUT */
/* LWS_ROPS_perform_user_POLLOUT */ 0x00,
/* LWS_ROPS_callback_on_writable */
/* LWS_ROPS_tx_credit */ 0x00,
/* LWS_ROPS_write_role_protocol */
/* LWS_ROPS_encapsulation_parent */ 0x00,
/* LWS_ROPS_alpn_negotiated */
/* LWS_ROPS_close_via_role_protocol */ 0x00,
/* LWS_ROPS_close_role */
/* LWS_ROPS_close_kill_connection */ 0x00,
/* LWS_ROPS_destroy_role */
#if defined(LWS_WITH_SERVER)
/* LWS_ROPS_adoption_bind */ 0x02,
#else
/* LWS_ROPS_adoption_bind */ 0x00,
#endif
#if defined(LWS_WITH_CLIENT)
/* LWS_ROPS_client_bind */
/* LWS_ROPS_issue_keepalive */ 0x30,
#else
/* LWS_ROPS_client_bind */
/* LWS_ROPS_issue_keepalive */ 0x00,
#endif
},
/* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_CONNECTED,
LWS_CALLBACK_RAW_ADOPT },
/* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_RX,
LWS_CALLBACK_RAW_RX },
/* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE,
LWS_CALLBACK_RAW_WRITEABLE},
/* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE,
LWS_CALLBACK_RAW_CLOSE },
/* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL,
LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL },
/* protocol_unbind cb c, srv */ { LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL,
LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL },
/* file_handle */ 0,
};

View File

@ -0,0 +1,61 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
list(APPEND SOURCES
roles/ws/ops-ws.c)
if (NOT LWS_WITHOUT_CLIENT)
list(APPEND SOURCES
roles/ws/client-ws.c
roles/ws/client-parser-ws.c)
endif()
if (NOT LWS_WITHOUT_SERVER)
list(APPEND SOURCES
roles/ws/server-ws.c)
endif()
if (NOT LWS_WITHOUT_EXTENSIONS)
list(APPEND HDR_PRIVATE
roles/ws/ext/extension-permessage-deflate.h)
list(APPEND SOURCES
roles/ws/ext/extension.c
roles/ws/ext/extension-permessage-deflate.c)
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View File

@ -0,0 +1,696 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
/*
* parsers.c: lws_ws_rx_sm() needs to be roughly kept in
* sync with changes here, esp related to ext draining
*/
int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
{
int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
struct lws_ext_pm_deflate_rx_ebufs pmdrx;
unsigned short close_code;
unsigned char *pp;
int handled, m, n;
#if !defined(LWS_WITHOUT_EXTENSIONS)
int rx_draining_ext = 0;
#endif
pmdrx.eb_in.token = NULL;
pmdrx.eb_in.len = 0;
pmdrx.eb_out.token = NULL;
pmdrx.eb_out.len = 0;
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
assert(!c);
lws_remove_wsi_from_draining_ext_list(wsi);
rx_draining_ext = 1;
lwsl_wsi_debug(wsi, "doing draining flow");
goto drain_extension;
}
#endif
switch (wsi->lws_rx_parse_state) {
case LWS_RXPS_NEW:
/* control frames (PING) may interrupt checkable sequences */
wsi->ws->defeat_check_utf8 = 0;
switch (wsi->ws->ietf_spec_revision) {
case 13:
wsi->ws->opcode = c & 0xf;
/* revisit if an extension wants them... */
switch (wsi->ws->opcode) {
case LWSWSOPC_TEXT_FRAME:
wsi->ws->rsv_first_msg = (c & 0x70);
#if !defined(LWS_WITHOUT_EXTENSIONS)
/*
* set the expectation that we will have to
* fake up the zlib trailer to the inflator for
* this frame
*/
wsi->ws->pmd_trailer_application = !!(c & 0x40);
#endif
wsi->ws->continuation_possible = 1;
wsi->ws->check_utf8 = lws_check_opt(
wsi->a.context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8);
wsi->ws->utf8 = 0;
wsi->ws->first_fragment = 1;
break;
case LWSWSOPC_BINARY_FRAME:
wsi->ws->rsv_first_msg = (c & 0x70);
#if !defined(LWS_WITHOUT_EXTENSIONS)
/*
* set the expectation that we will have to
* fake up the zlib trailer to the inflator for
* this frame
*/
wsi->ws->pmd_trailer_application = !!(c & 0x40);
#endif
wsi->ws->check_utf8 = 0;
wsi->ws->continuation_possible = 1;
wsi->ws->first_fragment = 1;
break;
case LWSWSOPC_CONTINUATION:
if (!wsi->ws->continuation_possible) {
lwsl_wsi_info(wsi, "disordered continuation");
return -1;
}
wsi->ws->first_fragment = 0;
break;
case LWSWSOPC_CLOSE:
wsi->ws->check_utf8 = 0;
wsi->ws->utf8 = 0;
break;
case 3:
case 4:
case 5:
case 6:
case 7:
case 0xb:
case 0xc:
case 0xd:
case 0xe:
case 0xf:
if (wsi->ws->allow_unknown_opcode)
break;
lwsl_wsi_info(wsi, "illegal opcode");
return -1;
default:
wsi->ws->defeat_check_utf8 = 1;
break;
}
wsi->ws->rsv = (c & 0x70);
/* revisit if an extension wants them... */
if (
#if !defined(LWS_WITHOUT_EXTENSIONS)
!wsi->ws->count_act_ext &&
#endif
wsi->ws->rsv && !wsi->ws->allow_reserved_bits) {
lwsl_wsi_info(wsi, "illegal rsv bits set");
return -1;
}
wsi->ws->final = !!((c >> 7) & 1);
lwsl_wsi_ext(wsi, " This RX frame Final %d",
wsi->ws->final);
if (wsi->ws->owed_a_fin &&
(wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
lwsl_wsi_info(wsi, "hey you owed us a FIN");
return -1;
}
if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) {
wsi->ws->continuation_possible = 0;
wsi->ws->owed_a_fin = 0;
}
if ((wsi->ws->opcode & 8) && !wsi->ws->final) {
lwsl_wsi_info(wsi, "control msg can't be fragmented");
return -1;
}
if (!wsi->ws->final)
wsi->ws->owed_a_fin = 1;
switch (wsi->ws->opcode) {
case LWSWSOPC_TEXT_FRAME:
case LWSWSOPC_BINARY_FRAME:
wsi->ws->frame_is_binary = wsi->ws->opcode ==
LWSWSOPC_BINARY_FRAME;
break;
}
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
break;
default:
lwsl_wsi_err(wsi, "unknown spec version %02d",
wsi->ws->ietf_spec_revision);
break;
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN:
wsi->ws->this_frame_masked = !!(c & 0x80);
if (wsi->ws->this_frame_masked)
goto server_cannot_mask;
switch (c & 0x7f) {
case 126:
/* control frames are not allowed to have big lengths */
if (wsi->ws->opcode & 8)
goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
break;
case 127:
/* control frames are not allowed to have big lengths */
if (wsi->ws->opcode & 8)
goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
break;
default:
wsi->ws->rx_packet_length = c & 0x7f;
if (wsi->ws->this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
if (wsi->ws->rx_packet_length) {
wsi->lws_rx_parse_state =
LWS_RXPS_WS_FRAME_PAYLOAD;
} else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
}
break;
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_2:
wsi->ws->rx_packet_length = (size_t)((unsigned int)c << 8);
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_1:
wsi->ws->rx_packet_length |= c;
if (wsi->ws->this_frame_masked)
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
if (wsi->ws->rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_WS_FRAME_PAYLOAD;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_8:
if (c & 0x80) {
lwsl_wsi_warn(wsi, "b63 of length must be zero");
/* kill the connection */
return -1;
}
#if defined __LP64__
wsi->ws->rx_packet_length = ((size_t)c) << 56;
#else
wsi->ws->rx_packet_length = 0;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_7:
#if defined __LP64__
wsi->ws->rx_packet_length |= ((size_t)c) << 48;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_6:
#if defined __LP64__
wsi->ws->rx_packet_length |= ((size_t)c) << 40;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_5:
#if defined __LP64__
wsi->ws->rx_packet_length |= ((size_t)c) << 32;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_4:
wsi->ws->rx_packet_length |= ((size_t)c) << 24;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_3:
wsi->ws->rx_packet_length |= ((size_t)c) << 16;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_2:
wsi->ws->rx_packet_length |= ((size_t)c) << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_1:
wsi->ws->rx_packet_length |= (size_t)c;
if (wsi->ws->this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
if (wsi->ws->rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_WS_FRAME_PAYLOAD;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
}
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
wsi->ws->mask[0] = c;
if (c)
wsi->ws->all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
wsi->ws->mask[1] = c;
if (c)
wsi->ws->all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
wsi->ws->mask[2] = c;
if (c)
wsi->ws->all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
wsi->ws->mask[3] = c;
if (c)
wsi->ws->all_zero_nonce = 0;
if (wsi->ws->rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_WS_FRAME_PAYLOAD;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
break;
case LWS_RXPS_WS_FRAME_PAYLOAD:
assert(wsi->ws->rx_ubuf);
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext)
goto drain_extension;
#endif
if (wsi->ws->this_frame_masked && !wsi->ws->all_zero_nonce)
c ^= wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
/*
* unmask and collect the payload body in
* rx_ubuf_head + LWS_PRE
*/
wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] = c;
if (--wsi->ws->rx_packet_length == 0) {
/* spill because we have the whole frame */
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
lwsl_wsi_debug(wsi, "spilling as we have the whole frame");
goto spill;
}
/*
* if there's no protocol max frame size given, we are
* supposed to default to context->pt_serv_buf_size
*/
if (!wsi->a.protocol->rx_buffer_size &&
wsi->ws->rx_ubuf_head != wsi->a.context->pt_serv_buf_size)
break;
if (wsi->a.protocol->rx_buffer_size &&
wsi->ws->rx_ubuf_head != wsi->a.protocol->rx_buffer_size)
break;
/* spill because we filled our rx buffer */
lwsl_wsi_debug(wsi, "spilling as we filled our rx buffer");
spill:
handled = 0;
/*
* is this frame a control packet we should take care of at this
* layer? If so service it and hide it from the user callback
*/
switch (wsi->ws->opcode) {
case LWSWSOPC_CLOSE:
pp = &wsi->ws->rx_ubuf[LWS_PRE];
if (lws_check_opt(wsi->a.context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8) &&
wsi->ws->rx_ubuf_head > 2 &&
lws_check_utf8(&wsi->ws->utf8, pp + 2,
wsi->ws->rx_ubuf_head - 2))
goto utf8_fail;
/* is this an acknowledgment of our close? */
if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
/*
* fine he has told us he is closing too, let's
* finish our close
*/
lwsl_wsi_parser(wsi, "seen server's close ack");
return -1;
}
lwsl_wsi_parser(wsi, "client sees server close len = %d",
(int)wsi->ws->rx_ubuf_head);
if (wsi->ws->rx_ubuf_head >= 2) {
close_code = (unsigned short)((pp[0] << 8) | pp[1]);
if (close_code < 1000 ||
close_code == 1004 ||
close_code == 1005 ||
close_code == 1006 ||
(close_code >= 1016 && close_code < 3000)
) {
pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff;
pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff;
}
}
if (user_callback_handle_rxflow(
wsi->a.protocol->callback, wsi,
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
wsi->user_space, pp,
wsi->ws->rx_ubuf_head))
return -1;
memcpy(wsi->ws->ping_payload_buf + LWS_PRE, pp,
wsi->ws->rx_ubuf_head);
wsi->ws->close_in_ping_buffer_len =
(uint8_t)wsi->ws->rx_ubuf_head;
lwsl_wsi_info(wsi, "scheduling return close as ack");
__lws_change_pollfd(wsi, LWS_POLLIN, 0);
lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 3);
wsi->waiting_to_send_close_frame = 1;
wsi->close_needs_ack = 0;
lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
lws_callback_on_writable(wsi);
handled = 1;
break;
case LWSWSOPC_PING:
lwsl_wsi_info(wsi, "received %d byte ping, sending pong",
(int)wsi->ws->rx_ubuf_head);
/* he set a close reason on this guy, ignore PING */
if (wsi->ws->close_in_ping_buffer_len)
goto ping_drop;
if (wsi->ws->pong_pending_flag) {
/*
* there is already a pending pong payload
* we should just log and drop
*/
lwsl_wsi_parser(wsi, "DROP PING since one pending");
goto ping_drop;
}
/* control packets can only be < 128 bytes long */
if (wsi->ws->rx_ubuf_head > 128 - 3) {
lwsl_wsi_parser(wsi, "DROP PING payload too large");
goto ping_drop;
}
/* stash the pong payload */
memcpy(wsi->ws->pong_payload_buf + LWS_PRE,
&wsi->ws->rx_ubuf[LWS_PRE],
wsi->ws->rx_ubuf_head);
wsi->ws->pong_payload_len = (uint8_t)wsi->ws->rx_ubuf_head;
wsi->ws->pong_pending_flag = 1;
/* get it sent as soon as possible */
lws_callback_on_writable(wsi);
ping_drop:
wsi->ws->rx_ubuf_head = 0;
handled = 1;
break;
case LWSWSOPC_PONG:
lwsl_wsi_info(wsi, "Received pong");
lwsl_hexdump_wsi_debug(wsi, &wsi->ws->rx_ubuf[LWS_PRE],
wsi->ws->rx_ubuf_head);
lws_validity_confirmed(wsi);
/* issue it */
callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
break;
case LWSWSOPC_CONTINUATION:
case LWSWSOPC_TEXT_FRAME:
case LWSWSOPC_BINARY_FRAME:
break;
default:
/* not handled or failed */
lwsl_wsi_ext(wsi, "Unhandled ext opc 0x%x", wsi->ws->opcode);
wsi->ws->rx_ubuf_head = 0;
return -1;
}
/*
* No it's real payload, pass it up to the user callback.
*
* We have been statefully collecting it in the
* LWS_RXPS_WS_FRAME_PAYLOAD clause above.
*
* It's nicely buffered with the pre-padding taken care of
* so it can be sent straight out again using lws_write.
*
* However, now we have a chunk of it, we want to deal with it
* all here. Since this may be input to permessage-deflate and
* there are block limits on that for input and output, we may
* need to iterate.
*/
if (handled)
goto already_done;
pmdrx.eb_in.token = &wsi->ws->rx_ubuf[LWS_PRE];
pmdrx.eb_in.len = (int)wsi->ws->rx_ubuf_head;
/* for the non-pm-deflate case */
pmdrx.eb_out = pmdrx.eb_in;
lwsl_wsi_debug(wsi, "starting disbursal of %d deframed rx",
(int)wsi->ws->rx_ubuf_head);
#if !defined(LWS_WITHOUT_EXTENSIONS)
drain_extension:
#endif
do {
// lwsl_wsi_notice("pmdrx.eb_in.len: %d",
// (int)pmdrx.eb_in.len);
n = PMDR_DID_NOTHING;
#if !defined(LWS_WITHOUT_EXTENSIONS)
lwsl_wsi_ext(wsi, "+++ passing %d %p to ext",
pmdrx.eb_in.len, pmdrx.eb_in.token);
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX,
&pmdrx, 0);
lwsl_wsi_ext(wsi, "Ext RX returned %d", n);
if (n < 0) {
wsi->socket_is_permanently_unusable = 1;
return -1;
}
if (n == PMDR_DID_NOTHING)
/* ie, not PMDR_NOTHING_WE_SHOULD_DO */
break;
#endif
lwsl_wsi_ext(wsi, "post inflate ebuf in len %d / out len %d",
pmdrx.eb_in.len, pmdrx.eb_out.len);
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (rx_draining_ext && !pmdrx.eb_out.len) {
lwsl_wsi_debug(wsi, " --- ending drain on 0 read result");
goto already_done;
}
if (n == PMDR_HAS_PENDING) { /* 1 means stuff to drain */
/* extension had more... main loop will come back */
lwsl_wsi_ext(wsi, "adding to draining ext list");
lws_add_wsi_to_draining_ext_list(wsi);
} else {
lwsl_wsi_ext(wsi, "removing from draining ext list");
lws_remove_wsi_from_draining_ext_list(wsi);
}
rx_draining_ext = wsi->ws->rx_draining_ext;
#endif
if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
if (lws_check_utf8(&wsi->ws->utf8,
pmdrx.eb_out.token,
(unsigned int)pmdrx.eb_out.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
#if !defined(LWS_WITHOUT_EXTENSIONS)
/* if ext not negotiated, going to be UNKNOWN */
&& (n == PMDR_EMPTY_FINAL || n == PMDR_UNKNOWN)
#endif
) {
lwsl_wsi_info(wsi, "FINAL utf8 error");
lws_close_reason(wsi,
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
(uint8_t *)"partial utf8", 12);
utf8_fail:
lwsl_wsi_info(wsi, "utf8 error");
lwsl_hexdump_wsi_info(wsi, pmdrx.eb_out.token,
(unsigned int)pmdrx.eb_out.len);
return -1;
}
}
if (pmdrx.eb_out.len < 0 &&
callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
goto already_done;
if (!pmdrx.eb_out.token)
goto already_done;
pmdrx.eb_out.token[pmdrx.eb_out.len] = '\0';
if (!wsi->a.protocol->callback)
goto already_done;
if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
lwsl_wsi_info(wsi, "Client doing pong callback");
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (n == PMDR_HAS_PENDING)
/* extension had more... main loop will come back
* we want callback to be done with this set, if so,
* because lws_is_final() hides it was final until the
* last chunk
*/
lws_add_wsi_to_draining_ext_list(wsi);
else
lws_remove_wsi_from_draining_ext_list(wsi);
#endif
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
goto already_done;
/* if pmd not enabled, in == out */
if (n == PMDR_DID_NOTHING
#if !defined(LWS_WITHOUT_EXTENSIONS)
|| n == PMDR_NOTHING_WE_SHOULD_DO
|| n == PMDR_UNKNOWN
#endif
)
pmdrx.eb_in.len -= pmdrx.eb_out.len;
m = wsi->a.protocol->callback(wsi,
(enum lws_callback_reasons)callback_action,
wsi->user_space, pmdrx.eb_out.token,
(unsigned int)pmdrx.eb_out.len);
wsi->ws->first_fragment = 0;
lwsl_wsi_debug(wsi, "bulk ws rx: inp used %d, output %d",
(int)wsi->ws->rx_ubuf_head,
(int)pmdrx.eb_out.len);
/* if user code wants to close, let caller know */
if (m)
return 1;
} while (pmdrx.eb_in.len
#if !defined(LWS_WITHOUT_EXTENSIONS)
|| rx_draining_ext
#endif
);
already_done:
wsi->ws->rx_ubuf_head = 0;
break;
default:
lwsl_wsi_err(wsi, "client rx illegal state");
return 1;
}
return 0;
illegal_ctl_length:
lwsl_wsi_warn(wsi, "Control frame asking for extended length is illegal");
/* kill the connection */
return -1;
server_cannot_mask:
lws_close_reason(wsi,
LWS_CLOSE_STATUS_PROTOCOL_ERR,
(uint8_t *)"srv mask", 8);
lwsl_wsi_warn(wsi, "Server must not mask");
/* kill the connection */
return -1;
}

View File

@ -0,0 +1,674 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <private-lib-core.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 = (char)tolower((int)*s);
#endif
s++;
}
}
int
lws_create_client_ws_object(const 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_wsi_notice(wsi, "OOM");
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 = (uint8_t)v;
if (i->allow_reserved_bits)
wsi->ws->allow_reserved_bits = 1;
if (i->allow_unknown_opcode)
wsi->ws->allow_unknown_opcode = 1;
return 0;
}
#if defined(LWS_WITH_CLIENT)
int
lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
{
unsigned char *bufin = *buf;
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_wsi_debug(wsi, "hs client feels it has %d in", (int)len);
while (len) {
/*
* we were accepting input but now we stopped doing so
*/
if (lws_is_flowcontrolled(wsi)) {
lwsl_wsi_debug(wsi, "caching %ld", (long)len);
/*
* Since we cached the remaining available input, we
* can say we "consumed" it.
*
* But what about the case where the available input
* came out of the rxflow cache already? If we are
* effectively "putting it back in the cache", we have
* to place it at the cache head, not the tail as usual.
*/
if (lws_rxflow_cache(wsi, *buf, 0, len) ==
LWSRXFC_TRIMMED) {
/*
* we dealt with it by trimming the existing
* rxflow cache HEAD to account for what we used.
*
* indicate we didn't use anything to the caller
* so he doesn't do any consumed processing
*/
lwsl_wsi_info(wsi, "trimming inside rxflow cache");
*buf = bufin;
} else
*buf += len;
return 0;
}
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
int m;
lwsl_wsi_info(wsi, "draining ext");
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 by studying what
* happened to *buf
*/
if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
lwsl_wsi_info(wsi, "client_rx_sm exited, DROPPING %d",
(int)len);
return -1;
}
len--;
}
// lwsl_wsi_notice(wsi, "finished with %ld", (long)len);
return 0;
}
#endif
char *
lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1)
{
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
*/
if (lws_get_random(wsi->a.context, hash, 16) != 16) {
lwsl_wsi_err(wsi, "Unable to read from random dev %s",
SYSTEM_RANDOM_FILEPATH);
return NULL;
}
/* coverity[tainted_scalar] */
lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
p += sprintf(p, "Upgrade: websocket\x0d\x0a"
"Connection: %sUpgrade\x0d\x0a"
"Sec-WebSocket-Key: ", conn1);
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->a.vhost->ws.extensions;
while (ext && ext->callback) {
n = wsi->a.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, (unsigned int)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)
{
struct lws_context *context = wsi->a.context;
struct lws_tokenize ts;
int n, len, okay = 0;
lws_tokenize_elem e;
char *p, buf[64];
const char *pc;
#if !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_context_per_thread *pt = &wsi->a.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;
int more = 1;
char ignore;
#endif
if (wsi->client_mux_substream) {/* !!! client ws-over-h2 not there yet */
lwsl_wsi_warn(wsi, "client ws-over-h2 upgrade not supported yet");
*cce = "HS: h2 / ws upgrade unsupported";
goto bail3;
}
if (wsi->http.ah->http_response == 401) {
lwsl_wsi_warn(wsi, "got bad HTTP response '%ld'",
(long)wsi->http.ah->http_response);
*cce = "HS: ws upgrade unauthorized";
goto bail3;
}
if (wsi->http.ah->http_response != 101) {
lwsl_wsi_warn(wsi, "got bad HTTP response '%ld'",
(long)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_wsi_info(wsi, "no ACCEPT");
*cce = "HS: ACCEPT missing";
goto bail3;
}
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
if (!p) {
lwsl_wsi_info(wsi, "no UPGRADE");
*cce = "HS: UPGRADE missing";
goto bail3;
}
strtolower(p);
if (strcmp(p, "websocket")) {
lwsl_wsi_warn(wsi, "got bad Upgrade header '%s'", p);
*cce = "HS: Upgrade to something other than websocket";
goto bail3;
}
/* connection: must have "upgrade" */
lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST |
LWS_TOKENIZE_F_MINUS_NONTERM);
n = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_CONNECTION);
if (n <= 0) /* won't fit, or absent */
goto bad_conn_format;
ts.len = (unsigned int)n;
do {
e = lws_tokenize(&ts);
switch (e) {
case LWS_TOKZE_TOKEN:
if (!strncasecmp(ts.token, "upgrade", ts.token_len))
e = LWS_TOKZE_ENDED;
break;
case LWS_TOKZE_DELIMITER:
break;
default: /* includes ENDED found by the tokenizer itself */
bad_conn_format:
lwsl_wsi_info(wsi, "malformed connection '%s'", buf);
*cce = "HS: UPGRADE malformed";
goto bail3;
}
} while (e > 0);
pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
#if defined(_DEBUG)
if (!pc)
lwsl_wsi_parser(wsi, "lws_client_int_s_hs: no protocol list");
else
lwsl_wsi_parser(wsi, "lws_client_int_s_hs: protocol list '%s'", pc);
#endif
/*
* 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_wsi_info(wsi, "WSI_TOKEN_PROTOCOL is null");
/*
* no protocol name to work from, if we don't already have one
* default to first protocol
*/
if (wsi->a.protocol) {
p = (char *)wsi->a.protocol->name;
goto identify_protocol;
}
/* no choice but to use the default protocol */
n = 0;
wsi->a.protocol = &wsi->a.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, (unsigned int)len) &&
(pc[len] == ',' || pc[len] == '\0')) {
okay = 1;
continue;
}
while (*pc && *pc++ != ',')
;
while (*pc == ' ')
pc++;
}
if (!okay) {
lwsl_wsi_info(wsi, "got bad protocol %s", p);
*cce = "HS: PROTOCOL malformed";
goto bail2;
}
identify_protocol:
#if defined(LWS_WITH_HTTP_PROXY)
lws_strncpy(wsi->ws->actual_protocol, p,
sizeof(wsi->ws->actual_protocol));
#endif
/*
* identify the selected protocol struct and set it
*/
n = 0;
/* keep client connection pre-bound protocol */
if (!lwsi_role_client(wsi))
wsi->a.protocol = NULL;
while (n < wsi->a.vhost->count_protocols) {
if (!wsi->a.protocol &&
strcmp(p, wsi->a.vhost->protocols[n].name) == 0) {
wsi->a.protocol = &wsi->a.vhost->protocols[n];
break;
}
n++;
}
if (n == wsi->a.vhost->count_protocols) { /* no match */
/* if server, that's already fatal */
if (!lwsi_role_client(wsi)) {
lwsl_wsi_info(wsi, "fail protocol %s", p);
*cce = "HS: Cannot match protocol";
goto bail2;
}
/* for client, find the index of our pre-bound protocol */
n = 0;
while (wsi->a.vhost->protocols[n].callback) {
if (wsi->a.protocol && strcmp(wsi->a.protocol->name,
wsi->a.vhost->protocols[n].name) == 0) {
wsi->a.protocol = &wsi->a.vhost->protocols[n];
break;
}
n++;
}
if (!wsi->a.vhost->protocols[n].callback) {
if (wsi->a.protocol)
lwsl_wsi_err(wsi, "Failed to match protocol %s",
wsi->a.protocol->name);
else
lwsl_wsi_err(wsi, "No protocol on client");
*cce = "ws protocol no match";
goto bail2;
}
}
lwsl_wsi_debug(wsi, "Selected protocol %s", wsi->a.protocol ?
wsi->a.protocol->name : "no pcol");
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_same_vh_protocol_insert(wsi, n);
#if !defined(LWS_WITHOUT_EXTENSIONS)
/* instantiate the accepted extensions */
if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
lwsl_wsi_ext(wsi, "no client extensions allowed by server");
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, (int)context->pt_serv_buf_size,
WSI_TOKEN_EXTENSIONS) < 0) {
lwsl_wsi_warn(wsi, "ext list from server failed to copy");
*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_wsi_notice(wsi, "checking client ext %s", ext_name);
n = 0;
ext = wsi->a.vhost->ws.extensions;
while (ext && ext->callback) {
if (strcmp(ext_name, ext->name)) {
ext++;
continue;
}
n = 1;
lwsl_wsi_notice(wsi, "instantiating client ext %s", 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_wsi_info(wsi, " ext %s failed construction",
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->a.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_wsi_err(wsi, "unable to parse user defaults '%s'",
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_wsi_err(wsi, "unable to parse remote def '%s'", 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_wsi_err(wsi, "ext %s rejects server options %s",
ext->name, a);
*cce = "HS: EXT: Rejects server options";
goto bail2;
}
wsi->ws->count_act_ext++;
ext++;
}
if (n == 0) {
lwsl_wsi_warn(wsi, "Unknown ext '%s'!", 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_wsi_warn(wsi, "lws_client_int_s_hs: accept '%s' wrong vs '%s'", 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_wsi_err(wsi, "Problem allocating wsi user mem");
*cce = "HS: OOM";
goto bail2;
}
/*
* we seem to be good to go, give client last chance to check
* headers and OK it
*/
if (wsi->a.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_validity_confirmed(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->a.protocol->rx_buffer_size;
if (!n)
n = (int)context->pt_serv_buf_size;
n += LWS_PRE;
wsi->ws->rx_ubuf = lws_malloc((unsigned int)n + 4 /* 0x0000ffff zlib */,
"client frame buffer");
if (!wsi->ws->rx_ubuf) {
lwsl_wsi_err(wsi, "OOM allocating rx buffer %d", n);
*cce = "HS: OOM";
goto bail2;
}
wsi->ws->rx_ubuf_alloc = (unsigned int)n;
lwsl_wsi_debug(wsi, "handshake OK for protocol %s", wsi->a.protocol->name);
/* call him back to inform him he is up */
if (wsi->a.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;
}

View File

@ -0,0 +1,559 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.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 = (int)wsi->a.context->pt_serv_buf_size;
if (wsi->a.protocol->rx_buffer_size)
n = (int)wsi->a.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] = (unsigned char)extra;
lwsl_wsi_info(wsi, " Capping pmd rx to %d", 1 << extra);
}
}
static unsigned char trail[] = { 0, 0, 0xff, 0xff };
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_ext_pm_deflate_rx_ebufs *pmdrx =
(struct lws_ext_pm_deflate_rx_ebufs *)in;
struct lws_ext_option_arg *oa;
int n, ret = 0, was_fin = 0, m;
unsigned int pen = 0;
int penbits = 0;
switch (reason) {
case LWS_EXT_CB_NAMED_OPTION_SET:
oa = in;
if (!oa->option_name)
break;
lwsl_wsi_ext(wsi, "named option set: %s", oa->option_name);
for (n = 0; n < (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options);
n++)
if (!strcmp(lws_ext_pm_deflate_options[n].name,
oa->option_name))
break;
if (n == (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options))
break;
oa->option_index = n;
/* fallthru */
case LWS_EXT_CB_OPTION_SET:
oa = in;
lwsl_wsi_ext(wsi, "option set: idx %d, %s, len %d",
oa->option_index, oa->start, oa->len);
if (oa->start)
priv->args[oa->option_index] = (unsigned char)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 = (int)context->pt_serv_buf_size;
if (wsi->a.protocol->rx_buffer_size)
n = (int)wsi->a.protocol->rx_buffer_size;
if (n < 128) {
lwsl_wsi_info(wsi, " permessage-deflate requires the protocol "
"(%s) to have an RX buffer >= 128",
wsi->a.protocol->name);
return -1;
}
/* fill in **user */
priv = lws_zalloc(sizeof(*priv), "pmd priv");
*((void **)user) = priv;
lwsl_wsi_ext(wsi, "LWS_EXT_CB_*CONSTRUCT");
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_wsi_ext(wsi, "LWS_EXT_CB_DESTROY");
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:
/*
* ie, we are INFLATING
*/
lwsl_wsi_ext(wsi, " LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d",
pmdrx->eb_in.len, priv->rx.avail_in);
/*
* If this frame is not marked as compressed,
* there is nothing we should do with it
*/
if (!(wsi->ws->rsv_first_msg & 0x40) || (wsi->ws->opcode & 8))
/*
* This is a bit different than DID_NOTHING... we have
* identified using ext-private bits in the packet, or
* by it being a control fragment that we SHOULD not do
* anything to it, parent should continue as if we
* processed it
*/
return PMDR_NOTHING_WE_SHOULD_DO;
/*
* we shouldn't come back in here if we already applied the
* trailer for this compressed packet
*/
if (!wsi->ws->pmd_trailer_application)
return PMDR_DID_NOTHING;
pmdrx->eb_out.len = 0;
lwsl_wsi_ext(wsi, "LWS_EXT_CB_PAYLOAD_RX: in %d, "
"existing avail in %d, pkt fin: %d",
pmdrx->eb_in.len, priv->rx.avail_in, wsi->ws->final);
/* if needed, initialize the inflator */
if (!priv->rx_init) {
if (inflateInit2(&priv->rx,
-priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
lwsl_wsi_err(wsi, "iniflateInit failed");
return PMDR_FAILED;
}
priv->rx_init = 1;
if (!priv->buf_rx_inflated)
priv->buf_rx_inflated = lws_malloc(
(unsigned int)(LWS_PRE + 7 + 5 +
(1 << priv->args[PMD_RX_BUF_PWR2])),
"pmd rx inflate buf");
if (!priv->buf_rx_inflated) {
lwsl_wsi_err(wsi, "OOM");
return PMDR_FAILED;
}
}
#if 0
/*
* don't give us new input while we still work through
* the last input
*/
if (priv->rx.avail_in && pmdrx->eb_in.token &&
pmdrx->eb_in.len) {
lwsl_wsi_warn(wsi, "priv->rx.avail_in %d while getting new in",
priv->rx.avail_in);
// assert(0);
}
#endif
if (!priv->rx.avail_in && pmdrx->eb_in.token && pmdrx->eb_in.len) {
priv->rx.next_in = (unsigned char *)pmdrx->eb_in.token;
priv->rx.avail_in = (uInt)pmdrx->eb_in.len;
}
priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
pmdrx->eb_out.token = priv->rx.next_out;
priv->rx.avail_out = (uInt)(1 << priv->args[PMD_RX_BUF_PWR2]);
/* so... 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->ws->final &&
!wsi->ws->rx_packet_length &&
wsi->ws->pmd_trailer_application) {
lwsl_wsi_ext(wsi, "trailer apply 1");
was_fin = 1;
wsi->ws->pmd_trailer_application = 0;
priv->rx.next_in = trail;
priv->rx.avail_in = sizeof(trail);
}
/*
* if after all that there's nothing pending and nothing to give
* him right now, bail without having done anything
*/
if (!priv->rx.avail_in)
return PMDR_DID_NOTHING;
n = inflate(&priv->rx, was_fin ? Z_SYNC_FLUSH : Z_NO_FLUSH);
lwsl_wsi_ext(wsi, "inflate ret %d, avi %d, avo %d, wsifinal %d", n,
priv->rx.avail_in, priv->rx.avail_out, wsi->ws->final);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
lwsl_wsi_err(wsi, "zlib error inflate %d: \"%s\"",
n, priv->rx.msg);
return PMDR_FAILED;
}
/*
* track how much input was used, and advance it
*/
pmdrx->eb_in.token = pmdrx->eb_in.token +
((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->rx.avail_in);
pmdrx->eb_in.len = (int)priv->rx.avail_in;
lwsl_wsi_debug(wsi, "%d %d %d %d %d",
priv->rx.avail_in,
wsi->ws->final,
(int)wsi->ws->rx_packet_length,
was_fin,
wsi->ws->pmd_trailer_application);
if (!priv->rx.avail_in &&
wsi->ws->final &&
!wsi->ws->rx_packet_length &&
!was_fin &&
wsi->ws->pmd_trailer_application) {
lwsl_wsi_ext(wsi, "RX trailer apply 2");
/* we overallocated just for this situation where
* we might issue something */
priv->rx.avail_out += 5;
was_fin = 1;
wsi->ws->pmd_trailer_application = 0;
priv->rx.next_in = trail;
priv->rx.avail_in = sizeof(trail);
n = inflate(&priv->rx, Z_SYNC_FLUSH);
lwsl_wsi_ext(wsi, "RX trailer infl ret %d, avi %d, avo %d",
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_wsi_info(wsi, "zlib error inflate %d: %s",
n, priv->rx.msg);
return -1;
}
assert(priv->rx.avail_out);
}
pmdrx->eb_out.len = lws_ptr_diff(priv->rx.next_out,
pmdrx->eb_out.token);
priv->count_rx_between_fin = priv->count_rx_between_fin + (size_t)pmdrx->eb_out.len;
lwsl_wsi_ext(wsi, " RX leaving with new effbuff len %d, "
"rx.avail_in=%d, TOTAL RX since FIN %lu",
pmdrx->eb_out.len, priv->rx.avail_in,
(unsigned long)priv->count_rx_between_fin);
if (was_fin) {
lwsl_wsi_ext(wsi, "was_fin");
priv->count_rx_between_fin = 0;
if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
lwsl_wsi_ext(wsi, "PMD_SERVER_NO_CONTEXT_TAKEOVER");
(void)inflateEnd(&priv->rx);
priv->rx_init = 0;
}
return PMDR_EMPTY_FINAL;
}
if (priv->rx.avail_in)
return PMDR_HAS_PENDING;
return PMDR_EMPTY_NONFINAL;
case LWS_EXT_CB_PAYLOAD_TX:
/*
* ie, we are DEFLATING
*
* initialize us if needed
*/
if (!priv->tx_init) {
n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
Z_DEFLATED,
-priv->args[PMD_SERVER_MAX_WINDOW_BITS +
(wsi->a.vhost->listen_port <= 0)],
priv->args[PMD_MEM_LEVEL],
Z_DEFAULT_STRATEGY);
if (n != Z_OK) {
lwsl_wsi_ext(wsi, "inflateInit2 failed %d", n);
return PMDR_FAILED;
}
priv->tx_init = 1;
}
if (!priv->buf_tx_deflated)
priv->buf_tx_deflated = lws_malloc((unsigned int)(LWS_PRE + 7 + 5 +
(1 << priv->args[PMD_TX_BUF_PWR2])),
"pmd tx deflate buf");
if (!priv->buf_tx_deflated) {
lwsl_wsi_err(wsi, "OOM");
return PMDR_FAILED;
}
/* hook us up with any deflated input that the caller has */
if (pmdrx->eb_in.token) {
assert(!priv->tx.avail_in);
priv->count_tx_between_fin = priv->count_tx_between_fin + (size_t)pmdrx->eb_in.len;
lwsl_wsi_ext(wsi, "TX: eb_in length %d, "
"TOTAL TX since FIN: %d",
pmdrx->eb_in.len,
(int)priv->count_tx_between_fin);
priv->tx.next_in = (unsigned char *)pmdrx->eb_in.token;
priv->tx.avail_in = (uInt)pmdrx->eb_in.len;
}
priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
pmdrx->eb_out.token = priv->tx.next_out;
priv->tx.avail_out = (uInt)(1 << priv->args[PMD_TX_BUF_PWR2]);
pen = 0;
penbits = 0;
deflatePending(&priv->tx, &pen, &penbits);
pen = pen | (unsigned int)penbits;
if (!priv->tx.avail_in && (len & LWS_WRITE_NO_FIN)) {
lwsl_wsi_ext(wsi, "no available in, pen: %u", pen);
if (!pen)
return PMDR_DID_NOTHING;
}
m = Z_NO_FLUSH;
if (!(len & LWS_WRITE_NO_FIN)) {
lwsl_wsi_ext(wsi, "deflate with SYNC_FLUSH, pkt len %d",
(int)wsi->ws->rx_packet_length);
m = Z_SYNC_FLUSH;
}
n = deflate(&priv->tx, m);
if (n == Z_STREAM_ERROR) {
lwsl_wsi_notice(wsi, "Z_STREAM_ERROR");
return PMDR_FAILED;
}
pen = (!priv->tx.avail_out) && n != Z_STREAM_END;
lwsl_wsi_ext(wsi, "deflate ret %d, len 0x%x", n,
(unsigned int)len);
if ((len & 0xf) == LWS_WRITE_TEXT)
priv->tx_first_frame_type = LWSWSOPC_TEXT_FRAME;
if ((len & 0xf) == LWS_WRITE_BINARY)
priv->tx_first_frame_type = LWSWSOPC_BINARY_FRAME;
pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
pmdrx->eb_out.token);
if (m == Z_SYNC_FLUSH && !(len & LWS_WRITE_NO_FIN) && !pen &&
pmdrx->eb_out.len < 4) {
lwsl_wsi_err(wsi, "FAIL want to trim out length %d",
(int)pmdrx->eb_out.len);
assert(0);
}
if (!(len & LWS_WRITE_NO_FIN) &&
m == Z_SYNC_FLUSH &&
!pen &&
pmdrx->eb_out.len >= 4) {
// lwsl_wsi_err(wsi, "Trimming 4 from end of write");
priv->tx.next_out -= 4;
priv->tx.avail_out += 4;
priv->count_tx_between_fin = 0;
assert(priv->tx.next_out[0] == 0x00 &&
priv->tx.next_out[1] == 0x00 &&
priv->tx.next_out[2] == 0xff &&
priv->tx.next_out[3] == 0xff);
}
/*
* track how much input was used and advance it
*/
pmdrx->eb_in.token = pmdrx->eb_in.token +
((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->tx.avail_in);
pmdrx->eb_in.len = (int)priv->tx.avail_in;
priv->compressed_out = 1;
pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
pmdrx->eb_out.token);
lwsl_wsi_ext(wsi, " TX rewritten with new eb_in len %d, "
"eb_out len %d, deflatePending %d",
pmdrx->eb_in.len, pmdrx->eb_out.len, pen);
if (pmdrx->eb_in.len || pen)
return PMDR_HAS_PENDING;
if (!(len & LWS_WRITE_NO_FIN))
return PMDR_EMPTY_FINAL;
return PMDR_EMPTY_NONFINAL;
case LWS_EXT_CB_PACKET_TX_PRESEND:
if (!priv->compressed_out)
break;
priv->compressed_out = 0;
/*
* we may have not produced any output for the actual "first"
* write... in that case, we need to fix up the inappropriate
* use of CONTINUATION when the first real write does come.
*/
if (priv->tx_first_frame_type & 0xf) {
*pmdrx->eb_in.token = (unsigned char)((((unsigned char)*pmdrx->eb_in.token) & (unsigned char)~0xf) |
((unsigned char)priv->tx_first_frame_type & (unsigned char)0xf));
/*
* We have now written the "first" fragment, only
* do that once
*/
priv->tx_first_frame_type = 0;
}
n = *(pmdrx->eb_in.token) & 15;
/* set RSV1, but not on CONTINUATION */
if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
*pmdrx->eb_in.token |= 0x40;
lwsl_wsi_ext(wsi, "PRESEND compressed: ws frame 0x%02X, len %d",
((*pmdrx->eb_in.token) & 0xff),
pmdrx->eb_in.len);
if (((*pmdrx->eb_in.token) & 0x80) && /* fin */
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
lwsl_wsi_debug(wsi, "PMD_CLIENT_NO_CONTEXT_TAKEOVER");
(void)deflateEnd(&priv->tx);
priv->tx_init = 0;
}
break;
default:
break;
}
return 0;
}

View File

@ -0,0 +1,69 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if defined(LWS_WITH_MINIZ)
#include <miniz.h>
#else
#include <zlib.h>
#endif
#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 */
unsigned char *buf_tx_holding;
size_t count_rx_between_fin;
size_t count_tx_between_fin;
size_t len_tx_holding;
unsigned char args[PMD_ARG_COUNT];
unsigned char tx_first_frame_type;
unsigned char tx_init:1;
unsigned char rx_init:1;
unsigned char compressed_out:1;
};

View File

@ -0,0 +1,382 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "extension-permessage-deflate.h"
void
lws_context_init_extensions(const struct lws_context_creation_info *info,
struct lws_context *context)
{
lwsl_cx_info(context, " LWS_MAX_EXTENSIONS_ACTIVE: %u", 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
};
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_wsi_ext(wsi, "'%c' %d", *in, leap);
switch (leap) {
case LEAPS_SEEK_NAME:
if (*in == ' ')
break;
if (*in == ',') {
len = 1;
break;
}
match_map = (unsigned int)(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)) {
m >>= 1;
n++;
continue;
}
lwsl_wsi_ext(wsi, " m=%d, n=%d, w=%d", m, n, w);
if (*in == opts[n].name[w]) {
if (!opts[n].name[w + 1]) {
oa.option_index = (int)n;
lwsl_wsi_ext(wsi, "hit %d",
oa.option_index);
leap = LEAPS_SEEK_VAL;
if (len == 1)
goto set_arg;
break;
}
} else {
match_map &= (unsigned int)~(1 << n);
if (!match_map) {
lwsl_wsi_ext(wsi, "empty match map");
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 = lws_ptr_diff(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;
if (!wsi->ws)
return 0;
for (n = 0; n < wsi->ws->count_act_ext; n++) {
m = wsi->ws->active_extensions[n]->callback(
lws_get_context(wsi), wsi->ws->active_extensions[n],
wsi, (enum lws_extension_callback_reasons)reason, wsi->ws->act_ext_user[n], arg, (size_t)len);
if (m < 0) {
lwsl_wsi_ext(wsi, "Ext '%s' failed to handle callback %d!",
wsi->ws->active_extensions[n]->name, reason);
return -1;
}
/* valgrind... */
if (reason == LWS_EXT_CB_DESTROY)
wsi->ws->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->a.vhost || !wsi->ws)
return 0;
ext = wsi->a.vhost->ws.extensions;
while (ext && ext->callback && !handled) {
m = ext->callback(context, ext, wsi, (enum lws_extension_callback_reasons)reason,
(void *)(lws_intptr_t)n, arg, (size_t)len);
if (m < 0) {
lwsl_wsi_ext(wsi, "Ext '%s' failed to handle callback %d!",
wsi->ws->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 ebuf;
int ret, m, n = 0;
ebuf.token = buf;
ebuf.len = (int)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,
&ebuf, 0);
if (m < 0)
return -1;
if (m) /* handled */
ret = 1;
if (buf != ebuf.token)
/*
* extension recreated it:
* need to buffer this if not all sent
*/
wsi->ws->clean_buffer = 0;
/* assuming they left us something to send, send it */
if (ebuf.len) {
n = lws_issue_raw(wsi, ebuf.token, (size_t)ebuf.len);
if (n < 0) {
lwsl_wsi_info(wsi, "closing from ext access");
return -1;
}
/* always either sent it all or privately buffered */
if (wsi->ws->clean_buffer)
len = (size_t)n;
lwsl_wsi_ext(wsi, "written %d bytes to client", n);
}
/* no extension has more to spill? Then we can go */
if (!ret)
break;
/* we used up what we had */
ebuf.token = NULL;
ebuf.len = 0;
/*
* Did that leave the pipe choked?
* Or we had to hold on to some of it?
*/
if (!lws_send_pipe_choked(wsi) && !lws_has_buffered_out(wsi))
/* no we could add more, lets's do that */
continue;
lwsl_wsi_debug(wsi, "choked");
/*
* 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->ws->extension_data_pending = 1;
ret = 0;
}
return (int)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->a.context;
int n, handled = 0;
if (!wsi->ws)
return 0;
/* maybe an extension will take care of it for us */
for (n = 0; n < wsi->ws->count_act_ext && !handled; n++) {
if (!wsi->ws->active_extensions[n]->callback)
continue;
handled |= wsi->ws->active_extensions[n]->callback(context,
wsi->ws->active_extensions[n], wsi,
r, wsi->ws->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;
if (!wsi->ws)
return 0;
/* first identify if the ext is active on this wsi */
while (idx < wsi->ws->count_act_ext &&
strcmp(wsi->ws->active_extensions[idx]->name, ext_name))
idx++;
if (idx == wsi->ws->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->ws->active_extensions[idx]->callback(wsi->a.context,
wsi->ws->active_extensions[idx], wsi,
LWS_EXT_CB_NAMED_OPTION_SET, wsi->ws->act_ext_user[idx],
&oa, 0);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,202 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is included from private-lib-core.h if LWS_ROLE_WS
*/
extern const 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
#define PAYLOAD_BUF_SIZE 128 - 3 + LWS_PRE
struct _lws_websocket_related {
unsigned 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
#if defined(LWS_WITH_HTTP_PROXY)
struct lws_dll2_owner proxy_owner;
char actual_protocol[16];
size_t proxy_buffered;
#endif
/* Also used for close content... control opcode == < 128 */
uint8_t ping_payload_buf[PAYLOAD_BUF_SIZE];
uint8_t pong_payload_buf[PAYLOAD_BUF_SIZE];
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 pong_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;
unsigned int pmd_trailer_application:1;
#endif
uint8_t mask[4];
size_t rx_packet_length;
uint32_t rx_ubuf_head;
uint32_t rx_ubuf_alloc;
uint8_t pong_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;
uint8_t allow_reserved_bits;
uint8_t allow_unknown_opcode;
#if !defined(LWS_WITHOUT_EXTENSIONS)
uint8_t count_act_ext;
#endif
};
/*
* we need to separately track what's happening with both compressed rx in
* and with inflated rx out that will be passed to the user code
*/
struct lws_ext_pm_deflate_rx_ebufs {
struct lws_tokens eb_in;
struct lws_tokens eb_out;
};
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);
LWS_EXTERN int
lws_extension_server_handshake(struct lws *wsi, char **p, int budget);
#endif
int
handshake_0405(struct lws_context *context, struct lws *wsi);
int
lws_process_ws_upgrade(struct lws *wsi);
int
lws_process_ws_upgrade2(struct lws *wsi);
extern const struct lws_protocols lws_ws_proxy;
int
lws_server_init_wsi_for_ws(struct lws *wsi);
void
lws_sul_wsping_cb(lws_sorted_usec_list_t *sul);

File diff suppressed because it is too large Load Diff